| sidebar_position | 6 |
|---|
Namespace: GeneralUpdate.Differential | Main Entry Points: IBinaryDiffer, BsdiffDiffer, StreamingHdiffDiffer | NuGet Package: GeneralUpdate.Differential
GeneralUpdate.Differential is the binary differential component of GeneralUpdate, focused on solving "one old file + one patch file = one new file". It provides pluggable file-level diff algorithms (BSDIFF 4.0 / Streaming HDiff), patch compression abstractions (BZip2 / Deflate / Brotli reserved), and BSDIFF-compatible patch read/write capabilities.
Directory-level comparison, batch patch generation, parallel scheduling, deleted file handling, and update workflow orchestration are handled by GeneralUpdate.Core's DiffPipeline or GeneralUpdate.Tools.
Core Capabilities:
| Capability | Description |
|---|---|
| File-Level Diff Generation | CleanAsync(oldFile, newFile, patchFile) — compare old & new files to generate .patch |
| File-Level Diff Application | DirtyAsync(oldFile, newFile, patchFile) — old file + patch → new file |
| Pluggable Diff Algorithms | BsdiffDiffer (BSDIFF 4.0, suffix sort) and StreamingHdiffDiffer (block hash indexing) |
| Pluggable Compression | BZip2 (0x00), Deflate (0x01), .NET 6+ Brotli (0x02) available via conditional compilation |
| BSDIFF Compatible Format | 33-byte extended header (32-byte BSDIFF40 + 1-byte compression format), 32-byte legacy compatible |
| Thread Safety | Built-in differ and compression providers support concurrent calls |
Business Problems Solved:
- Full updates have high bandwidth costs; differential updates can reduce packages from GB to MB or KB
- Different file types and change patterns need different diff strategies
- Compression algorithm choice affects client decompression speed vs patch size trade-off
Use Cases:
- Incremental updates for large desktop apps (multiple DLLs, resource files)
- Binary differential distribution for firmware/driver packages
- Game resource hot updates
- Automated incremental patch generation in CI/CD pipelines
| Item | Description |
|---|---|
| Version | 10.5.0-beta.2 |
| Target Framework | netstandard2.0 (.NET Framework 4.6.1+ / .NET Core 2.0+ / .NET 5+) |
| Dependencies | None (pure .NET BCL) |
| Compatibility | All .NET Standard 2.0 platforms |
| Feature | Description | Type | Required | Notes |
|---|---|---|---|---|
| BSDIFF 4.0 Diff Generation | Classic suffix-sort diff algorithm with stable patch sizes | Core | Optional | BsdiffDiffer, default BZip2 |
| BSDIFF 4.0 Patch Application | Apply BSDIFF format patches to old files | Core | Optional | Supports both 32/33 byte headers |
| Streaming HDiff Generation | FNV-1a block hash-based fast diff | Core | Optional | StreamingHdiffDiffer, default Deflate |
| BZip2 Compression | BZip2 compression for patch segments | Core | Optional | Format byte 0x00, BsdiffDiffer default |
| Deflate Compression | Deflate compression for patch segments, faster decompression | Core | Optional | Format byte 0x01, StreamingHdiffDiffer default |
| Custom Diff Algorithm | Implement IBinaryDiffer for proprietary algorithms |
Extended | Optional | Must ensure Clean/Dirty consistency |
| Custom Compression Provider | Implement ICompressionProvider to swap compression |
Extended | Optional | New format bytes require patch reader extension |
Differential is a low-level library with no configuration classes. All parameters are passed via constructors.
BsdiffDiffer Constructor Parameters:
| Parameter | Type | Default | Required | Values | Description |
|---|---|---|---|---|---|
compressionProvider |
ICompressionProvider |
BZip2CompressionProvider |
Optional | BZip2CompressionProvider / DeflateCompressionProvider |
Patch compression provider |
StreamingHdiffDiffer Constructor Parameters:
| Parameter | Type | Default | Required | Values | Description |
|---|---|---|---|---|---|
compressionProvider |
ICompressionProvider |
DeflateCompressionProvider |
Optional | BZip2CompressionProvider / DeflateCompressionProvider |
Patch compression provider |
blockSize |
int |
65536 (64 KB) |
Optional | Positive integer bytes | Block size for old file hash indexing |
maxWindowSize |
int |
134217728 (128 MB) |
Optional | Positive integer bytes | Max memory window for computation |
ICompressionProvider Format Identifiers:
| Provider | Format Byte | Availability | Description |
|---|---|---|---|
BZip2CompressionProvider |
0x00 |
Fully available | Legacy BSDIFF compatible, higher decompression cost |
DeflateCompressionProvider |
0x01 |
Fully available | Faster decompression, better for client batch apply |
BrotliCompressionProvider |
0x02 |
.NET 6+ only (full implementation in source behind #if NET6_0_OR_GREATER conditional compilation) |
Not included in current netstandard2.0 package; not recommended for production |
IBinaryDiffer:
| Method | Parameters | Returns | Use Case | Notes |
|---|---|---|---|---|
CleanAsync(string, string, string, CancellationToken) |
oldFilePath, newFilePath, patchFilePath, cancellationToken |
Task |
Generate patch during build/release | Large file cancellation not immediate |
DirtyAsync(string, string, string, CancellationToken) |
oldFilePath, newFilePath (restored output), patchFilePath, cancellationToken |
Task |
Apply patch during client upgrade | Result written to newFilePath, not overwriting old file |
Differential does not publish events. Progress reporting and event notifications are implemented by Core's DiffPipeline via DiffProgress and EventManager.
| Extension Interface | Description |
|---|---|
IBinaryDiffer |
Custom file-level diff algorithm; can integrate native libraries or proprietary algorithms |
ICompressionProvider |
Custom patch segment compression |
Description: Integrate an in-house high-compression-ratio diff algorithm.
using GeneralUpdate.Differential.Abstractions;
public sealed class HighRatioDiffer : IBinaryDiffer
{
public Task CleanAsync(
string oldFilePath, string newFilePath, string patchFilePath,
CancellationToken cancellationToken = default)
{
// Call proprietary algorithm to generate patch
return Task.CompletedTask;
}
public Task DirtyAsync(
string oldFilePath, string newFilePath, string patchFilePath,
CancellationToken cancellationToken = default)
{
// Call proprietary algorithm to apply patch
return Task.CompletedTask;
}
}
// Use in Core DiffPipeline
var pipeline = new DiffPipelineBuilder()
.UseDiffer(new HighRatioDiffer())
.WithParallelism(4)
.Build();Description: Use BsdiffDiffer's precise matching with Deflate's fast decompression for better client-side patch application speed.
using GeneralUpdate.Differential.Abstractions;
using GeneralUpdate.Differential.Differ;
var differ = new BsdiffDiffer(
new DeflateCompressionProvider(optimalLevel: false));
await differ.CleanAsync(oldFile, newFile, patchFile);
await differ.DirtyAsync(oldFile, outputFile, patchFile);Description: Large single files (200MB+) need adjusted window budget to avoid OOM.
var differ = new StreamingHdiffDiffer(
compressionProvider: new DeflateCompressionProvider(optimalLevel: true),
blockSize: 32 * 1024, // 32 KB blocks for denser hash indexing
maxWindowSize: 256 * 1024 * 1024); // 256 MB for large files
await differ.CleanAsync(oldLargeFile, newLargeFile, patchFile);using GeneralUpdate.Differential.Abstractions;
using GeneralUpdate.Differential.Differ;
IBinaryDiffer differ = new BsdiffDiffer();
var oldFile = @"D:\releases\1.0.0\app.dll";
var newFile = @"D:\releases\1.0.1\app.dll";
var patchFile = @"D:\patches\app.dll.patch";
var outputFile = @"D:\restore\app.dll";
// Generate patch: oldFile + newFile → patchFile
await differ.CleanAsync(oldFile, newFile, patchFile);
// Apply patch: oldFile + patchFile → outputFile
await differ.DirtyAsync(oldFile, outputFile, patchFile);// Option A: Classic BSDIFF + BZip2 → smallest patch size
var differA = new BsdiffDiffer();
// Option B: Classic BSDIFF + Deflate → small patch + faster apply
var differB = new BsdiffDiffer(new DeflateCompressionProvider(optimalLevel: true));
// Option C: Streaming HDiff + Deflate → fast generation + fastest apply
var differC = new StreamingHdiffDiffer(
new DeflateCompressionProvider(optimalLevel: true),
blockSize: 64 * 1024,
maxWindowSize: 128 * 1024 * 1024);Most scenarios use Differential indirectly through Core's DiffPipeline:
using GeneralUpdate.Core;
using GeneralUpdate.Core.Pipeline;
using GeneralUpdate.Core.Models;
using GeneralUpdate.Differential.Differ;
// Build side: compare old/new version directories, generate patches
var pipeline = new DiffPipelineBuilder()
.UseDiffer(new StreamingHdiffDiffer())
.WithParallelism(8)
.WithProgress(new Progress<DiffProgress>(p =>
Console.WriteLine($"[Build] {p.Completed}/{p.Total} {p.CurrentFile}")))
.Build();
await pipeline.CleanAsync(@"D:\builds\v1.0.0", @"D:\builds\v1.0.1", @"D:\patches\v1.0.0-to-v1.0.1");
// Client side: via GeneralUpdateBootstrap
await new GeneralUpdateBootstrap()
.SetSource(updateUrl: "https://update.mycompany.com/api/upgrade/verification", appSecretKey: "prod-key")
.SetOption(Option.AppType, AppType.Client)
.SetOption(Option.PatchEnabled, true)
.UseDiffPipeline(builder => builder
.UseDiffer(new StreamingHdiffDiffer())
.WithParallelism(4))
.LaunchAsync();| Term | Method | Input | Output | Typical Location |
|---|---|---|---|---|
| Clean | CleanAsync |
Old file, new file, patch output path | .patch file |
Build/release phase |
| Dirty | DirtyAsync |
Old file, output new file path, patch path | Restored new file | Client upgrade phase |
| Dimension | BsdiffDiffer |
StreamingHdiffDiffer |
|---|---|---|
| Core Approach | Classic BSDIFF 4.0, suffix sort + longest match | FNV-1a block hash indexing + byte-level extension |
| Default Compression | BZip2 (0x00) | Deflate (0x01) |
| Patch Application | Self-implemented BSDIFF Dirty | Delegates to BsdiffDiffer (BSDIFF compatible) |
| Patch Size | More stable, typically smaller | Sensitive to change distribution |
| Client Apply Speed | BZip2 decompression slower | Deflate decompression faster (~1.5-5x) |
| Generation Memory | Full read of old & new files | Budgeted by maxWindowSize |
| Compatibility | Compatible with legacy BSDIFF/BZip2 patches | Better for new projects |
| Scenario | Recommendation |
|---|---|
| Patch size priority | BsdiffDiffer + BZip2 |
| Client apply speed priority | StreamingHdiffDiffer (default Deflate) |
| Legacy patch format compatibility | BsdiffDiffer (32-byte legacy header auto-treated as BZip2) |
| Large files (>500MB) | Benchmark first; may need StreamingHdiffDiffer + larger maxWindowSize |
| Directory-level batch diff | Via Core DiffPipeline with WithParallelism |
| New project | Run baseline with defaults, adjust based on size/speed needs |