tree.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. 'use strict'
  2. const {
  3. wellknownHeaderNames,
  4. headerNameLowerCasedRecord
  5. } = require('./constants')
  6. class TstNode {
  7. /** @type {any} */
  8. value = null
  9. /** @type {null | TstNode} */
  10. left = null
  11. /** @type {null | TstNode} */
  12. middle = null
  13. /** @type {null | TstNode} */
  14. right = null
  15. /** @type {number} */
  16. code
  17. /**
  18. * @param {string} key
  19. * @param {any} value
  20. * @param {number} index
  21. */
  22. constructor (key, value, index) {
  23. if (index === undefined || index >= key.length) {
  24. throw new TypeError('Unreachable')
  25. }
  26. const code = this.code = key.charCodeAt(index)
  27. // check code is ascii string
  28. if (code > 0x7F) {
  29. throw new TypeError('key must be ascii string')
  30. }
  31. if (key.length !== ++index) {
  32. this.middle = new TstNode(key, value, index)
  33. } else {
  34. this.value = value
  35. }
  36. }
  37. /**
  38. * @param {string} key
  39. * @param {any} value
  40. */
  41. add (key, value) {
  42. const length = key.length
  43. if (length === 0) {
  44. throw new TypeError('Unreachable')
  45. }
  46. let index = 0
  47. let node = this
  48. while (true) {
  49. const code = key.charCodeAt(index)
  50. // check code is ascii string
  51. if (code > 0x7F) {
  52. throw new TypeError('key must be ascii string')
  53. }
  54. if (node.code === code) {
  55. if (length === ++index) {
  56. node.value = value
  57. break
  58. } else if (node.middle !== null) {
  59. node = node.middle
  60. } else {
  61. node.middle = new TstNode(key, value, index)
  62. break
  63. }
  64. } else if (node.code < code) {
  65. if (node.left !== null) {
  66. node = node.left
  67. } else {
  68. node.left = new TstNode(key, value, index)
  69. break
  70. }
  71. } else if (node.right !== null) {
  72. node = node.right
  73. } else {
  74. node.right = new TstNode(key, value, index)
  75. break
  76. }
  77. }
  78. }
  79. /**
  80. * @param {Uint8Array} key
  81. * @return {TstNode | null}
  82. */
  83. search (key) {
  84. const keylength = key.length
  85. let index = 0
  86. let node = this
  87. while (node !== null && index < keylength) {
  88. let code = key[index]
  89. // A-Z
  90. // First check if it is bigger than 0x5a.
  91. // Lowercase letters have higher char codes than uppercase ones.
  92. // Also we assume that headers will mostly contain lowercase characters.
  93. if (code <= 0x5a && code >= 0x41) {
  94. // Lowercase for uppercase.
  95. code |= 32
  96. }
  97. while (node !== null) {
  98. if (code === node.code) {
  99. if (keylength === ++index) {
  100. // Returns Node since it is the last key.
  101. return node
  102. }
  103. node = node.middle
  104. break
  105. }
  106. node = node.code < code ? node.left : node.right
  107. }
  108. }
  109. return null
  110. }
  111. }
  112. class TernarySearchTree {
  113. /** @type {TstNode | null} */
  114. node = null
  115. /**
  116. * @param {string} key
  117. * @param {any} value
  118. * */
  119. insert (key, value) {
  120. if (this.node === null) {
  121. this.node = new TstNode(key, value, 0)
  122. } else {
  123. this.node.add(key, value)
  124. }
  125. }
  126. /**
  127. * @param {Uint8Array} key
  128. * @return {any}
  129. */
  130. lookup (key) {
  131. return this.node?.search(key)?.value ?? null
  132. }
  133. }
  134. const tree = new TernarySearchTree()
  135. for (let i = 0; i < wellknownHeaderNames.length; ++i) {
  136. const key = headerNameLowerCasedRecord[wellknownHeaderNames[i]]
  137. tree.insert(key, key)
  138. }
  139. module.exports = {
  140. TernarySearchTree,
  141. tree
  142. }