xdelta3 in-memory encoding/decoding in .NET (C#)

I’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