Новая сериализация, оптимизации приема и парсинга пакетов
This commit is contained in:
@@ -14,6 +14,12 @@ export interface Attachment {
|
|||||||
blob: string;
|
blob: string;
|
||||||
type: AttachmentType;
|
type: AttachmentType;
|
||||||
preview: string;
|
preview: string;
|
||||||
|
transport_tag: string;
|
||||||
|
transport_server: string;
|
||||||
|
/**
|
||||||
|
* Обозначает, для кого закодировано это вложение, нужно для того, чтобы не кодировать и не загружать заново пересланные сообщения, если они уже были закодированы для этого диалога
|
||||||
|
*/
|
||||||
|
encoded_for: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PacketMessage extends Packet {
|
export class PacketMessage extends Packet {
|
||||||
@@ -42,7 +48,7 @@ export class PacketMessage extends Packet {
|
|||||||
this.toPublicKey = stream.readString();
|
this.toPublicKey = stream.readString();
|
||||||
this.content = stream.readString();
|
this.content = stream.readString();
|
||||||
this.chachaKey = stream.readString();
|
this.chachaKey = stream.readString();
|
||||||
this.timestamp = stream.readInt64();
|
this.timestamp = Number(stream.readInt64());
|
||||||
this.privateKey = stream.readString();
|
this.privateKey = stream.readString();
|
||||||
this.messageId = stream.readString();
|
this.messageId = stream.readString();
|
||||||
let attachmentsCount = stream.readInt8();
|
let attachmentsCount = stream.readInt8();
|
||||||
@@ -51,7 +57,10 @@ export class PacketMessage extends Packet {
|
|||||||
let preview = stream.readString();
|
let preview = stream.readString();
|
||||||
let blob = stream.readString();
|
let blob = stream.readString();
|
||||||
let type = stream.readInt8() as AttachmentType;
|
let type = stream.readInt8() as AttachmentType;
|
||||||
this.attachments.push({id, preview, type, blob});
|
let transport_tag = stream.readString();
|
||||||
|
let transport_server = stream.readString();
|
||||||
|
let encoded_for = stream.readString();
|
||||||
|
this.attachments.push({id, preview, type, blob, transport_tag, transport_server, encoded_for});
|
||||||
}
|
}
|
||||||
this.aesChachaKey = stream.readString();
|
this.aesChachaKey = stream.readString();
|
||||||
}
|
}
|
||||||
@@ -63,7 +72,7 @@ export class PacketMessage extends Packet {
|
|||||||
stream.writeString(this.toPublicKey);
|
stream.writeString(this.toPublicKey);
|
||||||
stream.writeString(this.content);
|
stream.writeString(this.content);
|
||||||
stream.writeString(this.chachaKey);
|
stream.writeString(this.chachaKey);
|
||||||
stream.writeInt64(this.timestamp);
|
stream.writeInt64(BigInt(this.timestamp));
|
||||||
stream.writeString(this.privateKey);
|
stream.writeString(this.privateKey);
|
||||||
stream.writeString(this.messageId);
|
stream.writeString(this.messageId);
|
||||||
stream.writeInt8(this.attachments.length);
|
stream.writeInt8(this.attachments.length);
|
||||||
@@ -72,6 +81,9 @@ export class PacketMessage extends Packet {
|
|||||||
stream.writeString(this.attachments[i].preview);
|
stream.writeString(this.attachments[i].preview);
|
||||||
stream.writeString(this.attachments[i].blob);
|
stream.writeString(this.attachments[i].blob);
|
||||||
stream.writeInt8(this.attachments[i].type);
|
stream.writeInt8(this.attachments[i].type);
|
||||||
|
stream.writeString(this.attachments[i].transport_tag);
|
||||||
|
stream.writeString(this.attachments[i].transport_server);
|
||||||
|
stream.writeString(this.attachments[i].encoded_for);
|
||||||
}
|
}
|
||||||
stream.writeString(this.aesChachaKey);
|
stream.writeString(this.aesChachaKey);
|
||||||
return stream;
|
return stream;
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ export class PacketSync extends Packet {
|
|||||||
|
|
||||||
public _receive(stream: Stream): void {
|
public _receive(stream: Stream): void {
|
||||||
this.status = stream.readInt8() as SyncStatus;
|
this.status = stream.readInt8() as SyncStatus;
|
||||||
this.timestamp = stream.readInt64();
|
this.timestamp = Number(stream.readInt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
public _send(): Promise<Stream> | Stream {
|
public _send(): Promise<Stream> | Stream {
|
||||||
let stream = new Stream();
|
let stream = new Stream();
|
||||||
stream.writeInt16(this.getPacketId());
|
stream.writeInt16(this.getPacketId());
|
||||||
stream.writeInt8(this.status);
|
stream.writeInt8(this.status);
|
||||||
stream.writeInt64(this.timestamp);
|
stream.writeInt64(BigInt(this.timestamp));
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,151 +1,372 @@
|
|||||||
export default class Stream {
|
export default class Stream {
|
||||||
|
private stream: Uint8Array;
|
||||||
|
private readPointer = 0; // bits
|
||||||
|
private writePointer = 0; // bits
|
||||||
|
|
||||||
private _stream: number[];
|
constructor(stream?: Uint8Array | number[]) {
|
||||||
private _readPoiner: number = 0;
|
if (!stream) {
|
||||||
private _writePointer: number = 0;
|
this.stream = new Uint8Array(0);
|
||||||
|
} else {
|
||||||
constructor(stream : number[] = []) {
|
const src = stream instanceof Uint8Array ? stream : Uint8Array.from(stream);
|
||||||
this._stream = stream;
|
this.stream = src;
|
||||||
}
|
this.writePointer = this.stream.length << 3;
|
||||||
|
|
||||||
public getStream(): number[] {
|
|
||||||
return this._stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setStream(stream: number[]) {
|
|
||||||
this._stream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public writeInt8(value: number) {
|
|
||||||
const negationBit = value < 0 ? 1 : 0;
|
|
||||||
const int8Value = Math.abs(value) & 0xFF;
|
|
||||||
this._stream[this._writePointer >> 3] |= negationBit << (7 - (this._writePointer & 7));
|
|
||||||
this._writePointer++;
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
const bit = (int8Value >> (7 - i)) & 1;
|
|
||||||
this._stream[this._writePointer >> 3] |= bit << (7 - (this._writePointer & 7));
|
|
||||||
this._writePointer++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readInt8(): number {
|
getStream(): Uint8Array {
|
||||||
let value = 0;
|
return this.stream.slice(0, this.length());
|
||||||
const negationBit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
|
||||||
this._readPoiner++;
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
const bit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
|
||||||
value |= bit << (7 - i);
|
|
||||||
this._readPoiner++;
|
|
||||||
}
|
|
||||||
return negationBit ? -value : value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeBit(value: number) {
|
setStream(stream?: Uint8Array | number[]) {
|
||||||
const bit = value & 1;
|
if (!stream) {
|
||||||
this._stream[this._writePointer >> 3] |= bit << (7 - (this._writePointer & 7));
|
this.stream = new Uint8Array(0);
|
||||||
this._writePointer++;
|
this.readPointer = 0;
|
||||||
|
this.writePointer = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const src = stream instanceof Uint8Array ? stream : Uint8Array.from(stream);
|
||||||
|
this.stream = src;
|
||||||
|
this.readPointer = 0;
|
||||||
|
this.writePointer = this.stream.length << 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readBit(): number {
|
getBuffer(): Uint8Array {
|
||||||
const bit = (this._stream[this._readPoiner >> 3] >> (7 - (this._readPoiner & 7))) & 1;
|
return this.getStream();
|
||||||
this._readPoiner++;
|
|
||||||
return bit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeBoolean(value: boolean) {
|
isEmpty(): boolean {
|
||||||
|
return this.writePointer === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
length(): number {
|
||||||
|
return (this.writePointer + 7) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- bit / boolean ----------
|
||||||
|
|
||||||
|
writeBit(value: number) {
|
||||||
|
this.writeBits(BigInt(value & 1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
readBit(): number {
|
||||||
|
return Number(this.readBits(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBoolean(value: boolean) {
|
||||||
this.writeBit(value ? 1 : 0);
|
this.writeBit(value ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readBoolean(): boolean {
|
readBoolean(): boolean {
|
||||||
return this.readBit() === 1;
|
return this.readBit() === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeInt16(value: number) {
|
// ---------- byte ----------
|
||||||
this.writeInt8(value >> 8);
|
|
||||||
this.writeInt8(value & 0xFF);
|
writeByte(b: number) {
|
||||||
|
this.writeUInt8(b & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readInt16(): number {
|
readByte(): number {
|
||||||
const value = this.readInt8() << 8;
|
const v = this.readUInt8();
|
||||||
return value | this.readInt8();
|
return (v << 24) >> 24; // signed byte
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeInt32(value: number) {
|
// ---------- UInt / Int 8 ----------
|
||||||
this.writeInt16(value >> 16);
|
|
||||||
this.writeInt16(value & 0xFFFF);
|
writeUInt8(value: number) {
|
||||||
|
const v = value & 0xff;
|
||||||
|
|
||||||
|
if ((this.writePointer & 7) === 0) {
|
||||||
|
this.reserveBits(8);
|
||||||
|
this.stream[this.writePointer >> 3] = v;
|
||||||
|
this.writePointer += 8;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readInt32(): number {
|
this.writeBits(BigInt(v), 8);
|
||||||
const value = this.readInt16() << 16;
|
|
||||||
return value | this.readInt16();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeInt64(value: number) {
|
readUInt8(): number {
|
||||||
const high = Math.floor(value / 0x100000000);
|
if (this.remainingBits() < 8n) {
|
||||||
const low = value >>> 0;
|
throw new Error("Not enough bits to read UInt8");
|
||||||
this.writeInt32(high);
|
|
||||||
this.writeInt32(low);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public readInt64(): number {
|
if ((this.readPointer & 7) === 0) {
|
||||||
const high = this.readInt32();
|
const v = this.stream[this.readPointer >> 3] & 0xff;
|
||||||
const low = this.readInt32() >>> 0;
|
this.readPointer += 8;
|
||||||
return high * 0x100000000 + low;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeFloat32(value: number) {
|
return Number(this.readBits(8));
|
||||||
const buffer = new ArrayBuffer(4);
|
|
||||||
new DataView(buffer).setFloat32(0, value, true);
|
|
||||||
const float32Value = new Uint32Array(buffer)[0];
|
|
||||||
this.writeInt32(float32Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public readFloat32(): number {
|
writeInt8(value: number) {
|
||||||
const float32Value = this.readInt32();
|
this.writeUInt8(value);
|
||||||
const buffer = new ArrayBuffer(4);
|
|
||||||
new Uint32Array(buffer)[0] = float32Value;
|
|
||||||
return new DataView(buffer).getFloat32(0, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeString(value: string) {
|
readInt8(): number {
|
||||||
let length = value.length;
|
const u = this.readUInt8();
|
||||||
this.writeInt32(length);
|
return (u << 24) >> 24;
|
||||||
for (let i = 0; i < value.length; i++) {
|
}
|
||||||
this.writeInt16(value.charCodeAt(i));
|
|
||||||
|
// ---------- UInt / Int 16 ----------
|
||||||
|
|
||||||
|
writeUInt16(value: number) {
|
||||||
|
const v = value & 0xffff;
|
||||||
|
this.writeUInt8((v >>> 8) & 0xff);
|
||||||
|
this.writeUInt8(v & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
readUInt16(): number {
|
||||||
|
const hi = this.readUInt8();
|
||||||
|
const lo = this.readUInt8();
|
||||||
|
return (hi << 8) | lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeInt16(value: number) {
|
||||||
|
this.writeUInt16(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
readInt16(): number {
|
||||||
|
const u = this.readUInt16();
|
||||||
|
return (u << 16) >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- UInt / Int 32 ----------
|
||||||
|
|
||||||
|
writeUInt32(value: number) {
|
||||||
|
if (!Number.isFinite(value) || value < 0 || value > 0xffffffff) {
|
||||||
|
throw new Error(`UInt32 out of range: ${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const v = Math.floor(value);
|
||||||
|
this.writeUInt8((v >>> 24) & 0xff);
|
||||||
|
this.writeUInt8((v >>> 16) & 0xff);
|
||||||
|
this.writeUInt8((v >>> 8) & 0xff);
|
||||||
|
this.writeUInt8(v & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
readUInt32(): number {
|
||||||
|
const b1 = this.readUInt8();
|
||||||
|
const b2 = this.readUInt8();
|
||||||
|
const b3 = this.readUInt8();
|
||||||
|
const b4 = this.readUInt8();
|
||||||
|
return (((b1 * 0x1000000) + (b2 << 16) + (b3 << 8) + b4) >>> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeInt32(value: number) {
|
||||||
|
this.writeUInt32(value >>> 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
readInt32(): number {
|
||||||
|
return this.readUInt32() | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- UInt / Int 64 ----------
|
||||||
|
|
||||||
|
writeUInt64(value: bigint) {
|
||||||
|
if (value < 0n || value > 0xffff_ffff_ffff_ffffn) {
|
||||||
|
throw new Error(`UInt64 out of range: ${value.toString()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.writeUInt8(Number((value >> 56n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 48n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 40n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 32n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 24n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 16n) & 0xffn));
|
||||||
|
this.writeUInt8(Number((value >> 8n) & 0xffn));
|
||||||
|
this.writeUInt8(Number(value & 0xffn));
|
||||||
|
}
|
||||||
|
|
||||||
|
readUInt64(): bigint {
|
||||||
|
const high = BigInt(this.readUInt32() >>> 0);
|
||||||
|
const low = BigInt(this.readUInt32() >>> 0);
|
||||||
|
return (high << 32n) | low;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeInt64(value: bigint) {
|
||||||
|
const u = BigInt.asUintN(64, value);
|
||||||
|
this.writeUInt64(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
readInt64(): bigint {
|
||||||
|
return BigInt.asIntN(64, this.readUInt64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- float ----------
|
||||||
|
|
||||||
|
writeFloat32(value: number) {
|
||||||
|
const ab = new ArrayBuffer(4);
|
||||||
|
const dv = new DataView(ab);
|
||||||
|
dv.setFloat32(0, value, false); // big-endian
|
||||||
|
this.writeUInt8(dv.getUint8(0));
|
||||||
|
this.writeUInt8(dv.getUint8(1));
|
||||||
|
this.writeUInt8(dv.getUint8(2));
|
||||||
|
this.writeUInt8(dv.getUint8(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
readFloat32(): number {
|
||||||
|
const ab = new ArrayBuffer(4);
|
||||||
|
const dv = new DataView(ab);
|
||||||
|
dv.setUint8(0, this.readUInt8());
|
||||||
|
dv.setUint8(1, this.readUInt8());
|
||||||
|
dv.setUint8(2, this.readUInt8());
|
||||||
|
dv.setUint8(3, this.readUInt8());
|
||||||
|
return dv.getFloat32(0, false); // big-endian
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- string / bytes ----------
|
||||||
|
// String: length(UInt32) + chars(UInt16), как в Java charAt()
|
||||||
|
|
||||||
|
writeString(value: string | null | undefined) {
|
||||||
|
const s = value ?? "";
|
||||||
|
this.writeUInt32(s.length);
|
||||||
|
|
||||||
|
if (s.length === 0) return;
|
||||||
|
|
||||||
|
this.reserveBits(BigInt(s.length) * 16n);
|
||||||
|
for (let i = 0; i < s.length; i++) {
|
||||||
|
this.writeUInt16(s.charCodeAt(i) & 0xffff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readString(): string {
|
readString(): string {
|
||||||
let length = this.readInt32();
|
const len = this.readUInt32();
|
||||||
/**
|
if (len > 0x7fffffff) {
|
||||||
* Фикс уязвимости с длинной строки, превышающей
|
throw new Error(`String length too large: ${len}`);
|
||||||
* возможность для чтения _stream
|
|
||||||
*/
|
|
||||||
if (length < 0 || length > (this._stream.length - (this._readPoiner >> 3))) {
|
|
||||||
console.info("Stream readString length invalid", length, this._stream.length, this._readPoiner);
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
let value = "";
|
|
||||||
for (let i = 0; i < length; i++) {
|
const requiredBits = BigInt(len) * 16n;
|
||||||
value += String.fromCharCode(this.readInt16());
|
if (requiredBits > this.remainingBits()) {
|
||||||
|
throw new Error("Not enough bits to read string");
|
||||||
|
}
|
||||||
|
|
||||||
|
const chars = new Array<number>(len);
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
chars[i] = this.readUInt16();
|
||||||
|
}
|
||||||
|
return String.fromCharCode(...chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte[]: length(UInt32) + payload
|
||||||
|
writeBytes(value: Uint8Array | number[] | null | undefined) {
|
||||||
|
const arr = value == null
|
||||||
|
? new Uint8Array(0)
|
||||||
|
: (value instanceof Uint8Array ? value : Uint8Array.from(value));
|
||||||
|
|
||||||
|
this.writeUInt32(arr.length);
|
||||||
|
if (arr.length === 0) return;
|
||||||
|
|
||||||
|
this.reserveBits(BigInt(arr.length) * 8n);
|
||||||
|
|
||||||
|
if ((this.writePointer & 7) === 0) {
|
||||||
|
const byteIndex = this.writePointer >> 3;
|
||||||
|
this.ensureCapacity(byteIndex + arr.length - 1);
|
||||||
|
this.stream.set(arr, byteIndex);
|
||||||
|
this.writePointer += arr.length << 3;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
this.writeUInt8(arr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readBytes(): Uint8Array {
|
||||||
|
const len = this.readUInt32();
|
||||||
|
if (len === 0) return new Uint8Array(0);
|
||||||
|
|
||||||
|
const requiredBits = BigInt(len) * 8n;
|
||||||
|
if (requiredBits > this.remainingBits()) {
|
||||||
|
return new Uint8Array(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const out = new Uint8Array(len);
|
||||||
|
|
||||||
|
if ((this.readPointer & 7) === 0) {
|
||||||
|
const byteIndex = this.readPointer >> 3;
|
||||||
|
out.set(this.stream.slice(byteIndex, byteIndex + len));
|
||||||
|
this.readPointer += len << 3;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
out[i] = this.readUInt8();
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- internals ----------
|
||||||
|
|
||||||
|
private remainingBits(): bigint {
|
||||||
|
return BigInt(this.writePointer - this.readPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeBits(value: bigint, bits: number) {
|
||||||
|
if (bits <= 0) return;
|
||||||
|
|
||||||
|
this.reserveBits(bits);
|
||||||
|
|
||||||
|
for (let i = bits - 1; i >= 0; i--) {
|
||||||
|
const bit = Number((value >> BigInt(i)) & 1n);
|
||||||
|
const byteIndex = this.writePointer >> 3;
|
||||||
|
const shift = 7 - (this.writePointer & 7);
|
||||||
|
|
||||||
|
if (bit === 1) {
|
||||||
|
this.stream[byteIndex] = this.stream[byteIndex] | (1 << shift);
|
||||||
|
} else {
|
||||||
|
this.stream[byteIndex] = this.stream[byteIndex] & ~(1 << shift);
|
||||||
|
}
|
||||||
|
this.writePointer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readBits(bits: number): bigint {
|
||||||
|
if (bits <= 0) return 0n;
|
||||||
|
if (this.remainingBits() < BigInt(bits)) {
|
||||||
|
throw new Error("Not enough bits to read");
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = 0n;
|
||||||
|
for (let i = 0; i < bits; i++) {
|
||||||
|
const bit = (this.stream[this.readPointer >> 3] >> (7 - (this.readPointer & 7))) & 1;
|
||||||
|
value = (value << 1n) | BigInt(bit);
|
||||||
|
this.readPointer++;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public writeBytes(value: number[]) {
|
private reserveBits(bitsToWrite: number | bigint) {
|
||||||
this.writeInt32(value.length);
|
const bits = typeof bitsToWrite === "number" ? BigInt(bitsToWrite) : bitsToWrite;
|
||||||
for (let i = 0; i < value.length; i++) {
|
if (bits <= 0n) return;
|
||||||
this.writeInt8(value[i]);
|
|
||||||
}
|
const lastBitIndex = BigInt(this.writePointer) + bits - 1n;
|
||||||
|
if (lastBitIndex < 0n) throw new Error("Bit index overflow");
|
||||||
|
|
||||||
|
const byteIndex = lastBitIndex >> 3n;
|
||||||
|
if (byteIndex > BigInt(Number.MAX_SAFE_INTEGER)) {
|
||||||
|
throw new Error("Stream too large");
|
||||||
}
|
}
|
||||||
|
|
||||||
public readBytes(): number[] {
|
this.ensureCapacity(Number(byteIndex));
|
||||||
let length = this.readInt32();
|
|
||||||
let value : any = [];
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
value.push(this.readInt8());
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ensureCapacity(byteIndex: number) {
|
||||||
|
const requiredSize = byteIndex + 1;
|
||||||
|
if (requiredSize <= this.stream.length) return;
|
||||||
|
|
||||||
|
let newSize = this.stream.length === 0 ? 32 : this.stream.length;
|
||||||
|
while (newSize < requiredSize) {
|
||||||
|
if (newSize > (0x7fffffff >> 1)) {
|
||||||
|
newSize = requiredSize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newSize <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = new Uint8Array(newSize);
|
||||||
|
next.set(this.stream);
|
||||||
|
this.stream = next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user