Skip to content
File

Blob: src/worker/git/object-store/delta.ts

typescript54 lines
1export 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}