From 070e32bb747c54c16d6ffc7dcde758323b65003a Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 22 Jan 2025 23:08:16 -0600 Subject: [PATCH 1/5] replace DataView with inline bit twiddling --- src/decoders/data/data-packet-chunk.ts | 3 +-- src/decoders/info/info-packet-chunk.ts | 3 +-- src/decoders/info/info-tracker-chunk.ts | 3 +-- src/decoders/packet-header-chunk.ts | 11 +++++++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/decoders/data/data-packet-chunk.ts b/src/decoders/data/data-packet-chunk.ts index 0519f32..3abe622 100644 --- a/src/decoders/data/data-packet-chunk.ts +++ b/src/decoders/data/data-packet-chunk.ts @@ -10,8 +10,7 @@ export default (buffer: Uint8Array): DataPacketChunk => { if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) { let offset = 0; while (offset < chunk.header.dataLen) { - const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength); - const chunkId = view.getUint16(offset, true); + const chunkId = (chunk.chunkData.subarray(offset)[1] << 8) + chunk.chunkData.subarray(offset)[0]; switch (chunkId) { case 0x0000: data.packetHeader = Decoders.PacketHeaderChunk(chunk.chunkData.subarray(offset)); diff --git a/src/decoders/info/info-packet-chunk.ts b/src/decoders/info/info-packet-chunk.ts index e102d82..2d3d603 100644 --- a/src/decoders/info/info-packet-chunk.ts +++ b/src/decoders/info/info-packet-chunk.ts @@ -10,8 +10,7 @@ export default (buffer: Uint8Array): InfoPacketChunk => { if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) { let offset = 0; while (offset < chunk.header.dataLen) { - const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength); - const chunkId = view.getUint16(offset, true); + const chunkId = (chunk.chunkData.subarray(offset)[1] << 8) + chunk.chunkData.subarray(offset)[0]; switch (chunkId) { case 0x0000: data.packetHeader = Decoders.PacketHeaderChunk(chunk.chunkData.subarray(offset)); diff --git a/src/decoders/info/info-tracker-chunk.ts b/src/decoders/info/info-tracker-chunk.ts index 6a41fb8..feb7095 100644 --- a/src/decoders/info/info-tracker-chunk.ts +++ b/src/decoders/info/info-tracker-chunk.ts @@ -8,8 +8,7 @@ export default (buffer: Uint8Array): InfoTrackerChunk => { if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) { let offset = 0; while (offset < chunk.header.dataLen) { - const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength); - const chunkId = view.getUint16(offset, true); + const chunkId = (chunk.chunkData.subarray(offset)[1] << 8) + chunk.chunkData.subarray(offset)[0]; switch (chunkId) { case 0x0000: { const data: InfoTrackerChunkData = { diff --git a/src/decoders/packet-header-chunk.ts b/src/decoders/packet-header-chunk.ts index 5919985..b541f96 100644 --- a/src/decoders/packet-header-chunk.ts +++ b/src/decoders/packet-header-chunk.ts @@ -4,9 +4,16 @@ import { PacketHeaderChunk, PacketHeaderChunkData } from '../models/packet-heade export default (buffer: Uint8Array): PacketHeaderChunk => { const chunk = Decoders.Chunk(buffer); - const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength); + let packetTimestamp = BigInt(chunk.chunkData[7]) << BigInt(56); + packetTimestamp += BigInt(chunk.chunkData[6]) << BigInt(48); + packetTimestamp += BigInt(chunk.chunkData[5]) << BigInt(40); + packetTimestamp += BigInt(chunk.chunkData[4]) << BigInt(32); + packetTimestamp += BigInt(chunk.chunkData[3]) << BigInt(24); + packetTimestamp += BigInt(chunk.chunkData[2]) << BigInt(16); + packetTimestamp += BigInt(chunk.chunkData[1]) << BigInt(8); + packetTimestamp += BigInt(chunk.chunkData[0]); const data: PacketHeaderChunkData = { - packetTimestamp: view.getBigUint64(0, true), + packetTimestamp, versionHigh: chunk.chunkData[8], versionLow: chunk.chunkData[9], frameId: chunk.chunkData[10], From e4ed695eb9aab6e6c06865ffb67035ed1258f7c1 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 22 Jan 2025 23:08:44 -0600 Subject: [PATCH 2/5] more dataview replacements --- src/decoders/chunk.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/decoders/chunk.ts b/src/decoders/chunk.ts index 8589710..f4be67f 100644 --- a/src/decoders/chunk.ts +++ b/src/decoders/chunk.ts @@ -1,16 +1,16 @@ import { Chunk } from '../models/chunk'; export default (buffer: Uint8Array): Chunk => { - let offset = 0; - const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); - const id = view.getUint16(offset, true); - offset += 2; + if (buffer.length < 4) { + throw new Error('Chunk buffer must be at least 4 bytes'); + } + + const id = (buffer[1] << 8) + buffer[0]; // NOTE(jwetzell): this data is split up as 1 bit for hasSubchunks and 15 bits for the dataLen - const combinedLengthAndFlag = view.getUint16(offset, true); + const combinedLengthAndFlag = (buffer[3] << 8) + buffer[2]; const hasSubchunks = combinedLengthAndFlag > 32768; const dataLen = hasSubchunks ? combinedLengthAndFlag - 32768 : combinedLengthAndFlag; - offset += 2; const header = { id, @@ -18,7 +18,7 @@ export default (buffer: Uint8Array): Chunk => { hasSubchunks, }; - const chunkData = buffer.subarray(offset, offset + header.dataLen); + const chunkData = buffer.subarray(4, 4 + header.dataLen); const chunk = { chunkData, From c33a2dfd3324b0d39ba2a1d29bd23ffb59d20218 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 22 Jan 2025 23:09:28 -0600 Subject: [PATCH 3/5] reuse instances of TextDecoder/Encoder --- src/decoders/info/info-system-name-chunk.ts | 3 ++- src/decoders/info/info-tracker-name-chunk.ts | 3 ++- src/encoders/info/info-system-name-chunk.ts | 4 ++-- src/encoders/info/info-tracker-name-chunk.ts | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/decoders/info/info-system-name-chunk.ts b/src/decoders/info/info-system-name-chunk.ts index 0b46916..adf5087 100644 --- a/src/decoders/info/info-system-name-chunk.ts +++ b/src/decoders/info/info-system-name-chunk.ts @@ -1,11 +1,12 @@ import { Decoders } from '..'; import { InfoSystemNameChunk, InfoSystemNameChunkData } from '../../models/info/info-system-name-chunk'; +const nameDecoder = new TextDecoder(); export default (buffer: Uint8Array): InfoSystemNameChunk => { const chunk = Decoders.Chunk(buffer); const data: InfoSystemNameChunkData = { - systemName: new TextDecoder().decode(chunk.chunkData.subarray(0, chunk.header.dataLen)), + systemName: nameDecoder.decode(chunk.chunkData.subarray(0, chunk.header.dataLen)), }; return { diff --git a/src/decoders/info/info-tracker-name-chunk.ts b/src/decoders/info/info-tracker-name-chunk.ts index 3d8be6f..80a8be1 100644 --- a/src/decoders/info/info-tracker-name-chunk.ts +++ b/src/decoders/info/info-tracker-name-chunk.ts @@ -1,11 +1,12 @@ import { Decoders } from '..'; import { InfoTrackerNameChunk, InfoTrackerNameChunkData } from '../../models/info/info-tracker-name-chunk'; +const nameDecoder = new TextDecoder(); export default (buffer: Uint8Array): InfoTrackerNameChunk => { const chunk = Decoders.Chunk(buffer); const data: InfoTrackerNameChunkData = { - trackerName: new TextDecoder().decode(chunk.chunkData.subarray(0, chunk.header.dataLen)), + trackerName: nameDecoder.decode(chunk.chunkData.subarray(0, chunk.header.dataLen)), }; return { diff --git a/src/encoders/info/info-system-name-chunk.ts b/src/encoders/info/info-system-name-chunk.ts index f6d00ee..71f347f 100644 --- a/src/encoders/info/info-system-name-chunk.ts +++ b/src/encoders/info/info-system-name-chunk.ts @@ -1,3 +1,3 @@ import chunk from '../chunk'; - -export default (systemName: string): Uint8Array => chunk(0x0001, new TextEncoder().encode(systemName), false); +const nameEncoder = new TextEncoder(); +export default (systemName: string): Uint8Array => chunk(0x0001, nameEncoder.encode(systemName), false); diff --git a/src/encoders/info/info-tracker-name-chunk.ts b/src/encoders/info/info-tracker-name-chunk.ts index 2157f27..e5a6e1a 100644 --- a/src/encoders/info/info-tracker-name-chunk.ts +++ b/src/encoders/info/info-tracker-name-chunk.ts @@ -1,3 +1,4 @@ import chunk from '../chunk'; -export default (trackerName: string) => chunk(0x0000, new TextEncoder().encode(trackerName), false); +const nameEncoder = new TextEncoder(); +export default (trackerName: string) => chunk(0x0000, nameEncoder.encode(trackerName), false); From 7d7b92678b0741bab0917ab6f51d596d13a78452 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 22 Jan 2025 23:10:42 -0600 Subject: [PATCH 4/5] reuse fixed size DataViews --- src/encoders/chunk.ts | 2 +- src/encoders/data/data-tracker-timestamp-chunk.ts | 2 +- src/encoders/packet-header-chunk.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/encoders/chunk.ts b/src/encoders/chunk.ts index bddec09..252cd60 100644 --- a/src/encoders/chunk.ts +++ b/src/encoders/chunk.ts @@ -1,3 +1,4 @@ +const header = new DataView(new ArrayBuffer(4)); export default (id: number, chunkData: Uint8Array, hasSubchunks: boolean): Uint8Array => { if (!Number.isInteger(id)) { throw new Error('chunk id must be an integer'); @@ -11,7 +12,6 @@ export default (id: number, chunkData: Uint8Array, hasSubchunks: boolean): Uint8 throw new Error('chunkData can not be greater than 32767 bytes'); } - const header = new DataView(new ArrayBuffer(4)); header.setUint16(0, id, true); const hasSubChunksBit = (hasSubchunks ? 1 : 0) << 15; header.setUint16(2, hasSubChunksBit + chunkData.length, true); diff --git a/src/encoders/data/data-tracker-timestamp-chunk.ts b/src/encoders/data/data-tracker-timestamp-chunk.ts index dcc674b..01e0456 100644 --- a/src/encoders/data/data-tracker-timestamp-chunk.ts +++ b/src/encoders/data/data-tracker-timestamp-chunk.ts @@ -1,7 +1,7 @@ import chunk from '../chunk'; +const buf = new DataView(new ArrayBuffer(8)); export default (timestamp: bigint): Uint8Array => { - const buf = new DataView(new ArrayBuffer(8)); buf.setBigUint64(0, BigInt(timestamp), true); return chunk(0x0006, new Uint8Array(buf.buffer), false); }; diff --git a/src/encoders/packet-header-chunk.ts b/src/encoders/packet-header-chunk.ts index d844877..84b3210 100644 --- a/src/encoders/packet-header-chunk.ts +++ b/src/encoders/packet-header-chunk.ts @@ -1,5 +1,6 @@ import chunk from './chunk'; +const packetHeader = new DataView(new ArrayBuffer(12)); export default ( timestamp: bigint, versionHigh: number, @@ -33,7 +34,6 @@ export default ( throw new Error('frame packet count must be >= 0 and <= 255'); } - const packetHeader = new DataView(new ArrayBuffer(12)); packetHeader.setBigUint64(0, BigInt(timestamp), true); packetHeader.setUint8(8, versionHigh); packetHeader.setUint8(9, versionLow); From cb60d225493898d254f8beb8f58029176d1b1144 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 22 Jan 2025 23:21:36 -0600 Subject: [PATCH 5/5] switch to reused float array where possible --- src/encoders/data/data-tracker-accel-chunk.ts | 10 +++++----- src/encoders/data/data-tracker-ori-chunk.ts | 11 +++++------ src/encoders/data/data-tracker-pos-chunk.ts | 11 +++++------ src/encoders/data/data-tracker-speed-chunk.ts | 11 +++++------ src/encoders/data/data-tracker-status-chunk.ts | 6 +++--- src/encoders/data/data-tracker-trgtpos-chunk.ts | 11 +++++------ 6 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/encoders/data/data-tracker-accel-chunk.ts b/src/encoders/data/data-tracker-accel-chunk.ts index b2a2451..b9799d8 100644 --- a/src/encoders/data/data-tracker-accel-chunk.ts +++ b/src/encoders/data/data-tracker-accel-chunk.ts @@ -1,9 +1,9 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(3); export default (x: number, y: number, z: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(12)); - buf.setFloat32(0, x, true); - buf.setFloat32(4, y, true); - buf.setFloat32(8, z, true); - return chunk(0x0004, new Uint8Array(buf.buffer), false); + floatArray[0] = x; + floatArray[1] = y; + floatArray[2] = z; + return chunk(0x0004, new Uint8Array(floatArray.buffer), false); }; diff --git a/src/encoders/data/data-tracker-ori-chunk.ts b/src/encoders/data/data-tracker-ori-chunk.ts index 56aa3ba..25a9883 100644 --- a/src/encoders/data/data-tracker-ori-chunk.ts +++ b/src/encoders/data/data-tracker-ori-chunk.ts @@ -1,10 +1,9 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(3); export default (x: number, y: number, z: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(12)); - - buf.setFloat32(0, x, true); - buf.setFloat32(4, y, true); - buf.setFloat32(8, z, true); - return chunk(0x0002, new Uint8Array(buf.buffer), false); + floatArray[0] = x; + floatArray[1] = y; + floatArray[2] = z; + return chunk(0x0002, new Uint8Array(floatArray.buffer), false); }; diff --git a/src/encoders/data/data-tracker-pos-chunk.ts b/src/encoders/data/data-tracker-pos-chunk.ts index 4813982..10858ee 100644 --- a/src/encoders/data/data-tracker-pos-chunk.ts +++ b/src/encoders/data/data-tracker-pos-chunk.ts @@ -1,11 +1,10 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(3); export default (x: number, y: number, z: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(12)); + floatArray[0] = x; + floatArray[1] = y; + floatArray[2] = z; - buf.setFloat32(0, x, true); - buf.setFloat32(4, y, true); - buf.setFloat32(8, z, true); - - return chunk(0x0000, new Uint8Array(buf.buffer), false); + return chunk(0x0000, new Uint8Array(floatArray.buffer), false); }; diff --git a/src/encoders/data/data-tracker-speed-chunk.ts b/src/encoders/data/data-tracker-speed-chunk.ts index 7cd226d..f8e5ac9 100644 --- a/src/encoders/data/data-tracker-speed-chunk.ts +++ b/src/encoders/data/data-tracker-speed-chunk.ts @@ -1,11 +1,10 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(3); export default (x: number, y: number, z: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(12)); + floatArray[0] = x; + floatArray[1] = y; + floatArray[2] = z; - buf.setFloat32(0, x, true); - buf.setFloat32(4, y, true); - buf.setFloat32(8, z, true); - - return chunk(0x0001, new Uint8Array(buf.buffer), false); + return chunk(0x0001, new Uint8Array(floatArray.buffer), false); }; diff --git a/src/encoders/data/data-tracker-status-chunk.ts b/src/encoders/data/data-tracker-status-chunk.ts index 7c62862..d1ebf98 100644 --- a/src/encoders/data/data-tracker-status-chunk.ts +++ b/src/encoders/data/data-tracker-status-chunk.ts @@ -1,7 +1,7 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(1); export default (validity: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(4)); - buf.setFloat32(0, validity, true); - return chunk(0x0003, new Uint8Array(buf.buffer), false); + floatArray[0] = validity; + return chunk(0x0003, new Uint8Array(floatArray.buffer), false); }; diff --git a/src/encoders/data/data-tracker-trgtpos-chunk.ts b/src/encoders/data/data-tracker-trgtpos-chunk.ts index 5d67d87..8df7b5f 100644 --- a/src/encoders/data/data-tracker-trgtpos-chunk.ts +++ b/src/encoders/data/data-tracker-trgtpos-chunk.ts @@ -1,11 +1,10 @@ import chunk from '../chunk'; +const floatArray = new Float32Array(3); export default (x: number, y: number, z: number): Uint8Array => { - const buf = new DataView(new ArrayBuffer(12)); + floatArray[0] = x; + floatArray[1] = y; + floatArray[2] = z; - buf.setFloat32(0, x, true); - buf.setFloat32(4, y, true); - buf.setFloat32(8, z, true); - - return chunk(0x0005, new Uint8Array(buf.buffer), false); + return chunk(0x0005, new Uint8Array(floatArray.buffer), false); };