Skip to content

Refactor: Extract IBinaryDiffer and ICompressionProvider abstractions (#258)#263

Merged
JusterZhu merged 1 commit into
masterfrom
refactor/differential-abstractions
May 23, 2026
Merged

Refactor: Extract IBinaryDiffer and ICompressionProvider abstractions (#258)#263
JusterZhu merged 1 commit into
masterfrom
refactor/differential-abstractions

Conversation

@JusterZhu
Copy link
Copy Markdown
Collaborator

Closes #258

Summary

Extract core abstractions from GeneralUpdate.Differential to enable pluggable binary diff algorithms and compression providers.

Changes

New Files

  • \Abstractions/IBinaryDiffer.cs\ — pluggable binary differ interface with \CleanAsync/\DirtyAsync\ + \CancellationToken\
  • \Abstractions/ICompressionProvider.cs\ — pluggable compression interface with format version byte
  • \Abstractions/BZip2CompressionProvider.cs\ — backward-compatible BZip2 provider (format version 0x00)

Modified Files

  • \Binary/BinaryHandler.cs\ — now implements \IBinaryDiffer, adds cancellation support
  • \DifferentialCore.cs\ — new overloads accepting \IBinaryDiffer\ + \CancellationToken\
  • \Matchers/DefaultCleanStrategy.cs\ — optional \IBinaryDiffer\ constructor parameter
  • \Matchers/DefaultDirtyStrategy.cs\ — optional \IBinaryDiffer\ constructor parameter

Backward Compatibility

  • All existing public APIs remain unchanged
  • Default behavior uses BSDIFF + BZip2, same as before
  • New overloads are additive only
  • No breaking changes

Next Steps

This PR is Part 1 of 5 in the Differential v2 refactor series:

- 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
Copilot AI review requested due to automatic review settings May 23, 2026 05:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 IBinaryDiffer and ICompressionProvider abstractions plus a BZip2CompressionProvider implementation.
  • Refactored BinaryHandler to implement IBinaryDiffer.
  • Added DifferentialCore.Clean/Dirty overloads intended to accept a custom IBinaryDiffer and CancellationToken, 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)
{
@JusterZhu JusterZhu merged commit 1150a39 into master May 23, 2026
1 check passed
@JusterZhu JusterZhu deleted the refactor/differential-abstractions branch May 23, 2026 06:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor Differential: Abstract interfaces for pluggable differs and compression

2 participants