static void Main(string[] args) { // check for correct usage if (args.Length != 3) { Console.Error.WriteLine("bsdiff oldfile newfile patchfile"); return; } // check for special command-line switch that performs a self-test if (args[0] == "--check") { CheckImplementation(args[1], args[2]); } else { string oldFile = args[0]; string newFile = args[1]; string patchFile = args[2]; try { using (FileStream output = new FileStream(patchFile, FileMode.Create)) BinaryPatchUtility.Create(File.ReadAllBytes(oldFile), File.ReadAllBytes(newFile), output); } catch (FileNotFoundException ex) { Console.Error.WriteLine("Could not open '{0}'.", ex.FileName); } } }
private static byte[] ReadExactly(Stream stream, int count) { if (count < 0) { throw new ArgumentOutOfRangeException("count"); } byte[] array = new byte[count]; BinaryPatchUtility.ReadExactly(stream, array, 0, count); return(array); }
private static int Search(int[] I, byte[] oldData, byte[] newData, int newOffset, int start, int end, out int pos) { if (end - start >= 2) { int num = start + (end - start) / 2; return((BinaryPatchUtility.CompareBytes(oldData, I[num], newData, newOffset) >= 0) ? BinaryPatchUtility.Search(I, oldData, newData, newOffset, start, num, out pos) : BinaryPatchUtility.Search(I, oldData, newData, newOffset, num, end, out pos)); } int num2 = BinaryPatchUtility.MatchLength(oldData, I[start], newData, newOffset); int num3 = BinaryPatchUtility.MatchLength(oldData, I[end], newData, newOffset); if (num2 > num3) { pos = I[start]; return(num2); } pos = I[end]; return(num3); }
// Creates a patch for oldFile and newFile with the reference C implementation and this C# code, then verifies that // the patch applies correctly. private static void CheckImplementation(string oldFile, string newFile) { // to test, download bsdiff and bspatch for Windows, e.g., from http://sites.inka.de/tesla/others.html#bsdiff const string bsdiffPath = @"C:\Util\bsdiff.exe"; const string rbspatchPath = @"C:\Util\bspatch.exe"; string tempPath = Path.GetTempPath(); string referencePatchFileName = Path.Combine(tempPath, "reference.patch"); string portPatchFileName = Path.Combine(tempPath, "port.patch"); // run reference implementation Stopwatch referenceTime = Stopwatch.StartNew(); using (Process process = Process.Start(bsdiffPath, QuotePaths(oldFile, newFile, referencePatchFileName))) { process.WaitForExit(); referenceTime.Stop(); } // run C# implementation Stopwatch portTime = Stopwatch.StartNew(); using (FileStream output = new FileStream(portPatchFileName, FileMode.Create)) BinaryPatchUtility.Create(File.ReadAllBytes(oldFile), File.ReadAllBytes(newFile), output); portTime.Stop(); Console.WriteLine("Patches created in {0} (reference) and {1} (C# port).", referenceTime.Elapsed, portTime.Elapsed); Console.WriteLine("File sizes (in bytes) are {0:n0} (reference) and {1:n0} (C# port).", new FileInfo(referencePatchFileName).Length, new FileInfo(portPatchFileName).Length); string[] outputFilePaths = new[] { "test-ref-ref.dat", "test-prt-ref.dat", "test-ref-prt.dat", "test-prt-prt.dat" } .Select(fn => Path.Combine(tempPath, fn)) .ToArray(); Console.Write("Applying reference patch with reference binary..."); using (Process process = Process.Start(rbspatchPath, QuotePaths(oldFile, outputFilePaths[0], referencePatchFileName))) process.WaitForExit(); Console.WriteLine("done."); Console.Write("Applying port patch with reference binary..."); using (Process process = Process.Start(rbspatchPath, QuotePaths(oldFile, outputFilePaths[1], portPatchFileName))) process.WaitForExit(); Console.WriteLine("done."); Console.Write("Applying reference patch with port binary..."); using (FileStream input = new FileStream(oldFile, FileMode.Open, FileAccess.Read, FileShare.Read)) using (FileStream output = new FileStream(outputFilePaths[2], FileMode.Create)) BinaryPatchUtility.Apply(input, () => new FileStream(referencePatchFileName, FileMode.Open, FileAccess.Read, FileShare.Read), output); Console.WriteLine("done."); Console.Write("Applying port patch with port binary..."); using (FileStream input = new FileStream(oldFile, FileMode.Open, FileAccess.Read, FileShare.Read)) using (FileStream output = new FileStream(outputFilePaths[3], FileMode.Create)) BinaryPatchUtility.Apply(input, () => new FileStream(portPatchFileName, FileMode.Open, FileAccess.Read, FileShare.Read), output); Console.WriteLine("done."); int errors = 0; byte[] expectedOutput = File.ReadAllBytes(newFile); foreach (string filePath in outputFilePaths) { byte[] actualOutput = File.ReadAllBytes(filePath); if (!expectedOutput.SequenceEqual(actualOutput)) { Console.Error.WriteLine("Incorrect results in {0}.", Path.GetFileName(filePath)); errors++; } } if (errors == 0) { Console.WriteLine("All patches are correct."); } }
private static int[] SuffixSort(byte[] oldData) { int[] array = new int[256]; for (int i = 0; i < oldData.Length; i++) { byte b = oldData[i]; array[(int)b]++; } for (int j = 1; j < 256; j++) { array[j] += array[j - 1]; } for (int k = 255; k > 0; k--) { array[k] = array[k - 1]; } array[0] = 0; int[] array2 = new int[oldData.Length + 1]; for (int l = 0; l < oldData.Length; l++) { array2[++array[(int)oldData[l]]] = l; } int[] array3 = new int[oldData.Length + 1]; for (int m = 0; m < oldData.Length; m++) { array3[m] = array[(int)oldData[m]]; } for (int n = 1; n < 256; n++) { if (array[n] == array[n - 1] + 1) { array2[array[n]] = -1; } } array2[0] = -1; int num = 1; while (array2[0] != -(oldData.Length + 1)) { int num2 = 0; int num3 = 0; while (num3 < oldData.Length + 1) { if (array2[num3] < 0) { num2 -= array2[num3]; num3 -= array2[num3]; } else { if (num2 != 0) { array2[num3 - num2] = -num2; } num2 = array3[array2[num3]] + 1 - num3; BinaryPatchUtility.Split(array2, array3, num3, num2, num); num3 += num2; num2 = 0; } } if (num2 != 0) { array2[num3 - num2] = -num2; } num += num; } for (int num4 = 0; num4 < oldData.Length + 1; num4++) { array2[array3[num4]] = num4; } return(array2); }
private static void Split(int[] I, int[] v, int start, int len, int h) { if (len < 16) { int num; for (int i = start; i < start + len; i += num) { num = 1; int num2 = v[I[i] + h]; int num3 = 1; while (i + num3 < start + len) { if (v[I[i + num3] + h] < num2) { num2 = v[I[i + num3] + h]; num = 0; } if (v[I[i + num3] + h] == num2) { BinaryPatchUtility.Swap(ref I[i + num], ref I[i + num3]); num++; } num3++; } for (int j = 0; j < num; j++) { v[I[i + j]] = i + num - 1; } if (num == 1) { I[i] = -1; } } } else { int num4 = v[I[start + len / 2] + h]; int num5 = 0; int num6 = 0; for (int k = start; k < start + len; k++) { if (v[I[k] + h] < num4) { num5++; } if (v[I[k] + h] == num4) { num6++; } } num5 += start; num6 += num5; int l = start; int num7 = 0; int num8 = 0; while (l < num5) { if (v[I[l] + h] < num4) { l++; } else if (v[I[l] + h] == num4) { BinaryPatchUtility.Swap(ref I[l], ref I[num5 + num7]); num7++; } else { BinaryPatchUtility.Swap(ref I[l], ref I[num6 + num8]); num8++; } } while (num5 + num7 < num6) { if (v[I[num5 + num7] + h] == num4) { num7++; } else { BinaryPatchUtility.Swap(ref I[num5 + num7], ref I[num6 + num8]); num8++; } } if (num5 > start) { BinaryPatchUtility.Split(I, v, start, num5 - start, h); } for (l = 0; l < num6 - num5; l++) { v[I[num5 + l]] = num6 - 1; } if (num5 == num6 - 1) { I[num5] = -1; } if (start + len > num6) { BinaryPatchUtility.Split(I, v, num6, start + len - num6, h); } } }
public static void Apply(Stream input, Func <Stream> openPatchStream, Stream output) { if (input == null) { throw new ArgumentNullException("input"); } if (openPatchStream == null) { throw new ArgumentNullException("openPatchStream"); } if (output == null) { throw new ArgumentNullException("output"); } long num2; long num3; long num4; using (Stream stream = openPatchStream.Invoke()) { if (!stream.get_CanRead()) { throw new ArgumentException("Patch stream must be readable.", "openPatchStream"); } if (!stream.get_CanSeek()) { throw new ArgumentException("Patch stream must be seekable.", "openPatchStream"); } byte[] buf = BinaryPatchUtility.ReadExactly(stream, 32); long num = BinaryPatchUtility.ReadInt64(buf, 0); if (num != 3473478480300364610L) { throw new InvalidOperationException("Corrupt patch."); } num2 = BinaryPatchUtility.ReadInt64(buf, 8); num3 = BinaryPatchUtility.ReadInt64(buf, 16); num4 = BinaryPatchUtility.ReadInt64(buf, 24); if (num2 < 0L || num3 < 0L || num4 < 0L) { throw new InvalidOperationException("Corrupt patch."); } } byte[] array = new byte[1048576]; byte[] array2 = new byte[1048576]; using (Stream stream2 = openPatchStream.Invoke()) { using (Stream stream3 = openPatchStream.Invoke()) { using (Stream stream4 = openPatchStream.Invoke()) { stream2.Seek(32L, 1); stream3.Seek(32L + num2, 1); stream4.Seek(32L + num2 + num3, 1); using (BZip2InputStream bZip2InputStream = new BZip2InputStream(stream2)) { using (BZip2InputStream bZip2InputStream2 = new BZip2InputStream(stream3)) { using (BZip2InputStream bZip2InputStream3 = new BZip2InputStream(stream4)) { long[] array3 = new long[3]; byte[] array4 = new byte[8]; int num5 = 0; int num6 = 0; while ((long)num6 < num4) { for (int i = 0; i < 3; i++) { BinaryPatchUtility.ReadExactly(bZip2InputStream, array4, 0, 8); array3[i] = BinaryPatchUtility.ReadInt64(array4, 0); } if ((long)num6 + array3[0] > num4) { throw new InvalidOperationException("Corrupt patch."); } input.set_Position((long)num5); int num7; for (int j = (int)array3[0]; j > 0; j -= num7) { num7 = Math.Min(j, 1048576); BinaryPatchUtility.ReadExactly(bZip2InputStream2, array, 0, num7); int num8 = Math.Min(num7, (int)(input.get_Length() - input.get_Position())); BinaryPatchUtility.ReadExactly(input, array2, 0, num8); for (int k = 0; k < num8; k++) { byte[] expr_22A_cp_0 = array; int expr_22A_cp_1 = k; expr_22A_cp_0[expr_22A_cp_1] += array2[k]; } output.Write(array, 0, num7); num6 += num7; num5 += num7; } if ((long)num6 + array3[1] > num4) { throw new InvalidOperationException("Corrupt patch."); } int num9; for (int j = (int)array3[1]; j > 0; j -= num9) { num9 = Math.Min(j, 1048576); BinaryPatchUtility.ReadExactly(bZip2InputStream3, array, 0, num9); output.Write(array, 0, num9); num6 += num9; } num5 = (int)((long)num5 + array3[2]); } } } } } } } }
public static void Create(byte[] oldData, byte[] newData, Stream output) { if (oldData == null) { throw new ArgumentNullException("oldData"); } if (newData == null) { throw new ArgumentNullException("newData"); } if (output == null) { throw new ArgumentNullException("output"); } if (!output.get_CanSeek()) { throw new ArgumentException("Output stream must be seekable.", "output"); } if (!output.get_CanWrite()) { throw new ArgumentException("Output stream must be writable.", "output"); } byte[] array = new byte[32]; BinaryPatchUtility.WriteInt64(3473478480300364610L, array, 0); BinaryPatchUtility.WriteInt64(0L, array, 8); BinaryPatchUtility.WriteInt64(0L, array, 16); BinaryPatchUtility.WriteInt64((long)newData.Length, array, 24); long position = output.get_Position(); output.Write(array, 0, array.Length); int[] i = BinaryPatchUtility.SuffixSort(oldData); byte[] array2 = new byte[newData.Length]; byte[] array3 = new byte[newData.Length]; int num = 0; int num2 = 0; BZip2OutputStream bZip2OutputStream = new BZip2OutputStream(output); bZip2OutputStream.set_IsStreamOwner(false); using (BZip2OutputStream bZip2OutputStream2 = bZip2OutputStream) { int j = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; while (j < newData.Length) { int num8 = 0; int k; for (j = (k = j + num4); j < newData.Length; j++) { num4 = BinaryPatchUtility.Search(i, oldData, newData, j, 0, oldData.Length, out num3); while (k < j + num4) { if (k + num7 < oldData.Length && oldData[k + num7] == newData[k]) { num8++; } k++; } if ((num4 == num8 && num4 != 0) || num4 > num8 + 8) { break; } if (j + num7 < oldData.Length && oldData[j + num7] == newData[j]) { num8--; } } if (num4 != num8 || j == newData.Length) { int num9 = 0; int num10 = 0; int num11 = 0; int num12 = 0; while (num5 + num12 < j && num6 + num12 < oldData.Length) { if (oldData[num6 + num12] == newData[num5 + num12]) { num9++; } num12++; if (num9 * 2 - num12 > num10 * 2 - num11) { num10 = num9; num11 = num12; } } int num13 = 0; if (j < newData.Length) { num9 = 0; int num14 = 0; int num15 = 1; while (j >= num5 + num15 && num3 >= num15) { if (oldData[num3 - num15] == newData[j - num15]) { num9++; } if (num9 * 2 - num15 > num14 * 2 - num13) { num14 = num9; num13 = num15; } num15++; } } if (num5 + num11 > j - num13) { int num16 = num5 + num11 - (j - num13); num9 = 0; int num17 = 0; int num18 = 0; for (int l = 0; l < num16; l++) { if (newData[num5 + num11 - num16 + l] == oldData[num6 + num11 - num16 + l]) { num9++; } if (newData[j - num13 + l] == oldData[num3 - num13 + l]) { num9--; } if (num9 > num17) { num17 = num9; num18 = l + 1; } } num11 += num18 - num16; num13 -= num18; } for (int m = 0; m < num11; m++) { array2[num + m] = newData[num5 + m] - oldData[num6 + m]; } for (int n = 0; n < j - num13 - (num5 + num11); n++) { array3[num2 + n] = newData[num5 + num11 + n]; } num += num11; num2 += j - num13 - (num5 + num11); byte[] array4 = new byte[8]; BinaryPatchUtility.WriteInt64((long)num11, array4, 0); bZip2OutputStream2.Write(array4, 0, 8); BinaryPatchUtility.WriteInt64((long)(j - num13 - (num5 + num11)), array4, 0); bZip2OutputStream2.Write(array4, 0, 8); BinaryPatchUtility.WriteInt64((long)(num3 - num13 - (num6 + num11)), array4, 0); bZip2OutputStream2.Write(array4, 0, 8); num5 = j - num13; num6 = num3 - num13; num7 = num3 - j; } } } long position2 = output.get_Position(); BinaryPatchUtility.WriteInt64(position2 - position - 32L, array, 8); bZip2OutputStream = new BZip2OutputStream(output); bZip2OutputStream.set_IsStreamOwner(false); using (BZip2OutputStream bZip2OutputStream3 = bZip2OutputStream) { bZip2OutputStream3.Write(array2, 0, num); } long position3 = output.get_Position(); BinaryPatchUtility.WriteInt64(position3 - position2, array, 16); bZip2OutputStream = new BZip2OutputStream(output); bZip2OutputStream.set_IsStreamOwner(false); using (BZip2OutputStream bZip2OutputStream4 = bZip2OutputStream) { bZip2OutputStream4.Write(array3, 0, num2); } long position4 = output.get_Position(); output.set_Position(position); output.Write(array, 0, array.Length); output.set_Position(position4); }