xdelta3 in-memory encoding/decoding in .NET (C#)
18 Mar 2015I’ve recently faced a problem of using xdelta3 for creating binary diffs of in-memory data in my C# code. I couldn’t really find any wrapper/PInvoke code on the web for xdelta3 in C#, so I went to figure out one myself…
First thing I needed, was xdelta3 compiled as win32 dll, so it could be PInvoked from C#. That was pretty easy to achieve with use of xdelta3 source and MinGW:
# In xdelta3 source folder
i686-w64-mingw32-gcc -DSIZEOF_SIZE_T=4 -fPIC -c xdelta3*.c && i686-w64-mingw32-gcc -shared -static-libgcc -Wl,-soname,xdelta3.dll -o xdelta3.dll xdelta3.o
Next, I created wrapper code in C#:
public class xdelta3
{
/// <summary>
/// Sets the maximum buffer size that xdelta3 is allowed to write to.
/// </summary>
static readonly int MAX_BUFFER = 32 * 1024 * 1024; // 32 MB
/// <summary>
/// Creates xdelta3 patch from source to target.
/// </summary>
/// <param name="target">The target of the patch (the outcome of patching).</param>
/// <param name="source">The source of the patch (what will be patched).</param>
/// <returns>Xdelta3 patch data.</returns>
public static byte[] CreatePatch(byte[] target, byte[] source)
{
byte[] obuf = new byte[MAX_BUFFER];
UInt32 obufSize;
// Call xdelta3 library
int result = xd3_encode_memory(target, (UInt32)target.Length,
source, (UInt32)source.Length,
obuf, out obufSize,
(UInt32)obuf.Length, 0);
// Check result
if (result != 0)
{
throw new xdelta3Exception(result);
}
// Trim the output
byte[] output = new byte[obufSize];
Buffer.BlockCopy(obuf, 0, output, 0, (int)obufSize);
return output;
}
/// <summary>
/// Applies xdelta3 patch to source.
/// </summary>
/// <param name="patch">xdelta3 patch data.</param>
/// <param name="source">The data to be patched.</param>
/// <returns>Patched data.</returns>
public static byte[] ApplyPatch(byte[] patch, byte[] source)
{
byte[] obuf = new byte[MAX_BUFFER];
UInt32 obufSize;
// Call xdelta3 library
int result = xd3_decode_memory(patch, (UInt32)patch.Length,
source, (UInt32)source.Length,
obuf, out obufSize,
(UInt32)obuf.Length, 0);
// Check result
if (result != 0)
{
throw new xdelta3Exception(result);
}
// Trim the output
byte[] output = new byte[obufSize];
Buffer.BlockCopy(obuf, 0, output, 0, (int)obufSize);
return output;
}
#region PInvoke wrappers
[DllImport("xdelta3.dll", EntryPoint="xd3_encode_memory", CallingConvention=CallingConvention.Cdecl)]
static extern int xd3_encode_memory (
byte[] input,
UInt32 input_size,
byte[] source,
UInt32 source_size,
byte[] output_buffer,
out UInt32 output_size,
UInt32 avail_output,
int flags);
[DllImport("xdelta3.dll", EntryPoint="xd3_decode_memory", CallingConvention=CallingConvention.Cdecl)]
static extern int xd3_decode_memory (
byte[] input,
UInt32 input_size,
byte[] source,
UInt32 source_size,
byte[] output_buffer,
out UInt32 output_size,
UInt32 avail_output,
int flags);
#endregion
}
# region Exceptions
public class xdelta3Exception : Exception
{
public int ExceptionCode { get; set; }
public xdelta3Exception(int rCode)
{
this.ExceptionCode = rCode;
}
}
#endregion
Result is an ability to create/patch xdelta3 data in-memory in C# with almost no drawbacks, as we call native C library. No disk I/O, no remote process calls. Only drawback so far is that you have to be careful with MAX_BUFFER variable, not to cause OutOfMemoryException, overflow the C# array or patch data bigger than the buffer’s size. Luckily, I don’t need it for large files, so this implementation serves my purpose.
Xdelta is great open-source binary diff library, you can find more info here: http://xdelta.org