UNPKG

mustreams

Version:

Binary stream and buffer for mudb

303 lines (258 loc) 8.29 kB
import { encodeString, decodeString, } from './browser-string'; // round to next highest power of 2 function ceilLog2 (v_) { let v = v_ - 1; let r = (v > 0xFFFF) ? 1 << 4 : 0; v >>>= r; let shift = (v > 0xFF) ? 1 << 3 : 0; v >>>= shift; r |= shift; shift = (v > 0xF) ? 1 << 2 : 0; v >>>= shift; r |= shift; shift = (v > 0x3) ? 1 << 1 : 0; v >>>= shift; r |= shift; return (r | (v >> 1)) + 1; } export class MuBuffer { public buffer:ArrayBuffer; public dataView:DataView; public uint8:Uint8Array; constructor (buffer:ArrayBuffer) { this.buffer = buffer; this.dataView = new DataView(buffer); this.uint8 = new Uint8Array(buffer); } } // initialize buffer pool const bufferPool:MuBuffer[][] = new Array(32); for (let i = 0; i < 32; ++i) { bufferPool[i] = []; } export function allocBuffer (size) : MuBuffer { const b = ceilLog2(size); return bufferPool[b].pop() || new MuBuffer(new ArrayBuffer(1 << b)); } export function freeBuffer (buffer:MuBuffer) { if (buffer.uint8.length > 0) { bufferPool[ceilLog2(buffer.uint8.length)].push(buffer); } } export function reallocBuffer (buffer:MuBuffer, nsize:number) { if (buffer.uint8.length >= nsize) { return buffer; } const result = allocBuffer(nsize); result.uint8.set(buffer.uint8); freeBuffer(buffer); return result; } const LITTLE_ENDIAN = true; export class MuWriteStream { public buffer:MuBuffer; public offset:number; constructor (capacity:number) { this.buffer = allocBuffer(capacity); this.offset = 0; } public bytes () { return this.buffer.uint8.subarray(0, this.offset); } public destroy () { freeBuffer(this.buffer); } public grow (bytes:number) { this.buffer = reallocBuffer(this.buffer, this.offset + bytes); } public writeInt8 (x:number) { this.buffer.dataView.setInt8(this.offset++, x); } public writeInt16 (x:number) { this.buffer.dataView.setInt16(this.offset, x, LITTLE_ENDIAN); this.offset += 2; } public writeInt32 (x:number) { this.buffer.dataView.setInt32(this.offset, x, LITTLE_ENDIAN); this.offset += 4; } public writeUint8 (x:number) { this.buffer.dataView.setUint8(this.offset++, x); } public writeUint16 (x:number) { this.buffer.dataView.setUint16(this.offset, x, LITTLE_ENDIAN); this.offset += 2; } public writeUint32 (x:number) { this.buffer.dataView.setUint32(this.offset, x, LITTLE_ENDIAN); this.offset += 4; } public writeFloat32 (x:number) { this.buffer.dataView.setFloat32(this.offset, x, LITTLE_ENDIAN); this.offset += 4; } public writeFloat64 (x:number) { this.buffer.dataView.setFloat64(this.offset, x, LITTLE_ENDIAN); this.offset += 8; } public writeVarInt (x_:number) { const x = x_ >>> 0; const bytes = this.buffer.uint8; const offset = this.offset; if (x < (1 << 7)) { bytes[offset] = x; this.offset += 1; } else if (x < (1 << 14)) { bytes[offset] = 0x80 | (x & 0x7f); bytes[offset + 1] = x >>> 7; this.offset += 2; } else if (x < (1 << 21)) { bytes[offset] = 0x80 | (x & 0x7f); bytes[offset + 1] = 0x80 | ((x >> 7) & 0x7f); bytes[offset + 2] = x >>> 14; this.offset += 3; } else if (x < (1 << 28)) { bytes[offset] = 0x80 | (x & 0x7f); bytes[offset + 1] = 0x80 | ((x >> 7) & 0x7f); bytes[offset + 2] = 0x80 | ((x >> 14) & 0x7f); bytes[offset + 3] = x >>> 21; this.offset += 4; } else { bytes[offset] = 0x80 | (x & 0x7f); bytes[offset + 1] = 0x80 | ((x >> 7) & 0x7f); bytes[offset + 2] = 0x80 | ((x >> 14) & 0x7f); bytes[offset + 3] = 0x80 | ((x >> 21) & 0x7f); bytes[offset + 4] = x >>> 28; this.offset += 5; } } public writeASCIINoLength (str:string) { for (let i = 0; i < str.length; ++i) { this.writeUint8(str.charCodeAt(i)); } } public writeASCII (str:string) { this.writeUint32(str.length); this.writeASCIINoLength(str); } public writeString (str:string) { const bytes = encodeString(str); this.writeUint32(bytes.length); this.buffer.uint8.set(bytes, this.offset); this.offset += bytes.length; } public writeUint8At (offset:number, x:number) { this.buffer.dataView.setUint8(offset, x); } public writeUint32At (offset:number, x:number) { this.buffer.dataView.setUint32(offset, x, LITTLE_ENDIAN); } } export class MuReadStream { public buffer:MuBuffer; public offset:number; public length:number; constructor (data:Uint8Array) { this.buffer = new MuBuffer(data.buffer); this.offset = data.byteOffset; this.length = data.byteLength + data.byteOffset; } public readInt8 () : number { return this.buffer.dataView.getInt8(this.offset++); } public readInt16 () : number { const offset = this.offset; this.offset += 2; return this.buffer.dataView.getInt16(offset, LITTLE_ENDIAN); } public readInt32 () : number { const offset = this.offset; this.offset += 4; return this.buffer.dataView.getInt32(offset, LITTLE_ENDIAN); } public readUint8 () : number { return this.buffer.dataView.getUint8(this.offset++); } public readUint16 () : number { const offset = this.offset; this.offset += 2; return this.buffer.dataView.getUint16(offset, LITTLE_ENDIAN); } public readUint32 () : number { const offset = this.offset; this.offset += 4; return this.buffer.dataView.getUint32(offset, LITTLE_ENDIAN); } public readFloat32 () : number { const offset = this.offset; this.offset += 4; return this.buffer.dataView.getFloat32(offset, LITTLE_ENDIAN); } public readFloat64 () : number { const offset = this.offset; this.offset += 8; return this.buffer.dataView.getFloat64(offset, LITTLE_ENDIAN); } public readVarInt () : number { const bytes = this.buffer.uint8; let offset = this.offset; const x0 = bytes[offset++]; if (x0 < 0x80) { this.offset = offset; return x0; } const x1 = bytes[offset++]; if (x1 < 0x80) { this.offset = offset; return (x0 & 0x7f) | (x1 << 7); } const x2 = bytes[offset++]; if (x2 < 0x80) { this.offset = offset; return (x0 & 0x7f) | ((x1 & 0x7f) << 7) | (x2 << 14); } const x3 = bytes[offset++]; if (x3 < 0x80) { this.offset = offset; return (x0 & 0x7f) | ((x1 & 0x7f) << 7) | ((x2 & 0x7f) << 14) | (x3 << 21); } const x4 = bytes[offset++]; this.offset = offset; return (x0 & 0x7f) + ((x1 & 0x7f) << 7) + ((x2 & 0x7f) << 14) + ((x3 & 0x7f) << 21) + (x4 * (1 << 28)); } public readASCIIOf (length:number) : string { const head = this.offset; this.offset += length; let str = ''; for (let i = head; i < this.offset; ++i) { str += String.fromCharCode(this.buffer.uint8[i]); } return str; } public readASCII () : string { const length = this.readUint32(); return this.readASCIIOf(length); } public readString () : string { const byteLength = this.readUint32(); const bytes = this.buffer.uint8.subarray(this.offset, this.offset + byteLength); this.offset += byteLength; return decodeString(bytes); } public readUint8At (offset:number) : number { return this.buffer.dataView.getUint8(offset); } }