class DataViewStream { _grow = false _byteOffset = 0 _view = null _textDecoder = new TextDecoder() _textEncoder = new TextEncoder() get byteOffset() { return this._byteOffset } get byteLength() { return this._view.byteLength } get bytesLeft() { return this._view.byteLength - this._offset } get buffer() { return this._view.buffer } constructor(buffer=null, grow=false) { this._grow = grow this._view = new DataView(buffer || new ArrayBuffer(0)) for (const name of Object.getOwnPropertyNames(DataView.prototype)) { const match = name.match(/^([gs]et)([^\d]+)(\d+)$/) if (!match) continue const variant = match[1] const type = match[2] const bits = match[3] const bytes = bits / 8 this[name] = function(...args) { return this._view[name](this._byteOffset, ...args) } if (variant === 'get') { this[`read${type}${bits}`] = function(...args) { const res = this._view[name](this._byteOffset, ...args) this._byteOffset += bytes return res } } else { this[`write${type}${bits}`] = function(...args) { this.expectBytes(bits/8) const res = this._view[name](this._byteOffset, ...args) this._byteOffset += bytes return res } } } } setCanGrow(grow) { this._grow = grow } seek(byteOffset) { this._byteOffset = byteOffset } appendBytes(num) { if (!this._grow) throw new Error('Cannot grow') const oldBuffer = new Uint8Array(this._view.buffer) const newBuffer = new Uint8Array(new ArrayBuffer(oldBuffer.length + num)) newBuffer.set(oldBuffer) this._view = new DataView(newBuffer.buffer) } expectBytes(byteCount) { if (!this._grow) return const missingBytes = (this.bytesLeft - byteCount) * -1 if (missingBytes > 0) { this.appendBytes(missingBytes) } } // Buffer getBuffer(len=-1) { if (len === -1) len = this._view.byteLength - this._byteOffset const byteOffset = this._view.byteOffset + this._byteOffset return this._view.buffer.slice(byteOffset, byteOffset + len) } readBuffer(len=-1) { const buffer = this.getBuffer(len) this._byteOffset += buffer.byteLength return buffer } setBuffer(buffer) { this.expectBytes(buffer.byteLength) const bytes = new Uint8Array(buffer) for (let i=0; i 0 ? this.readString(len) : '' this.seek(byteOffset) return string } readPString(byteLen=4) { const string = this.getPString(byteLen) this._byteOffset += byteLen + string.length return string } setPString(string, byteLen=4) { const byteOffset = this._byteOffset this.writePString(string, byteLen) this.seek(byteOffset) } writePString(string, byteLen=4) { if (byteLen === 1) this.writeInt8(string.length || -1) else if (byteLen === 2) this.writeInt16(string.length || -1) else if (byteLen === 4) this.writeInt32(string.length || -1) if (string.length > 0) this.writeString(string) } // String getString(len) { return this._textDecoder.decode(this.getBuffer(len)) } readString(len) { const string = this.getString(len) this._byteOffset += len return string } setString(string) { this.setBuffer(this._textEncoder.encode(string).buffer) } writeString(string) { this.writeBuffer(this._textEncoder.encode(string).buffer) } }