diff options
Diffstat (limited to 'packfile/delta.go')
-rw-r--r-- | packfile/delta.go | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/packfile/delta.go b/packfile/delta.go new file mode 100644 index 0000000..86b556f --- /dev/null +++ b/packfile/delta.go @@ -0,0 +1,93 @@ +package packfile + +const delta_size_min = 4 + +func deltaHeaderSize(b []byte) (uint, []byte) { + var size, j uint + var cmd byte + for { + cmd = b[j] + size |= (uint(cmd) & 0x7f) << (j * 7) + j++ + if uint(cmd)&0xb80 == 0 || j == uint(len(b)) { + break + } + } + return size, b[j:] +} + +func PatchDelta(src, delta []byte) []byte { + if len(delta) < delta_size_min { + return nil + } + size, delta := deltaHeaderSize(delta) + if size != uint(len(src)) { + return nil + } + size, delta = deltaHeaderSize(delta) + origSize := size + + dest := make([]byte, 0) + + // var offset uint + var cmd byte + for { + cmd = delta[0] + delta = delta[1:] + if (cmd & 0x80) != 0 { + var cp_off, cp_size uint + if (cmd & 0x01) != 0 { + cp_off = uint(delta[0]) + delta = delta[1:] + } + if (cmd & 0x02) != 0 { + cp_off |= uint(delta[0]) << 8 + delta = delta[1:] + } + if (cmd & 0x04) != 0 { + cp_off |= uint(delta[0]) << 16 + delta = delta[1:] + } + if (cmd & 0x08) != 0 { + cp_off |= uint(delta[0]) << 24 + delta = delta[1:] + } + + if (cmd & 0x10) != 0 { + cp_size = uint(delta[0]) + delta = delta[1:] + } + if (cmd & 0x20) != 0 { + cp_size |= uint(delta[0]) << 8 + delta = delta[1:] + } + if (cmd & 0x40) != 0 { + cp_size |= uint(delta[0]) << 16 + delta = delta[1:] + } + if cp_size == 0 { + cp_size = 0x10000 + } + if cp_off+cp_size < cp_off || + cp_off+cp_size > uint(len(src)) || + cp_size > origSize { + break + } + dest = append(dest, src[cp_off:cp_off+cp_size]...) + size -= cp_size + } else if cmd != 0 { + if uint(cmd) > origSize { + break + } + dest = append(dest, delta[0:uint(cmd)]...) + size -= uint(cmd) + delta = delta[uint(cmd):] + } else { + return nil + } + if size <= 0 { + break + } + } + return dest +} |