Cap delta copy instructions at 64k

Although all modern delta decoders can process copy instructions
with a count as large as 0xffffff (~15.9 MiB), pack version 2 streams
are only supposed to use delta copy instructions up to 64 KiB.

Rewrite our copy instruction encode loop to use the lower 64 KiB
limit, even though modern decoders would support longer copies.

To improve encoding performance we now try to encode up to four full
copy commands in our buffer before we flush it to the stream, but
we don't try to implement full buffering here.  We are just trying
to amortize the virtual method call to the destination stream when
we have to do a large copy.

Change-Id: I9410a16e6912faa83180a9788dc05f11e33fabae
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2010-07-07 08:51:04 -07:00
parent a215914a56
commit cd7dd8591e
1 changed files with 50 additions and 12 deletions

View File

@ -50,11 +50,29 @@
/** Encodes an instruction stream for {@link BinaryDelta}. */
public class DeltaEncoder {
private static final int MAX_COPY = (0xff << 16) + (0xff << 8) + 0xff;
/**
* Maximum number of bytes to be copied in pack v2 format.
* <p>
* Historical limitations have this at 64k, even though current delta
* decoders recognize larger copy instructions.
*/
private static final int MAX_V2_COPY = 0x10000;
/*
* Maximum number of bytes to be copied in pack v3 format.
*
* Current delta decoders can recognize a copy instruction with a count that
* is this large, but the historical limitation of {@link MAX_V2_COPY} is
* still used.
*/
// private static final int MAX_V3_COPY = (0xff << 16) | (0xff << 8) | 0xff;
/** Maximum number of bytes used by a copy instruction. */
private static final int MAX_COPY_CMD_SIZE = 8;
private final OutputStream out;
private final byte[] buf = new byte[16];
private final byte[] buf = new byte[MAX_COPY_CMD_SIZE * 4];
private int size;
@ -154,14 +172,35 @@ public void insert(byte[] text, int off, int cnt) throws IOException {
* the instruction buffer cannot store the instructions.
*/
public void copy(long offset, int cnt) throws IOException {
if (cnt > MAX_COPY) {
copy(offset, MAX_COPY);
offset += MAX_COPY;
cnt -= MAX_COPY;
if (cnt == 0)
return;
int p = 0;
// We cannot encode more than MAX_V2_COPY bytes in a single
// command, so encode that much and start a new command.
// This limit is imposed by the pack file format rules.
//
while (MAX_V2_COPY < cnt) {
p = encodeCopy(p, offset, MAX_V2_COPY);
offset += MAX_V2_COPY;
cnt -= MAX_V2_COPY;
if (buf.length < p + MAX_COPY_CMD_SIZE) {
out.write(buf, 0, p);
size += p;
p = 0;
}
}
p = encodeCopy(p, offset, cnt);
out.write(buf, 0, p);
size += p;
}
private int encodeCopy(int p, long offset, int cnt) {
int cmd = 0x80;
int p = 1;
final int cmdPtr = p++; // save room for the command
if ((offset & 0xff) != 0) {
cmd |= 0x01;
@ -180,7 +219,7 @@ public void copy(long offset, int cnt) throws IOException {
buf[p++] = (byte) ((offset >>> 24) & 0xff);
}
if (cnt != 0x10000) {
if (cnt != MAX_V2_COPY) {
if ((cnt & 0xff) != 0) {
cmd |= 0x10;
buf[p++] = (byte) (cnt & 0xff);
@ -195,8 +234,7 @@ public void copy(long offset, int cnt) throws IOException {
}
}
buf[0] = (byte) cmd;
out.write(buf, 0, p);
size += p;
buf[cmdPtr] = (byte) cmd;
return p;
}
}
}