Skip to content
File

Blob: src/worker/do/repo/debug/packed.ts

typescript139 lines
1import type { IdxView, PackCatalogRow } from "@/worker/git/object-store/types";
2 
3import { inflate } from "@/worker/common";
4import { applyGitDelta } from "@/worker/git/object-store/delta";
5import {
6 findOidIndex,
7 findOffsetIndex,
8 getNextOffset,
9 getNextOffsetByIndex,
10 getOidHexAt,
11 loadIdxView,
12} from "@/worker/git/object-store/idxView";
13import { typeCodeToObjectType } from "@/worker/git/object-store/support";
14import { readPackHeaderExFromBuf, readPackRange } from "@/worker/git/pack/packMeta";
15 
16type DebugResolvedLocation = {
17 pack: PackCatalogRow;
18 idx: IdxView;
19 objectIndex: number;
20 offset: number;
21 nextOffset: number;
22 oid: string;
23};
24 
25async function findCatalogObject(
26 env: Env,
27 rows: PackCatalogRow[],
28 oid: string
29): Promise<DebugResolvedLocation | undefined> {
30 const oidLc = oid.toLowerCase();
31 for (const pack of rows) {
32 const idx = await loadIdxView(env, pack.packKey, undefined, pack.packBytes);
33 if (!idx) continue;
34 const objectIndex = findOidIndex(idx, oidLc);
35 if (objectIndex < 0) continue;
36 const offset = idx.offsets[objectIndex];
37 const noff = getNextOffset(idx, offset);
38 if (noff === undefined) continue;
39 return {
40 pack,
41 idx,
42 objectIndex,
43 offset,
44 nextOffset: noff,
45 oid: oidLc,
46 };
47 }
48 return undefined;
49}
50 
51async function readPackedObjectFromLocation(
52 env: Env,
53 location: DebugResolvedLocation,
54 rows: PackCatalogRow[],
55 visited: Set<string>
56): Promise<{ type: "commit" | "tree" | "blob" | "tag"; payload: Uint8Array } | undefined> {
57 const visitKey = `${location.pack.packKey}#${location.objectIndex}`;
58 if (visited.has(visitKey)) throw new Error("pack object recursion cycle");
59 visited.add(visitKey);
60 try {
61 const entryLength = location.nextOffset - location.offset;
62 if (entryLength <= 0) return undefined;
63 const entry = await readPackRange(env, location.pack.packKey, location.offset, entryLength);
64 if (!entry) return undefined;
65 
66 const header = readPackHeaderExFromBuf(entry, 0);
67 if (!header) return undefined;
68 const inflated = await inflate(entry.subarray(header.headerLen));
69 
70 const baseType = typeCodeToObjectType(header.type);
71 if (baseType) return { type: baseType, payload: inflated };
72 
73 if (header.type === 6) {
74 const baseOffset = location.offset - (header.baseRel || 0);
75 const baseIndex = findOffsetIndex(location.idx, baseOffset);
76 if (baseIndex === undefined) return undefined;
77 const baseNextOffset = getNextOffsetByIndex(location.idx, baseIndex);
78 if (baseNextOffset === undefined) return undefined;
79 const base = await readPackedObjectFromLocation(
80 env,
81 {
82 pack: location.pack,
83 idx: location.idx,
84 objectIndex: baseIndex,
85 offset: baseOffset,
86 nextOffset: baseNextOffset,
87 oid: getOidHexAt(location.idx, baseIndex),
88 },
89 rows,
90 visited
91 );
92 if (!base) return undefined;
93 return {
94 type: base.type,
95 payload: applyGitDelta(base.payload, inflated),
96 };
97 }
98 
99 if (header.type === 7 && header.baseOid) {
100 const base = await readPackedObjectFromCatalogRows(env, rows, header.baseOid, visited);
101 if (!base) return undefined;
102 return {
103 type: base.type,
104 payload: applyGitDelta(base.payload, inflated),
105 };
106 }
107 
108 return undefined;
109 } finally {
110 visited.delete(visitKey);
111 }
112}
113 
114export async function readPackedObjectFromCatalogRows(
115 env: Env,
116 rows: PackCatalogRow[],
117 oid: string,
118 visited: Set<string> = new Set<string>()
119): Promise<{ type: "commit" | "tree" | "blob" | "tag"; payload: Uint8Array } | undefined> {
120 const location = await findCatalogObject(env, rows, oid);
121 if (!location) return undefined;
122 return await readPackedObjectFromLocation(env, location, rows, visited);
123}
124 
125export async function findCatalogPacksContainingOid(
126 env: Env,
127 rows: PackCatalogRow[],
128 oid: string
129): Promise<string[]> {
130 const oidLc = oid.toLowerCase();
131 const matches: string[] = [];
132 for (const row of rows) {
133 const idx = await loadIdxView(env, row.packKey, undefined, row.packBytes);
134 if (!idx) continue;
135 if (findOidIndex(idx, oidLc) >= 0) matches.push(row.packKey);
136 }
137 return matches;
138}