export class Byte32Array { private _maxLength: number = -1; private _arrayBuffer: ArrayBuffer; private _float32Array: Float32Array; private _int32Array: Int32Array; private _uint32Array: Uint32Array; public bytePosition: number = 0; public byteLength: number = -1; public get arrayBuffer(): ArrayBuffer { return this._arrayBuffer; } public set arrayBuffer(value: ArrayBuffer) { //reset position this.bytePosition = 0; //clear length this.byteLength = -1; this._maxLength = -1; //calc new length this._ensureSpace(value.byteLength); //write new buffer this._int32Array.set(new Int32Array(value)); } constructor(byteLength: number = 0) { this._ensureSpace(byteLength); } public readByte32Array(byte32Array: Byte32Array, byteLength: number = -1): void { if (byteLength == -1) byteLength = byte32Array.byteLength - byte32Array.bytePosition; byte32Array.writeInt32Array( this._int32Array.subarray(this.bytePosition / 4, (this.bytePosition + byteLength) / 4) ); this.bytePosition += byteLength; } public writeByte32Array(byte32Array: Byte32Array): void { byte32Array.bytePosition = 0; byte32Array.readByte32Array(this, byte32Array.byteLength); } public writeFloat32Array(float32Array: Float32Array): void { this._ensureSpace(this.bytePosition + float32Array.length * 4); this._float32Array.set(float32Array, this.bytePosition / 4); this.bytePosition += float32Array.length * 4; } public writeInt32Array(int32Array: Int32Array): void { this._ensureSpace(this.bytePosition + int32Array.length * 4); this._uint32Array.set(int32Array, this.bytePosition / 4); this.bytePosition += int32Array.length * 4; } public writeUint32Array(uint32Array: Uint32Array): void { this._ensureSpace(this.bytePosition + uint32Array.length * 4); this._uint32Array.set(uint32Array, this.bytePosition / 4); this.bytePosition += uint32Array.length * 4; } public readFloat32Array(float32Array: Float32Array): void { float32Array.set( this._float32Array.subarray(this.bytePosition / 4, this.bytePosition / 4 + float32Array.length) ); } public readInt32Array(int32Array: Int32Array): void { int32Array.set( this._int32Array.subarray(this.bytePosition / 4, this.bytePosition / 4 + int32Array.length) ); } public readUint32Array(uint32Array: Uint32Array): void { uint32Array.set( this._uint32Array.subarray(this.bytePosition / 4, this.bytePosition / 4 + uint32Array.length) ); } public getBytesAvailable(): number { return this.byteLength - this.bytePosition; } public readUTFBytes(len: number): string { let value: string = ''; const max: number = this.bytePosition + len; const data: DataView = new DataView(this._arrayBuffer); // utf8-encode while (this.bytePosition < max) { const c: number = data.getUint8(this.bytePosition++); if (c < 0x80) { if (c == 0) break; value += String.fromCharCode(c); } else if (c < 0xE0) { value += String.fromCharCode(((c & 0x3F) << 6) | (data.getUint8(this.bytePosition++) & 0x7F)); } else if (c < 0xF0) { const c2 = data.getUint8(this.bytePosition++); value += String.fromCharCode(((c & 0x1F) << 12) | ((c2 & 0x7F) << 6) | (data.getUint8(this.bytePosition++) & 0x7F)); } else { const c2 = data.getUint8(this.bytePosition++); const c3 = data.getUint8(this.bytePosition++); value += String.fromCharCode(((c & 0x0F) << 18) | ((c2 & 0x7F) << 12) | ((c3 << 6) & 0x7F) | (data.getUint8(this.bytePosition++) & 0x7F)); } } return value; } public writeUTFBytes(s: string) { const escstr: string = encodeURIComponent(s); let binstr: string = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) { return String.fromCharCode(parseInt('0x' + p1)); }); if (binstr.length % 4) { const padding: number = 4 - (binstr.length % 4); for (let i = 0; i < padding; i++) binstr += ' '; } this._ensureSpace(this.bytePosition + 4 + binstr.length); this.writeInt(binstr.length); for (let i = 0; i < binstr.length; i += 4) this.writeUnsignedInt((binstr.charCodeAt(i + 3) << 24) | (binstr.charCodeAt(i + 2) << 16) | (binstr.charCodeAt(i + 1) << 8) | binstr.charCodeAt(i)); return binstr.length; } public readInt(): number { if (this.bytePosition > this.byteLength + 4) throw 'ByteArray out of bounds read. Position=' + this.bytePosition + ', Length=' + this.byteLength; const pa: number = this.bytePosition >> 2; this.bytePosition += 4; return this._int32Array[ pa ]; } public writeUnsignedInt(b: number): void { this._ensureSpace(this.bytePosition + 4); this._uint32Array[ this.bytePosition >> 2 ] = (~~b) & 0xffffffff; // ~~ is cast to int in js... this.bytePosition += 4; } public writeInt(b: number): void { this._ensureSpace(this.bytePosition + 4); this._int32Array[ this.bytePosition >> 2 ] = (~~b); // ~~ is cast to int in js... this.bytePosition += 4; } public readUnsignedInt(): number { if (this.bytePosition > this.byteLength + 4) throw 'ByteArray out of bounds read. Position=' + this.bytePosition + ', Length=' + this.byteLength; const pa: number = this.bytePosition >> 2; this.bytePosition += 4; return this._uint32Array[ pa ]; } public writeFloat(b: number): void { this._ensureSpace(this.bytePosition + 4); this._float32Array[this.bytePosition >> 2] = b; this.bytePosition += 4; } public readFloat(): number { if (this.bytePosition > this.byteLength + 4) throw 'ByteArray out of bounds read. Positon=' + this.bytePosition + ', Length=' + this.byteLength; const pa: number = this.bytePosition >> 2; this.bytePosition += 4; return this._float32Array[pa]; } public _ensureSpace(length: number): void { if (this.byteLength < length) { this.byteLength = length; if (this._maxLength < length) { //add additional length in minimum 255-byte increments this._maxLength = Math.max((length + 255) & (~255), 4); const newArrayBuffer: ArrayBuffer = new ArrayBuffer(this._maxLength); const newInt32Array: Int32Array = new Int32Array(newArrayBuffer); if (this._int32Array) newInt32Array.set(this._int32Array); // memcpy this._uint32Array = new Uint32Array(newArrayBuffer); this._float32Array = new Float32Array(newArrayBuffer); this._int32Array = newInt32Array; this._arrayBuffer = newArrayBuffer; } } } }