Skip to content

Commit aa024ce

Browse files
committed
add loop and restBuffer types
1 parent c5c0e59 commit aa024ce

7 files changed

Lines changed: 219 additions & 2 deletions

File tree

src/compiler.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const utils = require('./datatypes/utils')
44
const conditionalDatatypes = require('./datatypes/compiler-conditional')
55
const structuresDatatypes = require('./datatypes/compiler-structures')
66
const utilsDatatypes = require('./datatypes/compiler-utils')
7+
const extrasDatatypes = require('./datatypes/compiler-extras')
78

89
const { tryCatch } = require('./utils')
910

@@ -270,6 +271,7 @@ class ReadCompiler extends Compiler {
270271
this.addTypes(conditionalDatatypes.Read)
271272
this.addTypes(structuresDatatypes.Read)
272273
this.addTypes(utilsDatatypes.Read)
274+
this.addTypes(extrasDatatypes.Read)
273275

274276
// Add default types
275277
for (const key in numeric) {
@@ -320,6 +322,7 @@ class WriteCompiler extends Compiler {
320322
this.addTypes(conditionalDatatypes.Write)
321323
this.addTypes(structuresDatatypes.Write)
322324
this.addTypes(utilsDatatypes.Write)
325+
this.addTypes(extrasDatatypes.Write)
323326

324327
// Add default types
325328
for (const key in numeric) {
@@ -370,6 +373,7 @@ class SizeOfCompiler extends Compiler {
370373
this.addTypes(conditionalDatatypes.SizeOf)
371374
this.addTypes(structuresDatatypes.SizeOf)
372375
this.addTypes(utilsDatatypes.SizeOf)
376+
this.addTypes(extrasDatatypes.SizeOf)
373377

374378
// Add default types
375379
for (const key in numeric) {

src/datatypes/compiler-extras.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* eslint-disable multiline-ternary */
2+
function validateNT (nt) {
3+
if (nt !== null && typeof nt !== 'number') throw new Error('Loop terminator must be a number like 0 or null for EOF')
4+
return nt !== null
5+
}
6+
7+
module.exports = {
8+
Read: {
9+
loop: ['parametrizable', (compiler, struct) => {
10+
const nt = struct.nt
11+
const hasTerminator = validateNT(nt)
12+
13+
return compiler.wrapCode(`
14+
const results = []
15+
let size = 0
16+
while (offset !== buffer.length) {
17+
${hasTerminator ? `
18+
const typ = ctx.i8(buffer, offset)
19+
if (typ.value === ${nt}) {
20+
return { value: results, size: size + 1 }
21+
}` : ''}
22+
const entry = ${compiler.callType(struct.type)}
23+
results.push(entry.value)
24+
offset += entry.size
25+
size += entry.size
26+
}
27+
return { value: results, size }
28+
`)
29+
}],
30+
restBuffer: ['native', (buffer, offset) => {
31+
return {
32+
value: buffer.slice(offset),
33+
size: buffer.length - offset
34+
}
35+
}]
36+
},
37+
Write: {
38+
loop: ['parametrizable', (compiler, struct) => {
39+
const nt = struct.nt
40+
const hasTerminator = validateNT(nt)
41+
42+
return compiler.wrapCode(`
43+
for (const key in value) {
44+
offset = ${compiler.callType('value[key]', struct.type)}
45+
}
46+
${hasTerminator ? `offset = ctx.i8(${nt}, buffer, offset)` : ''}
47+
return offset
48+
`)
49+
}],
50+
restBuffer: ['native', (value, buffer, offset) => {
51+
if (!(value instanceof Buffer)) value = Buffer.from(value)
52+
value.copy(buffer, offset)
53+
return offset + value.length
54+
}]
55+
},
56+
SizeOf: {
57+
loop: ['parametrizable', (compiler, struct) => {
58+
const nt = struct.nt
59+
const hasTerminator = validateNT(nt)
60+
61+
return compiler.wrapCode(`
62+
let size = ${hasTerminator ? '1' : '0'}
63+
for (const key in value) {
64+
size += ${compiler.callType('value[key]', struct.type)}
65+
}
66+
return size
67+
`)
68+
}],
69+
restBuffer: ['native', (value) => {
70+
if (!(value instanceof Buffer)) value = Buffer.from(value)
71+
return value.length
72+
}]
73+
}
74+
}

src/datatypes/extras.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
const { PartialReadError } = require('../utils')
2+
3+
module.exports = {
4+
loop: [readLoop, writeLoop, sizeOfLoop, require('../../ProtoDef/schemas/extras.json').loop],
5+
restBuffer: [readRestBuffer, writeRestBuffer, sizeOfRestBuffer, require('../../ProtoDef/schemas/extras.json').restBuffer]
6+
}
7+
8+
function readLoop (buffer, offset, typeArgs, rootNode) {
9+
if (!typeArgs) {
10+
throw new Error('typeArgs is required for loop type')
11+
}
12+
13+
const results = []
14+
const startOffset = offset
15+
const nt = typeArgs.nt
16+
const hasTerminator = nt !== null && typeof nt === 'number'
17+
18+
while (offset < buffer.length) {
19+
// Check for terminator if specified
20+
if (hasTerminator) {
21+
if (offset >= buffer.length) break
22+
const terminatorValue = buffer.readInt8(offset)
23+
if (terminatorValue === nt) {
24+
// Found terminator, consume it and return
25+
return {
26+
value: results,
27+
size: offset - startOffset + 1
28+
}
29+
}
30+
}
31+
32+
// Read the next element
33+
try {
34+
const entry = this.read(buffer, offset, typeArgs.type, rootNode)
35+
results.push(entry.value)
36+
offset += entry.size
37+
} catch (error) {
38+
if (error instanceof PartialReadError) {
39+
break
40+
}
41+
throw error
42+
}
43+
}
44+
45+
return {
46+
value: results,
47+
size: offset - startOffset
48+
}
49+
}
50+
51+
function writeLoop (value, buffer, offset, typeArgs, rootNode) {
52+
if (!typeArgs) {
53+
throw new Error('typeArgs is required for loop type')
54+
}
55+
56+
const nt = typeArgs.nt
57+
const hasTerminator = nt !== null && typeof nt === 'number'
58+
59+
// Write each element in the array
60+
for (const item of value) {
61+
offset = this.write(item, buffer, offset, typeArgs.type, rootNode)
62+
}
63+
64+
// Write terminator if specified
65+
if (hasTerminator) {
66+
buffer.writeInt8(nt, offset)
67+
offset++
68+
}
69+
70+
return offset
71+
}
72+
73+
function sizeOfLoop (value, typeArgs, rootNode) {
74+
if (!typeArgs) {
75+
throw new Error('typeArgs is required for loop type')
76+
}
77+
78+
const nt = typeArgs.nt
79+
const hasTerminator = nt !== null && typeof nt === 'number'
80+
let size = hasTerminator ? 1 : 0 // 1 byte for terminator if present
81+
82+
// Calculate size of all elements
83+
for (const item of value) {
84+
size += this.sizeOf(item, typeArgs.type, rootNode)
85+
}
86+
87+
return size
88+
}
89+
90+
function readRestBuffer (buffer, offset) {
91+
const remainingBuffer = buffer.slice(offset)
92+
return {
93+
value: remainingBuffer,
94+
size: remainingBuffer.length
95+
}
96+
}
97+
98+
function writeRestBuffer (value, buffer, offset) {
99+
if (!(value instanceof Buffer)) {
100+
value = Buffer.from(value)
101+
}
102+
value.copy(buffer, offset)
103+
return offset + value.length
104+
}
105+
106+
function sizeOfRestBuffer (value) {
107+
if (!(value instanceof Buffer)) {
108+
value = Buffer.from(value)
109+
}
110+
return value.length
111+
}

src/protodef.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class ProtoDef {
5252
this.addTypes(require('./datatypes/utils'))
5353
this.addTypes(require('./datatypes/structures'))
5454
this.addTypes(require('./datatypes/conditional'))
55+
this.addTypes(require('./datatypes/extras'))
5556
}
5657

5758
addProtocol (protocolData, path) {

test/dataTypes/datatypes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function testType (type, values) {
5555
})
5656
} else { testValue(type, value.value, value.buffer) }
5757
})
58-
if (type !== 'void') {
58+
if (type !== 'void' && type !== 'restBuffer' && !type.startsWith('loop_')) {
5959
it('reads 0 bytes and throw a PartialReadError', () => {
6060
try {
6161
proto.parsePacketBuffer(type, Buffer.alloc(0))

test/dataTypes/prepareTests.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const testData = [
2020
{
2121
kind: 'utils',
2222
data: require('../../ProtoDef/test/utils.json')
23+
},
24+
{
25+
kind: 'extras',
26+
data: require('../../ProtoDef/test/extras.json')
2327
}
2428
]
2529

@@ -30,7 +34,7 @@ function arrayToBuffer (arr) {
3034
function transformValues (type, values) {
3135
return values.map(val => {
3236
let value = val.value
33-
if (type.indexOf('buffer') === 0) {
37+
if (type.indexOf('buffer') === 0 || type.endsWith('Buffer')) {
3438
value = arrayToBuffer(value)
3539
} else if (value) {
3640
// we cannot use undefined type in JSON so need to convert it here to pass strictEquals test

test_extras.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const ProtoDef = require('./src/index').ProtoDef
2+
3+
const proto = new ProtoDef()
4+
5+
// Test basic types are available
6+
console.log('Available types:', Object.keys(proto.types))
7+
8+
// Test the restBuffer type
9+
try {
10+
const testBuffer = Buffer.from([1, 2, 3, 4, 5])
11+
const result = proto.read(testBuffer, 2, 'restBuffer')
12+
console.log('restBuffer result:', result)
13+
} catch (e) {
14+
console.log('restBuffer error:', e.message)
15+
}
16+
17+
// Test the loop type
18+
try {
19+
const result = proto.read(Buffer.from([1, 2, 3, 0]), 0, ['loop', { type: 'i8', nt: 0 }])
20+
console.log('loop result:', result)
21+
} catch (e) {
22+
console.log('loop error:', e.message)
23+
}

0 commit comments

Comments
 (0)