UNPKG

26.5 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3*/
4
5"use strict";
6
7const memoize = require("../util/memoize");
8const SerializerMiddleware = require("./SerializerMiddleware");
9
10/** @typedef {import("./types").BufferSerializableType} BufferSerializableType */
11/** @typedef {import("./types").PrimitiveSerializableType} PrimitiveSerializableType */
12
13/*
14Format:
15
16File -> Section*
17
18Section -> NullsSection |
19 BooleansSection |
20 F64NumbersSection |
21 I32NumbersSection |
22 I8NumbersSection |
23 ShortStringSection |
24 StringSection |
25 BufferSection |
26 NopSection
27
28
29
30NullsSection ->
31 NullHeaderByte | Null2HeaderByte | Null3HeaderByte |
32 Nulls8HeaderByte 0xnn (n:count - 4) |
33 Nulls32HeaderByte n:ui32 (n:count - 260) |
34BooleansSection -> TrueHeaderByte | FalseHeaderByte | BooleansSectionHeaderByte BooleansCountAndBitsByte
35F64NumbersSection -> F64NumbersSectionHeaderByte f64*
36I32NumbersSection -> I32NumbersSectionHeaderByte i32*
37I8NumbersSection -> I8NumbersSectionHeaderByte i8*
38ShortStringSection -> ShortStringSectionHeaderByte ascii-byte*
39StringSection -> StringSectionHeaderByte i32:length utf8-byte*
40BufferSection -> BufferSectionHeaderByte i32:length byte*
41NopSection --> NopSectionHeaderByte
42
43ShortStringSectionHeaderByte -> 0b1nnn_nnnn (n:length)
44
45F64NumbersSectionHeaderByte -> 0b001n_nnnn (n:count - 1)
46I32NumbersSectionHeaderByte -> 0b010n_nnnn (n:count - 1)
47I8NumbersSectionHeaderByte -> 0b011n_nnnn (n:count - 1)
48
49NullsSectionHeaderByte -> 0b0001_nnnn (n:count - 1)
50BooleansCountAndBitsByte ->
51 0b0000_1xxx (count = 3) |
52 0b0001_xxxx (count = 4) |
53 0b001x_xxxx (count = 5) |
54 0b01xx_xxxx (count = 6) |
55 0b1nnn_nnnn (n:count - 7, 7 <= count <= 133)
56 0xff n:ui32 (n:count, 134 <= count < 2^32)
57
58StringSectionHeaderByte -> 0b0000_1110
59BufferSectionHeaderByte -> 0b0000_1111
60NopSectionHeaderByte -> 0b0000_1011
61FalseHeaderByte -> 0b0000_1100
62TrueHeaderByte -> 0b0000_1101
63
64RawNumber -> n (n <= 10)
65
66*/
67
68const LAZY_HEADER = 0x0b;
69const TRUE_HEADER = 0x0c;
70const FALSE_HEADER = 0x0d;
71const BOOLEANS_HEADER = 0x0e;
72const NULL_HEADER = 0x10;
73const NULL2_HEADER = 0x11;
74const NULL3_HEADER = 0x12;
75const NULLS8_HEADER = 0x13;
76const NULLS32_HEADER = 0x14;
77const NULL_AND_I8_HEADER = 0x15;
78const NULL_AND_I32_HEADER = 0x16;
79const NULL_AND_TRUE_HEADER = 0x17;
80const NULL_AND_FALSE_HEADER = 0x18;
81const STRING_HEADER = 0x1e;
82const BUFFER_HEADER = 0x1f;
83const I8_HEADER = 0x60;
84const I32_HEADER = 0x40;
85const F64_HEADER = 0x20;
86const SHORT_STRING_HEADER = 0x80;
87
88/** Uplift high-order bits */
89const NUMBERS_HEADER_MASK = 0xe0;
90const NUMBERS_COUNT_MASK = 0x1f; // 0b0001_1111
91const SHORT_STRING_LENGTH_MASK = 0x7f; // 0b0111_1111
92
93const HEADER_SIZE = 1;
94const I8_SIZE = 1;
95const I32_SIZE = 4;
96const F64_SIZE = 8;
97
98const MEASURE_START_OPERATION = Symbol("MEASURE_START_OPERATION");
99const MEASURE_END_OPERATION = Symbol("MEASURE_END_OPERATION");
100
101/** @typedef {typeof MEASURE_START_OPERATION} MEASURE_START_OPERATION_TYPE */
102/** @typedef {typeof MEASURE_END_OPERATION} MEASURE_END_OPERATION_TYPE */
103
104const identifyNumber = n => {
105 if (n === (n | 0)) {
106 if (n <= 127 && n >= -128) return 0;
107 if (n <= 2147483647 && n >= -2147483648) return 1;
108 }
109 return 2;
110};
111
112/**
113 * @typedef {PrimitiveSerializableType[]} DeserializedType
114 * @typedef {BufferSerializableType[]} SerializedType
115 * @extends {SerializerMiddleware<DeserializedType, SerializedType>}
116 */
117class BinaryMiddleware extends SerializerMiddleware {
118 /**
119 * @param {DeserializedType} data data
120 * @param {Object} context context object
121 * @returns {SerializedType|Promise<SerializedType>} serialized data
122 */
123 serialize(data, context) {
124 return this._serialize(data, context);
125 }
126
127 _serializeLazy(fn, context) {
128 return SerializerMiddleware.serializeLazy(fn, data =>
129 this._serialize(data, context)
130 );
131 }
132
133 /**
134 * @param {DeserializedType} data data
135 * @param {Object} context context object
136 * @param {{ leftOverBuffer: Buffer | null, allocationSize: number, increaseCounter: number }} allocationScope allocation scope
137 * @returns {SerializedType} serialized data
138 */
139 _serialize(
140 data,
141 context,
142 allocationScope = {
143 allocationSize: 1024,
144 increaseCounter: 0,
145 leftOverBuffer: null
146 }
147 ) {
148 /** @type {Buffer} */
149 let leftOverBuffer = null;
150 /** @type {BufferSerializableType[]} */
151 let buffers = [];
152 /** @type {Buffer} */
153 let currentBuffer = allocationScope ? allocationScope.leftOverBuffer : null;
154 allocationScope.leftOverBuffer = null;
155 let currentPosition = 0;
156 if (currentBuffer === null) {
157 currentBuffer = Buffer.allocUnsafe(allocationScope.allocationSize);
158 }
159 const allocate = bytesNeeded => {
160 if (currentBuffer !== null) {
161 if (currentBuffer.length - currentPosition >= bytesNeeded) return;
162 flush();
163 }
164 if (leftOverBuffer && leftOverBuffer.length >= bytesNeeded) {
165 currentBuffer = leftOverBuffer;
166 leftOverBuffer = null;
167 } else {
168 currentBuffer = Buffer.allocUnsafe(
169 Math.max(bytesNeeded, allocationScope.allocationSize)
170 );
171 if (
172 !(allocationScope.increaseCounter =
173 (allocationScope.increaseCounter + 1) % 4) &&
174 allocationScope.allocationSize < 16777216
175 ) {
176 allocationScope.allocationSize = allocationScope.allocationSize << 1;
177 }
178 }
179 };
180 const flush = () => {
181 if (currentBuffer !== null) {
182 if (currentPosition > 0) {
183 buffers.push(
184 Buffer.from(
185 currentBuffer.buffer,
186 currentBuffer.byteOffset,
187 currentPosition
188 )
189 );
190 }
191 if (
192 !leftOverBuffer ||
193 leftOverBuffer.length < currentBuffer.length - currentPosition
194 ) {
195 leftOverBuffer = Buffer.from(
196 currentBuffer.buffer,
197 currentBuffer.byteOffset + currentPosition,
198 currentBuffer.byteLength - currentPosition
199 );
200 }
201
202 currentBuffer = null;
203 currentPosition = 0;
204 }
205 };
206 const writeU8 = byte => {
207 currentBuffer.writeUInt8(byte, currentPosition++);
208 };
209 const writeU32 = ui32 => {
210 currentBuffer.writeUInt32LE(ui32, currentPosition);
211 currentPosition += 4;
212 };
213 const measureStack = [];
214 const measureStart = () => {
215 measureStack.push(buffers.length, currentPosition);
216 };
217 const measureEnd = () => {
218 const oldPos = measureStack.pop();
219 const buffersIndex = measureStack.pop();
220 let size = currentPosition - oldPos;
221 for (let i = buffersIndex; i < buffers.length; i++) {
222 size += buffers[i].length;
223 }
224 return size;
225 };
226 for (let i = 0; i < data.length; i++) {
227 const thing = data[i];
228 switch (typeof thing) {
229 case "function": {
230 if (!SerializerMiddleware.isLazy(thing))
231 throw new Error("Unexpected function " + thing);
232 /** @type {SerializedType | (() => SerializedType)} */
233 let serializedData =
234 SerializerMiddleware.getLazySerializedValue(thing);
235 if (serializedData === undefined) {
236 if (SerializerMiddleware.isLazy(thing, this)) {
237 flush();
238 allocationScope.leftOverBuffer = leftOverBuffer;
239 const result =
240 /** @type {(Exclude<PrimitiveSerializableType, Promise<PrimitiveSerializableType>>)[]} */ (
241 thing()
242 );
243 const data = this._serialize(result, context, allocationScope);
244 leftOverBuffer = allocationScope.leftOverBuffer;
245 allocationScope.leftOverBuffer = null;
246 SerializerMiddleware.setLazySerializedValue(thing, data);
247 serializedData = data;
248 } else {
249 serializedData = this._serializeLazy(thing, context);
250 flush();
251 buffers.push(serializedData);
252 break;
253 }
254 } else {
255 if (typeof serializedData === "function") {
256 flush();
257 buffers.push(serializedData);
258 break;
259 }
260 }
261 const lengths = [];
262 for (const item of serializedData) {
263 let last;
264 if (typeof item === "function") {
265 lengths.push(0);
266 } else if (item.length === 0) {
267 // ignore
268 } else if (
269 lengths.length > 0 &&
270 (last = lengths[lengths.length - 1]) !== 0
271 ) {
272 const remaining = 0xffffffff - last;
273 if (remaining >= item.length) {
274 lengths[lengths.length - 1] += item.length;
275 } else {
276 lengths.push(item.length - remaining);
277 lengths[lengths.length - 2] = 0xffffffff;
278 }
279 } else {
280 lengths.push(item.length);
281 }
282 }
283 allocate(5 + lengths.length * 4);
284 writeU8(LAZY_HEADER);
285 writeU32(lengths.length);
286 for (const l of lengths) {
287 writeU32(l);
288 }
289 flush();
290 for (const item of serializedData) {
291 buffers.push(item);
292 }
293 break;
294 }
295 case "string": {
296 const len = Buffer.byteLength(thing);
297 if (len >= 128 || len !== thing.length) {
298 allocate(len + HEADER_SIZE + I32_SIZE);
299 writeU8(STRING_HEADER);
300 writeU32(len);
301 currentBuffer.write(thing, currentPosition);
302 currentPosition += len;
303 } else if (len >= 70) {
304 allocate(len + HEADER_SIZE);
305 writeU8(SHORT_STRING_HEADER | len);
306
307 currentBuffer.write(thing, currentPosition, "latin1");
308 currentPosition += len;
309 } else {
310 allocate(len + HEADER_SIZE);
311 writeU8(SHORT_STRING_HEADER | len);
312
313 for (let i = 0; i < len; i++) {
314 currentBuffer[currentPosition++] = thing.charCodeAt(i);
315 }
316 }
317 break;
318 }
319 case "number": {
320 const type = identifyNumber(thing);
321 if (type === 0 && thing >= 0 && thing <= 10) {
322 // shortcut for very small numbers
323 allocate(I8_SIZE);
324 writeU8(thing);
325 break;
326 }
327 /**
328 * amount of numbers to write
329 * @type {number}
330 */
331 let n = 1;
332 for (; n < 32 && i + n < data.length; n++) {
333 const item = data[i + n];
334 if (typeof item !== "number") break;
335 if (identifyNumber(item) !== type) break;
336 }
337 switch (type) {
338 case 0:
339 allocate(HEADER_SIZE + I8_SIZE * n);
340 writeU8(I8_HEADER | (n - 1));
341 while (n > 0) {
342 currentBuffer.writeInt8(
343 /** @type {number} */ (data[i]),
344 currentPosition
345 );
346 currentPosition += I8_SIZE;
347 n--;
348 i++;
349 }
350 break;
351 case 1:
352 allocate(HEADER_SIZE + I32_SIZE * n);
353 writeU8(I32_HEADER | (n - 1));
354 while (n > 0) {
355 currentBuffer.writeInt32LE(
356 /** @type {number} */ (data[i]),
357 currentPosition
358 );
359 currentPosition += I32_SIZE;
360 n--;
361 i++;
362 }
363 break;
364 case 2:
365 allocate(HEADER_SIZE + F64_SIZE * n);
366 writeU8(F64_HEADER | (n - 1));
367 while (n > 0) {
368 currentBuffer.writeDoubleLE(
369 /** @type {number} */ (data[i]),
370 currentPosition
371 );
372 currentPosition += F64_SIZE;
373 n--;
374 i++;
375 }
376 break;
377 }
378
379 i--;
380 break;
381 }
382 case "boolean": {
383 let lastByte = thing === true ? 1 : 0;
384 const bytes = [];
385 let count = 1;
386 let n;
387 for (n = 1; n < 0xffffffff && i + n < data.length; n++) {
388 const item = data[i + n];
389 if (typeof item !== "boolean") break;
390 const pos = count & 0x7;
391 if (pos === 0) {
392 bytes.push(lastByte);
393 lastByte = item === true ? 1 : 0;
394 } else if (item === true) {
395 lastByte |= 1 << pos;
396 }
397 count++;
398 }
399 i += count - 1;
400 if (count === 1) {
401 allocate(HEADER_SIZE);
402 writeU8(lastByte === 1 ? TRUE_HEADER : FALSE_HEADER);
403 } else if (count === 2) {
404 allocate(HEADER_SIZE * 2);
405 writeU8(lastByte & 1 ? TRUE_HEADER : FALSE_HEADER);
406 writeU8(lastByte & 2 ? TRUE_HEADER : FALSE_HEADER);
407 } else if (count <= 6) {
408 allocate(HEADER_SIZE + I8_SIZE);
409 writeU8(BOOLEANS_HEADER);
410 writeU8((1 << count) | lastByte);
411 } else if (count <= 133) {
412 allocate(HEADER_SIZE + I8_SIZE + I8_SIZE * bytes.length + I8_SIZE);
413 writeU8(BOOLEANS_HEADER);
414 writeU8(0x80 | (count - 7));
415 for (const byte of bytes) writeU8(byte);
416 writeU8(lastByte);
417 } else {
418 allocate(
419 HEADER_SIZE +
420 I8_SIZE +
421 I32_SIZE +
422 I8_SIZE * bytes.length +
423 I8_SIZE
424 );
425 writeU8(BOOLEANS_HEADER);
426 writeU8(0xff);
427 writeU32(count);
428 for (const byte of bytes) writeU8(byte);
429 writeU8(lastByte);
430 }
431 break;
432 }
433 case "object": {
434 if (thing === null) {
435 let n;
436 for (n = 1; n < 0x100000104 && i + n < data.length; n++) {
437 const item = data[i + n];
438 if (item !== null) break;
439 }
440 i += n - 1;
441 if (n === 1) {
442 if (i + 1 < data.length) {
443 const next = data[i + 1];
444 if (next === true) {
445 allocate(HEADER_SIZE);
446 writeU8(NULL_AND_TRUE_HEADER);
447 i++;
448 } else if (next === false) {
449 allocate(HEADER_SIZE);
450 writeU8(NULL_AND_FALSE_HEADER);
451 i++;
452 } else if (typeof next === "number") {
453 const type = identifyNumber(next);
454 if (type === 0) {
455 allocate(HEADER_SIZE + I8_SIZE);
456 writeU8(NULL_AND_I8_HEADER);
457 currentBuffer.writeInt8(next, currentPosition);
458 currentPosition += I8_SIZE;
459 i++;
460 } else if (type === 1) {
461 allocate(HEADER_SIZE + I32_SIZE);
462 writeU8(NULL_AND_I32_HEADER);
463 currentBuffer.writeInt32LE(next, currentPosition);
464 currentPosition += I32_SIZE;
465 i++;
466 } else {
467 allocate(HEADER_SIZE);
468 writeU8(NULL_HEADER);
469 }
470 } else {
471 allocate(HEADER_SIZE);
472 writeU8(NULL_HEADER);
473 }
474 } else {
475 allocate(HEADER_SIZE);
476 writeU8(NULL_HEADER);
477 }
478 } else if (n === 2) {
479 allocate(HEADER_SIZE);
480 writeU8(NULL2_HEADER);
481 } else if (n === 3) {
482 allocate(HEADER_SIZE);
483 writeU8(NULL3_HEADER);
484 } else if (n < 260) {
485 allocate(HEADER_SIZE + I8_SIZE);
486 writeU8(NULLS8_HEADER);
487 writeU8(n - 4);
488 } else {
489 allocate(HEADER_SIZE + I32_SIZE);
490 writeU8(NULLS32_HEADER);
491 writeU32(n - 260);
492 }
493 } else if (Buffer.isBuffer(thing)) {
494 if (thing.length < 8192) {
495 allocate(HEADER_SIZE + I32_SIZE + thing.length);
496 writeU8(BUFFER_HEADER);
497 writeU32(thing.length);
498 thing.copy(currentBuffer, currentPosition);
499 currentPosition += thing.length;
500 } else {
501 allocate(HEADER_SIZE + I32_SIZE);
502 writeU8(BUFFER_HEADER);
503 writeU32(thing.length);
504 flush();
505 buffers.push(thing);
506 }
507 }
508 break;
509 }
510 case "symbol": {
511 if (thing === MEASURE_START_OPERATION) {
512 measureStart();
513 } else if (thing === MEASURE_END_OPERATION) {
514 const size = measureEnd();
515 allocate(HEADER_SIZE + I32_SIZE);
516 writeU8(I32_HEADER);
517 currentBuffer.writeInt32LE(size, currentPosition);
518 currentPosition += I32_SIZE;
519 }
520 break;
521 }
522 }
523 }
524 flush();
525
526 allocationScope.leftOverBuffer = leftOverBuffer;
527
528 // avoid leaking memory
529 currentBuffer = null;
530 leftOverBuffer = null;
531 allocationScope = undefined;
532 const _buffers = buffers;
533 buffers = undefined;
534 return _buffers;
535 }
536
537 /**
538 * @param {SerializedType} data data
539 * @param {Object} context context object
540 * @returns {DeserializedType|Promise<DeserializedType>} deserialized data
541 */
542 deserialize(data, context) {
543 return this._deserialize(data, context);
544 }
545
546 _createLazyDeserialized(content, context) {
547 return SerializerMiddleware.createLazy(
548 memoize(() => this._deserialize(content, context)),
549 this,
550 undefined,
551 content
552 );
553 }
554
555 _deserializeLazy(fn, context) {
556 return SerializerMiddleware.deserializeLazy(fn, data =>
557 this._deserialize(data, context)
558 );
559 }
560
561 /**
562 * @param {SerializedType} data data
563 * @param {Object} context context object
564 * @returns {DeserializedType} deserialized data
565 */
566 _deserialize(data, context) {
567 let currentDataItem = 0;
568 let currentBuffer = data[0];
569 let currentIsBuffer = Buffer.isBuffer(currentBuffer);
570 let currentPosition = 0;
571
572 const retainedBuffer = context.retainedBuffer || (x => x);
573
574 const checkOverflow = () => {
575 if (currentPosition >= currentBuffer.length) {
576 currentPosition = 0;
577 currentDataItem++;
578 currentBuffer =
579 currentDataItem < data.length ? data[currentDataItem] : null;
580 currentIsBuffer = Buffer.isBuffer(currentBuffer);
581 }
582 };
583 const isInCurrentBuffer = n => {
584 return currentIsBuffer && n + currentPosition <= currentBuffer.length;
585 };
586 const ensureBuffer = () => {
587 if (!currentIsBuffer) {
588 throw new Error(
589 currentBuffer === null
590 ? "Unexpected end of stream"
591 : "Unexpected lazy element in stream"
592 );
593 }
594 };
595 /**
596 * Reads n bytes
597 * @param {number} n amount of bytes to read
598 * @returns {Buffer} buffer with bytes
599 */
600 const read = n => {
601 ensureBuffer();
602 const rem = currentBuffer.length - currentPosition;
603 if (rem < n) {
604 const buffers = [read(rem)];
605 n -= rem;
606 ensureBuffer();
607 while (currentBuffer.length < n) {
608 const b = /** @type {Buffer} */ (currentBuffer);
609 buffers.push(b);
610 n -= b.length;
611 currentDataItem++;
612 currentBuffer =
613 currentDataItem < data.length ? data[currentDataItem] : null;
614 currentIsBuffer = Buffer.isBuffer(currentBuffer);
615 ensureBuffer();
616 }
617 buffers.push(read(n));
618 return Buffer.concat(buffers);
619 }
620 const b = /** @type {Buffer} */ (currentBuffer);
621 const res = Buffer.from(b.buffer, b.byteOffset + currentPosition, n);
622 currentPosition += n;
623 checkOverflow();
624 return res;
625 };
626 /**
627 * Reads up to n bytes
628 * @param {number} n amount of bytes to read
629 * @returns {Buffer} buffer with bytes
630 */
631 const readUpTo = n => {
632 ensureBuffer();
633 const rem = currentBuffer.length - currentPosition;
634 if (rem < n) {
635 n = rem;
636 }
637 const b = /** @type {Buffer} */ (currentBuffer);
638 const res = Buffer.from(b.buffer, b.byteOffset + currentPosition, n);
639 currentPosition += n;
640 checkOverflow();
641 return res;
642 };
643 const readU8 = () => {
644 ensureBuffer();
645 /**
646 * There is no need to check remaining buffer size here
647 * since {@link checkOverflow} guarantees at least one byte remaining
648 */
649 const byte = /** @type {Buffer} */ (currentBuffer).readUInt8(
650 currentPosition
651 );
652 currentPosition += I8_SIZE;
653 checkOverflow();
654 return byte;
655 };
656 const readU32 = () => {
657 return read(I32_SIZE).readUInt32LE(0);
658 };
659 const readBits = (data, n) => {
660 let mask = 1;
661 while (n !== 0) {
662 result.push((data & mask) !== 0);
663 mask = mask << 1;
664 n--;
665 }
666 };
667 const dispatchTable = Array.from({ length: 256 }).map((_, header) => {
668 switch (header) {
669 case LAZY_HEADER:
670 return () => {
671 const count = readU32();
672 const lengths = Array.from({ length: count }).map(() => readU32());
673 const content = [];
674 for (let l of lengths) {
675 if (l === 0) {
676 if (typeof currentBuffer !== "function") {
677 throw new Error("Unexpected non-lazy element in stream");
678 }
679 content.push(currentBuffer);
680 currentDataItem++;
681 currentBuffer =
682 currentDataItem < data.length ? data[currentDataItem] : null;
683 currentIsBuffer = Buffer.isBuffer(currentBuffer);
684 } else {
685 do {
686 const buf = readUpTo(l);
687 l -= buf.length;
688 content.push(retainedBuffer(buf));
689 } while (l > 0);
690 }
691 }
692 result.push(this._createLazyDeserialized(content, context));
693 };
694 case BUFFER_HEADER:
695 return () => {
696 const len = readU32();
697 result.push(retainedBuffer(read(len)));
698 };
699 case TRUE_HEADER:
700 return () => result.push(true);
701 case FALSE_HEADER:
702 return () => result.push(false);
703 case NULL3_HEADER:
704 return () => result.push(null, null, null);
705 case NULL2_HEADER:
706 return () => result.push(null, null);
707 case NULL_HEADER:
708 return () => result.push(null);
709 case NULL_AND_TRUE_HEADER:
710 return () => result.push(null, true);
711 case NULL_AND_FALSE_HEADER:
712 return () => result.push(null, false);
713 case NULL_AND_I8_HEADER:
714 return () => {
715 if (currentIsBuffer) {
716 result.push(
717 null,
718 /** @type {Buffer} */ (currentBuffer).readInt8(currentPosition)
719 );
720 currentPosition += I8_SIZE;
721 checkOverflow();
722 } else {
723 result.push(null, read(I8_SIZE).readInt8(0));
724 }
725 };
726 case NULL_AND_I32_HEADER:
727 return () => {
728 result.push(null);
729 if (isInCurrentBuffer(I32_SIZE)) {
730 result.push(
731 /** @type {Buffer} */ (currentBuffer).readInt32LE(
732 currentPosition
733 )
734 );
735 currentPosition += I32_SIZE;
736 checkOverflow();
737 } else {
738 result.push(read(I32_SIZE).readInt32LE(0));
739 }
740 };
741 case NULLS8_HEADER:
742 return () => {
743 const len = readU8() + 4;
744 for (let i = 0; i < len; i++) {
745 result.push(null);
746 }
747 };
748 case NULLS32_HEADER:
749 return () => {
750 const len = readU32() + 260;
751 for (let i = 0; i < len; i++) {
752 result.push(null);
753 }
754 };
755 case BOOLEANS_HEADER:
756 return () => {
757 const innerHeader = readU8();
758 if ((innerHeader & 0xf0) === 0) {
759 readBits(innerHeader, 3);
760 } else if ((innerHeader & 0xe0) === 0) {
761 readBits(innerHeader, 4);
762 } else if ((innerHeader & 0xc0) === 0) {
763 readBits(innerHeader, 5);
764 } else if ((innerHeader & 0x80) === 0) {
765 readBits(innerHeader, 6);
766 } else if (innerHeader !== 0xff) {
767 let count = (innerHeader & 0x7f) + 7;
768 while (count > 8) {
769 readBits(readU8(), 8);
770 count -= 8;
771 }
772 readBits(readU8(), count);
773 } else {
774 let count = readU32();
775 while (count > 8) {
776 readBits(readU8(), 8);
777 count -= 8;
778 }
779 readBits(readU8(), count);
780 }
781 };
782 case STRING_HEADER:
783 return () => {
784 const len = readU32();
785 if (isInCurrentBuffer(len) && currentPosition + len < 0x7fffffff) {
786 result.push(
787 currentBuffer.toString(
788 undefined,
789 currentPosition,
790 currentPosition + len
791 )
792 );
793 currentPosition += len;
794 checkOverflow();
795 } else {
796 result.push(read(len).toString());
797 }
798 };
799 case SHORT_STRING_HEADER:
800 return () => result.push("");
801 case SHORT_STRING_HEADER | 1:
802 return () => {
803 if (currentIsBuffer && currentPosition < 0x7ffffffe) {
804 result.push(
805 currentBuffer.toString(
806 "latin1",
807 currentPosition,
808 currentPosition + 1
809 )
810 );
811 currentPosition++;
812 checkOverflow();
813 } else {
814 result.push(read(1).toString("latin1"));
815 }
816 };
817 case I8_HEADER:
818 return () => {
819 if (currentIsBuffer) {
820 result.push(
821 /** @type {Buffer} */ (currentBuffer).readInt8(currentPosition)
822 );
823 currentPosition++;
824 checkOverflow();
825 } else {
826 result.push(read(1).readInt8(0));
827 }
828 };
829 default:
830 if (header <= 10) {
831 return () => result.push(header);
832 } else if ((header & SHORT_STRING_HEADER) === SHORT_STRING_HEADER) {
833 const len = header & SHORT_STRING_LENGTH_MASK;
834 return () => {
835 if (
836 isInCurrentBuffer(len) &&
837 currentPosition + len < 0x7fffffff
838 ) {
839 result.push(
840 currentBuffer.toString(
841 "latin1",
842 currentPosition,
843 currentPosition + len
844 )
845 );
846 currentPosition += len;
847 checkOverflow();
848 } else {
849 result.push(read(len).toString("latin1"));
850 }
851 };
852 } else if ((header & NUMBERS_HEADER_MASK) === F64_HEADER) {
853 const len = (header & NUMBERS_COUNT_MASK) + 1;
854 return () => {
855 const need = F64_SIZE * len;
856 if (isInCurrentBuffer(need)) {
857 for (let i = 0; i < len; i++) {
858 result.push(
859 /** @type {Buffer} */ (currentBuffer).readDoubleLE(
860 currentPosition
861 )
862 );
863 currentPosition += F64_SIZE;
864 }
865 checkOverflow();
866 } else {
867 const buf = read(need);
868 for (let i = 0; i < len; i++) {
869 result.push(buf.readDoubleLE(i * F64_SIZE));
870 }
871 }
872 };
873 } else if ((header & NUMBERS_HEADER_MASK) === I32_HEADER) {
874 const len = (header & NUMBERS_COUNT_MASK) + 1;
875 return () => {
876 const need = I32_SIZE * len;
877 if (isInCurrentBuffer(need)) {
878 for (let i = 0; i < len; i++) {
879 result.push(
880 /** @type {Buffer} */ (currentBuffer).readInt32LE(
881 currentPosition
882 )
883 );
884 currentPosition += I32_SIZE;
885 }
886 checkOverflow();
887 } else {
888 const buf = read(need);
889 for (let i = 0; i < len; i++) {
890 result.push(buf.readInt32LE(i * I32_SIZE));
891 }
892 }
893 };
894 } else if ((header & NUMBERS_HEADER_MASK) === I8_HEADER) {
895 const len = (header & NUMBERS_COUNT_MASK) + 1;
896 return () => {
897 const need = I8_SIZE * len;
898 if (isInCurrentBuffer(need)) {
899 for (let i = 0; i < len; i++) {
900 result.push(
901 /** @type {Buffer} */ (currentBuffer).readInt8(
902 currentPosition
903 )
904 );
905 currentPosition += I8_SIZE;
906 }
907 checkOverflow();
908 } else {
909 const buf = read(need);
910 for (let i = 0; i < len; i++) {
911 result.push(buf.readInt8(i * I8_SIZE));
912 }
913 }
914 };
915 } else {
916 return () => {
917 throw new Error(
918 `Unexpected header byte 0x${header.toString(16)}`
919 );
920 };
921 }
922 }
923 });
924
925 /** @type {DeserializedType} */
926 let result = [];
927 while (currentBuffer !== null) {
928 if (typeof currentBuffer === "function") {
929 result.push(this._deserializeLazy(currentBuffer, context));
930 currentDataItem++;
931 currentBuffer =
932 currentDataItem < data.length ? data[currentDataItem] : null;
933 currentIsBuffer = Buffer.isBuffer(currentBuffer);
934 } else {
935 const header = readU8();
936 dispatchTable[header]();
937 }
938 }
939
940 // avoid leaking memory in context
941 let _result = result;
942 result = undefined;
943 return _result;
944 }
945}
946
947module.exports = BinaryMiddleware;
948
949module.exports.MEASURE_START_OPERATION = MEASURE_START_OPERATION;
950module.exports.MEASURE_END_OPERATION = MEASURE_END_OPERATION;