138 lines
3.8 KiB
JavaScript
138 lines
3.8 KiB
JavaScript
'use strict'
|
|
|
|
/**
|
|
* @typedef ReadEscapeSequenceResult
|
|
* @property {number} endPos The position in the buffer that marks the end of
|
|
* the escape sequence.
|
|
* @property {Buffer} parsed The parsed escape sequence as a buffer of bytes.
|
|
*/
|
|
|
|
/**
|
|
* Read an escape sequence from a buffer. It reads until no escape sequences
|
|
* are found. Thus, a sequence of escape sequences will all be parsed at once
|
|
* and returned as a single result.
|
|
*
|
|
* @example A Single ASCII Sequence
|
|
* const toParse = Buffer.from('foo\\#bar', 'utf8')
|
|
* const {parsed, endPos} = readEscapeSequence({
|
|
* searchBuffer: toParse,
|
|
* startPos: 3
|
|
* })
|
|
* // => parsed = '#', endPos = 5
|
|
*
|
|
* @example Multiple ASCII Sequences In Succession
|
|
* const toParse = Buffer.from('foo\\#\\!bar', 'utf8')
|
|
* const {parsed, endPos} = readEscapeSequence({
|
|
* searchBuffer: toParse,
|
|
* startPos: 3
|
|
* })
|
|
* // => parsed = '#!', endPos = 7
|
|
*
|
|
* @param searchBuffer
|
|
* @param startPos
|
|
*
|
|
* @returns {ReadEscapeSequenceResult}
|
|
*
|
|
* @throws When an escaped sequence is not a valid hexadecimal value.
|
|
*/
|
|
module.exports = function readEscapeSequence ({ searchBuffer, startPos }) {
|
|
// This is very similar to the `readEscapedCharacters` algorithm in
|
|
// the `utils/escape-filter-value` in `@ldapjs/filter`. The difference being
|
|
// that here we want to interpret the escape sequence instead of return it
|
|
// as a string to be embedded in an "escaped" string.
|
|
// https://github.com/ldapjs/filter/blob/1423612/lib/utils/escape-filter-value.js
|
|
|
|
let pos = startPos
|
|
const buf = []
|
|
|
|
while (pos < searchBuffer.byteLength) {
|
|
const char = searchBuffer[pos]
|
|
const nextChar = searchBuffer[pos + 1]
|
|
|
|
if (char !== 0x5c) {
|
|
// End of sequence reached.
|
|
break
|
|
}
|
|
|
|
const strHexCode = String.fromCharCode(nextChar) +
|
|
String.fromCharCode(searchBuffer[pos + 2])
|
|
const hexCode = parseInt(strHexCode, 16)
|
|
if (Number.isNaN(hexCode) === true) {
|
|
if (nextChar >= 0x00 && nextChar <= 0x7f) {
|
|
// Sequence is a single escaped ASCII character
|
|
buf.push(nextChar)
|
|
pos += 2
|
|
continue
|
|
} else {
|
|
throw Error('invalid hex code in escape sequence')
|
|
}
|
|
}
|
|
|
|
if (hexCode >= 0xc0 && hexCode <= 0xdf) {
|
|
// Sequence is a 2-byte utf-8 character.
|
|
const secondByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 4]) +
|
|
String.fromCharCode(searchBuffer[pos + 5]),
|
|
16
|
|
)
|
|
buf.push(hexCode)
|
|
buf.push(secondByte)
|
|
pos += 6
|
|
continue
|
|
}
|
|
|
|
if (hexCode >= 0xe0 && hexCode <= 0xef) {
|
|
// Sequence is a 3-byte utf-8 character.
|
|
const secondByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 4]) +
|
|
String.fromCharCode(searchBuffer[pos + 5]),
|
|
16
|
|
)
|
|
const thirdByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 7]) +
|
|
String.fromCharCode(searchBuffer[pos + 8]),
|
|
16
|
|
)
|
|
buf.push(hexCode)
|
|
buf.push(secondByte)
|
|
buf.push(thirdByte)
|
|
pos += 9
|
|
continue
|
|
}
|
|
|
|
if (hexCode >= 0xf0 && hexCode <= 0xf7) {
|
|
// Sequence is a 4-byte utf-8 character.
|
|
const secondByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 4]) +
|
|
String.fromCharCode(searchBuffer[pos + 5]),
|
|
16
|
|
)
|
|
const thirdByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 7]) +
|
|
String.fromCharCode(searchBuffer[pos + 8]),
|
|
16
|
|
)
|
|
const fourthByte = parseInt(
|
|
String.fromCharCode(searchBuffer[pos + 10]) +
|
|
String.fromCharCode(searchBuffer[pos + 11]),
|
|
16
|
|
)
|
|
buf.push(hexCode)
|
|
buf.push(secondByte)
|
|
buf.push(thirdByte)
|
|
buf.push(fourthByte)
|
|
pos += 12
|
|
continue
|
|
}
|
|
|
|
// The escaped character should be a single hex value.
|
|
buf.push(hexCode)
|
|
pos += 3
|
|
}
|
|
|
|
return {
|
|
endPos: pos,
|
|
parsed: Buffer.from(buf)
|
|
}
|
|
}
|