UNPKG

p2p-media-loader-core

Version:
175 lines 6.31 kB
import { joinChunks } from "../../utils/utils.js"; // restricted up to 16 item types (4 bits to type definition) export var SerializedItem; (function (SerializedItem) { SerializedItem[SerializedItem["Min"] = -1] = "Min"; SerializedItem[SerializedItem["Int"] = 0] = "Int"; SerializedItem[SerializedItem["SimilarIntArray"] = 1] = "SimilarIntArray"; SerializedItem[SerializedItem["String"] = 2] = "String"; SerializedItem[SerializedItem["Max"] = 3] = "Max"; })(SerializedItem || (SerializedItem = {})); function abs(num) { return num < 0 ? -num : num; } function getRequiredBytesForInt(num) { const binaryString = num.toString(2); const necessaryBits = num < 0 ? binaryString.length : binaryString.length + 1; return Math.ceil(necessaryBits / 8); } function intToBytes(num) { const isNegative = num < 0; const bytesAmountNumber = getRequiredBytesForInt(num); const bytes = new Uint8Array(bytesAmountNumber); const bytesAmount = BigInt(bytesAmountNumber); num = abs(num); for (let i = 0; i < bytesAmountNumber; i++) { const shift = 8n * (bytesAmount - 1n - BigInt(i)); const byte = (num >> shift) & 0xffn; bytes[i] = Number(byte); } if (isNegative) bytes[0] = bytes[0] | 0b10000000; return bytes; } function bytesToInt(bytes) { const byteLength = BigInt(bytes.length); const getNumberPart = (byte, i) => { const shift = 8n * (byteLength - 1n - BigInt(i)); return BigInt(byte) << shift; }; // ignore first bit of first byte as it is sign bit let number = getNumberPart(bytes[0] & 0b01111111, 0); for (let i = 1; i < byteLength; i++) { number = getNumberPart(bytes[i], i) | number; } if ((bytes[0] & 0b10000000) >> 7 !== 0) number = -number; return number; } export function serializeInt(num) { const numBytes = intToBytes(num); const numberMetadata = (SerializedItem.Int << 4) | numBytes.length; return new Uint8Array([numberMetadata, ...numBytes]); } export function deserializeInt(bytes) { const metadata = bytes[0]; const code = metadata >> 4; if (code !== SerializedItem.Int) { throw new Error("Trying to deserialize integer with invalid serialized item code"); } const numberBytesLength = metadata & 0b1111; const start = 1; const end = start + numberBytesLength; return { number: bytesToInt(bytes.slice(start, end)), byteLength: numberBytesLength + 1, }; } export function serializeSimilarIntArray(numbers) { const commonPartNumbersMap = new Map(); for (const number of numbers) { const common = number & ~0xffn; const diffByte = number & 0xffn; const bytes = commonPartNumbersMap.get(common) ?? new ResizableUint8Array(); if (!bytes.length) commonPartNumbersMap.set(common, bytes); bytes.push(Number(diffByte)); } const result = new ResizableUint8Array(); result.push([SerializedItem.SimilarIntArray << 4, commonPartNumbersMap.size]); for (const [commonPart, binaryArray] of commonPartNumbersMap) { const { length } = binaryArray.getBytesChunks(); const commonPartWithLength = commonPart | (BigInt(length) & 0xffn); binaryArray.unshift(serializeInt(commonPartWithLength)); result.push(binaryArray.getBuffer()); } return result.getBuffer(); } export function deserializeSimilarIntArray(bytes) { const [codeByte, commonPartArraysAmount] = bytes; const code = codeByte >> 4; if (code !== SerializedItem.SimilarIntArray) { throw new Error("Trying to deserialize similar int array with invalid serialized item code"); } let offset = 2; const originalIntArr = []; for (let i = 0; i < commonPartArraysAmount; i++) { const { number: commonPartWithLength, byteLength } = deserializeInt(bytes.slice(offset)); offset += byteLength; const arrayLength = commonPartWithLength & 0xffn; const commonPart = commonPartWithLength & ~0xffn; for (let j = 0; j < arrayLength; j++) { const diffPart = BigInt(bytes[offset]); originalIntArr.push(commonPart | diffPart); offset++; } } return { numbers: originalIntArr, byteLength: offset }; } export function serializeString(string) { const { length } = string; const bytes = new ResizableUint8Array(); bytes.push([ (SerializedItem.String << 4) | ((length >> 8) & 0x0f), length & 0xff, ]); bytes.push(new TextEncoder().encode(string)); return bytes.getBuffer(); } export function deserializeString(bytes) { const [codeByte, lengthByte] = bytes; const code = codeByte >> 4; if (code !== SerializedItem.String) { throw new Error("Trying to deserialize bytes (sting) with invalid serialized item code."); } const length = ((codeByte & 0x0f) << 8) | lengthByte; const stringBytes = bytes.slice(2, length + 2); const string = new TextDecoder("utf8").decode(stringBytes); return { string, byteLength: length + 2 }; } export class ResizableUint8Array { constructor() { Object.defineProperty(this, "bytes", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_length", { enumerable: true, configurable: true, writable: true, value: 0 }); } push(bytes) { this.addBytes(bytes, "end"); } unshift(bytes) { this.addBytes(bytes, "start"); } addBytes(bytes, position) { let bytesToAdd; if (bytes instanceof Uint8Array) { bytesToAdd = bytes; } else if (Array.isArray(bytes)) { bytesToAdd = new Uint8Array(bytes); } else { bytesToAdd = new Uint8Array([bytes]); } this._length += bytesToAdd.length; this.bytes[position === "start" ? "unshift" : "push"](bytesToAdd); } getBytesChunks() { return this.bytes; } getBuffer() { return joinChunks(this.bytes, this._length); } get length() { return this._length; } } //# sourceMappingURL=binary-serialization.js.map