Skip to content

Commit 758d543

Browse files
committed
cache repeated string operations
1 parent c006b1c commit 758d543

1 file changed

Lines changed: 93 additions & 21 deletions

File tree

src/lib/revlogParser.ts

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export async function parseREVLOG(
7777
const ENCODER_ID = 7;
7878

7979
const RECORDS = new Map<string, { id: number; type: string }>();
80+
const FAST_RECORDS = new Map<number, { id: number; type: string }>();
8081
let NEXT_RECORD_ID = 1;
8182
let recordsProcessed = false;
8283

@@ -222,6 +223,63 @@ export async function parseREVLOG(
222223
writeOut(payload);
223224
};
224225

226+
const writeRecordFast = (
227+
recordInfo: { id: number; type: string },
228+
entryValue: string | number | boolean | bigint,
229+
timestampMs: number
230+
) => {
231+
let payload: Buffer;
232+
switch (recordInfo.type) {
233+
case 'boolean':
234+
payload = Buffer.alloc(1);
235+
payload.writeUInt8(entryValue ? 1 : 0, 0);
236+
break;
237+
case 'int64':
238+
payload = Buffer.alloc(8);
239+
try {
240+
payload.writeBigInt64LE(
241+
BigInt(
242+
typeof entryValue === 'number'
243+
? Math.round(entryValue)
244+
: entryValue
245+
),
246+
0
247+
);
248+
} catch {
249+
payload.writeBigInt64LE(0n, 0);
250+
}
251+
break;
252+
case 'float':
253+
payload = Buffer.alloc(4);
254+
payload.writeFloatLE(Number(entryValue), 0);
255+
break;
256+
case 'double':
257+
payload = Buffer.alloc(8);
258+
payload.writeDoubleLE(Number(entryValue), 0);
259+
break;
260+
case 'string':
261+
payload = Buffer.from(String(entryValue), 'utf-8');
262+
break;
263+
default:
264+
return;
265+
}
266+
267+
const payloadSize = payload.length;
268+
const timestampUs = BigInt(timestampMs) * 1000n;
269+
const bitfield =
270+
(requiredBytes(recordInfo.id) - 1) |
271+
((requiredBytes(payloadSize) - 1) << 2) |
272+
((requiredTimestampBytes(timestampUs) - 1) << 4);
273+
274+
writeOut(Buffer.from([bitfield]));
275+
writeOut(writeVariableInt(recordInfo.id, requiredBytes(recordInfo.id)));
276+
writeOut(writeVariableInt(payloadSize, requiredBytes(payloadSize)));
277+
writeOut(
278+
writeVariableInt(timestampUs, requiredTimestampBytes(timestampUs))
279+
);
280+
writeOut(payload);
281+
};
282+
225283
const getWpilogTypeFromSignal = (signal: Signal): string => {
226284
if (signal.length === 1) return 'boolean';
227285
const isFloat =
@@ -256,10 +314,7 @@ export async function parseREVLOG(
256314
firmwareFrameId?: number;
257315
firmwareMessageName?: string;
258316
periodicFrames: Map<number, Message>;
259-
parseFirmwareVersion(
260-
decoded: DecodedSignal[],
261-
data?: Buffer
262-
): string;
317+
parseFirmwareVersion(decoded: DecodedSignal[], data?: Buffer): string;
263318
}
264319

265320
const sparkDbc = await readDbcFile('spark.public.dbc');
@@ -445,29 +500,46 @@ export async function parseREVLOG(
445500
device.canDecoder.createFrame(messageSpec.id, alignedData)
446501
);
447502
if (decoded) {
448-
for (const sig of decoded) {
449-
const signalSpec = messageSpec.signals.get(sig.name);
450-
if (!signalSpec) continue;
503+
let sigIndex = 0; // Track the index manually
451504

452-
const folder = sig.name.endsWith('FAULT')
453-
? '/FAULT/'
454-
: sig.name.endsWith('WARNING')
455-
? '/WARNING/'
456-
: '/';
457-
const name = `${device.prefix}${messageId & 0x3f}${folder}${sig.name}`;
505+
for (const sig of decoded) {
506+
// Generate our unique math-based key
507+
const fastKey = messageId * 256 + sigIndex;
508+
509+
let recordInfo = FAST_RECORDS.get(fastKey);
510+
511+
// SLOW PATH: We have never seen this signal before.
512+
// Do the string math, register it, and cache it.
513+
if (!recordInfo) {
514+
const signalSpec = messageSpec.signals.get(sig.name);
515+
if (!signalSpec) {
516+
sigIndex++;
517+
continue;
518+
}
519+
520+
const folder = sig.name.endsWith('FAULT')
521+
? '/FAULT/'
522+
: sig.name.endsWith('WARNING')
523+
? '/WARNING/'
524+
: '/';
525+
const name = `${device.prefix}${messageId & 0x3f}${folder}${sig.name}`;
458526

459-
if (!RECORDS.has(name)) {
460527
const wpilogType = getWpilogTypeFromSignal(signalSpec);
461528
const metadata =
462529
signalSpec.description ?? signalSpec.unit ?? '';
463-
writeControlRecord(
464-
NEXT_RECORD_ID++,
465-
name,
466-
wpilogType,
467-
metadata
468-
);
530+
531+
const newId = NEXT_RECORD_ID++;
532+
writeControlRecord(newId, name, wpilogType, metadata);
533+
534+
// Save it to the fast cache
535+
recordInfo = { id: newId, type: wpilogType };
536+
FAST_RECORDS.set(fastKey, recordInfo);
469537
}
470-
writeRecord(name, sig.value, msgTsMs);
538+
539+
// FAST PATH: Write immediately using the cached info. No strings attached!
540+
writeRecordFast(recordInfo, sig.value, msgTsMs);
541+
542+
sigIndex++;
471543
}
472544
}
473545
} catch (e) {

0 commit comments

Comments
 (0)