File
Blob: src/worker/git/object-store/delta.ts
| 1 | export function applyGitDelta(base: Uint8Array, delta: Uint8Array): Uint8Array { |
| 2 | let pos = 0; |
| 3 | |
| 4 | const readVarint = () => { |
| 5 | let out = 0; |
| 6 | let shift = 0; |
| 7 | while (true) { |
| 8 | if (pos >= delta.length) throw new Error("delta:truncated-varint"); |
| 9 | const byte = delta[pos++]; |
| 10 | out |= (byte & 0x7f) << shift; |
| 11 | if (!(byte & 0x80)) return out; |
| 12 | shift += 7; |
| 13 | } |
| 14 | }; |
| 15 | |
| 16 | const baseSize = readVarint(); |
| 17 | const resultSize = readVarint(); |
| 18 | if (baseSize !== base.length) throw new Error("delta:base-size-mismatch"); |
| 19 | |
| 20 | const out = new Uint8Array(resultSize); |
| 21 | let outPos = 0; |
| 22 | |
| 23 | while (pos < delta.length) { |
| 24 | const opcode = delta[pos++]; |
| 25 | if (opcode & 0x80) { |
| 26 | let copyOffset = 0; |
| 27 | let copySize = 0; |
| 28 | if (opcode & 0x01) copyOffset |= delta[pos++]; |
| 29 | if (opcode & 0x02) copyOffset |= delta[pos++] << 8; |
| 30 | if (opcode & 0x04) copyOffset |= delta[pos++] << 16; |
| 31 | if (opcode & 0x08) copyOffset |= delta[pos++] << 24; |
| 32 | if (opcode & 0x10) copySize |= delta[pos++]; |
| 33 | if (opcode & 0x20) copySize |= delta[pos++] << 8; |
| 34 | if (opcode & 0x40) copySize |= delta[pos++] << 16; |
| 35 | if (copySize === 0) copySize = 0x10000; |
| 36 | if (copyOffset < 0 || copyOffset + copySize > base.length) { |
| 37 | throw new Error("delta:copy-out-of-bounds"); |
| 38 | } |
| 39 | out.set(base.subarray(copyOffset, copyOffset + copySize), outPos); |
| 40 | outPos += copySize; |
| 41 | continue; |
| 42 | } |
| 43 | |
| 44 | if (opcode === 0) throw new Error("delta:invalid-opcode"); |
| 45 | if (pos + opcode > delta.length) throw new Error("delta:insert-out-of-bounds"); |
| 46 | out.set(delta.subarray(pos, pos + opcode), outPos); |
| 47 | outPos += opcode; |
| 48 | pos += opcode; |
| 49 | } |
| 50 | |
| 51 | if (outPos !== out.length) throw new Error("delta:result-size-mismatch"); |
| 52 | return out; |
| 53 | } |