Refactor: Extract IBinaryDiffer and ICompressionProvider abstractions (#258)#263
Merged
Merged
Conversation
- Add IBinaryDiffer interface with CleanAsync/DirtyAsync + CancellationToken - Add ICompressionProvider interface for pluggable compression - Add BZip2CompressionProvider (backward-compatible, format version byte 0x00) - Refactor BinaryHandler to implement IBinaryDiffer - Refactor DefaultCleanStrategy/DefaultDirtyStrategy to accept optional IBinaryDiffer - Add DifferentialCore overloads accepting IBinaryDiffer Part of: #258
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extracts new core abstractions from GeneralUpdate.Differential to support pluggable binary diff algorithms (IBinaryDiffer) and compression providers (ICompressionProvider), and wires the default clean/dirty strategies to optionally use an injected differ.
Changes:
- Added
IBinaryDifferandICompressionProviderabstractions plus aBZip2CompressionProviderimplementation. - Refactored
BinaryHandlerto implementIBinaryDiffer. - Added
DifferentialCore.Clean/Dirtyoverloads intended to accept a customIBinaryDifferandCancellationToken, and updated default strategies to optionally use a provided differ.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/c#/GeneralUpdate.Differential/Matchers/DefaultDirtyStrategy.cs | Allows injecting an IBinaryDiffer for patch application. |
| src/c#/GeneralUpdate.Differential/Matchers/DefaultCleanStrategy.cs | Allows injecting an IBinaryDiffer for patch generation. |
| src/c#/GeneralUpdate.Differential/DifferentialCore.cs | Adds overloads intended for custom differs and cancellation support. |
| src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs | Implements IBinaryDiffer and refactors internals (incl. int64 encode/decode). |
| src/c#/GeneralUpdate.Differential/Abstractions/ICompressionProvider.cs | Introduces a compression-provider abstraction with a format version byte. |
| src/c#/GeneralUpdate.Differential/Abstractions/IBinaryDiffer.cs | Introduces a binary-differ abstraction with async + cancellation signatures. |
| src/c#/GeneralUpdate.Differential/Abstractions/BZip2CompressionProvider.cs | Adds a legacy/compat BZip2 compression provider implementation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public Stream CreateDecompressStream(Stream input, CancellationToken cancellationToken = default) | ||
| { | ||
| // BZip2InputStream is the existing managed BZip2 decompressor. | ||
| return new BZip2InputStream(input, false); |
| /// <summary> | ||
| /// Defines a pluggable binary differential algorithm (diff generation and patch application). | ||
| /// Implementations may use different strategies: BSDIFF, HDiffPatch-style, VCDIFF, etc. | ||
| /// All methods are stateless 鈥?callers pass file paths and instances are safe for concurrent use. |
Comment on lines
18
to
+36
| private const long c_fileSignature = 0x3034464649445342L; | ||
| private const int c_headerSize = 32; | ||
| private string _oldfilePath, _newfilePath, _patchPath; | ||
|
|
||
| #endregion Private Members | ||
|
|
||
| #region IBinaryDiffer Implementation | ||
|
|
||
| /// <inheritdoc/> | ||
| public Task CleanAsync(string oldFilePath, string newFilePath, string patchFilePath, CancellationToken cancellationToken = default) | ||
| { | ||
| return Clean(oldFilePath, newFilePath, patchFilePath); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public Task DirtyAsync(string oldFilePath, string newFilePath, string patchFilePath, CancellationToken cancellationToken = default) | ||
| { | ||
| return Dirty(oldFilePath, newFilePath, patchFilePath); | ||
| } |
Comment on lines
+619
to
642
| private static void WriteInt64(long value, byte[] buf, int offset) | ||
| { | ||
| int temp = first; | ||
| first = second; | ||
| second = temp; | ||
| buf[offset + 0] = (byte)(value & 0xFF); | ||
| buf[offset + 1] = (byte)((value >> 8) & 0xFF); | ||
| buf[offset + 2] = (byte)((value >> 16) & 0xFF); | ||
| buf[offset + 3] = (byte)((value >> 24) & 0xFF); | ||
| buf[offset + 4] = (byte)((value >> 32) & 0xFF); | ||
| buf[offset + 5] = (byte)((value >> 40) & 0xFF); | ||
| buf[offset + 6] = (byte)((value >> 48) & 0xFF); | ||
| buf[offset + 7] = (byte)((value >> 56) & 0xFF); | ||
| } | ||
|
|
||
| private long ReadInt64(byte[] buf, int offset) | ||
| private static long ReadInt64(byte[] buf, int offset) | ||
| { | ||
| long value = buf[offset + 7] & 0x7F; | ||
| for (int index = 6; index >= 0; index--) | ||
| { | ||
| value *= 256; | ||
| value += buf[offset + index]; | ||
| } | ||
| if ((buf[offset + 7] & 0x80) != 0) value = -value; | ||
| return value; | ||
| return (long)( | ||
| ((ulong)buf[offset + 0]) | | ||
| ((ulong)buf[offset + 1] << 8) | | ||
| ((ulong)buf[offset + 2] << 16) | | ||
| ((ulong)buf[offset + 3] << 24) | | ||
| ((ulong)buf[offset + 4] << 32) | | ||
| ((ulong)buf[offset + 5] << 40) | | ||
| ((ulong)buf[offset + 6] << 48) | | ||
| ((ulong)buf[offset + 7] << 56)); | ||
| } |
Comment on lines
+28
to
+34
| { | ||
| return Clean(oldFilePath, newFilePath, patchFilePath); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| public Task DirtyAsync(string oldFilePath, string newFilePath, string patchFilePath, CancellationToken cancellationToken = default) | ||
| { |
Comment on lines
+1
to
5
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using GeneralUpdate.Differential.Abstractions; | ||
| using GeneralUpdate.Differential.Binary; | ||
| using GeneralUpdate.Differential.Matchers; |
Comment on lines
+43
to
+64
| /// <summary> | ||
| /// Compares <paramref name="sourcePath"/> with <paramref name="targetPath"/> and | ||
| /// writes patch artifacts to <paramref name="patchPath"/> using the specified binary differ. | ||
| /// </summary> | ||
| /// <param name="sourcePath">The source (old-version) directory.</param> | ||
| /// <param name="targetPath">The target (new-version) directory.</param> | ||
| /// <param name="patchPath">Output patch directory.</param> | ||
| /// <param name="binaryDiffer">The binary differ algorithm to use.</param> | ||
| /// <param name="strategy">Optional strategy override.</param> | ||
| /// <param name="cancellationToken">Optional cancellation token.</param> | ||
| public static Task Clean( | ||
| string sourcePath, | ||
| string targetPath, | ||
| string patchPath, | ||
| IBinaryDiffer binaryDiffer, | ||
| ICleanStrategy? strategy = null, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| var cleanMatcher = new DefaultCleanMatcher(); | ||
| var usedStrategy = strategy ?? new DefaultCleanStrategy(cleanMatcher, binaryDiffer); | ||
| return usedStrategy.ExecuteAsync(sourcePath, targetPath, patchPath); | ||
| } |
| Path.GetDirectoryName(appFilePath)!, | ||
| $"{Path.GetRandomFileName()}_{Path.GetFileName(appFilePath)}"); | ||
| await new BinaryHandler().Dirty(appFilePath, newPath, patchFilePath); | ||
| await _binaryDiffer.DirtyAsync(appFilePath, newPath, patchFilePath); |
Comment on lines
+43
to
+60
| /// <summary> | ||
| /// Compares <paramref name="sourcePath"/> with <paramref name="targetPath"/> and | ||
| /// writes patch artifacts to <paramref name="patchPath"/> using the specified binary differ. | ||
| /// </summary> | ||
| /// <param name="sourcePath">The source (old-version) directory.</param> | ||
| /// <param name="targetPath">The target (new-version) directory.</param> | ||
| /// <param name="patchPath">Output patch directory.</param> | ||
| /// <param name="binaryDiffer">The binary differ algorithm to use.</param> | ||
| /// <param name="strategy">Optional strategy override.</param> | ||
| /// <param name="cancellationToken">Optional cancellation token.</param> | ||
| public static Task Clean( | ||
| string sourcePath, | ||
| string targetPath, | ||
| string patchPath, | ||
| IBinaryDiffer binaryDiffer, | ||
| ICleanStrategy? strategy = null, | ||
| CancellationToken cancellationToken = default) | ||
| { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #258
Summary
Extract core abstractions from GeneralUpdate.Differential to enable pluggable binary diff algorithms and compression providers.
Changes
New Files
Modified Files
Backward Compatibility
Next Steps
This PR is Part 1 of 5 in the Differential v2 refactor series: