From 59617463d4652cca2b1febaed1f57bd43b03a222 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 06:47:44 +0000 Subject: [PATCH] Thread augmentation cache through borrow paths in fixChild The borrow-left and borrow-right branches in fixChild were rebuilding the receiving node without a cache, re-extracting augmentations for unchanged entries. Now combineCaches() merges the parent separator's cached augmentation with the child node's cached augmentations, and passes the combined map into make(). Co-Authored-By: Konstantin Wohlwend --- .../data-structures/augmented-tree-map.ts | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/apps/bulldozer-js/src/databases/piledriver/data-structures/augmented-tree-map.ts b/apps/bulldozer-js/src/databases/piledriver/data-structures/augmented-tree-map.ts index ab2c890b3..ace924512 100644 --- a/apps/bulldozer-js/src/databases/piledriver/data-structures/augmented-tree-map.ts +++ b/apps/bulldozer-js/src/databases/piledriver/data-structures/augmented-tree-map.ts @@ -361,6 +361,22 @@ export class AugmentedTreeMultiMap | undefined, + separator: Entry, StoredValue>, + childCache: ReadonlyMap | undefined, + ): ReadonlyMap | undefined { + const sepAug = parentCache?.get(separator); + if (sepAug === undefined && !childCache) return undefined; + const combined = new Map(); + if (childCache) for (const [e, a] of childCache) combined.set(e, a); + if (sepAug !== undefined) combined.set(separator, sepAug); + return combined; + } + private arity() { return Math.max(3, this.options.arity ?? defaultArity); } @@ -532,25 +548,29 @@ export class AugmentedTreeMultiMap this.minEntries()) { const leftNode = (await this.node(left.ref))!; - const childNode = child ? (await this.node(child.ref))! : { entries: [], children: [] }; + const loadedChild = child ? (await this.node(child.ref))! : undefined; + const childNode = loadedChild ?? { entries: [] as Entry, StoredValue>[], children: [] as Child, Augmentation>[] }; const separator = entries[index - 1]; const borrowedEntry = leftNode.entries[leftNode.entries.length - 1]; const borrowedChild = leftNode.children.at(-1); entries[index - 1] = borrowedEntry; children[index - 1] = await this.make(leftNode.entries.slice(0, -1), leftNode.children.slice(0, -1), this.buildEntryAugCache(leftNode)); - children[index] = await this.make([separator, ...childNode.entries], borrowedChild === undefined ? childNode.children : [borrowedChild, ...childNode.children]); + const borrowRecvCache = this.combineCaches(cachedEntryAugs, separator, loadedChild ? this.buildEntryAugCache(loadedChild) : undefined); + children[index] = await this.make([separator, ...childNode.entries], borrowedChild === undefined ? childNode.children : [borrowedChild, ...childNode.children], borrowRecvCache); return await this.afterDelete(entries, children, isRoot, true, cachedEntryAugs); } const right = children[index + 1]; if (right && right.entryCount > this.minEntries()) { - const childNode = child ? (await this.node(child.ref))! : { entries: [], children: [] }; + const loadedChild = child ? (await this.node(child.ref))! : undefined; + const childNode = loadedChild ?? { entries: [] as Entry, StoredValue>[], children: [] as Child, Augmentation>[] }; const rightNode = (await this.node(right.ref))!; const separator = entries[index]; const borrowedEntry = rightNode.entries[0]; const borrowedChild = rightNode.children.at(0); entries[index] = borrowedEntry; - children[index] = await this.make([...childNode.entries, separator], borrowedChild === undefined ? childNode.children : [...childNode.children, borrowedChild]); + const borrowRecvCache = this.combineCaches(cachedEntryAugs, separator, loadedChild ? this.buildEntryAugCache(loadedChild) : undefined); + children[index] = await this.make([...childNode.entries, separator], borrowedChild === undefined ? childNode.children : [...childNode.children, borrowedChild], borrowRecvCache); children[index + 1] = await this.make(rightNode.entries.slice(1), rightNode.children.slice(1), this.buildEntryAugCache(rightNode)); return await this.afterDelete(entries, children, isRoot, true, cachedEntryAugs); }