Skip to content

Instantly share code, notes, and snippets.

@tong
Created December 29, 2025 08:44
Show Gist options
  • Select an option

  • Save tong/cf51d6d92cc60435a6ad3f64380429cf to your computer and use it in GitHub Desktop.

Select an option

Save tong/cf51d6d92cc60435a6ad3f64380429cf to your computer and use it in GitHub Desktop.
//const textDecoder = new TextDecoder();
function readUint32(packet) {
const v = packet.view.getUint32(packet.offset, true);
packet.offset += 4;
return v;
}
function readUint16(packet) {
const v = packet.view.getUint16(packet.offset, true);
packet.offset += 2;
return v;
}
function readString(packet) {
const len = packet.view.getUint8(packet.offset++);
const b = new Uint8Array(packet.view.buffer, packet.offset, len);
packet.offset += len;
//return textDecoder.decode(b);
return new TextDecoder().decode(b);
}
const template = { __proto__: null };
// const template = Object.create(null);
template.field1 = readUint32;
template.field2 = readString;
template.field3 = readUint16;
template.field4 = readString;
const keys = Object.keys(template);
// Challenge: find another way of implementing dynamic deserializer
function decodePacket(packet) {
const row = {};
for (const key of keys) row[key] = template[key](packet);
return row;
}
///////////////////////////////////////////////////////////////////////////////
// - 1: use array indexing
const schema = [
["field1", readUint32],
["field2", readString],
["field3", readUint16],
["field4", readString]
];
function decodePacket1(packet) {
const row = {};
for (let i = 0; i < schema.length; i++) {
const [key, fn] = schema[i];
row[key] = fn(packet);
}
return row;
}
// - 2: precreate decoder
function precreateDecoder() {
const row = {};
const readers = keys.map(key => template[key]);
return function (packet) {
for (let i = 0; i < keys.length; i++) {
row[keys[i]] = readers[i](packet);
}
return row;
}
}
const decodePacket2 = precreateDecoder(template);
// - 3: re-use result
const row = { field1: 0, field2: "", field3: 0, field4: "" };
function decodePacket3(packet) {
row.field1 = readUint32(packet);
row.field2 = readString(packet);
row.field3 = readUint16(packet);
row.field4 = readString(packet);
return row;
}
// - 4: dynamic code generation
const decodePacket4 = new Function('packet', `return {
field1: this.readUint32(packet),
field2: this.readString(packet),
field3: this.readUint16(packet),
field4: this.readString(packet),
};`).bind({
readUint32: readUint32,
readString: readString,
readUint16: readUint16
});
// - 5: hardcode
function decodePacket5(packet) {
return {
field1: readUint32(packet),
field2: readString(packet),
field3: readUint16(packet),
field4: readString(packet)
};
}
// - 6:
// const createDecoder = (O => (t, p = O.prototype) => (o => ((t, p) => v => (o = v, { __proto__: p, ...t }))(O.create({}, O.keys(t).reduce((d, k) => (d[k] = { enumerable: 1, get: (g => () => g(o))(t[k]) }, d), {})), p))(null))(Object);
// const decodePacket6 = createDecoder({
// id: readUint32,
// value: String
// });
// const createDecoder6 = (O =>
// (t, p = O.prototype) =>
// (o =>
// ((t, p) =>
// v => (
// o = v,
// { __proto__: p, ...t }
// )
// )(
// O.create(
// {},
// O.keys(t).reduce((d, k) => (
// d[k] = {
// enumerable: 1,
// get: (g => () => g(o))(t[k])
// },
// d
// ), {})
// ),
// p
// )
// )(null)
// )(Object);
// const decodePacket6 = createDecoder6({
// id: readUint32,
// value: String
// });
function createDecoder7(schema, proto = Object.prototype) {
let packet = null;
const descriptors = {};
for (const key of Object.keys(schema)) {
const reader = schema[key];
descriptors[key] = {
enumerable: true,
configurable: true,
get() {
const value = reader(packet);
Object.defineProperty(this, key, {
value,
enumerable: true,
writable: false,
configurable: false
});
return value;
}
};
}
const base = Object.create(proto, descriptors);
return function decode(p) {
packet = p;
return Object.create(base);
};
}
const decodePacket7 = createDecoder7({
field1: readUint32,
field2: readString,
field3: readUint16,
field4: readString
});
//----- test ------------------------------------------------------------------
const buf = new ArrayBuffer(20);
const view = new DataView(buf);
let o = 0;
view.setUint32(o, 123456890, true);
o += 4;
view.setUint8(o++, 5);
new Uint8Array(buf, o, 5).set([116, 111, 116, 97, 108]);
o += 5;
view.setUint16(o, 666, true);
o += 2;
view.setUint8(o++, 7);
new Uint8Array(buf, o, 7).set([97, 110, 97, 114, 99, 104, 121]);
o += 7;
const packet = { view: new DataView(buf), offset: 0 };
//console.log(decodePacket5(packet))
console.time("decode");
for (let i = 0; i < 10000000; i++) {
packet.offset = 0;
decodePacket(packet); // ~ 2.5s
// decodePacket1(packet); // ~ 2.3s
// decodePacket2(packet); // ~ 2.3s
// decodePacket3(packet); // ~ 2.3s
// decodePacket4(packet); // ~ 2.0s
// decodePacket5(packet); // ~ 2.0s
// decodePacket6(packet);
decodePacket7(packet); // ~ 3.4s
}
console.timeEnd("decode");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment