From f06a79ba62a66f0ec6be1ec83ba21e4b5a42b272 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Fri, 29 May 2026 18:21:08 +0000 Subject: [PATCH] perf(csharp): replace remaining O(A) .some dedup scans with seeded Sets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The using-static member-injection loop and the cross-namespace import loop both de-duped via `bucketArr.some((b) => b.def.nodeId === ...)` — O(A) per item. Both now use a per-file `Map>`, seeded lazily from the augmentation bucket (capturing entries from earlier passes), matching the global and named-namespace paths. Same dedup semantics, O(1) amortized. Verified: tsc --noEmit clean; csharp-hooks unit (27) + C# integration resolver (210) tests pass; prettier + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../languages/csharp/namespace-siblings.ts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts index 59d191af6..f4d29d7e7 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts @@ -443,6 +443,9 @@ export function populateCsharpNamespaceSiblings( if (struct === undefined) continue; const moduleScope = parsed.scopes.find((s) => s.kind === 'Module'); if (moduleScope === undefined) continue; + // Per-file de-dup sets keyed by simple name, seeded lazily from the + // augmentation bucket — replaces the per-member O(A) `.some` scan below. + const seenByName = new Map>(); for (const fullPath of struct.usingStaticPaths) { const lastDot = fullPath.lastIndexOf('.'); @@ -477,7 +480,14 @@ export function populateCsharpNamespaceSiblings( // `lookupBindingsAt`, which fans out across `bindings` + // `bindingAugmentations`. const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName); - if (bucketArr.some((b) => b.def.nodeId === memberDef.nodeId)) continue; + let seen = seenByName.get(simpleName); + if (seen === undefined) { + seen = new Set(); + for (const b of bucketArr) seen.add(b.def.nodeId); + seenByName.set(simpleName, seen); + } + if (seen.has(memberDef.nodeId)) continue; + seen.add(memberDef.nodeId); bucketArr.push({ def: memberDef, origin: 'import' }); } } @@ -493,6 +503,9 @@ export function populateCsharpNamespaceSiblings( for (const parsed of parsedFiles) { const moduleScope = parsed.scopes.find((s) => s.kind === 'Module'); if (moduleScope === undefined) continue; + // Per-file de-dup sets keyed by simple name, seeded lazily from the + // augmentation bucket — replaces the per-def O(A) `.some` scan below. + const seenByName = new Map>(); for (const imp of parsed.parsedImports) { if (imp.kind !== 'namespace') continue; const targetNs = imp.targetRaw; @@ -505,7 +518,14 @@ export function populateCsharpNamespaceSiblings( const simpleName = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q; if (simpleName === '') continue; const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName); - if (bucketArr.some((b) => b.def.nodeId === def.nodeId)) continue; + let seen = seenByName.get(simpleName); + if (seen === undefined) { + seen = new Set(); + for (const b of bucketArr) seen.add(b.def.nodeId); + seenByName.set(simpleName, seen); + } + if (seen.has(def.nodeId)) continue; + seen.add(def.nodeId); bucketArr.push({ def, origin: 'namespace' }); } }