Skip to content

Commit

Permalink
perf: rewrite tree
Browse files Browse the repository at this point in the history
  • Loading branch information
tsctx committed Dec 7, 2023
1 parent 41c0e46 commit feb97c2
Showing 1 changed file with 110 additions and 33 deletions.
143 changes: 110 additions & 33 deletions lib/core/tree.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,133 @@
const { wellknownHeaderNames } = require('./constants')

class Tree {
#node = {}
#depth = 0
class Node {
/** @type {any} */
value
/** @type {null | Node} */
left
/** @type {null | Node} */
middle
/** @type {null | Node} */
right
/** @type {number} */
code
/**
* @param {Uint8Array} key
* @param {any} value
*/
constructor (key, value) {
const length = key.length
if (length === 0) {
throw new TypeError('Unreachable')
}
this.value = null
this.left = null
this.middle = null
this.right = null
this.code = key[0]
if (length > 1) {
this.middle = new Node(key.subarray(1), value)
} else {
this.value = value
}
}

/**
* Lowercases key and inserts its value into the node.
* @param {string} value
* @param {Uint8Array} key
* @param {any} value
*/
insert (value) {
const target = Buffer.from((value = value.toLowerCase()))
const length = target.length
let node = this.#node
for (let i = 0; i < length; ++i) {
const key = target[i]
node[key] ??= {}
// a-z
if (key >= 0x61 && key <= 0x7a) {
// Uppercase letters preserve references to lowercase ones.
node[key & ~32] ??= node[key]
add (key, value) {
const code = key[0]
if (this.code === code) {
if (key.length === 1) {
this.value = value
} else if (this.middle !== null) {
this.middle.add(key.subarray(1), value)
} else {
this.middle = new Node(key.subarray(1), value)
}
} else if (this.code < code) {
if (this.left !== null) {
this.left.add(key, value)
} else {
this.left = new Node(key, value)
}
} else {
if (this.right !== null) {
this.right.add(key, value)
} else {
this.right = new Node(key, value)
}
node = node[key]
}
node[256] = value
if (length > this.#depth) {
this.#depth = length
}

/**
* @param {Uint8Array} key
* @return {Node | null}
*/
search (key) {
const keylength = key.length
if (keylength === 0) {
return null
}
let index = 0
let node = this
while (node !== null && index < keylength) {
let code = key[index]
// A-Z
if (code >= 0x41 && code <= 0x5a) {
// Lowercase for uppercase.
code |= 32
}
while (node !== null) {
if (code === node.code) {
if (keylength === ++index) {
// Returns Node since it is the last key.
return node
}
node = node.middle
break
}
node = node.code < code ? node.left : node.right
}
}
return null
}
}

class TernarySearchTree {
/** @type {Node | null} */
node = null

/**
* Retrieves values from a node.
* @param {Uint8Array} key Node Key
* @returns {string | null} Value
* @param {Uint8Array} key
* @param {any} value
* */
insert (key, value) {
if (this.node === null) {
this.node = new Node(key, value)
} else {
this.node.add(key, value)
}
}

/**
* @param {Uint8Array} key
*/
lookup (key) {
const length = key.length
if (length > this.#depth) return null
let node = this.#node
for (let i = 0; i < length; ++i) {
if ((node = node?.[key[i]]) === undefined) return null
}
return node?.[256] ?? null
return this.node?.search(key)?.value ?? null
}
}

const tree = new Tree()
const tree = new TernarySearchTree()

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

module.exports = {
Tree,
TernarySearchTree,
tree
}

0 comments on commit feb97c2

Please sign in to comment.