Skip to content
File

Blob: src/worker/git/pack/indexer/resolve/refLookup.ts

typescript143 lines
1import type { PackEntryTable, ScanResult } from "../types";
2import { getRefBaseOidAt } from "../types";
3 
4import { enqueueReadyDeferred, extendDeadlineChain } from "./dependencies";
5 
6export interface RefBaseLookup {
7 mask: number;
8 used: Uint8Array;
9 keys: Uint8Array;
10 resolvedEntries: Int32Array;
11 waitHeadBySlot: Int32Array;
12 nextWait: Int32Array;
13 entrySlots: Int32Array;
14}
15 
16function nextPowerOfTwo(value: number): number {
17 let out = 1;
18 while (out < value) out <<= 1;
19 return out;
20}
21 
22function hashOid(oid: Uint8Array): number {
23 let hash = 2166136261;
24 for (let i = 0; i < 20; i++) {
25 hash ^= oid[i];
26 hash = Math.imul(hash, 16777619);
27 }
28 return hash >>> 0;
29}
30 
31function slotKeyEquals(keys: Uint8Array, slot: number, oid: Uint8Array): boolean {
32 const start = slot * 20;
33 for (let i = 0; i < 20; i++) {
34 if (keys[start + i] !== oid[i]) return false;
35 }
36 return true;
37}
38 
39function findOrCreateSlot(lookup: RefBaseLookup, oid: Uint8Array): number {
40 let slot = hashOid(oid) & lookup.mask;
41 while (lookup.used[slot]) {
42 if (slotKeyEquals(lookup.keys, slot, oid)) return slot;
43 slot = (slot + 1) & lookup.mask;
44 }
45 lookup.used[slot] = 1;
46 lookup.keys.set(oid, slot * 20);
47 return slot;
48}
49 
50function findExistingSlot(lookup: RefBaseLookup, oid: Uint8Array): number {
51 let slot = hashOid(oid) & lookup.mask;
52 while (lookup.used[slot]) {
53 if (slotKeyEquals(lookup.keys, slot, oid)) return slot;
54 slot = (slot + 1) & lookup.mask;
55 }
56 return -1;
57}
58 
59export function createRefBaseLookup(scanResult: ScanResult): RefBaseLookup | null {
60 if (scanResult.refDeltaCount === 0) return null;
61 
62 const slotCount = nextPowerOfTwo(Math.max(4, scanResult.refDeltaCount * 2));
63 const lookup: RefBaseLookup = {
64 mask: slotCount - 1,
65 used: new Uint8Array(slotCount),
66 keys: new Uint8Array(slotCount * 20),
67 resolvedEntries: new Int32Array(slotCount).fill(-1),
68 waitHeadBySlot: new Int32Array(slotCount).fill(-1),
69 nextWait: new Int32Array(scanResult.objectCount).fill(-1),
70 entrySlots: new Int32Array(scanResult.objectCount).fill(-1),
71 };
72 
73 for (let i = 0; i < scanResult.objectCount; i++) {
74 if (scanResult.table.types[i] !== 7) continue;
75 lookup.entrySlots[i] = findOrCreateSlot(lookup, getRefBaseOidAt(scanResult.refBaseOids, i));
76 }
77 
78 return lookup;
79}
80 
81export function noteResolvedEntry(
82 lookup: RefBaseLookup,
83 oidBuffer: Uint8Array,
84 entryIndex: number
85): void {
86 const start = entryIndex * 20;
87 const slot = findExistingSlot(lookup, oidBuffer.subarray(start, start + 20));
88 if (slot >= 0) lookup.resolvedEntries[slot] = entryIndex;
89}
90 
91export function getResolvedBaseEntry(lookup: RefBaseLookup, entryIndex: number): number {
92 const slot = lookup.entrySlots[entryIndex];
93 return slot < 0 ? -1 : lookup.resolvedEntries[slot];
94}
95 
96export function enqueueWaitingRefDelta(lookup: RefBaseLookup, entryIndex: number): void {
97 const slot = lookup.entrySlots[entryIndex];
98 if (slot < 0) return;
99 lookup.nextWait[entryIndex] = lookup.waitHeadBySlot[slot];
100 lookup.waitHeadBySlot[slot] = entryIndex;
101}
102 
103export function promoteWaitingRefDeltas(
104 lookup: RefBaseLookup,
105 resolvedIndex: number,
106 table: PackEntryTable,
107 baseIndexArr: Int32Array,
108 isBaseArr: Uint8Array,
109 deadlines: Uint32Array,
110 readyDeferred?: number[],
111 deferredQueued?: Uint8Array
112): void {
113 const slot = findExistingSlot(
114 lookup,
115 table.oids.subarray(resolvedIndex * 20, resolvedIndex * 20 + 20)
116 );
117 if (slot < 0) return;
118 
119 lookup.resolvedEntries[slot] = resolvedIndex;
120 let waiter = lookup.waitHeadBySlot[slot];
121 if (waiter < 0) return;
122 
123 lookup.waitHeadBySlot[slot] = -1;
124 isBaseArr[resolvedIndex] = 1;
125 while (waiter >= 0) {
126 const next = lookup.nextWait[waiter];
127 lookup.nextWait[waiter] = -1;
128 if (baseIndexArr[waiter] < 0) {
129 baseIndexArr[waiter] = resolvedIndex;
130 extendDeadlineChain(
131 deadlines,
132 baseIndexArr,
133 resolvedIndex,
134 Math.max(table.offsets[waiter], deadlines[waiter])
135 );
136 if (readyDeferred && deferredQueued) {
137 enqueueReadyDeferred(readyDeferred, deferredQueued, table, baseIndexArr, waiter);
138 }
139 }
140 waiter = next;
141 }
142}