Gdiff { public static long patch( RandomAccessFile inputFile, InputStream patchFile, OutputStream output, long expectedOutputSize) throws IOException { byte[] buffer = new byte[COPY_BUFFER_SIZE]; long outputSize = 0; patchFile = new BufferedInputStream(patchFile, PATCH_STREAM_BUFFER_SIZE); DataInputStream patchDataStream = new DataInputStream(patchFile); int magic = patchDataStream.readInt(); if (magic != GDIFF_FILE_MAGIC) { throw new PatchFormatException("Unexpected magic=" + String.format("%x", magic)); } int version = patchDataStream.read(); if (version != GDIFF_FILE_VERSION) { throw new PatchFormatException("Unexpected version=" + version); } try { while (true) { int copyLength; long copyOffset; long maxCopyLength = expectedOutputSize - outputSize; int command = patchDataStream.read(); switch (command) { case -1: throw new IOException("Patch file overrun"); case EOF: return outputSize; case DATA_USHORT: copyLength = patchDataStream.readUnsignedShort(); copyFromPatch(buffer, patchDataStream, output, copyLength, maxCopyLength); break; case DATA_INT: copyLength = patchDataStream.readInt(); copyFromPatch(buffer, patchDataStream, output, copyLength, maxCopyLength); break; case COPY_USHORT_UBYTE: copyOffset = patchDataStream.readUnsignedShort(); copyLength = patchDataStream.read(); if (copyLength == -1) { throw new IOException("Unexpected end of patch"); } copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_USHORT_USHORT: copyOffset = patchDataStream.readUnsignedShort(); copyLength = patchDataStream.readUnsignedShort(); copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_USHORT_INT: copyOffset = patchDataStream.readUnsignedShort(); copyLength = patchDataStream.readInt(); copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_INT_UBYTE: copyOffset = patchDataStream.readInt(); copyLength = patchDataStream.read(); if (copyLength == -1) { throw new IOException("Unexpected end of patch"); } copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_INT_USHORT: copyOffset = patchDataStream.readInt(); copyLength = patchDataStream.readUnsignedShort(); copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_INT_INT: copyOffset = patchDataStream.readInt(); copyLength = patchDataStream.readInt(); copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; case COPY_LONG_INT: copyOffset = patchDataStream.readLong(); copyLength = patchDataStream.readInt(); copyFromOriginal(buffer, inputFile, output, copyOffset, copyLength, maxCopyLength); break; default: copyLength = command; copyFromPatch(buffer, patchDataStream, output, copyLength, maxCopyLength); break; } outputSize += copyLength; } } finally { output.flush(); } }  static long patch(
      RandomAccessFile inputFile,
      InputStream patchFile,
      OutputStream output,
      long expectedOutputSize);  }
@Test public void testExample() throws IOException { byte[] oldBytes = new byte[] { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G' }; byte[] newBytes = new byte[] { (byte) 'A', (byte) 'B', (byte) 'X', (byte) 'Y', (byte) 'C', (byte) 'D', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E' }; byte[] patch = new byte[] { (byte) 0xd1, (byte) 0xff, (byte) 0xd1, (byte) 0xff, (byte) 4, (byte) 249, 0, 0, 2, (byte) 2, (byte) 'X', (byte) 'Y', (byte) 249, 0, 2, 2, (byte) 249, 0, 1, 4, 0 }; File inputFile = File.createTempFile("testExample", null); FileOutputStream writeInputFile = new FileOutputStream(inputFile); writeInputFile.write(oldBytes); writeInputFile.close(); RandomAccessFile readInputFile = new RandomAccessFile(inputFile, "r"); ByteArrayInputStream patchStream = new ByteArrayInputStream(patch); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(newBytes.length); long outputLength = Gdiff.patch(readInputFile, patchStream, outputStream, newBytes.length); Assert.assertEquals(newBytes.length, outputLength); Assert.assertArrayEquals(newBytes, outputStream.toByteArray()); }
@Test public void testOpcodes() throws IOException { byte[] oldBytes = new byte[] { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J' }; byte[] newBytes = new byte[] { (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'A', (byte) 'B', (byte) 'B', (byte) 'C', (byte) 'C', (byte) 'D', (byte) 'D', (byte) 'E', (byte) 'E', (byte) 'F', (byte) 'F', (byte) 'G', (byte) 'G', (byte) 'H' }; byte[] patch = new byte[] { (byte) 0xd1, (byte) 0xff, (byte) 0xd1, (byte) 0xff, (byte) 4, (byte) 1, (byte) 'X', (byte) 247, 0, 1, (byte) 'Y', (byte) 248, 0, 0, 0, 1, (byte) 'Z', (byte) 249, 0, 0, 2, (byte) 250, 0, 1, 0, 2, (byte) 251, 0, 2, 0, 0, 0, 2, (byte) 252, 0, 0, 0, 3, 2, (byte) 253, 0, 0, 0, 4, 0, 2, (byte) 254, 0, 0, 0, 5, 0, 0, 0, 2, (byte) 255, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0 }; File inputFile = File.createTempFile("testExample", null); FileOutputStream writeInputFile = new FileOutputStream(inputFile); writeInputFile.write(oldBytes); writeInputFile.close(); RandomAccessFile readInputFile = new RandomAccessFile(inputFile, "r"); ByteArrayInputStream patchStream = new ByteArrayInputStream(patch); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(newBytes.length); long outputLength = Gdiff.patch(readInputFile, patchStream, outputStream, newBytes.length); Assert.assertEquals(newBytes.length, outputLength); Assert.assertArrayEquals(newBytes, outputStream.toByteArray()); }
@Test public void testInlineDataCommands() throws IOException { File inputFile = File.createTempFile("testExample", null); FileOutputStream writeInputFile = new FileOutputStream(inputFile); writeInputFile.close(); RandomAccessFile readInputFile = new RandomAccessFile(inputFile, "r"); byte[] magicAndVersion = new byte[] { (byte) 0xd1, (byte) 0xff, (byte) 0xd1, (byte) 0xff, (byte) 4 }; Random random = new Random(); for (int spanLength = 1; spanLength <= 246; spanLength++) { byte[] data = new byte[spanLength]; random.setSeed(spanLength); random.nextBytes(data); int patchLength = 5 + 1 + spanLength + 1; byte[] patch = new byte[patchLength]; System.arraycopy(magicAndVersion, 0, patch, 0, magicAndVersion.length); patch[5] = (byte) spanLength; System.arraycopy(data, 0, patch, 6, spanLength); patch[6 + spanLength] = 0; ByteArrayInputStream patchStream = new ByteArrayInputStream(patch); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); long outputLength = Gdiff.patch(readInputFile, patchStream, outputStream, data.length); Assert.assertEquals(spanLength, outputLength); Assert.assertArrayEquals(data, outputStream.toByteArray()); } }