Skip to content

Commit

Permalink
Add number and array binary serialization functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
i-zolotarenko committed Nov 2, 2023
1 parent e96befe commit fb56f65
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 1 deletion.
17 changes: 16 additions & 1 deletion packages/p2p-media-loader-core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { LinkedMap } from "./linked-map";
import { BandwidthApproximator } from "./bandwidth-approximator";
import { EngineCallbacks } from "./request-container";
import { SegmentsMemoryStorage } from "./segments-storage";
import * as Bits from "./p2p/bits";
import { serializeNumbersArray } from "./p2p/bits";

export class Core<TStream extends Stream = Stream> {
private manifestResponseUrl?: string;
Expand All @@ -33,7 +35,20 @@ export class Core<TStream extends Stream = Stream> {
private mainStreamLoader?: HybridLoader;
private secondaryStreamLoader?: HybridLoader;

constructor(private readonly eventHandlers?: CoreEventHandlers) {}
constructor(private readonly eventHandlers?: CoreEventHandlers) {
const numbers = [
70001, 70002, 70003, 70004, 70005, 70006, 70007, 70008, 70009, 700010,
700011, 70012,
];
const stringified = JSON.stringify(numbers);
const encoded = new TextEncoder().encode(stringified);
const serialized = Bits.serializeNumbersArray(numbers, "l");
const { numbersArray } = Bits.deserializeNumbersArray(serialized, 0);

console.log("Encoded bytes: ", encoded.length);
console.log("Serialized: ", serialized);
console.log("deserialized", numbersArray);
}

setManifestResponseUrl(url: string): void {
this.manifestResponseUrl = url.split("?")[0];
Expand Down
117 changes: 117 additions & 0 deletions packages/p2p-media-loader-core/src/p2p/bits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { PeerCommandType } from "../enums";

export function integerToBytesArray(num: number): number[] {
const bytesAmount = requiredBytesForInteger(num);
const bytes: number[] = [];

for (let i = 0; i < bytesAmount; i++) {
bytes[i] = (num >> (8 * i)) & 0xff;
}
return bytes;
}

export function bytesArrayToInteger(bytes: Uint8Array): number {
let number = 0;
for (let i = 0; i < bytes.length; i++) {
const byte = bytes[i];
number += byte << (8 * i);
}

return number;
}

function requiredBytesForInteger(num: number) {
num = Math.abs(num);
const bits = Math.floor(Math.log2(num)) + 1;
return Math.ceil(bits / 8);
}

// restricted to max 8 item types (3 bits to type definition)
enum SerializedItem {
Number,
NumberArray,
}

export function serializeNumber(num: number) {
const numBytes = integerToBytesArray(num);
// 5 bits for
const numberMetadata = (SerializedItem.Number << 5) | numBytes.length;
return new Uint8Array([numberMetadata, ...numBytes]);
}

export function deserializeNumber(bytes: Uint8Array, position: number) {
const metadata = bytes[position];
const code = (metadata & 0b11100000) >> 5;
if (code !== SerializedItem.Number) {
throw new Error("error");
}
const numberBytesLength = metadata & 0b00011111;
const start = position + 1;
const end = start + numberBytesLength;
return {
number: bytesArrayToInteger(bytes.slice(start, end)),
byteLength: numberBytesLength + 1,
};
}

export function serializeNumbersArray(numbers: number[], arrayName: string) {
const byteLengthNumbersBytesMap = new Map<number, number[][]>();

for (const number of numbers) {
const numberBytes = integerToBytesArray(number);
const { length } = numberBytes;
const list = byteLengthNumbersBytesMap.get(length);
if (!list) {
byteLengthNumbersBytesMap.set(length, [numberBytes]);
} else {
list.push(numberBytes);
}
}

const arrayBytes: number[] = [
(SerializedItem.NumberArray << 5) | byteLengthNumbersBytesMap.size,
arrayName.charCodeAt(0), // amount of different byte length arrays with same name
];
for (const [byteLength, bytesLists] of byteLengthNumbersBytesMap.entries()) {
arrayBytes.push(byteLength, bytesLists.length & 0xff);
bytesLists.forEach((list) => arrayBytes.push(...list));
}

return new Uint8Array(arrayBytes);
}

export function deserializeNumbersArray(bytes: Uint8Array, position: number) {
const [metadata, arrayName] = bytes;
const code = (metadata & 0b11100000) >> 5;
const arraysAmount = metadata & 0b00011111;
if (code !== SerializedItem.NumberArray) {
throw new Error("error");
}

const numbersArray: number[] = [];
let arrayStart = position + 2;
for (let i = 0; i < arraysAmount; i++) {
const numberByteLength = bytes[arrayStart];
const arrayLength = bytes[arrayStart + 1];

let itemPosition = arrayStart + 2;
for (let j = 0; j < arrayLength; j++) {
const end = itemPosition + numberByteLength;
const number = bytesArrayToInteger(bytes.slice(itemPosition, end));
numbersArray.push(number);
itemPosition += numberByteLength;
arrayStart = itemPosition;
}
}

return { numbersArray, name: arrayName };
}

function getCommandBytes() {
const bytes: number[] = [
"{".charCodeAt(0),
PeerCommandType.CancelSegmentRequest,
...serializeNumber(65411),
"}".charCodeAt(0),
];
}

0 comments on commit fb56f65

Please sign in to comment.