read-escape-sequence.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. 'use strict'
  2. /**
  3. * @typedef ReadEscapeSequenceResult
  4. * @property {number} endPos The position in the buffer that marks the end of
  5. * the escape sequence.
  6. * @property {Buffer} parsed The parsed escape sequence as a buffer of bytes.
  7. */
  8. /**
  9. * Read an escape sequence from a buffer. It reads until no escape sequences
  10. * are found. Thus, a sequence of escape sequences will all be parsed at once
  11. * and returned as a single result.
  12. *
  13. * @example A Single ASCII Sequence
  14. * const toParse = Buffer.from('foo\\#bar', 'utf8')
  15. * const {parsed, endPos} = readEscapeSequence({
  16. * searchBuffer: toParse,
  17. * startPos: 3
  18. * })
  19. * // => parsed = '#', endPos = 5
  20. *
  21. * @example Multiple ASCII Sequences In Succession
  22. * const toParse = Buffer.from('foo\\#\\!bar', 'utf8')
  23. * const {parsed, endPos} = readEscapeSequence({
  24. * searchBuffer: toParse,
  25. * startPos: 3
  26. * })
  27. * // => parsed = '#!', endPos = 7
  28. *
  29. * @param searchBuffer
  30. * @param startPos
  31. *
  32. * @returns {ReadEscapeSequenceResult}
  33. *
  34. * @throws When an escaped sequence is not a valid hexadecimal value.
  35. */
  36. module.exports = function readEscapeSequence ({ searchBuffer, startPos }) {
  37. // This is very similar to the `readEscapedCharacters` algorithm in
  38. // the `utils/escape-filter-value` in `@ldapjs/filter`. The difference being
  39. // that here we want to interpret the escape sequence instead of return it
  40. // as a string to be embedded in an "escaped" string.
  41. // https://github.com/ldapjs/filter/blob/1423612/lib/utils/escape-filter-value.js
  42. let pos = startPos
  43. const buf = []
  44. while (pos < searchBuffer.byteLength) {
  45. const char = searchBuffer[pos]
  46. const nextChar = searchBuffer[pos + 1]
  47. if (char !== 0x5c) {
  48. // End of sequence reached.
  49. break
  50. }
  51. const strHexCode = String.fromCharCode(nextChar) +
  52. String.fromCharCode(searchBuffer[pos + 2])
  53. const hexCode = parseInt(strHexCode, 16)
  54. if (Number.isNaN(hexCode) === true) {
  55. if (nextChar >= 0x00 && nextChar <= 0x7f) {
  56. // Sequence is a single escaped ASCII character
  57. buf.push(nextChar)
  58. pos += 2
  59. continue
  60. } else {
  61. throw Error('invalid hex code in escape sequence')
  62. }
  63. }
  64. if (hexCode >= 0xc0 && hexCode <= 0xdf) {
  65. // Sequence is a 2-byte utf-8 character.
  66. const secondByte = parseInt(
  67. String.fromCharCode(searchBuffer[pos + 4]) +
  68. String.fromCharCode(searchBuffer[pos + 5]),
  69. 16
  70. )
  71. buf.push(hexCode)
  72. buf.push(secondByte)
  73. pos += 6
  74. continue
  75. }
  76. if (hexCode >= 0xe0 && hexCode <= 0xef) {
  77. // Sequence is a 3-byte utf-8 character.
  78. const secondByte = parseInt(
  79. String.fromCharCode(searchBuffer[pos + 4]) +
  80. String.fromCharCode(searchBuffer[pos + 5]),
  81. 16
  82. )
  83. const thirdByte = parseInt(
  84. String.fromCharCode(searchBuffer[pos + 7]) +
  85. String.fromCharCode(searchBuffer[pos + 8]),
  86. 16
  87. )
  88. buf.push(hexCode)
  89. buf.push(secondByte)
  90. buf.push(thirdByte)
  91. pos += 9
  92. continue
  93. }
  94. if (hexCode >= 0xf0 && hexCode <= 0xf7) {
  95. // Sequence is a 4-byte utf-8 character.
  96. const secondByte = parseInt(
  97. String.fromCharCode(searchBuffer[pos + 4]) +
  98. String.fromCharCode(searchBuffer[pos + 5]),
  99. 16
  100. )
  101. const thirdByte = parseInt(
  102. String.fromCharCode(searchBuffer[pos + 7]) +
  103. String.fromCharCode(searchBuffer[pos + 8]),
  104. 16
  105. )
  106. const fourthByte = parseInt(
  107. String.fromCharCode(searchBuffer[pos + 10]) +
  108. String.fromCharCode(searchBuffer[pos + 11]),
  109. 16
  110. )
  111. buf.push(hexCode)
  112. buf.push(secondByte)
  113. buf.push(thirdByte)
  114. buf.push(fourthByte)
  115. pos += 12
  116. continue
  117. }
  118. // The escaped character should be a single hex value.
  119. buf.push(hexCode)
  120. pos += 3
  121. }
  122. return {
  123. endPos: pos,
  124. parsed: Buffer.from(buf)
  125. }
  126. }