Skip to content

Commit 5e06df3

Browse files
authored
perf(writer): replace recursive reachable-ref walk with iterative stack (#52)
Avoids potential stack overflow on deeply nested object graphs, consistent with the fix in 6db670a.
1 parent 18c5d84 commit 5e06df3

File tree

1 file changed

+19
-18
lines changed

1 file changed

+19
-18
lines changed

src/writer/pdf-writer.ts

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -284,44 +284,45 @@ function collectReachableRefs(
284284
encrypt?: PdfRef,
285285
): Set<string> {
286286
const visited = new Set<string>();
287+
const stack: PdfObject[] = [root];
287288

288-
const walk = (obj: PdfObject | null): void => {
289-
if (obj === null) {
290-
return;
291-
}
289+
if (info) {
290+
stack.push(info);
291+
}
292+
293+
if (encrypt) {
294+
stack.push(encrypt);
295+
}
296+
297+
while (stack.length > 0) {
298+
const obj = stack.pop()!;
292299

293300
if (obj instanceof PdfRef) {
294301
const key = `${obj.objectNumber} ${obj.generation}`;
295302

296303
if (visited.has(key)) {
297-
return;
304+
continue;
298305
}
299306

300307
visited.add(key);
301308

302309
const resolved = registry.resolve(obj);
303310

304-
walk(resolved);
311+
if (resolved !== null) {
312+
stack.push(resolved);
313+
}
305314
} else if (obj instanceof PdfDict) {
306315
// PdfStream extends PdfDict, so this handles both
307316
for (const [, value] of obj) {
308-
walk(value);
317+
if (value != null) {
318+
stack.push(value);
319+
}
309320
}
310321
} else if (obj instanceof PdfArray) {
311322
for (const item of obj) {
312-
walk(item);
323+
stack.push(item);
313324
}
314325
}
315-
};
316-
317-
walk(root);
318-
319-
if (info) {
320-
walk(info);
321-
}
322-
323-
if (encrypt) {
324-
walk(encrypt);
325326
}
326327

327328
return visited;

0 commit comments

Comments
 (0)