| 1 | /** The size a delta says its result has (after its base's size), read from its header. */ |
| 2 | export function deltaResultSize(delta: Uint8Array): number { |
| 3 | let i = 0; |
| 4 | const varint = (): number => { |
| 5 | let result = 0; |
| 6 | let shift = 0; |
| 7 | let c: number; |
| 8 | do { |
| 9 | if (i >= delta.length) throw new Error("truncated delta header"); |
| 10 | c = delta[i++]!; |
| 11 | result += (c & 0x7f) * 2 ** shift; |
| 12 | shift += 7; |
| 13 | } while (c & 0x80); |
| 14 | return result; |
| 15 | }; |
| 16 | varint(); |
| 17 | return varint(); |
| 18 | } |
| 19 | |
| 20 | /** Apply a git delta (as found in OFS_DELTA / REF_DELTA pack entries) to a base. */ |
| 21 | export function applyDelta(base: Uint8Array, delta: Uint8Array, maxSize = Infinity): Uint8Array { |
| 22 | let i = 0; |
| 23 | const varint = (): number => { |
| 24 | let result = 0; |
| 25 | let shift = 0; |
| 26 | let c: number; |
| 27 | do { |
| 28 | c = delta[i++]!; |
| 29 | result += (c & 0x7f) * 2 ** shift; |
| 30 | shift += 7; |
| 31 | } while (c & 0x80); |
| 32 | return result; |
| 33 | }; |
| 34 | const srcSize = varint(); |
| 35 | if (srcSize !== base.length) throw new Error(`delta base size mismatch: ${srcSize} != ${base.length}`); |
| 36 | const dstSize = varint(); |
| 37 | if (dstSize > maxSize) throw new Error(`delta result of ${dstSize} bytes is larger than the ${maxSize} bytes allowed`); |
| 38 | const out = new Uint8Array(dstSize); |
| 39 | let o = 0; |
| 40 | while (i < delta.length) { |
| 41 | const op = delta[i++]!; |
| 42 | if (op & 0x80) { |
| 43 | let off = 0; |
| 44 | let size = 0; |
| 45 | if (op & 0x01) off |= delta[i++]!; |
| 46 | if (op & 0x02) off |= delta[i++]! << 8; |
| 47 | if (op & 0x04) off |= delta[i++]! << 16; |
| 48 | if (op & 0x08) off = (off | (delta[i++]! << 24)) >>> 0; |
| 49 | if (op & 0x10) size |= delta[i++]!; |
| 50 | if (op & 0x20) size |= delta[i++]! << 8; |
| 51 | if (op & 0x40) size |= delta[i++]! << 16; |
| 52 | if (size === 0) size = 0x10000; |
| 53 | if (off + size > base.length || o + size > dstSize) throw new Error("delta copy out of range"); |
| 54 | out.set(base.subarray(off, off + size), o); |
| 55 | o += size; |
| 56 | } else if (op > 0) { |
| 57 | if (o + op > dstSize || i + op > delta.length) throw new Error("delta insert out of range"); |
| 58 | out.set(delta.subarray(i, i + op), o); |
| 59 | i += op; |
| 60 | o += op; |
| 61 | } else { |
| 62 | throw new Error("invalid delta opcode 0"); |
| 63 | } |
| 64 | } |
| 65 | if (o !== dstSize) throw new Error(`delta produced ${o} bytes, expected ${dstSize}`); |
| 66 | return out; |
| 67 | } |