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