Skip to content

Commit

Permalink
feat: add abilitiy for multiple delimitiers and multibyte delimiters …
Browse files Browse the repository at this point in the history
…to packet length parser
  • Loading branch information
bbeck authored and reconbot committed Dec 24, 2024
1 parent 929ebf8 commit 4b9eda1
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 20 deletions.
41 changes: 41 additions & 0 deletions packages/parser-packet-length/lib/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,45 @@ describe('DelimiterParser', () => {
assert.deepEqual(spy.getCall(1).args[0], Buffer.concat([Buffer.from([0xaa, 0x02, 0x13, 0x00]), Buffer.from('Each and Every One\n')]))
assert(spy.calledTwice)
})

it('works with multiple delimiters', () => {
const spy = sinon.spy()
const parser = new PacketLengthParser({ delimiter: [0xaa, 0xbb], lengthOffset: 2, packetOverhead: 4, lengthBytes: 2 })
parser.on('data', spy)
parser.write(Buffer.from('\xbb\x01\x0d'))
parser.write(Buffer.from('\x00I love hobits\xaa\x02\x13\x00Each '))
parser.write(Buffer.from('and Every One\n'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.concat([Buffer.from([0xbb, 0x01, 0x0d, 0x00]), Buffer.from('I love hobits')]))
assert.deepEqual(spy.getCall(1).args[0], Buffer.concat([Buffer.from([0xaa, 0x02, 0x13, 0x00]), Buffer.from('Each and Every One\n')]))
assert(spy.calledTwice)
})

it('works with multibyte delimiters', () => {
const spy = sinon.spy()
const parser = new PacketLengthParser({ delimiter: [0xababba], delimiterBytes: 3, lengthOffset: 3, packetOverhead: 4, lengthBytes: 1 })
parser.on('data', spy)
parser.write(Buffer.from([0xab, 0xab, 0xba, 0x0d]))
parser.write(Buffer.from('I love hobits'))
parser.write(Buffer.from([0xab, 0xab, 0xba, 0x13]))
parser.write(Buffer.from('Each and Every One\n'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.concat([Buffer.from([0xab, 0xab, 0xba, 0x0d]), Buffer.from('I love hobits')]))
assert.deepEqual(spy.getCall(1).args[0], Buffer.concat([Buffer.from([0xab, 0xab, 0xba, 0x13]), Buffer.from('Each and Every One\n')]))
assert(spy.calledTwice)
})

it('works with multiple multibyte delimiters', () => {
const spy = sinon.spy()
const parser = new PacketLengthParser({ delimiter: [0xababba, 0xbaddad], delimiterBytes: 3, lengthOffset: 3, packetOverhead: 4, lengthBytes: 1 })
parser.on('data', spy)
parser.write(Buffer.from([0xab, 0xab, 0xba, 0x0d]))
parser.write(Buffer.from('I love hobits'))
parser.write(Buffer.from([0xba, 0xdd, 0xad, 0x13]))
parser.write(Buffer.from('Each and Every One\n'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.concat([Buffer.from([0xab, 0xab, 0xba, 0x0d]), Buffer.from('I love hobits')]))
assert.deepEqual(spy.getCall(1).args[0], Buffer.concat([Buffer.from([0xba, 0xdd, 0xad, 0x13]), Buffer.from('Each and Every One\n')]))
assert(spy.calledTwice)
})
})
29 changes: 19 additions & 10 deletions packages/parser-packet-length/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Transform, TransformCallback, TransformOptions } from 'stream'

export interface PacketLengthOptions extends TransformOptions {
/** delimiter to use defaults to 0xaa */
delimiter?: number
/** delimiter(s) to use defaults to 0xaa */
delimiter?: number | number[]
/** delimiter length in bytes defaults to 1 */
delimiterBytes?: number
/** overhead of packet (including length, delimiter and any checksum / packet footer) defaults to 2 */
packetOverhead?: number
/** number of bytes containing length defaults to 1 */
Expand All @@ -29,14 +31,15 @@ export interface PacketLengthOptions extends TransformOptions {
export class PacketLengthParser extends Transform {
buffer: Buffer
start: boolean
opts: { delimiter: number, packetOverhead: number, lengthBytes: number, lengthOffset: number, maxLen: number }
opts: { delimiter: number[], delimiterBytes: number, packetOverhead: number, lengthBytes: number, lengthOffset: number, maxLen: number }
constructor(options: PacketLengthOptions = {}) {
super(options)

const { delimiter = 0xaa, packetOverhead = 2, lengthBytes = 1, lengthOffset = 1, maxLen = 0xff } = options
const { delimiter = [0xaa], delimiterBytes = 1, packetOverhead = 2, lengthBytes = 1, lengthOffset = 1, maxLen = 0xff } = options

this.opts = {
delimiter,
delimiter: ([] as number[]).concat(delimiter),
delimiterBytes,
packetOverhead,
lengthBytes,
lengthOffset,
Expand All @@ -51,13 +54,8 @@ export class PacketLengthParser extends Transform {
for (let ndx = 0; ndx < chunk.length; ndx++) {
const byte = chunk[ndx]

if (byte === this.opts.delimiter) {
this.start = true
}

if (true === this.start) {
this.buffer = Buffer.concat([this.buffer, Buffer.from([byte])])

if (this.buffer.length >= this.opts.lengthOffset + this.opts.lengthBytes) {
const len = this.buffer.readUIntLE(this.opts.lengthOffset, this.opts.lengthBytes)

Expand All @@ -67,6 +65,17 @@ export class PacketLengthParser extends Transform {
this.start = false
}
}
} else {
this.buffer = Buffer.concat([Buffer.from([byte]), this.buffer])
if (this.buffer.length === this.opts.delimiterBytes) {
const delimiter = this.buffer.readUIntLE(0, this.opts.delimiterBytes)
if (this.opts.delimiter.includes(delimiter)) {
this.start = true
this.buffer = Buffer.from([...this.buffer].reverse())
} else {
this.buffer = Buffer.from(this.buffer.subarray(1, this.buffer.length))
}
}
}
}

Expand Down
18 changes: 9 additions & 9 deletions packages/parser-start-end/lib/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('StartEndParser', () => {
parser.write(Buffer.from(`${STX}I love robots${ETX}${STX}Each `))
parser.write(Buffer.from(`and Every One${ETX}`))
parser.write(Buffer.from(STX))
parser.write(Buffer.from(`even you!`))
parser.write(Buffer.from('even you!'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.from('I love robots'))
assert.deepEqual(spy.getCall(1).args[0], Buffer.from('Each and Every One'))
Expand All @@ -35,7 +35,7 @@ describe('StartEndParser', () => {
parser.write(Buffer.from(`${STX}I love robots${ETX}${STX}Each `))
parser.write(Buffer.from(`and Every One${ETX}`))
parser.write(Buffer.from(STX))
parser.write(Buffer.from(`even you!`))
parser.write(Buffer.from('even you!'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.from(`${STX}I love robots`))
assert.deepEqual(spy.getCall(1).args[0], Buffer.from(`${STX}Each and Every One`))
Expand All @@ -53,7 +53,7 @@ describe('StartEndParser', () => {
parser.write(Buffer.from(`${STX}I love robots${ETX}${STX}Each `))
parser.write(Buffer.from(`and Every One${ETX}`))
parser.write(Buffer.from(STX))
parser.write(Buffer.from(`even you!`))
parser.write(Buffer.from('even you!'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.from(`I love robots${ETX}`))
assert.deepEqual(spy.getCall(1).args[0], Buffer.from(`Each and Every One${ETX}`))
Expand All @@ -72,7 +72,7 @@ describe('StartEndParser', () => {
parser.write(Buffer.from(`${STX}I love robots${ETX}${STX}Each `))
parser.write(Buffer.from(`and Every One${ETX}`))
parser.write(Buffer.from(STX))
parser.write(Buffer.from(`even you!`))
parser.write(Buffer.from('even you!'))

assert.deepEqual(spy.getCall(0).args[0], Buffer.from(`${STX}I love robots${ETX}`))
assert.deepEqual(spy.getCall(1).args[0], Buffer.from(`${STX}Each and Every One${ETX}`))
Expand Down Expand Up @@ -108,7 +108,7 @@ describe('StartEndParser', () => {
})
})

it(`throws when called with a 0 length startDelimiter`, () => {
it('throws when called with a 0 length startDelimiter', () => {
assert.throws(() => {
new StartEndParser({
startDelimiter: Buffer.alloc(0),
Expand All @@ -128,7 +128,7 @@ describe('StartEndParser', () => {
})
})

it(`throws when called with a 0 length endDelimiter`, () => {
it('throws when called with a 0 length endDelimiter', () => {
assert.throws(() => {
new StartEndParser({
endDelimiter: Buffer.alloc(0),
Expand All @@ -148,15 +148,15 @@ describe('StartEndParser', () => {
})
})

it(`allows setting of the startDelimiter and endDelimiter with strings`, () => {
it('allows setting of the startDelimiter and endDelimiter with strings', () => {
new StartEndParser({ startDelimiter: 'string', endDelimiter: 'string' })
})

it(`allows setting of the startDelimiter and endDelimiter with buffers`, () => {
it('allows setting of the startDelimiter and endDelimiter with buffers', () => {
new StartEndParser({ startDelimiter: Buffer.from([1]), endDelimiter: Buffer.from([1]) })
})

it(`allows setting of the startDelimiter and endDelimiter with arrays of bytes`, () => {
it('allows setting of the startDelimiter and endDelimiter with arrays of bytes', () => {
new StartEndParser({ startDelimiter: [1], endDelimiter: [1] })
})

Expand Down
2 changes: 1 addition & 1 deletion packages/parser-start-end/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class StartEndParser extends Transform {
if (startIndex >= 0 && endIndex >= 0) {
const block = data.slice(
startIndex + (this.includeStartDelimiter ? 0 : this.startDelimiter.length),
endIndex + (this.includeEndDelimiter ? this.endDelimiter.length : 0)
endIndex + (this.includeEndDelimiter ? this.endDelimiter.length : 0),
)

this.push(block)
Expand Down

0 comments on commit 4b9eda1

Please sign in to comment.