Skip to content

Commit d5f986a

Browse files
committed
limit normalize-unicode cache size
This implements a very bare-bones LRU algorithm to prevent the string normalization cache from growing unbounded. Fix: #432
1 parent e79814e commit d5f986a

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

src/normalize-unicode.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,30 @@
22
// This has been meticulously optimized for use
33
// within npm install on large package trees.
44
// Do not edit without careful benchmarking.
5-
const normalizeCache = Object.create(null)
6-
const { hasOwnProperty } = Object.prototype
7-
export const normalizeUnicode = (s: string) => {
8-
if (!hasOwnProperty.call(normalizeCache, s)) {
5+
const normalizeCache: Record<string, string> = Object.create(null)
6+
7+
// Limit the size of this. Very low-sophistication LRU cache
8+
const MAX = 10000
9+
const cache = new Set<string>()
10+
export const normalizeUnicode = (s: string): string => {
11+
if (!cache.has(s)) {
912
normalizeCache[s] = s.normalize('NFD')
13+
} else {
14+
cache.delete(s)
1015
}
11-
return normalizeCache[s]
16+
cache.add(s)
17+
18+
const ret = normalizeCache[s] as string
19+
20+
let i = cache.size - MAX
21+
// only prune when we're 10% over the max
22+
if (i > MAX / 10) {
23+
for (const s of cache) {
24+
cache.delete(s)
25+
delete normalizeCache[s]
26+
if (--i <= 0) break
27+
}
28+
}
29+
30+
return ret
1231
}

test/normalize-unicode.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ if (fakePlatform === 'win32') {
5353
})
5454
}
5555

56+
t.test('blow out the cache', t => {
57+
const cafBuf = Buffer.from([0x63, 0x61, 0x66])
58+
const e1 = Buffer.from([0x65, 0xcc, 0x81])
59+
const e2 = Buffer.from([0xc3, 0xa9])
60+
let cafe1 = cafBuf
61+
let cafe2 = cafBuf
62+
for (let i = 0; i < 11_001; i++) {
63+
cafe1 = Buffer.concat([cafe1, e1])
64+
cafe2 = Buffer.concat([cafe2, e2])
65+
66+
const n1 = normalizeUnicode(cafe1.toString())
67+
const n2 = normalizeUnicode(cafe2.toString())
68+
// don't test all of these, too noisy
69+
if (!(i % 500)) {
70+
t.equal(n1, n2)
71+
}
72+
}
73+
t.end()
74+
})
75+
5676
if (fakePlatform !== 'win32') {
5777
t.spawn(process.execPath, [__filename, 'win32'], {
5878
env: {

0 commit comments

Comments
 (0)