@indutny/protopiler 1.0.0-rc.1

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,570 @@
1
+ import {
2
+ TYPE_VARINT,
3
+ TYPE_I64,
4
+ TYPE_LEN,
5
+ TYPE_I32,
6
+ FIELD_UNKNOWN,
7
+ FIELD_SIZE_UNKNOWN,
8
+ FIELD_SIZE_32,
9
+ FIELD_SIZE_64,
10
+ FIELD_SIZE_FIXED_32,
11
+ FIELD_SIZE_FIXED_64,
12
+ FIELD_SIZE_MASK,
13
+ FIELD_ENC_SIGNED,
14
+ FIELD_ENC_UNSIGNED,
15
+ FIELD_ENC_RSIGNED,
16
+ FIELD_ENC_BOOL,
17
+ FIELD_ENC_IEEE754,
18
+ FIELD_ENC_BYTES,
19
+ FIELD_ENC_MESSAGE,
20
+ FIELD_ENC_STRING,
21
+ FIELD_ENC_MASK,
22
+ FIELD_FLAG_PACKED,
23
+ } from './constants.mjs';
24
+ import { stringByteLength, encodeStringInto } from './utf8.mjs';
25
+
26
+ export const ERR_VARINT_INVALID_ENCODING = 'Unexpected varint encoding';
27
+ export const ERR_UNEXPECTED_SIZE = 'Unexpected field size';
28
+ export const ERR_UNEXPECTED_ENCODING = 'Unexpected field encoding';
29
+ export const ERR_INVALID_OUTPUT_LEN = 'Invalid length of produced output';
30
+
31
+ export const FIELD_ID = 'i';
32
+ export const FIELD_TYPE = 't';
33
+ export const FIELD_VALUE = 'v';
34
+ export const FIELD_BYTE_LENGTH = 'b';
35
+
36
+ // This is faster than naive branched version by 15%
37
+ function intByteLength(value) {
38
+ if (value === 0) {
39
+ return 1;
40
+ }
41
+ return 5 - (((Math.clz32(value) + 3) / 7) | 0);
42
+ }
43
+
44
+ function bigintByteLength(raw) {
45
+ const value = BigInt.asUintN(64, raw);
46
+ if (value < 0x80n) {
47
+ return 1;
48
+ }
49
+ if (value < 0x4000n) {
50
+ return 2;
51
+ }
52
+ if (value < 0x200000n) {
53
+ return 3;
54
+ }
55
+ if (value < 0x10000000n) {
56
+ return 4;
57
+ }
58
+ if (value < 0x800000000n) {
59
+ return 5;
60
+ }
61
+ if (value < 0x40000000000n) {
62
+ return 6;
63
+ }
64
+ if (value < 0x2000000000000n) {
65
+ return 7;
66
+ }
67
+ if (value < 0x100000000000000n) {
68
+ return 8;
69
+ }
70
+ if (value < 0x8000000000000000n) {
71
+ return 9;
72
+ }
73
+ return 10;
74
+ }
75
+
76
+ function byteLength(fields) {
77
+ let size = 0;
78
+ for (const f of fields) {
79
+ const { i: id, t: field, v: value } = f;
80
+
81
+ // Unknown field
82
+ if (field === FIELD_UNKNOWN) {
83
+ size += value.length;
84
+ continue;
85
+ }
86
+
87
+ // Known field
88
+ size += intByteLength(id << 3);
89
+
90
+ // Packed fields
91
+ if ((field & FIELD_FLAG_PACKED) !== 0) {
92
+ let len = 0;
93
+ for (const elem of value) {
94
+ switch (field & FIELD_SIZE_MASK) {
95
+ case FIELD_SIZE_32: {
96
+ switch (field & FIELD_ENC_MASK) {
97
+ case FIELD_ENC_SIGNED:
98
+ case FIELD_ENC_UNSIGNED:
99
+ len += intByteLength(elem);
100
+ break;
101
+ case FIELD_ENC_BOOL:
102
+ len += intByteLength(elem ? 1 : 0);
103
+ break;
104
+ case FIELD_ENC_RSIGNED:
105
+ len += intByteLength((elem << 1) ^ (elem >> 31));
106
+ break;
107
+ default:
108
+ throw new Error(ERR_UNEXPECTED_ENCODING);
109
+ }
110
+ break;
111
+ }
112
+ case FIELD_SIZE_64: {
113
+ switch (field & FIELD_ENC_MASK) {
114
+ case FIELD_ENC_SIGNED:
115
+ case FIELD_ENC_UNSIGNED:
116
+ len += bigintByteLength(elem);
117
+ break;
118
+ case FIELD_ENC_RSIGNED:
119
+ len += bigintByteLength((elem << 1n) ^ (elem >> 63n));
120
+ break;
121
+ default:
122
+ throw new Error(ERR_UNEXPECTED_ENCODING);
123
+ }
124
+ break;
125
+ }
126
+ case FIELD_SIZE_FIXED_32:
127
+ len += 4;
128
+ break;
129
+ case FIELD_SIZE_FIXED_64:
130
+ len += 8;
131
+ break;
132
+ default:
133
+ throw new Error(ERR_UNEXPECTED_SIZE);
134
+ }
135
+ }
136
+
137
+ size += intByteLength(len) + len;
138
+
139
+ // Cache size for encoder
140
+ f.b = len;
141
+ continue;
142
+ }
143
+
144
+ switch (field & FIELD_SIZE_MASK) {
145
+ case FIELD_SIZE_32: {
146
+ switch (field & FIELD_ENC_MASK) {
147
+ case FIELD_ENC_SIGNED:
148
+ case FIELD_ENC_UNSIGNED:
149
+ size += intByteLength(value);
150
+ break;
151
+ case FIELD_ENC_RSIGNED:
152
+ size += intByteLength((value << 1) ^ (value >> 31));
153
+ break;
154
+ case FIELD_ENC_BOOL:
155
+ size += intByteLength(value ? 1 : 0);
156
+ break;
157
+ default:
158
+ throw new Error(ERR_UNEXPECTED_ENCODING);
159
+ }
160
+ break;
161
+ }
162
+ case FIELD_SIZE_64: {
163
+ switch (field & FIELD_ENC_MASK) {
164
+ case FIELD_ENC_SIGNED:
165
+ case FIELD_ENC_UNSIGNED:
166
+ size += bigintByteLength(value);
167
+ break;
168
+ case FIELD_ENC_RSIGNED:
169
+ size += bigintByteLength((value << 1n) ^ (value >> 63n));
170
+ break;
171
+ default:
172
+ throw new Error(ERR_UNEXPECTED_ENCODING);
173
+ }
174
+ break;
175
+ }
176
+ case FIELD_SIZE_FIXED_32:
177
+ size += 4;
178
+ break;
179
+ case FIELD_SIZE_FIXED_64:
180
+ size += 8;
181
+ break;
182
+ case FIELD_SIZE_UNKNOWN:
183
+ switch (field & FIELD_ENC_MASK) {
184
+ case FIELD_ENC_BYTES:
185
+ size += intByteLength(value.length) + value.length;
186
+ break;
187
+ case FIELD_ENC_STRING: {
188
+ const len = stringByteLength(value);
189
+ size += intByteLength(len) + len;
190
+
191
+ // Cache size for encoder
192
+ f.b = len;
193
+ break;
194
+ }
195
+ case FIELD_ENC_MESSAGE: {
196
+ const len = byteLength(value);
197
+ size += intByteLength(len) + len;
198
+
199
+ // Cache size for encoder
200
+ f.b = len;
201
+ break;
202
+ }
203
+ default:
204
+ throw new Error(ERR_UNEXPECTED_ENCODING);
205
+ }
206
+ break;
207
+ default:
208
+ throw new Error(ERR_UNEXPECTED_SIZE);
209
+ }
210
+ }
211
+ return size;
212
+ }
213
+
214
+ export function encode(fields) {
215
+ const size = byteLength(fields);
216
+ const result =
217
+ typeof Buffer === 'function'
218
+ ? // eslint-disable-next-line no-undef
219
+ Buffer.allocUnsafe(size)
220
+ : new Uint8Array(size);
221
+
222
+ const end = encodeInner(fields, result, 0);
223
+ if (end !== size) {
224
+ throw new Error(ERR_INVALID_OUTPUT_LEN);
225
+ }
226
+ return result;
227
+ }
228
+
229
+ function encodeUInt32(raw, data, start) {
230
+ const value = raw >>> 0;
231
+ let offset = start;
232
+
233
+ if (value < 0x80) {
234
+ data[offset++] = value;
235
+ } else if (value < 0x4000) {
236
+ data[offset++] = 0x80 | (value & 0x7f);
237
+ data[offset++] = value >>> 7;
238
+ } else if (value < 0x200000) {
239
+ data[offset++] = 0x80 | (value & 0x7f);
240
+ data[offset++] = 0x80 | ((value >>> 7) & 0x7f);
241
+ data[offset++] = value >>> 14;
242
+ } else if (value < 0x10000000) {
243
+ data[offset++] = 0x80 | (value & 0x7f);
244
+ data[offset++] = 0x80 | ((value >>> 7) & 0x7f);
245
+ data[offset++] = 0x80 | ((value >>> 14) & 0x7f);
246
+ data[offset++] = value >>> 21;
247
+ } else {
248
+ data[offset++] = 0x80 | (value & 0x7f);
249
+ data[offset++] = 0x80 | ((value >>> 7) & 0x7f);
250
+ data[offset++] = 0x80 | ((value >>> 14) & 0x7f);
251
+ data[offset++] = 0x80 | ((value >>> 21) & 0x7f);
252
+ data[offset++] = value >>> 28;
253
+ }
254
+
255
+ return offset;
256
+ }
257
+
258
+ function encodeUInt64(value, data, start) {
259
+ let offset = start;
260
+
261
+ const low = Number(BigInt.asUintN(32, value));
262
+ const high = Number(BigInt.asUintN(32, value >> 32n));
263
+
264
+ if (high === 0) {
265
+ return encodeUInt32(low, data, offset);
266
+ }
267
+
268
+ data[offset++] = 0x80 | (low & 0x7f);
269
+ data[offset++] = 0x80 | ((low >>> 7) & 0x7f);
270
+ data[offset++] = 0x80 | ((low >>> 14) & 0x7f);
271
+ data[offset++] = 0x80 | ((low >>> 21) & 0x7f);
272
+
273
+ if (high < 0x08) {
274
+ data[offset++] = (high << 4) | (low >>> 28);
275
+ } else if (high < 0x400) {
276
+ data[offset++] = 0x80 | ((high << 4) & 0x7f) | (low >>> 28);
277
+ data[offset++] = high >>> 3;
278
+ } else if (high < 0x20000) {
279
+ data[offset++] = 0x80 | ((high << 4) & 0x7f) | (low >>> 28);
280
+ data[offset++] = 0x80 | ((high >>> 3) & 0x7f);
281
+ data[offset++] = high >>> 10;
282
+ } else if (high < 0x1000000) {
283
+ data[offset++] = 0x80 | ((high << 4) & 0x7f) | (low >>> 28);
284
+ data[offset++] = 0x80 | ((high >>> 3) & 0x7f);
285
+ data[offset++] = 0x80 | ((high >>> 10) & 0x7f);
286
+ data[offset++] = high >>> 17;
287
+ } else if (high < 0x80000000) {
288
+ data[offset++] = 0x80 | ((high << 4) & 0x7f) | (low >>> 28);
289
+ data[offset++] = 0x80 | ((high >>> 3) & 0x7f);
290
+ data[offset++] = 0x80 | ((high >>> 10) & 0x7f);
291
+ data[offset++] = 0x80 | ((high >>> 17) & 0x7f);
292
+ data[offset++] = high >>> 24;
293
+ } else {
294
+ data[offset++] = 0x80 | ((high << 4) & 0x7f) | (low >>> 28);
295
+ data[offset++] = 0x80 | ((high >>> 3) & 0x7f);
296
+ data[offset++] = 0x80 | ((high >>> 10) & 0x7f);
297
+ data[offset++] = 0x80 | ((high >>> 17) & 0x7f);
298
+ data[offset++] = 0x80 | ((high >>> 24) & 0x7f);
299
+ data[offset++] = high >>> 31;
300
+ }
301
+
302
+ return offset;
303
+ }
304
+
305
+ const nativeUTF8 = new TextEncoder();
306
+
307
+ // Rough value based on benchmarks. Below this cost of calling into C++ is
308
+ // higher than decoding the string in JS.
309
+ const NATIVE_UTF8_THRESHOLD = 48;
310
+
311
+ function encodeInner(fields, data, start) {
312
+ let offset = start;
313
+ for (const f of fields) {
314
+ const { i: id, t: field, v: value } = f;
315
+
316
+ // Unknown field
317
+ if (field === FIELD_UNKNOWN) {
318
+ data.set(value, offset);
319
+ offset += value.length;
320
+ continue;
321
+ }
322
+
323
+ // Known field
324
+
325
+ // Packed fields
326
+ // Note: this duplicates a lot of code above, but only because V8
327
+ // refuses inlining it if moved to a separate function.
328
+ if ((field & FIELD_FLAG_PACKED) !== 0) {
329
+ // Encode LEN tag
330
+ offset = encodeUInt32((id << 3) | TYPE_LEN, data, offset);
331
+
332
+ // Encode length
333
+ offset = encodeUInt32(f.b, data, offset);
334
+
335
+ for (const elem of value) {
336
+ switch (field & FIELD_SIZE_MASK) {
337
+ case FIELD_SIZE_32:
338
+ switch (field & FIELD_ENC_MASK) {
339
+ case FIELD_ENC_SIGNED:
340
+ case FIELD_ENC_UNSIGNED:
341
+ offset = encodeUInt32(elem, data, offset);
342
+ break;
343
+ case FIELD_ENC_BOOL:
344
+ offset = encodeUInt32(elem ? 1 : 0, data, offset);
345
+ break;
346
+ case FIELD_ENC_RSIGNED:
347
+ offset = encodeUInt32((elem << 1) ^ (elem >> 31), data, offset);
348
+ break;
349
+ default:
350
+ throw new Error(ERR_UNEXPECTED_ENCODING);
351
+ }
352
+ break;
353
+ case FIELD_SIZE_64: {
354
+ switch (field & FIELD_ENC_MASK) {
355
+ case FIELD_ENC_SIGNED:
356
+ case FIELD_ENC_UNSIGNED:
357
+ offset = encodeUInt64(elem, data, offset);
358
+ break;
359
+ case FIELD_ENC_RSIGNED: {
360
+ offset = encodeUInt64(
361
+ (elem << 1n) ^ (elem >> 63n),
362
+ data,
363
+ offset
364
+ );
365
+ break;
366
+ }
367
+ default:
368
+ throw new Error(ERR_UNEXPECTED_ENCODING);
369
+ }
370
+ break;
371
+ }
372
+ case FIELD_SIZE_FIXED_32:
373
+ switch (field & FIELD_ENC_MASK) {
374
+ case FIELD_ENC_UNSIGNED:
375
+ case FIELD_ENC_SIGNED:
376
+ data[offset++] = elem & 0xff;
377
+ data[offset++] = (elem >>> 8) & 0xff;
378
+ data[offset++] = (elem >>> 16) & 0xff;
379
+ data[offset++] = (elem >>> 24) & 0xff;
380
+ break;
381
+ case FIELD_ENC_IEEE754:
382
+ new DataView(
383
+ data.buffer,
384
+ data.byteOffset + offset,
385
+ 4
386
+ ).setFloat32(0, elem, true);
387
+ offset += 4;
388
+ break;
389
+ default:
390
+ throw new Error(ERR_UNEXPECTED_ENCODING);
391
+ }
392
+ break;
393
+ case FIELD_SIZE_FIXED_64:
394
+ switch (field & FIELD_ENC_MASK) {
395
+ case FIELD_ENC_UNSIGNED:
396
+ case FIELD_ENC_SIGNED: {
397
+ const low = Number(BigInt.asUintN(32, elem));
398
+ const high = Number(BigInt.asUintN(32, elem >> 32n));
399
+
400
+ data[offset++] = low & 0xff;
401
+ data[offset++] = (low >>> 8) & 0xff;
402
+ data[offset++] = (low >>> 16) & 0xff;
403
+ data[offset++] = (low >>> 24) & 0xff;
404
+ data[offset++] = high & 0xff;
405
+ data[offset++] = (high >>> 8) & 0xff;
406
+ data[offset++] = (high >>> 16) & 0xff;
407
+ data[offset++] = (high >>> 24) & 0xff;
408
+ break;
409
+ }
410
+ case FIELD_ENC_IEEE754:
411
+ new DataView(
412
+ data.buffer,
413
+ data.byteOffset + offset,
414
+ 8
415
+ ).setFloat64(0, elem, true);
416
+ offset += 8;
417
+ break;
418
+ default:
419
+ throw new Error(ERR_UNEXPECTED_ENCODING);
420
+ }
421
+ break;
422
+ default:
423
+ throw new Error(ERR_UNEXPECTED_SIZE);
424
+ }
425
+ }
426
+ continue;
427
+ }
428
+
429
+ let type;
430
+ switch (field & FIELD_SIZE_MASK) {
431
+ case FIELD_SIZE_32:
432
+ case FIELD_SIZE_64:
433
+ type = TYPE_VARINT;
434
+ break;
435
+ case FIELD_SIZE_FIXED_32:
436
+ type = TYPE_I32;
437
+ break;
438
+ case FIELD_SIZE_FIXED_64:
439
+ type = TYPE_I64;
440
+ break;
441
+ case FIELD_SIZE_UNKNOWN:
442
+ type = TYPE_LEN;
443
+ break;
444
+ default:
445
+ throw new Error(ERR_UNEXPECTED_SIZE);
446
+ }
447
+
448
+ // Encode tag
449
+ offset = encodeUInt32((id << 3) | type, data, offset);
450
+
451
+ // Encode value
452
+ switch (field & FIELD_SIZE_MASK) {
453
+ case FIELD_SIZE_32:
454
+ switch (field & FIELD_ENC_MASK) {
455
+ case FIELD_ENC_SIGNED:
456
+ case FIELD_ENC_UNSIGNED:
457
+ offset = encodeUInt32(value, data, offset);
458
+ break;
459
+ case FIELD_ENC_RSIGNED:
460
+ offset = encodeUInt32((value << 1) ^ (value >> 31), data, offset);
461
+ break;
462
+ case FIELD_ENC_BOOL:
463
+ offset = encodeUInt32(value ? 1 : 0, data, offset);
464
+ break;
465
+ default:
466
+ throw new Error(ERR_UNEXPECTED_ENCODING);
467
+ }
468
+ break;
469
+ case FIELD_SIZE_64: {
470
+ switch (field & FIELD_ENC_MASK) {
471
+ case FIELD_ENC_SIGNED:
472
+ case FIELD_ENC_UNSIGNED:
473
+ offset = encodeUInt64(value, data, offset);
474
+ break;
475
+ case FIELD_ENC_RSIGNED: {
476
+ offset = encodeUInt64((value << 1n) ^ (value >> 63n), data, offset);
477
+ break;
478
+ }
479
+ default:
480
+ throw new Error(ERR_UNEXPECTED_ENCODING);
481
+ }
482
+ break;
483
+ }
484
+ case FIELD_SIZE_FIXED_32:
485
+ switch (field & FIELD_ENC_MASK) {
486
+ case FIELD_ENC_UNSIGNED:
487
+ case FIELD_ENC_SIGNED:
488
+ data[offset++] = value & 0xff;
489
+ data[offset++] = (value >>> 8) & 0xff;
490
+ data[offset++] = (value >>> 16) & 0xff;
491
+ data[offset++] = (value >>> 24) & 0xff;
492
+ break;
493
+ case FIELD_ENC_IEEE754:
494
+ new DataView(data.buffer, data.byteOffset + offset, 4).setFloat32(
495
+ 0,
496
+ value,
497
+ true
498
+ );
499
+ offset += 4;
500
+ break;
501
+ default:
502
+ throw new Error(ERR_UNEXPECTED_ENCODING);
503
+ }
504
+ break;
505
+ case FIELD_SIZE_FIXED_64:
506
+ switch (field & FIELD_ENC_MASK) {
507
+ case FIELD_ENC_UNSIGNED:
508
+ case FIELD_ENC_SIGNED: {
509
+ const low = Number(BigInt.asUintN(32, value));
510
+ const high = Number(BigInt.asUintN(32, value >> 32n));
511
+
512
+ data[offset++] = low & 0xff;
513
+ data[offset++] = (low >>> 8) & 0xff;
514
+ data[offset++] = (low >>> 16) & 0xff;
515
+ data[offset++] = (low >>> 24) & 0xff;
516
+ data[offset++] = high & 0xff;
517
+ data[offset++] = (high >>> 8) & 0xff;
518
+ data[offset++] = (high >>> 16) & 0xff;
519
+ data[offset++] = (high >>> 24) & 0xff;
520
+ break;
521
+ }
522
+ case FIELD_ENC_IEEE754:
523
+ new DataView(data.buffer, data.byteOffset + offset, 8).setFloat64(
524
+ 0,
525
+ value,
526
+ true
527
+ );
528
+ offset += 8;
529
+ break;
530
+ default:
531
+ throw new Error(ERR_UNEXPECTED_ENCODING);
532
+ }
533
+ break;
534
+ case FIELD_SIZE_UNKNOWN:
535
+ switch (field & FIELD_ENC_MASK) {
536
+ case FIELD_ENC_BYTES:
537
+ offset = encodeUInt32(value.length, data, offset);
538
+ data.set(value, offset);
539
+ offset += value.length;
540
+ break;
541
+ case FIELD_ENC_STRING: {
542
+ const len = f.b;
543
+ offset = encodeUInt32(len, data, offset);
544
+ if ('write' in data) {
545
+ // Buffer
546
+ data.write(value, offset, len);
547
+ offset += len;
548
+ } else if (len <= NATIVE_UTF8_THRESHOLD) {
549
+ offset = encodeStringInto(value, data, offset);
550
+ } else {
551
+ nativeUTF8.encodeInto(value, data.subarray(offset, offset + len));
552
+ offset += len;
553
+ }
554
+ break;
555
+ }
556
+ case FIELD_ENC_MESSAGE: {
557
+ offset = encodeUInt32(f.b, data, offset);
558
+ offset = encodeInner(value, data, offset);
559
+ break;
560
+ }
561
+ default:
562
+ throw new Error(ERR_UNEXPECTED_ENCODING);
563
+ }
564
+ break;
565
+ default:
566
+ throw new Error(ERR_UNEXPECTED_SIZE);
567
+ }
568
+ }
569
+ return offset;
570
+ }
package/lib/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ export { decode } from './decoder.mjs';
2
+ export { encode } from './encoder.mjs';
3
+ export { compile } from './compiler.mjs';