Skip to content

Commit

Permalink
perf(tree): reduce overhead of build TernarySearchTree
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx committed Feb 25, 2024
1 parent 95bd929 commit 5ec5f4b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 15 deletions.
20 changes: 14 additions & 6 deletions lib/core/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ class TstNode {
/** @type {number} */
code
/**
* @param {Uint8Array} key
* @param {string} key
* @param {any} value
* @param {number} index
*/
constructor (key, value, index) {
if (index === undefined || index >= key.length) {
throw new TypeError('Unreachable')
}
this.code = key[index]
const code = this.code = key.charCodeAt(index)
// check code is ascii string
if (code > 0x7F) {
throw new TypeError('key must be ascii string')
}
if (key.length !== ++index) {
this.middle = new TstNode(key, value, index)
} else {
Expand All @@ -34,7 +38,7 @@ class TstNode {
}

/**
* @param {Uint8Array} key
* @param {string} key
* @param {any} value
*/
add (key, value) {
Expand All @@ -45,7 +49,11 @@ class TstNode {
let index = 0
let node = this
while (true) {
const code = key[index]
const code = key.charCodeAt(index)
// check code is ascii string
if (code > 0x7F) {
throw new TypeError('key must be ascii string')
}
if (node.code === code) {
if (length === ++index) {
node.value = value
Expand Down Expand Up @@ -111,7 +119,7 @@ class TernarySearchTree {
node = null

/**
* @param {Uint8Array} key
* @param {string} key
* @param {any} value
* */
insert (key, value) {
Expand All @@ -135,7 +143,7 @@ const tree = new TernarySearchTree()

for (let i = 0; i < wellknownHeaderNames.length; ++i) {
const key = headerNameLowerCasedRecord[wellknownHeaderNames[i]]
tree.insert(Buffer.from(key), key)
tree.insert(key, key)
}

module.exports = {
Expand Down
26 changes: 17 additions & 9 deletions test/node-test/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,34 @@ const assert = require('node:assert')

describe('Ternary Search Tree', () => {
test('The empty key cannot be added.', () => {
assert.throws(() => new TernarySearchTree().insert(Buffer.from(''), ''))
assert.throws(() => new TernarySearchTree().insert('', ''))
const tst = new TernarySearchTree()
tst.insert(Buffer.from('a'), 'a')
assert.throws(() => tst.insert(Buffer.from(''), ''))
tst.insert('a', 'a')
assert.throws(() => tst.insert('', ''))
})

test('looking up not inserted key returns null', () => {
assert.throws(() => new TernarySearchTree().insert(Buffer.from(''), ''))
const tst = new TernarySearchTree()
tst.insert(Buffer.from('a'), 'a')
tst.insert('a', 'a')
assert.strictEqual(tst.lookup(Buffer.from('non-existant')), null)
})

test('not ascii string', () => {
assert.throws(() => new TernarySearchTree().insert('\x80', 'a'))
const tst = new TernarySearchTree()
tst.insert('a', 'a')
// throw on TstNode
assert.throws(() => tst.insert('a\x80', 'a'))
})

test('duplicate key', () => {
const tst = new TernarySearchTree()
const key = Buffer.from('a')
const key = 'a'
const lookupKey = Buffer.from(key)
tst.insert(key, 'a')
assert.strictEqual(tst.lookup(key), 'a')
assert.strictEqual(tst.lookup(lookupKey), 'a')
tst.insert(key, 'b')
assert.strictEqual(tst.lookup(key), 'b')
assert.strictEqual(tst.lookup(lookupKey), 'b')
})

test('tree', () => {
Expand Down Expand Up @@ -59,7 +67,7 @@ describe('Ternary Search Tree', () => {
const key = generateAsciiString((Math.random() * 100 + 5) | 0)
const lowerCasedKey = random[i] = key.toLowerCase()
randomBuffer[i] = Buffer.from(key)
tst.insert(Buffer.from(lowerCasedKey), lowerCasedKey)
tst.insert(lowerCasedKey, lowerCasedKey)
}

for (let i = 0; i < LENGTH; ++i) {
Expand Down

0 comments on commit 5ec5f4b

Please sign in to comment.