_deserialize(data, context) {
		let currentDataItem = 0;
		/** @type {BufferSerializableType | null} */
		let currentBuffer = data[0];
		let currentIsBuffer = Buffer.isBuffer(currentBuffer);
		let currentPosition = 0;

		const retainedBuffer = context.retainedBuffer || (x => x);

		const checkOverflow = () => {
			if (currentPosition >= /** @type {Buffer} */ (currentBuffer).length) {
				currentPosition = 0;
				currentDataItem++;
				currentBuffer =
					currentDataItem < data.length ? data[currentDataItem] : null;
				currentIsBuffer = Buffer.isBuffer(currentBuffer);
			}
		};
		/**
		 * @param {number} n n
		 * @returns {boolean} true when in current buffer, otherwise false
		 */
		const isInCurrentBuffer = n =>
			currentIsBuffer &&
			n + currentPosition <= /** @type {Buffer} */ (currentBuffer).length;
		const ensureBuffer = () => {
			if (!currentIsBuffer) {
				throw new Error(
					currentBuffer === null
						? "Unexpected end of stream"
						: "Unexpected lazy element in stream"
				);
			}
		};
		/**
		 * Reads n bytes
		 * @param {number} n amount of bytes to read
		 * @returns {Buffer} buffer with bytes
		 */
		const read = n => {
			ensureBuffer();
			const rem =
				/** @type {Buffer} */ (currentBuffer).length - currentPosition;
			if (rem < n) {
				const buffers = [read(rem)];
				n -= rem;
				ensureBuffer();
				while (/** @type {Buffer} */ (currentBuffer).length < n) {
					const b = /** @type {Buffer} */ (currentBuffer);
					buffers.push(b);
					n -= b.length;
					currentDataItem++;
					currentBuffer =
						currentDataItem < data.length ? data[currentDataItem] : null;
					currentIsBuffer = Buffer.isBuffer(currentBuffer);
					ensureBuffer();
				}
				buffers.push(read(n));
				return Buffer.concat(buffers);
			}
			const b = /** @type {Buffer} */ (currentBuffer);
			const res = Buffer.from(b.buffer, b.byteOffset + currentPosition, n);
			currentPosition += n;
			checkOverflow();
			return res;
		};
		/**
		 * Reads up to n bytes
		 * @param {number} n amount of bytes to read
		 * @returns {Buffer} buffer with bytes
		 */
		const readUpTo = n => {
			ensureBuffer();
			const rem =
				/** @type {Buffer} */
				(currentBuffer).length - currentPosition;
			if (rem < n) {
				n = rem;
			}
			const b = /** @type {Buffer} */ (currentBuffer);
			const res = Buffer.from(b.buffer, b.byteOffset + currentPosition, n);
			currentPosition += n;
			checkOverflow();
			return res;
		};
		/**
		 * @returns {number} U8
		 */
		const readU8 = () => {
			ensureBuffer();
			/**
			 * There is no need to check remaining buffer size here
			 * since {@link checkOverflow} guarantees at least one byte remaining
			 */
			const byte =
				/** @type {Buffer} */
				(currentBuffer).readUInt8(currentPosition);
			currentPosition += I8_SIZE;
			checkOverflow();
			return byte;
		};
		/**
		 * @returns {number} U32
		 */
		const readU32 = () => read(I32_SIZE).readUInt32LE(0);
		/**
		 * @param {number} data data
		 * @param {number} n n
		 */
		const readBits = (data, n) => {
			let mask = 1;
			while (n !== 0) {
				result.push((data & mask) !== 0);
				mask = mask << 1;
				n--;
			}
		};
		const dispatchTable = Array.from({ length: 256 }).map((_, header) => {
			switch (header) {
				case LAZY_HEADER:
					return () => {
						const count = readU32();
						const lengths = Array.from({ length: count }).map(() => readU32());
						/** @type {(Buffer | LazyFunction<SerializedType, DeserializedType>)[]} */
						const content = [];
						for (let l of lengths) {
							if (l === 0) {
								if (typeof currentBuffer !== "function") {
									throw new Error("Unexpected non-lazy element in stream");
								}
								content.push(currentBuffer);
								currentDataItem++;
								currentBuffer =
									currentDataItem < data.length ? data[currentDataItem] : null;
								currentIsBuffer = Buffer.isBuffer(currentBuffer);
							} else {
								do {
									const buf = readUpTo(l);
									l -= buf.length;
									content.push(retainedBuffer(buf));
								} while (l > 0);
							}
						}
						result.push(this._createLazyDeserialized(content, context));
					};
				case BUFFER_HEADER:
					return () => {
						const len = readU32();
						result.push(retainedBuffer(read(len)));
					};
				case TRUE_HEADER:
					return () => result.push(true);
				case FALSE_HEADER:
					return () => result.push(false);
				case NULL3_HEADER:
					return () => result.push(null, null, null);
				case NULL2_HEADER:
					return () => result.push(null, null);
				case NULL_HEADER:
					return () => result.push(null);
				case NULL_AND_TRUE_HEADER:
					return () => result.push(null, true);
				case NULL_AND_FALSE_HEADER:
					return () => result.push(null, false);
				case NULL_AND_I8_HEADER:
					return () => {
						if (currentIsBuffer) {
							result.push(
								null,
								/** @type {Buffer} */ (currentBuffer).readInt8(currentPosition)
							);
							currentPosition += I8_SIZE;
							checkOverflow();
						} else {
							result.push(null, read(I8_SIZE).readInt8(0));
						}
					};
				case NULL_AND_I32_HEADER:
					return () => {
						result.push(null);
						if (isInCurrentBuffer(I32_SIZE)) {
							result.push(
								/** @type {Buffer} */ (currentBuffer).readInt32LE(
									currentPosition
								)
							);
							currentPosition += I32_SIZE;
							checkOverflow();
						} else {
							result.push(read(I32_SIZE).readInt32LE(0));
						}
					};
				case NULLS8_HEADER:
					return () => {
						const len = readU8() + 4;
						for (let i = 0; i < len; i++) {
							result.push(null);
						}
					};
				case NULLS32_HEADER:
					return () => {
						const len = readU32() + 260;
						for (let i = 0; i < len; i++) {
							result.push(null);
						}
					};
				case BOOLEANS_HEADER:
					return () => {
						const innerHeader = readU8();
						if ((innerHeader & 0xf0) === 0) {
							readBits(innerHeader, 3);
						} else if ((innerHeader & 0xe0) === 0) {
							readBits(innerHeader, 4);
						} else if ((innerHeader & 0xc0) === 0) {
							readBits(innerHeader, 5);
						} else if ((innerHeader & 0x80) === 0) {
							readBits(innerHeader, 6);
						} else if (innerHeader !== 0xff) {
							let count = (innerHeader & 0x7f) + 7;
							while (count > 8) {
								readBits(readU8(), 8);
								count -= 8;
							}
							readBits(readU8(), count);
						} else {
							let count = readU32();
							while (count > 8) {
								readBits(readU8(), 8);
								count -= 8;
							}
							readBits(readU8(), count);
						}
					};
				case STRING_HEADER:
					return () => {
						const len = readU32();
						if (isInCurrentBuffer(len) && currentPosition + len < 0x7fffffff) {
							result.push(
								/** @type {Buffer} */
								(currentBuffer).toString(
									undefined,
									currentPosition,
									currentPosition + len
								)
							);
							currentPosition += len;
							checkOverflow();
						} else {
							result.push(read(len).toString());
						}
					};
				case SHORT_STRING_HEADER:
					return () => result.push("");
				case SHORT_STRING_HEADER | 1:
					return () => {
						if (currentIsBuffer && currentPosition < 0x7ffffffe) {
							result.push(
								/** @type {Buffer} */
								(currentBuffer).toString(
									"latin1",
									currentPosition,
									currentPosition + 1
								)
							);
							currentPosition++;
							checkOverflow();
						} else {
							result.push(read(1).toString("latin1"));
						}
					};
				case I8_HEADER:
					return () => {
						if (currentIsBuffer) {
							result.push(
								/** @type {Buffer} */ (currentBuffer).readInt8(currentPosition)
							);
							currentPosition++;
							checkOverflow();
						} else {
							result.push(read(1).readInt8(0));
						}
					};
				case BIGINT_I8_HEADER: {
					const len = 1;
					return () => {
						const need = I8_SIZE * len;

						if (isInCurrentBuffer(need)) {
							for (let i = 0; i < len; i++) {
								const value =
									/** @type {Buffer} */
									(currentBuffer).readInt8(currentPosition);
								result.push(BigInt(value));
								currentPosition += I8_SIZE;
							}
							checkOverflow();
						} else {
							const buf = read(need);
							for (let i = 0; i < len; i++) {
								const value = buf.readInt8(i * I8_SIZE);
								result.push(BigInt(value));
							}
						}
					};
				}
				case BIGINT_I32_HEADER: {
					const len = 1;
					return () => {
						const need = I32_SIZE * len;
						if (isInCurrentBuffer(need)) {
							for (let i = 0; i < len; i++) {
								const value = /** @type {Buffer} */ (currentBuffer).readInt32LE(
									currentPosition
								);
								result.push(BigInt(value));
								currentPosition += I32_SIZE;
							}
							checkOverflow();
						} else {
							const buf = read(need);
							for (let i = 0; i < len; i++) {
								const value = buf.readInt32LE(i * I32_SIZE);
								result.push(BigInt(value));
							}
						}
					};
				}
				case BIGINT_HEADER: {
					return () => {
						const len = readU32();
						if (isInCurrentBuffer(len) && currentPosition + len < 0x7fffffff) {
							const value =
								/** @type {Buffer} */
								(currentBuffer).toString(
									undefined,
									currentPosition,
									currentPosition + len
								);

							result.push(BigInt(value));
							currentPosition += len;
							checkOverflow();
						} else {
							const value = read(len).toString();
							result.push(BigInt(value));
						}
					};
				}
				default:
					if (header <= 10) {
						return () => result.push(header);
					} else if ((header & SHORT_STRING_HEADER) === SHORT_STRING_HEADER) {
						const len = header & SHORT_STRING_LENGTH_MASK;
						return () => {
							if (
								isInCurrentBuffer(len) &&
								currentPosition + len < 0x7fffffff
							) {
								result.push(
									/** @type {Buffer} */
									(currentBuffer).toString(
										"latin1",
										currentPosition,
										currentPosition + len
									)
								);
								currentPosition += len;
								checkOverflow();
							} else {
								result.push(read(len).toString("latin1"));
							}
						};
					} else if ((header & NUMBERS_HEADER_MASK) === F64_HEADER) {
						const len = (header & NUMBERS_COUNT_MASK) + 1;
						return () => {
							const need = F64_SIZE * len;
							if (isInCurrentBuffer(need)) {
								for (let i = 0; i < len; i++) {
									result.push(
										/** @type {Buffer} */ (currentBuffer).readDoubleLE(
											currentPosition
										)
									);
									currentPosition += F64_SIZE;
								}
								checkOverflow();
							} else {
								const buf = read(need);
								for (let i = 0; i < len; i++) {
									result.push(buf.readDoubleLE(i * F64_SIZE));
								}
							}
						};
					} else if ((header & NUMBERS_HEADER_MASK) === I32_HEADER) {
						const len = (header & NUMBERS_COUNT_MASK) + 1;
						return () => {
							const need = I32_SIZE * len;
							if (isInCurrentBuffer(need)) {
								for (let i = 0; i < len; i++) {
									result.push(
										/** @type {Buffer} */ (currentBuffer).readInt32LE(
											currentPosition
										)
									);
									currentPosition += I32_SIZE;
								}
								checkOverflow();
							} else {
								const buf = read(need);
								for (let i = 0; i < len; i++) {
									result.push(buf.readInt32LE(i * I32_SIZE));
								}
							}
						};
					} else if ((header & NUMBERS_HEADER_MASK) === I8_HEADER) {
						const len = (header & NUMBERS_COUNT_MASK) + 1;
						return () => {
							const need = I8_SIZE * len;
							if (isInCurrentBuffer(need)) {
								for (let i = 0; i < len; i++) {
									result.push(
										/** @type {Buffer} */ (currentBuffer).readInt8(
											currentPosition
										)
									);
									currentPosition += I8_SIZE;
								}
								checkOverflow();
							} else {
								const buf = read(need);
								for (let i = 0; i < len; i++) {
									result.push(buf.readInt8(i * I8_SIZE));
								}
							}
						};
					}
					return () => {
						throw new Error(`Unexpected header byte 0x${header.toString(16)}`);
					};
			}
		});

		/** @type {DeserializedType} */
		let result = [];
		while (currentBuffer !== null) {
			if (typeof currentBuffer === "function") {
				result.push(this._deserializeLazy(currentBuffer, context));
				currentDataItem++;
				currentBuffer =
					currentDataItem < data.length ? data[currentDataItem] : null;
				currentIsBuffer = Buffer.isBuffer(currentBuffer);
			} else {
				const header = readU8();
				dispatchTable[header]();
			}
		}

		// avoid leaking memory in context
		// eslint-disable-next-line prefer-const
		let _result = result;
		result = /** @type {EXPECTED_ANY} */ (undefined);
		return _result;
	}
@param {SerializedType} data data
@param {Context} context context object
@returns {DeserializedType} deserialized data