Tweaked the main format documentation.
Added a CRC test.
Added a DEZFormatException for error states.
public class DEZPrinter implements DEZFormat {
private final byte[] patch;
- private int pi, si;
+ private int pi;
// Recent address cache. TODO: These can be combined
private final int[] matchAddr = new int[64];
private int matchNext = 0;
- private int[] recentAddr = new int[32];
+ private final int[] recentAddr = new int[32];
private int recentNext = 0;
// some statistics.
private Stats copy = new Stats();
this.patch = patch;
}
+ private int decodeInt32() {
+ int v = (patch[pi++] & 0xff) << 24;
+
+ v |= (patch[pi++] & 0xff) << 16;
+ v |= (patch[pi++] & 0xff) << 8;
+ v |= (patch[pi++] & 0xff);
+
+ return v;
+ }
+
private int decodeInt() {
int v = 0;
byte b;
/**
* Dumps the patch details to stdout.
- *
+ * <p>
*/
public void print() {
DEZPrinter.this.print(System.out);
public void print(PrintStream out) {
int ti = 0;
int flags;
- int smallest = 4;
+ int smallest;
pi = 0;
- si = 0;
- // 'decode' magic
- out.printf("magic: %c%c%c%c\n", patch[0], patch[1], patch[2], patch[3]);
+ // Read header
+ out.printf("magic: %c%c%c%c\n", (char) patch[0], (char) patch[1], (char) patch[2], (char) patch[3]);
pi += 4;
- // decode flags
- flags = patch[pi++];
- out.printf("flags: %02x\n", flags & 0xff);
-
- if ((flags & DEZ_SMALLEST) != 0)
- smallest = decodeInt();
+ flags = decodeInt();
+ out.printf("flags: %08x\n", flags);
+ smallest = decodeInt();
out.printf("smallest: %d\n", smallest);
- // get sizes
int sourceSize = decodeInt();
int targetSize = decodeInt();
- int oc = 0;
-
out.printf("source: %d\ntarget: %d\n", sourceSize, targetSize, patch.length);
while (ti < targetSize) {
if (ti != targetSize)
throw new ArrayIndexOutOfBoundsException(String.format("Target short write %d != %d", ti, targetSize));
+ int crc = decodeInt32();
+
out.printf("summary\n");
out.printf(" copy: %s\n", copy);
out.printf(" add: %s\n", add);
out.printf("patch: %d\n", pi);
out.printf("sorce: %d\n", sourceSize);
out.printf("targt: %d\n", targetSize);
+ out.printf(" crc: %08x\n", crc);
}
}
}
@Override
- public byte[] toPatch() {
+ public byte[] toPatch(int crc) {
+ System.out.printf("crc = %08x\n", crc);
return new byte[0];
}
}
*/
package au.notzed.dez;
+import java.util.zip.CRC32;
+
/**
* The interface for encoding a delta.
* <p>
/**
* Initialises creating a new patch.
+ * <p>
+ * Source is the data which will be common to both the encoder and decoder,
+ * target is only known by the encoder.
*
- * @param sourceSize
- * @param targetSize
+ * @param sourceSize Size of source data.
+ * @param targetSize Size of target data.
*/
public void init(int sourceSize, int targetSize);
/**
* Retrieves the patch.
*
+ * @param targetCRC32 CRC32 of target data.
* @return
*/
- public byte[] toPatch();
+ public byte[] toPatch(int targetCRC32);
/**
* Creates a delta from a matcher and writes it to an encoder.
public static byte[] toDiff(ByteMatcher matcher, ByteDeltaEncoder enc) {
byte[] source = matcher.getSource();
byte[] target = matcher.getTarget();
-
- enc.init(source.length, target.length);
-
+ CRC32 crc32 = new CRC32();
int targetEnd = 0;
int state;
+ enc.init(source.length, target.length);
+
while ((state = matcher.nextMatch()) != ByteMatcher.EOF) {
int toff = matcher.getTargetOffset();
int slength = matcher.getLength();
if (targetEnd != target.length)
enc.add(target, targetEnd, target.length - targetEnd);
- return enc.toPatch();
+ crc32.update(target);
+ return enc.toPatch((int) crc32.getValue());
}
}
import java.io.PrintStream;
import java.util.Arrays;
+import java.util.zip.CRC32;
/**
* Decodes a DEZ patch.
* <p>
- * @see au.notzed.dez.DEZEncoder
+ * Typically one would use {@link DEZFormat#decode} instead of this class.
+ *
+ * @see DEZEncoder
+ * @see DEZFormat
*/
public class DEZDecoder implements DEZFormat {
- final static boolean dump = false;
+ private final static boolean dump = false;
+ /**
+ * All source bytes.
+ */
private final byte[] source;
+ /**
+ * All patch bytes.
+ */
private final byte[] patch;
- private int pi, si;
- // recent address cache. TODO: These can be combined
- private int[] matchAddr = new int[64];
+ /**
+ * Patch data index.
+ */
+ private int pi;
+ /**
+ * Recent exact address cache.
+ */
+ private final int[] matchAddr = new int[64];
+ /**
+ * Next matchAddr index.
+ */
private int matchNext = 0;
- private int[] recentAddr = new int[32];
+ /**
+ * Recent relative address cache.
+ */
+ private final int[] recentAddr = new int[32];
+ /**
+ * next recentAddr index.
+ */
private int recentNext = 0;
+ /**
+ * Create a new decoder.
+ * <p>
+ * The decoder will transform source using patch to create
+ * the original target sequence.
+ *
+ * @param source Must match the source parameter used when generating the patch.
+ * @param patch Patch byte stream.
+ */
public DEZDecoder(byte[] source, byte[] patch) {
this.source = source;
this.patch = patch;
}
+ private int decodeInt32() {
+ int v = (patch[pi++] & 0xff) << 24;
+
+ v |= (patch[pi++] & 0xff) << 16;
+ v |= (patch[pi++] & 0xff) << 8;
+ v |= (patch[pi++] & 0xff);
+
+ return v;
+ }
+
private int decodeInt() {
int v = 0;
byte b;
if ((op & 0x20) != 0)
d = -d;
addr = recentAddr[op & 31] + d;
- //updateAddr(addr);
}
} else {
// Normal address
addr = decodeInt();
- //updateAddr(addr);
}
updateAddr(addr);
-
+
return addr;
}
/**
+ * Perorm decoding.
+ * <p>
* Recreates the original target data from the source and patch.
*
- * @return
+ * @return The decoded data.
+ * @throws au.notzed.dez.DEZFormatException If the patch is invalid or
+ * the crc check fails.
*/
- public byte[] decode() {
+ public byte[] decode() throws DEZFormatException {
int ti = 0;
- int smallest = 4;
- int flags;
-
+ CRC32 crc32 = new CRC32();
PrintStream out = dump ? System.out : null;
- pi = 0;
- si = 0;
- // 'decode' magic
- //out.printf("magic: %c%c%c%c\n", patch[0] & 0xff, patch[1] & 0xff, patch[2] & 0xff, patch[3] & 0xff);
+ // Read header
+ if (!Arrays.equals(MAGIC, pi, 4, patch, 0, 4))
+ throw new DEZFormatException("Magic missing");
+
pi += 4;
- // decode flags
- flags = patch[pi++];
- if ((flags & DEZ_SMALLEST) != 0)
- smallest = decodeInt();
- // get sizes
+ int flags = decodeInt();
+ int smallest = decodeInt();
int sourceSize = decodeInt();
int targetSize = decodeInt();
-
- int oc = 0;
-
byte[] target = new byte[targetSize];
+ // Read opcodes until we reach the target size
while (ti < targetSize) {
int op = patch[pi++] & 0xff;
}
}
+ int crc = decodeInt32();
+ crc32.update(target);
+ if ((int) (crc32.getValue()) != crc)
+ throw new DEZFormatException("CRC32 Mismatch");
+
return target;
}
*/
public class DEZEncoder implements ByteDeltaEncoder, DEZFormat {
- final static boolean dump = false;
+ private final static boolean dump = false;
private final ByteArrayOutputStream patch = new ByteArrayOutputStream();
- int here;
- int header;
- int sourceSize, targetSize;
+ private int here;
+ private int header;
+ private int sourceSize, targetSize;
// Configurable parameters.
final int smallest;
// Last operation information
- int last = -1;
- byte[] lastData;
- int lastOff;
- int lastLen;
- int lastAddr;
+ private int last = -1;
+ private byte[] lastData;
+ private int lastOff;
+ private int lastLen;
+ private int lastAddr;
// Recent address cache. These can be combined?
- private int[] matchAddr = new int[64];
+ private final int[] matchAddr = new int[64];
private int matchNext = 0;
- private int[] recentAddr = new int[32];
+ private final int[] recentAddr = new int[32];
private int recentNext = 0;
/**
/**
* Creates a new encoder with a smallest copy size.
*
- * @param smallest Sets the smallest copy allowed.
+ * @param smallest Sets the smallest copy allowed. It must be ≥ 4.
*/
public DEZEncoder(int smallest) {
this.smallest = smallest;
patch.reset();
patch.write(MAGIC);
- if (smallest != 4)
- flags |= DEZ_SMALLEST;
- patch.write(flags);
- if ((flags & DEZ_SMALLEST) != 0)
- encodeInt(smallest);
+
+ encodeInt(flags);
+ encodeInt(smallest);
encodeInt(sourceSize);
encodeInt(targetSize);
header = patch.size();
here += len;
}
- public byte[] toPatch() {
+ public byte[] toPatch(int targetCRC32) {
flush();
if (here != targetSize)
throw new RuntimeException("Insufficiant data"); // FIXME: better exception
+ patch.write(targetCRC32 >> 24);
+ patch.write(targetCRC32 >> 16);
+ patch.write(targetCRC32 >> 8);
+ patch.write(targetCRC32);
+
return patch.toByteArray();
}
}
/**
* Defines constants used in the DEZ1 format.
* <p>
- * <h3>Header</h3>
+ * <h3>Data Types</h3>
+ * <p>
+ * The patch format is a byte stream consisting of a header and a sequence of
+ * bytes, followed by a crc code.
* <pre>
- * magic: 'D' 'E' 'Z' '1'
- * flags: one byte
- * 00000001 Includes 'smallest' value setting
- * [smallest: one integer] Indicates the smallest value in any copy. The default is 4.
- * source size: one integer
- * target size: one integer
- * instructions follow directly
- * ?? no epilogue defined ??
+ * Ciiiiiiii integer A multi-byte integer. C is a continue bit. The
+ * bits are in big-endian order.
+ * aaaaaaaaa address An encoded address. See below.
+ * ddddddddd data A data byte.
+ * XXXX 4-bytes 4 sequential bytes.
* </pre>
- * <h3>Instruction stream</h3>
+ * A trailing (*) indicates zero or more instances.
+ * A trailing (+) indicates a self-describing encoding of one or more bytes.
+ * A trailing (?) indicates an implied encoding of one or more bytes.
+ * <h3>Patch Format</h3>
* <pre>
- * Dual commands:
+ * HEADER:
+ * XXXX magic 4 bytes ASCII, 'DEZ1'.
+ * Ciiiiiii+ flags None are defined.
+ * Ciiiiiii+ smallest Smallest copy offset.
+ * Ciiiiiii+ source Source data size.
+ * Ciiiiiii+ target Target data size.
*
- * 00lllccc dddddddd* aaaaaaaa* - add 1-8 then copy (0-7)+smallest
- * 01cccCCC aaaaaaaa* aaaaaaaa* - copy (0-7)+smallest then copy (0-7)+smallest
+ * DATA:
+ * IIIIIIII* commands Zero or more instruction opcodes and operands
*
- * Single commands:
+ * CRC:
+ * XXXX crc 4-byte CRC of target bytes, network order.
+ * </pre>
+ * <h3>Instruction stream</h3>
+ * Instructions can perform either a single
+ * operation or two operations with limited ranges.
+ * <p>
+ * Single commands perform one operation. They are a 7-bit value with the
+ * 8th bit set. A range test defines the operation.
+ * <pre>
*
- * 1nnnnnnn
+ * 1nnnnnnn:
*
- * n is interpreted as a number of ranges, inclusive:
- * 000 ... 099 aaaaaaaa* - copy (0-99)+smallest
- * 100 ... 123 dddddddd* - add 1-24
- * 124 Ciiiiiii* aaaaaaaa* - copy i + 100 + smallest
- * 125 Ciiiiiii* dddddddd* - add i+24+1
- * 126 Ciiiiiii* dddddddd - run length of i+3
- * 127 - reserved
+ * [000 099] aaaaaaaa+ COPY Copy (n+smallest) from the address.
+ * [100 123] dddddddd? ADD Append (n-99) from the patch.
+ * [ 124] Ciiiiiii+ aaaaaaaa+ COPY Copy (i+100+smallest) from the address.
+ * [ 125] Ciiiiiii+ dddddddd? ADD Append (i+12+1) from the patch.
+ * [ 126] Ciiiiiii+ dddddddd RUN Append (i+3) copies of the data byte.
+ * [ 127] Reserved.
* </pre>
+ * The split point between immediate-length COPY and ADD can be altered via
+ * modifying {@link #OP_SINGLE_SPLIT}, but it will create an incompatible
+ * patch.
* <p>
- * Integers are encoded as a compacted big-endian sequence
- * with 7 bits per byte. Leading zero septets are discarded.
- * The MSB of each byte is a continue bit which indicates
- * another 7 bits are to be read.
- * <p>
+ * Dual command perform two operations with a single opcode. The lengths
+ * are encoded as immediate values and have a limited range of 3 bits each.
+ * <pre>
+ *
+ * 00lllccc dddddddd? aaaaaaaa+ ADD+COPY
+ *
+ * Append the next (lll+1) data bytes (dddddddd?) from the patch.
+ * Then copy (ccc+smallest) bytes from the address (aaaaaaaa+).
+ *
+ * 01cccCCC aaaaaaaa+ AAAAAAAA+ COPY+COPY
+ *
+ * Copy (ccc+smallest) bytes from the first address (aaaaaaaa+).
+ * Then copy (CCC+smallest) bytes from the second address (AAAAAAAA+).
+ * </pre>
* <h3>Addresses</h3>
+ * Addresses are a logical pointer which refers to a location within
+ * a buffer consisting of the concatenated source and target buffers. The
+ * encoder only referneces addresses that have already been seen so they
+ * will also be visible to the decoder.
* <p>
- * All addresses (aaaaaaaa*) above are encoded using a rolling lookup table.
- * The first byte of each address indicates whether a lookup-table specific
- * address is used or an absolute address.
+ * A small address cache is maintained in both the encoder and decoder to
+ * the last N recently accessed addresses. This can be used to encode
+ * the address with fewer bytes.
+ * <p>
+ * Addresses can be encoded in three ways.
+ * <ol>
+ * <li>An exact reference to an address cache entry.
+ * <li>A reference to an address cache entry with a signed offset.
+ * <li>An absolute address.
+ * </ol>
+ * The first byte determines the address encoding.
* <pre>
+ *
* 00nnnnnn - use address 'n' exactly.
- * 01Smmmmm iiiiiiii* - use address 'm' plus or minus 'i'. S={ 0: plus, 1: minus }
- * 1iiiiiii Ciiiiiii* - use address 'i' absolute. Always at least 2 bytes.
+ * 01Snnnnn Ciiiiiii+ - use address 'n' plus (S=0) or minus (S=1) 'i'
+ * 1iiiiiii Ciiiiiii+ - use address 'i' absolute. Always at least 2 bytes.
* </pre>
* <p>
- * Given that 7-bit absolute addresses will rarely occur in typical data all
- * absolute addresses use at least 2x bytes. This allows 2+ byte values to be encoded
- * as compactly as they would otherwise be and leaves the lower 128 values for other
- * encoding options.
- * <p>
* The address table is 64 elements long but the relative instruction can only
* reference the most recent 32 elements. It is updated each time an address is
* encoded or decoded in a simple rolling manner.
* is encoded implicitly in the opcode via an index.
* <p>
* Next the encoder finds the nearest match from the recent table as absolute
- * distance. The encoder may encode the address as a relative offset from
- * a member of the table if the total is smaller than encoding the address absolutely.
- * <p>
+ * distance. The encoder will then encode the address as a relative offset from
+ * a member of the table if the total encoded length is shorter than encoding
+ * the address absolutely.
+ * </p>
*/
public interface DEZFormat {
*/
public static final byte[] MAGIC = {'D', 'E', 'Z', '1'};
- /**
- * Header flags indicating the smallest copy size is present in the header.
- */
- public static final int DEZ_SMALLEST = 0x01;
-
/**
* COPY select bit in dual opcode.
*/
*
* @param source
* @param target
- * @return
+ * @return patch, the binary delta.
*/
public static byte[] encode(byte[] source, byte[] target) {
return ByteDeltaEncoder.toDiff(new ByteMatcherHash(6, 4, source, 1, target), new DEZEncoder());
}
/**
- * Applies a DEZ-1 patch.
+ * Apply a DEZ-1 patch.
*
* @param source
* @param patch
- * @return
+ * @return target, the reconstructed data.
+ * @throws au.notzed.dez.DEZFormatException If the patch is not a
+ * compatible format, or the crc check failed.
*/
- public static byte[] decode(byte[] source, byte[] patch) {
+ public static byte[] decode(byte[] source, byte[] patch) throws DEZFormatException {
return new au.notzed.dez.DEZDecoder(source, patch).decode();
}
}
--- /dev/null
+/*
+ * Copyright (C) 2015 Michael Zucchi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package au.notzed.dez;
+
+import java.io.IOException;
+
+/**
+ *
+ */
+public class DEZFormatException extends IOException {
+
+ public DEZFormatException(String why) {
+ super(why);
+ }
+
+}
instance.init(len, len);
instance.copy(addr, len);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
(byte) len,
(byte) len,
- (byte) (128 + len - 4), 0
+ (byte) (128 + len - 4), 0,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.init(0, len);
instance.add(data, off, len);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
0,
(byte) len,
- (byte) (128 + 100 + len - 1), 1, 2, 3, 4
+ (byte) (128 + 100 + len - 1), 1, 2, 3, 4,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.init(0, len);
instance.run(b, len);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
0,
(byte) len,
(byte) (126 | 0x80),
(byte) (len - 3),
- b
+ b,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.init(0, len);
instance.run(b, len);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
0,
(byte) ((len >> 7) | 0x80),
(byte) (len & 127),
(byte) (126 | 0x80),
(byte) (len - 3),
- b
+ b,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.init(0, len);
instance.run(b, len);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
0,
(byte) ((len >> 7) | 0x80),
(byte) (len & 127),
(byte) (126 | 0x80),
(byte) (((len - 3) >> 7) | 0x80),
(byte) ((len - 3) & 0x7f),
- b
+ b,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.add(data, 0, 5);
instance.add(data, 5, 3);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(patch, new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
0,
(byte) len,
(byte) (128 + 100 + 5 - 1), 1, 2, 3, 4, 5,
- (byte) (128 + 100 + 3 - 1), 6, 7, 8
+ (byte) (128 + 100 + 3 - 1), 6, 7, 8,
+ 0x01, 0x23, 0x45, 0x67
});
}
instance.copy(0, 10);
instance.copy(0, 10);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
16,
(byte) len,
(byte) ((10 - smallest) << 3 | (10 - smallest) | 0x40),
- 0, 0
+ 0, 0,
+ 0x01, 0x23, 0x45, 0x67
}, patch);
}
instance.copy(0, 10);
instance.copy(0, 10);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
- DEZEncoder.DEZ_SMALLEST,
+ 0,
(byte) smallest,
16,
(byte) len,
(byte) ((10 - smallest) << 3 | (10 - smallest) | 0x40),
- 0, 0
+ 0, 0,
+ 0x01, 0x23, 0x45, 0x67
}, patch);
}
instance.copy(10, 10);
instance.copy(8, 10);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
16,
(byte) len,
(byte) (((10 - smallest) << 3) | (10 - smallest) | 0x40),
0x40, 10,
// - 2
- 0x60, 2
+ 0x60, 2,
+ 0x01, 0x23, 0x45, 0x67
}, patch);
}
instance.copy(8, 10);
instance.copy(10, 10);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
16,
(byte) len,
(byte) (((10 - smallest) << 3) | (10 - smallest) | 0x40),
0x40, 8,
// + 2
- 0x40, 2
+ 0x40, 2,
+ 0x01, 0x23, 0x45, 0x67
}, patch);
}
byte[] target = new byte[0];
instance.init(source.length, target.length);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
- assertEquals(7, patch.length);
- assertArrayEquals(patch, new byte[]{
+ assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
+ 0,
0,
- 0});
+ 0x01, 0x23, 0x45, 0x67
+ }, patch);
}
@Test
byte[] target = new byte[0];
instance.init(source.length, target.length);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
- assertEquals(8, patch.length);
- assertArrayEquals(patch, new byte[]{
+ assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
- DEZEncoder.DEZ_SMALLEST,
+ 0,
6,
0,
- 0});
+ 0,
+ 0x01, 0x23, 0x45, 0x67
+ }, patch);
}
@Test
byte[] target = new byte[0];
instance.init(source.length, target.length);
- byte[] patch = instance.toPatch();
+ byte[] patch = instance.toPatch(0x01234567);
- assertEquals(7, patch.length);
- assertArrayEquals(patch, new byte[]{
+ assertArrayEquals(new byte[]{
DEZEncoder.MAGIC[0], DEZEncoder.MAGIC[1], DEZEncoder.MAGIC[2], DEZEncoder.MAGIC[3],
0,
+ 4,
+ 0,
0,
- 0});
+ 0x01, 0x23, 0x45, 0x67
+ }, patch);
}
}
}
}
+ @Test
+ public void testCorruptCRC() throws Exception {
+ System.out.println("decode corrupt crc");
+
+ byte[] source = "the rains in Spain fall mainly on the plains".getBytes();
+ byte[] target = "plain breads in Spain aids your dames brains".getBytes();
+
+ for (int pos = 1; pos <= 4; pos++) {
+ DEZEncoder de = new DEZEncoder(4);
+ ByteMatcherHash matcher = new ByteMatcherHash(4, 4, source, 1, target);
+ byte[] patch = ByteDeltaEncoder.toDiff(matcher, de);
+
+ Exception x = null;
+ try {
+ patch[patch.length - pos] ^= 1;
+ DEZDecoder dd = new DEZDecoder(source, patch);
+
+ dd.decode();
+ } catch (Exception xx) {
+ x = xx;
+ }
+ assertNotNull(x);
+ assertEquals(x.getClass(), DEZFormatException.class);
+ }
+ }
}