Skip to content

Commit

Permalink
different approach entirely
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed Jan 26, 2022
1 parent 5c74c2b commit e6c6e2b
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 321 deletions.
99 changes: 99 additions & 0 deletions address-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// a doubly-linked list of indexes and their prev/next
// basically a lru cache, but only for index numbers
// if we did not support delete(), we could do this with
// an AddressQueue that automatically shifted as it filled.

const AddressStack = require('./address-stack.js')

const SIZE = 0
const HEAD = 1
const TAIL = 2

class LRUAddressList {
constructor ({max}) {
this.max = max
const bits = max < 1 ? null
: max < Math.pow(2, 8) ? 8
: max < Math.pow(2, 16) ? 16
: max < Math.pow(2, 32) ? 32
: null
if (!bits) {
throw new TypeError('invalid max: ' + max)
}
const UintArray = bits === 8 ? Uint8Array
: bits === 16 ? Uint16Array
: Uint32Array
const bytes = bits >> 3
const lists = 2
const ab = new ArrayBuffer(max * lists * bytes)
this.prev = new Array(max).fill(0) //new UintArray(ab, 0, max)
this.next = new Array(max).fill(0) // new UintArray(ab, max, max)
this.free = new AddressStack({max})
this.size = 0
this.head = 0
this.tail = 0
this.evicted = null
}

newIndex () {
return this.isFull() ? this.evict()
: !this.free.isEmpty() ? this.free.pop()
: this.size ++
}

isFull () {
this.size === this.prev.length && this.free.isEmpty()
}

moveToTail (i) {
if (this.size === 0) {
this.head = this.tail = i
} else if (i !== this.tail) {
if (i !== this.head) {
this.next[this.prev[i]] = this.prev[i]
} else {
this.head = this.next[i]
}
this.prev[this.next[i]] = this.next[i]
}
this.tail = i
}

evict () {
const head = this.evicted = this.head
this.head = this.next[head]
return head
}

clear () {
this.free.clear()
this.size = 0
this.head = 0
this.tail = 0
}

delete (i) {
if (this.size !== 0) {
const head = this.head
const tail = this.tail
if (i !== head && i !== tail) {
// in the middle somewhere
this.next[this.prev[i]] = this.next[i]
this.prev[this.next[i]] = this.prev[i]
this.free.push(i)
} else if (i === head && i === tail) {
// only item, simpler to just reset, no reads
this.clear()
} else if (i === head) {
// head, but not the tail
this.head = this.next[head]
this.free.push(i)
} else if (i === tail) {
this.tail = this.prev[tail]
this.free.push(i)
}
}
}
}

module.exports = LRUAddressList
44 changes: 44 additions & 0 deletions address-stack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// a stack of up to max uints, big enough to hold addresses within max
// used to store free indexes within the lru
const top = 0
class AddressStack {
constructor ({max}) {
const bits = max < 1 ? null
: max < Math.pow(2, 8) ? 8
: max < Math.pow(2, 16) ? 16
: max < Math.pow(2, 32) ? 32
: null
if (!bits) {
throw new TypeError('invalid max: ' + max)
}
const UintArray = bits === 8 ? Uint8Array
: bits === 16 ? Uint16Array
: Uint32Array
const bytes = bits >> 3
const ab = new ArrayBuffer(max * bytes)
this.top = 0
this.heap = new Array(max).fill(0) // new UintArray(ab, max, bytes)
}
push (n) {
if (this.top === this.heap.length) {
throw new RangeError('stack overflow')
}
this.heap[this.top++] = n
}
pop (n) {
if (this.top === 0) {
return undefined
}
return this.heap[--this.top]
}
clear () {
this.top = 0
}
isEmpty () {
return this.top === 0
}
isFull () {
return this.top === this.heap.length
}
}
module.exports = AddressStack

0 comments on commit e6c6e2b

Please sign in to comment.