94 lines
2.3 KiB
JavaScript
94 lines
2.3 KiB
JavaScript
import { isHeadless, parse, serialize, sizeof, sizeofHead, Type } from '.'
|
|
|
|
export function Struct(type_obj) {
|
|
const obj = {}
|
|
Object.setPrototypeOf(obj, Struct.prototype)
|
|
|
|
obj._info_by_key = new Map()
|
|
obj._headless = false
|
|
|
|
const arg = {}
|
|
let offset = 0
|
|
|
|
for (const [key, value] of Object.entries(type_obj)) {
|
|
let types
|
|
if (Array.isArray(value)) {
|
|
types = value
|
|
} else {
|
|
types = [value]
|
|
}
|
|
|
|
arg[key] = types.map((type) => type.name)
|
|
|
|
const headless = isHeadless(...types)
|
|
obj._info_by_key.set(key, {
|
|
offset,
|
|
types,
|
|
headless,
|
|
})
|
|
obj._headless |= headless
|
|
offset += sizeofHead(...types)
|
|
}
|
|
|
|
obj._size = offset
|
|
obj.new(Struct, [arg])
|
|
|
|
return obj
|
|
}
|
|
Struct.prototype.serialize = function (dv, src) {
|
|
let data_offset = this._size
|
|
|
|
for (const [key, value] of Object.entries(src)) {
|
|
const info = this._info_by_key.get(key)
|
|
|
|
if (info.headless) {
|
|
dv.setUint32(info.offset, data_offset)
|
|
const frame = new DataView(dv.buffer, dv.byteOffset + data_offset)
|
|
serialize(frame, value, ...info.types)
|
|
data_offset += sizeof(value, ...info.types)
|
|
} else {
|
|
const frame = new DataView(dv.buffer, dv.byteOffset + info.offset)
|
|
serialize(frame, value, ...info.types)
|
|
}
|
|
}
|
|
}
|
|
Struct.prototype.parse = function (dv) {
|
|
const res = {}
|
|
|
|
for (const [key, info] of this._info_by_key.entries()) {
|
|
if (info.headless) {
|
|
const data_offset = dv.getUint32(info.offset)
|
|
const frame = new DataView(dv.buffer, dv.byteOffset + data_offset)
|
|
res[key] = parse(frame, ...info.types)
|
|
} else {
|
|
const frame = new DataView(dv.buffer, dv.byteOffset + info.offset)
|
|
res[key] = parse(frame, ...info.types)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
Struct.prototype.isHeadless = function () {
|
|
return this._headless
|
|
}
|
|
Struct.prototype.sizeof = function (arg) {
|
|
if (this._headless) {
|
|
if (arg === undefined) {
|
|
throw new Error('unknown size of ' + this)
|
|
}
|
|
let size = this._size
|
|
|
|
for (const [key, info] of this._info_by_key.entries()) {
|
|
if (info.headless) {
|
|
size += sizeof(arg[key], ...info.types)
|
|
}
|
|
}
|
|
|
|
return size
|
|
} else {
|
|
return this._size
|
|
}
|
|
}
|
|
Object.setPrototypeOf(Struct.prototype, Type.prototype)
|
|
Object.freeze(Struct.prototype)
|