Created
December 29, 2025 08:44
-
-
Save tong/cf51d6d92cc60435a6ad3f64380429cf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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