Skip to content

Instantly share code, notes, and snippets.

@freehuntx
Last active November 5, 2024 23:34
Show Gist options
  • Save freehuntx/8b554edcc6238105286b73e6f8388761 to your computer and use it in GitHub Desktop.
Save freehuntx/8b554edcc6238105286b73e6f8388761 to your computer and use it in GitHub Desktop.
Buffer Serialization tools
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<bytes.length; i++) {
this._view.setUint8(this._byteOffset + i, bytes[i])
}
}
writeBuffer(buffer) {
this.setBuffer(buffer)
this._byteOffset += buffer.byteLength
}
// Prefixed String
getPString(byteLen=4) {
const byteOffset = this._byteOffset
let len = -1
if (byteLen === 1) len = this.readInt8()
else if (byteLen === 2) len = this.readInt16()
else if (byteLen === 4) len = this.readInt32()
const string = len > 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)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment