Skip to content

Commit

Permalink
fix bug when tail moves during initial fill
Browse files Browse the repository at this point in the history
Fix #192
  • Loading branch information
isaacs committed Feb 9, 2022
1 parent 51cda55 commit faf81e2
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class LRUCache {
this.head = 0
this.tail = 0
this.free = new Stack(max)
this.initialFill = 1
this.size = 0

if (typeof dispose === 'function') {
Expand Down Expand Up @@ -337,7 +338,7 @@ class LRUCache {
return this.free.pop()
}
// initial fill, just keep writing down the list
return this.tail + 1
return this.initialFill++
}

evict () {
Expand Down Expand Up @@ -448,6 +449,7 @@ class LRUCache {
}
this.head = 0
this.tail = 0
this.initialFill = 1
this.free.length = 0
this.calculatedSize = 0
this.size = 0
Expand Down
11 changes: 11 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,14 @@ t.test('peek does not disturb order', t => {
t.strictSame([...c.values()], [4, 3, 2, 1, 0])
t.end()
})

t.test('re-use key before initial fill completed', t => {
const c = new LRU({ max: 5 })
c.set(0, 0)
c.set(1, 1)
c.set(2, 2)
c.set(1, 2)
c.set(3, 3)
t.same([...c.entries()], [ [ 3, 3 ], [ 1, 2 ], [ 2, 2 ], [ 0, 0 ] ])
t.end()
})
53 changes: 53 additions & 0 deletions test/load-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
process.env.TAP_BAIL = '1'
const t = require('tap')
const LRU = require('../')
const max = 10000
const cache = new LRU({ max })

const crypto = require('crypto')
const getVal = () => [
crypto.randomBytes(12).toString('hex'),
crypto.randomBytes(12).toString('hex'),
crypto.randomBytes(12).toString('hex'),
crypto.randomBytes(12).toString('hex'),
]

const seeds = new Array(max * 3)
// fill up the cache to start
for (let i = 0; i < max * 3; i++) {
const v = getVal()
seeds[i] = [v.join(':'), v]
}
t.pass('generated seed data')

const verifyCache = () => {
// walk down the internal list ensuring that every key is the key to that
// index in the keyMap, and the value matches.
for (const [k, i] of (cache.map || cache.keyMap).entries()) {
const v = cache.valList[i]
const key = cache.keyList[i]
if (k !== key) {
t.equal(k, key, 'key at proper index', { k, i })
}
if (v.join(':') !== k) {
t.equal(k, v.join(':'), 'proper value at index', { v, i })
}
}
}

let cycles = 0
const cycleLength = Math.floor(max / 100)
while (cycles < max * 5) {
const r = Math.floor(Math.random() * seeds.length)
const seed = seeds[r]
const v = cache.get(seed[0])
if (v === undefined) {
cache.set(seed[0], seed[1])
} else {
t.equal(v.join(':'), seed[0], 'correct get ' + cycles, { seed, v })
}
if (++cycles % cycleLength === 0) {
verifyCache()
t.pass('cycle check ' + cycles)
}
}

0 comments on commit faf81e2

Please sign in to comment.