diff --git a/gitnexus/src/core/ingestion/import-resolvers/r.ts b/gitnexus/src/core/ingestion/import-resolvers/r.ts index 15a06a5bc..aa34ffd4a 100644 --- a/gitnexus/src/core/ingestion/import-resolvers/r.ts +++ b/gitnexus/src/core/ingestion/import-resolvers/r.ts @@ -152,7 +152,17 @@ function packageDefIndex( return byPackage.get(pkg); } -const stripBackticks = (s: string): string => s.replace(/^`+|`+$/g, ''); +/** + * `s` without its leading and trailing backticks. Two scans, not `/^`+|`+$/g`: the pattern is + * quadratic on `x` + n backticks + `y`, and a NAMESPACE entry is repository-controlled text. + */ +const stripBackticks = (s: string): string => { + let start = 0; + let end = s.length; + while (start < end && s.charCodeAt(start) === 96) start++; + while (end > start && s.charCodeAt(end - 1) === 96) end--; + return s.slice(start, end); +}; /** * Resolve a NAMESPACE `importFrom(pkg, importedName)` to the `R/` file(s) of the diff --git a/gitnexus/test/unit/scope-resolution/r/r-namespace-imports.test.ts b/gitnexus/test/unit/scope-resolution/r/r-namespace-imports.test.ts index 45bff26dd..cc2e85e07 100644 --- a/gitnexus/test/unit/scope-resolution/r/r-namespace-imports.test.ts +++ b/gitnexus/test/unit/scope-resolution/r/r-namespace-imports.test.ts @@ -637,6 +637,16 @@ describe('resolveRImportTarget — named imports (NAMESPACE importFrom)', () => expect(resolve(t, 'scorelib', 'g')).toEqual([F]); }); + it('looks a name up among export entries that hold a long run of backticks in linear time', () => { + // `/^`+|`+$/g` took ~12 s per entry on this shape; the bound leaves a ten-fold margin. + const hostile = `x${'`'.repeat(200_000)}y`; + const s = setup({ [F]: src }, { exports: [hostile, 'f'] }); + const start = performance.now(); + const answers = [resolve(s, 'scorelib', 'hidden'), resolve(s, 'scorelib', 'f')]; + expect(performance.now() - start).toBeLessThan(2_000); + expect(answers).toEqual([null, [F]]); + }); + it('treats a package without a NAMESPACE file as exporting everything', () => { const s = setup({ [F]: src }, undefined); expect(resolve(s, 'scorelib', 'hidden')).toEqual([F]);