binary-structures-values 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,335 @@
1
+ export class BitStreamWriter {
2
+ deletePreviousDataOnGetFixedBytes;
3
+ bufferByte = 0; // Accumulate 32 bits
4
+ bufferSize = 0;
5
+ byteArray;
6
+ bitPosition = 0;
7
+ nextFixedBytePosition = 0;
8
+ guaranteedCapacity;
9
+ static textEncoder = new TextEncoder();
10
+ _shared_array_buffer = new ArrayBuffer(8);
11
+ _shared_float_64_array = new Float64Array(this._shared_array_buffer);
12
+ _shared_float_32_array = new Float32Array(this._shared_array_buffer);
13
+ _shared_uint8_array = new Uint8Array(this._shared_array_buffer);
14
+ _shared_data_view = new DataView(this._shared_array_buffer);
15
+ constructor(deletePreviousDataOnGetFixedBytes = true) {
16
+ this.deletePreviousDataOnGetFixedBytes = deletePreviousDataOnGetFixedBytes;
17
+ const bytes = 1024;
18
+ this.byteArray = new Uint8Array(bytes);
19
+ this.guaranteedCapacity = bytes * 8;
20
+ }
21
+ clear() {
22
+ const bytes = 1024;
23
+ this.byteArray = new Uint8Array(bytes);
24
+ this.bitPosition = 0;
25
+ this.guaranteedCapacity = bytes * 8;
26
+ }
27
+ ensureCapacity(newBits) {
28
+ const needed = this.bitPosition + newBits;
29
+ if (needed <= this.guaranteedCapacity)
30
+ return;
31
+ const requiredBytes = Math.ceil(needed / 8);
32
+ const newBuffer = new Uint8Array(requiredBytes * 2);
33
+ newBuffer.set(this.byteArray);
34
+ this.byteArray = newBuffer;
35
+ this.guaranteedCapacity = requiredBytes * 16;
36
+ }
37
+ writeBit(bit) {
38
+ if (this.bitPosition >= this.guaranteedCapacity) {
39
+ this.ensureCapacity(1);
40
+ }
41
+ const byteIndex = this.bitPosition >>> 3;
42
+ const bitIndex = 7 - (this.bitPosition & 7);
43
+ this.byteArray[byteIndex] |= bit << bitIndex;
44
+ this.bitPosition++;
45
+ }
46
+ // INCOMPLETE: write with buffer, need to implement for all individual write functions
47
+ // private writeBitFast(bit: 0 | 1) {
48
+ // this.bufferByte = (this.bufferByte << 1) | (bit ? 1 : 0);
49
+ // this.bufferSize++;
50
+ //
51
+ // if (this.bufferSize === 8) { // Flush every 8 bits (1 byte)
52
+ // this.bitPosition += 8;
53
+ // if (this.bitPosition >= this.guaranteedCapacity) {
54
+ // this.ensureCapacity(8);
55
+ // }
56
+ // const byteIndex = this.bitPosition >>> 3;
57
+ // this.byteArray[byteIndex] = this.bufferByte & 0xFF;
58
+ // this.bufferByte = 0;
59
+ // this.bufferSize = 0;
60
+ // }
61
+ // }
62
+ writeBits(bits) {
63
+ const len = bits.length;
64
+ this.ensureCapacity(len);
65
+ const data = this.byteArray;
66
+ let pos = this.bitPosition;
67
+ for (let i = 0; i < len; i++) {
68
+ const byteIndex = pos >>> 3;
69
+ const bitIndex = 7 - (pos & 7);
70
+ data[byteIndex] |= (bits[i] ? 1 : 0) << bitIndex;
71
+ pos++;
72
+ }
73
+ this.bitPosition = pos;
74
+ }
75
+ // // dynamic sized int
76
+ // writeInt(value: number | bigint) {
77
+ //
78
+ // }
79
+ writeIntBytes(value, bytes) {
80
+ this.ensureCapacity(bytes * 8);
81
+ const val = typeof value === 'bigint' ? value : BigInt(Math.floor(value));
82
+ const data = this.byteArray;
83
+ let pos = this.bitPosition;
84
+ const bitOffset = pos & 7;
85
+ const byteIndex = pos >> 3;
86
+ // Extract bytes from BigInt (MSB first to match original bit order)
87
+ const byteValues = new Uint8Array(bytes);
88
+ for (let i = 0; i < bytes; i++) {
89
+ byteValues[i] = Number((val >> BigInt((bytes - 1 - i) * 8)) & 0xffn);
90
+ }
91
+ if (bitOffset === 0) {
92
+ // Byte-aligned: fastest path
93
+ for (let i = 0; i < bytes; i++) {
94
+ data[byteIndex + i] = byteValues[i];
95
+ }
96
+ }
97
+ else {
98
+ // Bit-unaligned: optimized bit insertion
99
+ const shift = 8 - bitOffset;
100
+ const mask = 0xFF >> bitOffset;
101
+ // First byte (partial)
102
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((byteValues[0] >> bitOffset) & mask);
103
+ // Middle bytes
104
+ for (let i = 1; i < bytes; i++) {
105
+ data[byteIndex + i] = (byteValues[i - 1] << shift) | ((byteValues[i] >> bitOffset) & mask);
106
+ }
107
+ // Last byte (partial)
108
+ data[byteIndex + bytes] = (data[byteIndex + bytes] & (0xFF << shift)) | (byteValues[bytes - 1] << shift);
109
+ }
110
+ this.bitPosition = pos + (bytes * 8);
111
+ }
112
+ // Specialized versions for common sizes (even faster)
113
+ writeInt8(value) {
114
+ this.ensureCapacity(8);
115
+ const val = Math.floor(value) & 0xFF;
116
+ const data = this.byteArray;
117
+ let pos = this.bitPosition;
118
+ const bitOffset = pos & 7;
119
+ const byteIndex = pos >> 3;
120
+ if (bitOffset === 0) {
121
+ data[byteIndex] = val;
122
+ }
123
+ else {
124
+ const shift = 8 - bitOffset;
125
+ const mask = 0xFF >> bitOffset;
126
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((val >> bitOffset) & mask);
127
+ data[byteIndex + 1] = (data[byteIndex + 1] & (0xFF << shift)) | (val << shift);
128
+ }
129
+ this.bitPosition = pos + 8;
130
+ }
131
+ writeInt16(value) {
132
+ this.ensureCapacity(16);
133
+ const val = Math.floor(value) & 0xFFFF;
134
+ const byte0 = (val >> 8) & 0xFF;
135
+ const byte1 = val & 0xFF;
136
+ const data = this.byteArray;
137
+ let pos = this.bitPosition;
138
+ const bitOffset = pos & 7;
139
+ const byteIndex = pos >> 3;
140
+ if (bitOffset === 0) {
141
+ data[byteIndex] = byte0;
142
+ data[byteIndex + 1] = byte1;
143
+ }
144
+ else {
145
+ const shift = 8 - bitOffset;
146
+ const mask = 0xFF >> bitOffset;
147
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((byte0 >> bitOffset) & mask);
148
+ data[byteIndex + 1] = (byte0 << shift) | ((byte1 >> bitOffset) & mask);
149
+ data[byteIndex + 2] = (data[byteIndex + 2] & (0xFF << shift)) | (byte1 << shift);
150
+ }
151
+ this.bitPosition = pos + 16;
152
+ }
153
+ writeInt32(value) {
154
+ this.ensureCapacity(32);
155
+ const val = Math.floor(value);
156
+ const byte0 = (val >>> 24) & 0xFF;
157
+ const byte1 = (val >>> 16) & 0xFF;
158
+ const byte2 = (val >>> 8) & 0xFF;
159
+ const byte3 = val & 0xFF;
160
+ const data = this.byteArray;
161
+ let pos = this.bitPosition;
162
+ const bitOffset = pos & 7;
163
+ const byteIndex = pos >> 3;
164
+ if (bitOffset === 0) {
165
+ data[byteIndex] = byte0;
166
+ data[byteIndex + 1] = byte1;
167
+ data[byteIndex + 2] = byte2;
168
+ data[byteIndex + 3] = byte3;
169
+ }
170
+ else {
171
+ const shift = 8 - bitOffset;
172
+ const mask = 0xFF >> bitOffset;
173
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((byte0 >> bitOffset) & mask);
174
+ data[byteIndex + 1] = (byte0 << shift) | ((byte1 >> bitOffset) & mask);
175
+ data[byteIndex + 2] = (byte1 << shift) | ((byte2 >> bitOffset) & mask);
176
+ data[byteIndex + 3] = (byte2 << shift) | ((byte3 >> bitOffset) & mask);
177
+ data[byteIndex + 4] = (data[byteIndex + 4] & (0xFF << shift)) | (byte3 << shift);
178
+ }
179
+ this.bitPosition = pos + 32;
180
+ }
181
+ writeBigInt(value) {
182
+ const encoded = value >= 0n ? value << 1n : ((-value) << 1n) - 1n;
183
+ let temp = encoded;
184
+ let bytesNeeded = 0;
185
+ do {
186
+ bytesNeeded++;
187
+ temp >>= 7n;
188
+ } while (temp !== 0n);
189
+ this.ensureCapacity(bytesNeeded * 8);
190
+ let val = encoded;
191
+ let currentBitPos = this.bitPosition;
192
+ do {
193
+ let byte = Number(val & 0x7fn);
194
+ val >>= 7n;
195
+ if (val !== 0n)
196
+ byte |= 0x80;
197
+ const byteIndex = currentBitPos >> 3;
198
+ const bitOffset = currentBitPos & 7;
199
+ if (bitOffset === 0) {
200
+ this.byteArray[byteIndex] = byte;
201
+ }
202
+ else {
203
+ const shift = 8 - bitOffset;
204
+ const mask = 0xFF >> bitOffset;
205
+ this.byteArray[byteIndex] = (this.byteArray[byteIndex] & ~mask) | ((byte >> bitOffset) & mask);
206
+ this.byteArray[byteIndex + 1] = (this.byteArray[byteIndex + 1] & (0xFF << shift)) | (byte << shift);
207
+ }
208
+ currentBitPos += 8;
209
+ } while (val !== 0n);
210
+ this.bitPosition = currentBitPos;
211
+ }
212
+ dumpBytes(label, bytes) {
213
+ console.log(label, Array.from(bytes).map(b => b.toString(2).padStart(8, '0')).join(' '));
214
+ }
215
+ writeFloat32(value) {
216
+ this.ensureCapacity(32);
217
+ this._shared_float_32_array[0] = value;
218
+ const data = this.byteArray;
219
+ let pos = this.bitPosition;
220
+ const bitOffset = pos & 7;
221
+ const byteIndex = pos >> 3;
222
+ if (bitOffset === 0) {
223
+ // Byte-aligned: fastest path
224
+ data[byteIndex] = this._shared_uint8_array[0];
225
+ data[byteIndex + 1] = this._shared_uint8_array[1];
226
+ data[byteIndex + 2] = this._shared_uint8_array[2];
227
+ data[byteIndex + 3] = this._shared_uint8_array[3];
228
+ }
229
+ else {
230
+ // Bit-unaligned: optimized bit insertion
231
+ const shift = 8 - bitOffset;
232
+ const mask = 0xFF >> bitOffset;
233
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((this._shared_uint8_array[0] >> bitOffset) & mask);
234
+ data[byteIndex + 1] = (this._shared_uint8_array[0] << shift) | ((this._shared_uint8_array[1] >> bitOffset) & mask);
235
+ data[byteIndex + 2] = (this._shared_uint8_array[1] << shift) | ((this._shared_uint8_array[2] >> bitOffset) & mask);
236
+ data[byteIndex + 3] = (this._shared_uint8_array[2] << shift) | ((this._shared_uint8_array[3] >> bitOffset) & mask);
237
+ data[byteIndex + 4] = (data[byteIndex + 4] & (0xFF << shift)) | (this._shared_uint8_array[3] << shift);
238
+ }
239
+ this.bitPosition = pos + 32;
240
+ }
241
+ writeFloat64(value) {
242
+ this.ensureCapacity(32);
243
+ this._shared_float_64_array[0] = value;
244
+ const data = this.byteArray;
245
+ let pos = this.bitPosition;
246
+ const bitOffset = pos & 7;
247
+ const byteIndex = pos >> 3;
248
+ if (bitOffset === 0) {
249
+ // Byte-aligned: fastest path
250
+ data[byteIndex] = this._shared_uint8_array[0];
251
+ data[byteIndex + 1] = this._shared_uint8_array[1];
252
+ data[byteIndex + 2] = this._shared_uint8_array[2];
253
+ data[byteIndex + 3] = this._shared_uint8_array[3];
254
+ data[byteIndex + 4] = this._shared_uint8_array[4];
255
+ data[byteIndex + 5] = this._shared_uint8_array[5];
256
+ data[byteIndex + 6] = this._shared_uint8_array[6];
257
+ data[byteIndex + 7] = this._shared_uint8_array[7];
258
+ }
259
+ else {
260
+ // Bit-unaligned: optimized bit insertion
261
+ const shift = 8 - bitOffset;
262
+ const mask = 0xFF >> bitOffset;
263
+ data[byteIndex] = (data[byteIndex] & ~mask) | ((this._shared_uint8_array[0] >> bitOffset) & mask);
264
+ data[byteIndex + 1] = (this._shared_uint8_array[0] << shift) | ((this._shared_uint8_array[1] >> bitOffset) & mask);
265
+ data[byteIndex + 2] = (this._shared_uint8_array[1] << shift) | ((this._shared_uint8_array[2] >> bitOffset) & mask);
266
+ data[byteIndex + 3] = (this._shared_uint8_array[2] << shift) | ((this._shared_uint8_array[3] >> bitOffset) & mask);
267
+ data[byteIndex + 4] = (this._shared_uint8_array[3] << shift) | ((this._shared_uint8_array[4] >> bitOffset) & mask);
268
+ data[byteIndex + 5] = (this._shared_uint8_array[4] << shift) | ((this._shared_uint8_array[5] >> bitOffset) & mask);
269
+ data[byteIndex + 6] = (this._shared_uint8_array[5] << shift) | ((this._shared_uint8_array[6] >> bitOffset) & mask);
270
+ data[byteIndex + 7] = (this._shared_uint8_array[6] << shift) | ((this._shared_uint8_array[7] >> bitOffset) & mask);
271
+ data[byteIndex + 8] = (data[byteIndex + 8] & (0xFF << shift)) | (this._shared_uint8_array[7] << shift);
272
+ }
273
+ this.bitPosition = pos + 64;
274
+ }
275
+ writeString(value, end = 0) {
276
+ const data = BitStreamWriter.textEncoder.encode(value);
277
+ const len = data.length;
278
+ this.ensureCapacity((len + 1) * 8);
279
+ for (let i = 0; i < len; i++) {
280
+ this.writeIntBytes(data[i], 1);
281
+ }
282
+ this.writeIntBytes(end, 1);
283
+ }
284
+ removeBits(count) {
285
+ if (this.nextFixedBytePosition > 0)
286
+ throw new Error(`Cannot remove bits after fixed bytes have been read (using getNextFixedBytes)`);
287
+ if (count > this.bitPosition)
288
+ throw new Error(`Cannot remove more bits (${count}) than are available (${this.bitPosition})`);
289
+ this.bitPosition -= count;
290
+ const data = this.byteArray;
291
+ const startBit = this.bitPosition;
292
+ for (let i = 0; i < count; i++) {
293
+ const absBit = startBit + i;
294
+ const byteIndex = absBit >>> 3;
295
+ const bitInByte = 7 - (absBit & 7);
296
+ data[byteIndex] &= ~(1 << bitInByte);
297
+ }
298
+ }
299
+ getBytes() {
300
+ const dataLength = Math.ceil(this.bitPosition / 8);
301
+ const output = new Uint8Array(dataLength + 1);
302
+ output.set(this.byteArray.subarray(0, dataLength));
303
+ output[dataLength] = (8 - (this.bitPosition & 7)) & 7;
304
+ return output;
305
+ }
306
+ getNextFixedBytes() {
307
+ const fixedBytesCount = this.bitPosition >>> 3;
308
+ const nextFixedBytes = this.byteArray.subarray(this.nextFixedBytePosition, fixedBytesCount);
309
+ this.nextFixedBytePosition = fixedBytesCount;
310
+ if (this.deletePreviousDataOnGetFixedBytes) {
311
+ // remove fixed bits from the beginning to free up space
312
+ this.byteArray = this.byteArray.subarray(fixedBytesCount);
313
+ this.bitPosition -= fixedBytesCount * 8;
314
+ this.guaranteedCapacity -= fixedBytesCount * 8;
315
+ }
316
+ return nextFixedBytes;
317
+ }
318
+ getRemainingBytesAfterFixed() {
319
+ const dataLength = Math.ceil(this.bitPosition / 8);
320
+ const remaining = this.byteArray.subarray(this.nextFixedBytePosition, dataLength);
321
+ const output = new Uint8Array(remaining.length + 1);
322
+ output.set(remaining);
323
+ output[remaining.length] = (8 - (this.bitPosition & 7)) & 7;
324
+ return output;
325
+ }
326
+ getNextFixedBuffer() {
327
+ return Buffer.from(this.getNextFixedBytes());
328
+ }
329
+ getRemainingBufferAfterFixed() {
330
+ return Buffer.from(this.getRemainingBytesAfterFixed());
331
+ }
332
+ getBuffer() {
333
+ return Buffer.from(this.getBytes());
334
+ }
335
+ }
@@ -0,0 +1,9 @@
1
+ import { SchemaBase } from "./schema-structure";
2
+ /**
3
+ * Compiles a SchemaBase into a high-performance writer function.
4
+ */
5
+ export declare function compileWriter(schema: SchemaBase): string;
6
+ /**
7
+ * Compiles a SchemaBase into a high-performance reader function.
8
+ */
9
+ export declare function compileReader(schema: SchemaBase): (reader: any) => any;
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Compiles a SchemaBase into a high-performance writer function.
3
+ */
4
+ export function compileWriter(schema) {
5
+ let code = "";
6
+ let varCount = 0;
7
+ function nextVar() {
8
+ return `v${varCount++}`;
9
+ }
10
+ function generateTypeEncoder(def, dataPath, indent) {
11
+ if ("types" in def) {
12
+ generateMultiTypeEncoder(def, dataPath, indent);
13
+ }
14
+ else {
15
+ generateSingleTypeEncoder(def, dataPath, indent);
16
+ }
17
+ }
18
+ function generateSingleTypeEncoder(def, dataPath, indent) {
19
+ const isOptional = 'optional' in def && def.optional;
20
+ if (isOptional) {
21
+ code += `${indent}if (${dataPath} === undefined || ${dataPath} === null) {\n`;
22
+ code += `${indent} writer.writeBit(false);\n`;
23
+ if ('default' in def && def.default !== undefined) {
24
+ // We handle defaults by not writing anything if it's missing,
25
+ // the reader will fill it in.
26
+ }
27
+ code += `${indent}} else {\n`;
28
+ code += `${indent} writer.writeBit(true);\n`;
29
+ generateBaseTypeEncoder(def, dataPath, indent + " ");
30
+ code += `${indent}}\n`;
31
+ }
32
+ else {
33
+ generateBaseTypeEncoder(def, dataPath, indent);
34
+ }
35
+ }
36
+ function generateBaseTypeEncoder(def, dataPath, indent) {
37
+ switch (def.type) {
38
+ case 'null':
39
+ break;
40
+ case 'boolean':
41
+ code += `${indent}writer.writeBit(${dataPath});\n`;
42
+ break;
43
+ case 'string':
44
+ code += `${indent}writer.writeString(${dataPath});\n`;
45
+ break;
46
+ case 'int-8':
47
+ code += `${indent}writer.writeInt8(${dataPath});\n`;
48
+ break;
49
+ case 'int-16':
50
+ code += `${indent}writer.writeInt16(${dataPath});\n`;
51
+ break;
52
+ case 'int-32':
53
+ code += `${indent}writer.writeInt32(${dataPath});\n`;
54
+ break;
55
+ case 'bigint':
56
+ code += `${indent}writer.writeBigInt(${dataPath});\n`;
57
+ break;
58
+ case 'float-32':
59
+ code += `${indent}writer.writeFloat32(${dataPath});\n`;
60
+ break;
61
+ case 'float-64':
62
+ code += `${indent}writer.writeFloat64(${dataPath});\n`;
63
+ break;
64
+ case 'object':
65
+ generateObjectEncoder(def.items, dataPath, indent);
66
+ break;
67
+ case 'array':
68
+ generateArrayEncoder(def.items, dataPath, indent);
69
+ break;
70
+ }
71
+ }
72
+ function generateObjectEncoder(schema, dataPath, indent) {
73
+ const keys = Object.keys(schema);
74
+ for (const key of keys) {
75
+ generateTypeEncoder(schema[key], `${dataPath}["${key}"]`, indent);
76
+ }
77
+ }
78
+ function generateArrayEncoder(itemDef, dataPath, indent) {
79
+ const iVar = nextVar();
80
+ const lenVar = nextVar();
81
+ code += `${indent}const ${lenVar} = ${dataPath}.length;\n`;
82
+ code += `${indent}writer.writeInt32(${lenVar});\n`;
83
+ code += `${indent}for (let ${iVar} = 0; ${iVar} < ${lenVar}; ${iVar}++) {\n`;
84
+ generateTypeEncoder(itemDef, `${dataPath}[${iVar}]`, indent + " ");
85
+ code += `${indent}}\n`;
86
+ }
87
+ function generateMultiTypeEncoder(def, dataPath, indent) {
88
+ const noOfDefs = def.types.length;
89
+ const minBits = Math.ceil(Math.log2(noOfDefs));
90
+ // This part is complex because we need to match the type at runtime.
91
+ // For now, I'll use a simplified version that matches exactly like the DataStreamWriter does.
92
+ // But for performance, we should ideally have a pre-calculated mapping.
93
+ code += `${indent}// MultiType matching for ${dataPath}\n`;
94
+ for (let i = 0; i < noOfDefs; i++) {
95
+ let typeDef = def.types[i];
96
+ if (typeof typeDef === 'string')
97
+ typeDef = { type: typeDef };
98
+ const condition = getTypeMatchCondition(typeDef, dataPath);
99
+ code += `${indent}${i === 0 ? 'if' : 'else if'} (${condition}) {\n`;
100
+ // Write type index bits
101
+ const bits = intToBits(i, minBits);
102
+ for (const bit of bits) {
103
+ code += `${indent} writer.writeBit(${bit});\n`;
104
+ }
105
+ generateSingleTypeEncoder(typeDef, dataPath, indent + " ");
106
+ code += `${indent}}\n`;
107
+ }
108
+ code += `${indent}else {\n`;
109
+ code += `${indent} throw new Error("No matching type found in MultiType for value at ${dataPath}");\n`;
110
+ code += `${indent}}\n`;
111
+ }
112
+ function getTypeMatchCondition(def, dataPath) {
113
+ switch (def.type) {
114
+ case 'null':
115
+ return `${dataPath} === null`;
116
+ case 'boolean':
117
+ return `typeof ${dataPath} === "boolean"`;
118
+ case 'string':
119
+ return `typeof ${dataPath} === "string"`;
120
+ case 'int-8':
121
+ case 'int-16':
122
+ case 'int-32':
123
+ case 'float-32':
124
+ case 'float-64':
125
+ return `typeof ${dataPath} === "number"`;
126
+ case 'bigint':
127
+ return `typeof ${dataPath} === "bigint"`;
128
+ case 'array':
129
+ return `Array.isArray(${dataPath})`;
130
+ case 'object':
131
+ return `typeof ${dataPath} === "object" && ${dataPath} !== null && !Array.isArray(${dataPath})`;
132
+ default:
133
+ return "false";
134
+ }
135
+ }
136
+ function intToBits(int, bitCount) {
137
+ const bits = [];
138
+ for (let i = 0; i < bitCount; i++) {
139
+ bits.push((int & (1 << i)) !== 0);
140
+ }
141
+ return bits;
142
+ }
143
+ generateObjectEncoder(schema, "data", "");
144
+ return code;
145
+ // return new Function("writer", "data", code) as (writer: any, data: any) => void;
146
+ }
147
+ /**
148
+ * Compiles a SchemaBase into a high-performance reader function.
149
+ */
150
+ export function compileReader(schema) {
151
+ let code = "const result = {};\n";
152
+ let varCount = 0;
153
+ function nextVar() {
154
+ return `v${varCount++}`;
155
+ }
156
+ function generateTypeDecoder(def, indent) {
157
+ if ("types" in def) {
158
+ return generateMultiTypeDecoder(def, indent);
159
+ }
160
+ else {
161
+ return generateSingleTypeDecoder(def, indent);
162
+ }
163
+ }
164
+ function generateSingleTypeDecoder(def, indent) {
165
+ const isOptional = 'optional' in def && def.optional;
166
+ const v = nextVar();
167
+ if (isOptional) {
168
+ code += `${indent}let ${v};\n`;
169
+ code += `${indent}if (reader.readBit()) {\n`;
170
+ const baseDecoder = generateBaseTypeDecoder(def, indent + " ");
171
+ code += `${indent} ${v} = ${baseDecoder};\n`;
172
+ code += `${indent}} else {\n`;
173
+ if ('default' in def && def.default !== undefined) {
174
+ code += `${indent} ${v} = ${JSON.stringify(def.default)};\n`;
175
+ }
176
+ else {
177
+ code += `${indent} ${v} = undefined;\n`;
178
+ }
179
+ code += `${indent}}\n`;
180
+ }
181
+ else {
182
+ const baseDecoder = generateBaseTypeDecoder(def, indent);
183
+ code += `${indent}const ${v} = ${baseDecoder};\n`;
184
+ }
185
+ return v;
186
+ }
187
+ function generateBaseTypeDecoder(def, indent) {
188
+ switch (def.type) {
189
+ case 'null':
190
+ return "null";
191
+ case 'boolean':
192
+ return "reader.readBit()";
193
+ case 'string':
194
+ return "reader.readString()";
195
+ case 'int-8':
196
+ return "reader.readInt8()";
197
+ case 'int-16':
198
+ return "reader.readInt16()";
199
+ case 'int-32':
200
+ return "reader.readInt32()";
201
+ case 'bigint':
202
+ return "reader.readBigInt()";
203
+ case 'float-32':
204
+ return "reader.readFloat32()";
205
+ case 'float-64':
206
+ return "reader.readFloat64()";
207
+ case 'object':
208
+ return generateObjectDecoder(def.items, indent);
209
+ case 'array':
210
+ return generateArrayDecoder(def.items, indent);
211
+ default:
212
+ throw new Error(`Unsupported type: ${def.type}`);
213
+ }
214
+ }
215
+ function generateObjectDecoder(schema, indent) {
216
+ const objVar = nextVar();
217
+ code += `${indent}const ${objVar} = {};\n`;
218
+ const keys = Object.keys(schema);
219
+ for (const key of keys) {
220
+ const valVar = generateTypeDecoder(schema[key], indent);
221
+ code += `${indent}${objVar}["${key}"] = ${valVar};\n`;
222
+ }
223
+ return objVar;
224
+ }
225
+ function generateArrayDecoder(itemDef, indent) {
226
+ const arrVar = nextVar();
227
+ const lenVar = nextVar();
228
+ const iVar = nextVar();
229
+ code += `${indent}const ${lenVar} = reader.readInt32();\n`;
230
+ code += `${indent}const ${arrVar} = new Array(${lenVar});\n`;
231
+ code += `${indent}for (let ${iVar} = 0; ${iVar} < ${lenVar}; ${iVar}++) {\n`;
232
+ const itemVar = generateTypeDecoder(itemDef, indent + " ");
233
+ code += `${indent} ${arrVar}[${iVar}] = ${itemVar};\n`;
234
+ code += `${indent}}\n`;
235
+ return arrVar;
236
+ }
237
+ function generateMultiTypeDecoder(def, indent) {
238
+ const resVar = nextVar();
239
+ const noOfDefs = def.types.length;
240
+ const minBits = Math.ceil(Math.log2(noOfDefs));
241
+ const indexVar = nextVar();
242
+ code += `${indent}let ${indexVar} = 0;\n`;
243
+ for (let i = 0; i < minBits; i++) {
244
+ code += `${indent}if (reader.readBit()) ${indexVar} |= (1 << ${i});\n`;
245
+ }
246
+ code += `${indent}let ${resVar};\n`;
247
+ code += `${indent}switch (${indexVar}) {\n`;
248
+ for (let i = 0; i < noOfDefs; i++) {
249
+ let typeDef = def.types[i];
250
+ if (typeof typeDef === 'string')
251
+ typeDef = { type: typeDef };
252
+ code += `${indent} case ${i}:\n`;
253
+ const valVar = generateSingleTypeDecoder(typeDef, indent + " ");
254
+ code += `${indent} ${resVar} = ${valVar};\n`;
255
+ code += `${indent} break;\n`;
256
+ }
257
+ code += `${indent} default: throw new Error("Invalid MultiType index: " + ${indexVar});\n`;
258
+ code += `${indent}}\n`;
259
+ return resVar;
260
+ }
261
+ const finalObjVar = generateObjectDecoder(schema, "");
262
+ code += `return ${finalObjVar};\n`;
263
+ return new Function("reader", code);
264
+ }
@@ -0,0 +1,23 @@
1
+ import { BitStreamReader } from "./bit-stream-reader";
2
+ import { DataValue, SchemaBase } from "./schema-structure";
3
+ export declare class DataStreamReader extends BitStreamReader {
4
+ private schema;
5
+ private metaData;
6
+ constructor();
7
+ constructor(initialData: Uint8Array | Buffer<ArrayBuffer>);
8
+ constructor(initialData: Uint8Array | Buffer<ArrayBuffer>, isComplete: true);
9
+ constructor(initialData: Uint8Array | Buffer<ArrayBuffer>, isComplete: false);
10
+ constructor(schema: string | SchemaBase);
11
+ constructor(schema: string | SchemaBase, initialData: Uint8Array | Buffer<ArrayBuffer>);
12
+ constructor(schema: string | SchemaBase, initialData: Uint8Array | Buffer<ArrayBuffer>, isComplete: true);
13
+ constructor(schema: string | SchemaBase, initialData: Uint8Array | Buffer<ArrayBuffer>, isComplete: false);
14
+ static compile(schema: SchemaBase): (reader: any) => any;
15
+ readCompiled(compiledFn: (reader: DataStreamReader) => any): any;
16
+ addData(data: Uint8Array): void;
17
+ isReadyToRead(): boolean;
18
+ read(): DataValue | null;
19
+ private readObject;
20
+ private readSingleType;
21
+ private readMultiType;
22
+ private readArray;
23
+ }