Skip to content

Commit a970771

Browse files
authored
Add getTypeIndex and getPacketByIndex functions to Decoder (#8)
1 parent 68fa3f0 commit a970771

File tree

5 files changed

+211
-5
lines changed

5 files changed

+211
-5
lines changed

nbsdecoder.d.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export interface NbsTypeSubtypeBuffer extends NbsTypeSubtype {
6161
type: Buffer;
6262
}
6363

64+
/** The timestamps for a type subtype */
65+
export interface TypeIndex {
66+
typeSubType: NbsTypeSubtype;
67+
timestamps: NbsTimestamp[];
68+
}
69+
6470
/**
6571
* A decoder that can be used to read packets from NBS files
6672
*/
@@ -73,6 +79,13 @@ export declare class NbsDecoder {
7379
*/
7480
public constructor(paths: string[]);
7581

82+
/**
83+
* Get all the timestamps of a specified message type subtype.
84+
*
85+
* @param typeSubtype A type subtype object to get the timestamps for
86+
*/
87+
public getTypeIndex(typeSubtype: NbsTypeSubtype): NbsTimestamp[];
88+
7689
/** Get a list of all the types present in the loaded nbs files */
7790
public getAvailableTypes(): NbsTypeSubtypeBuffer[];
7891

@@ -82,9 +95,9 @@ export declare class NbsDecoder {
8295
/**
8396
* Get the timestamp range (start, end) for all packets of the given type in the loaded nbs files
8497
*
85-
* @param type A type subtype object to get the timestamp range for
98+
* @param typeSubtype A type subtype object to get the timestamp range for
8699
*/
87-
public getTimestampRange(type: NbsTypeSubtype): [NbsTimestamp, NbsTimestamp];
100+
public getTimestampRange(typeSubtype: NbsTypeSubtype): [NbsTimestamp, NbsTimestamp];
88101

89102
/**
90103
* Get the packets at or before the given timestamp for all types in the loaded nbs files
@@ -114,6 +127,17 @@ export declare class NbsDecoder {
114127
types: NbsTypeSubtype[]
115128
): NbsPacket[];
116129

130+
/**
131+
* Get the packet of the given type at the given index in the loaded nbs file.
132+
*
133+
* Returns either the packet at the index with the given type, or `undefined` if the given
134+
* index is outside the range of packets for the type.
135+
*
136+
* @param index The index of the requested packet
137+
* @param typeSubtype The type of the requested packet
138+
*/
139+
public getPacketByIndex(index: number, typeSubtype: NbsTypeSubtype): NbsPacket | undefined;
140+
117141
/**
118142
* Get the timestamp to seek to such that all messages of the given types are stepped by (n) steps
119143
*

src/Decoder.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ namespace nbs {
2323
InstanceMethod<&Decoder::GetTimestampRange>(
2424
"getTimestampRange",
2525
napi_property_attributes(napi_writable | napi_configurable)),
26+
InstanceMethod<&Decoder::GetTypeIndex>("getTypeIndex",
27+
napi_property_attributes(napi_writable | napi_configurable)),
2628
InstanceMethod<&Decoder::GetPackets>("getPackets",
2729
napi_property_attributes(napi_writable | napi_configurable)),
30+
InstanceMethod<&Decoder::GetPacketByIndex>("getPacketByIndex",
31+
napi_property_attributes(napi_writable | napi_configurable)),
2832
InstanceMethod<&Decoder::NextTimestamp>("nextTimestamp",
2933
napi_property_attributes(napi_writable | napi_configurable)),
3034
InstanceMethod<&Decoder::Close>("close", napi_property_attributes(napi_writable | napi_configurable)),
@@ -106,6 +110,30 @@ namespace nbs {
106110
}
107111
}
108112

113+
Napi::Value Decoder::GetTypeIndex(const Napi::CallbackInfo& info) {
114+
Napi::Env env = info.Env();
115+
116+
TypeSubtype typeSubtype;
117+
try {
118+
typeSubtype = this->TypeSubtypeFromJsValue(info[0], env);
119+
}
120+
catch (const std::exception& ex) {
121+
Napi::TypeError::New(env, "invalid type for argument `typeSubtype`: " + std::string(ex.what()))
122+
.ThrowAsJavaScriptException();
123+
return env.Undefined();
124+
}
125+
126+
auto typeIterator = this->index.getIteratorForType(typeSubtype);
127+
auto timestamps = Napi::Array::New(env, std::distance(typeIterator.first, typeIterator.second));
128+
129+
uint64_t idx = 0;
130+
for (auto it = typeIterator.first; it != typeIterator.second; it++) {
131+
timestamps[idx++] = timestamp::ToJsValue(it->item.timestamp, env);
132+
}
133+
134+
return timestamps;
135+
}
136+
109137
Napi::Value Decoder::GetAvailableTypes(const Napi::CallbackInfo& info) {
110138
Napi::Env env = info.Env();
111139

@@ -278,6 +306,46 @@ namespace nbs {
278306
return jsPackets;
279307
}
280308

309+
Napi::Value Decoder::GetPacketByIndex(const Napi::CallbackInfo& info) {
310+
Napi::Env env = info.Env();
311+
312+
int64_t index = 0;
313+
auto jsIndex = info[0];
314+
if (jsIndex.IsNumber()) {
315+
index = jsIndex.As<Napi::Number>().Int64Value();
316+
}
317+
else {
318+
Napi::TypeError::New(env, "invalid type for argument `index`: expected integer")
319+
.ThrowAsJavaScriptException();
320+
return env.Undefined();
321+
}
322+
323+
if (index < 0) {
324+
return env.Undefined();
325+
}
326+
327+
TypeSubtype typeSubtype;
328+
try {
329+
typeSubtype = this->TypeSubtypeFromJsValue(info[1], env);
330+
}
331+
catch (const std::exception& ex) {
332+
Napi::TypeError::New(env, "invalid type for argument `typeSubtype`: " + std::string(ex.what()))
333+
.ThrowAsJavaScriptException();
334+
return env.Undefined();
335+
}
336+
337+
auto typeIterator = this->index.getIteratorForType(typeSubtype);
338+
339+
// If the index is out of range return undefined
340+
if (std::distance(typeIterator.first, typeIterator.second) <= index) {
341+
return env.Undefined();
342+
}
343+
344+
auto packetLocation = std::next(typeIterator.first, index);
345+
auto packet = this->Read(*packetLocation);
346+
return Packet::ToJsValue(packet, env);
347+
}
348+
281349
std::vector<Packet> Decoder::GetMatchingPackets(const uint64_t& timestamp, const std::vector<TypeSubtype>& types) {
282350
std::vector<Packet> packets;
283351

src/Decoder.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ namespace nbs {
2222
/// Returns a JS array of type subtype objects
2323
Napi::Value GetAvailableTypes(const Napi::CallbackInfo& info);
2424

25+
/// Get all the timestamps of a specified message type subtype from the index
26+
Napi::Value GetTypeIndex(const Napi::CallbackInfo& info);
27+
2528
/// Get a list of the available types in the nbs files of this decoder
2629
/// Returns a JS array with two elements: the start timestamp object and the end timestamp object
2730
Napi::Value GetTimestampRange(const Napi::CallbackInfo& info);
@@ -30,6 +33,9 @@ namespace nbs {
3033
/// Returns a JS array of packet objects
3134
Napi::Value GetPackets(const Napi::CallbackInfo& info);
3235

36+
/// Get the packet at the given index of the given type subtype
37+
Napi::Value GetPacketByIndex(const Napi::CallbackInfo& info);
38+
3339
Napi::Value NextTimestamp(const Napi::CallbackInfo& info);
3440

3541
/**

src/Index.hpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "zstr/zstr.hpp"
1212
namespace nbs {
1313

14+
using IndexIterator = std::vector<IndexItemFile>::iterator;
15+
1416
class Index {
1517
public:
1618
/// Empty default constructor
@@ -79,9 +81,14 @@ namespace nbs {
7981
}
8082

8183
/// Get the iterator range (begin, end) for the given type and subtype in the index
82-
std::pair<std::vector<IndexItemFile>::iterator, std::vector<IndexItemFile>::iterator> getIteratorForType(
83-
const TypeSubtype& type) {
84-
return this->typeMap[type];
84+
std::pair<nbs::IndexIterator, nbs::IndexIterator> getIteratorForType(const TypeSubtype& type) {
85+
// Check that the type actually exists, otherwise the type map will add the given type
86+
// with an empty iterator
87+
auto typeExists = (this->typeMap.find(type) != this->typeMap.end());
88+
if (typeExists == true) {
89+
return this->typeMap[type];
90+
}
91+
return {};
8592
}
8693

8794
/// Get a list of iterator ranges (begin, end) for the given list of type and subtype in the index

tests/test_decoder.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,35 @@ test('NbsDecoder constructor throws for invalid arguments', () => {
7979
);
8080
});
8181

82+
test('NbsDecoder.getTypeIndex() returns the correct index for the given type subtype', () => {
83+
const pingIndices = decoder.getTypeIndex({ type: pingType, subtype: 0 });
84+
assert.equal(pingIndices.length, 300);
85+
86+
const pongIndices = decoder.getTypeIndex({ type: pongType, subtype: 0 });
87+
assert.equal(pongIndices.length, 300);
88+
89+
const pang100Indices = decoder.getTypeIndex({ type: pangType, subtype: 100 });
90+
assert.equal(pang100Indices.length, 150);
91+
92+
const pang200Indices = decoder.getTypeIndex({ type: pangType, subtype: 200 });
93+
assert.equal(pang200Indices.length, 150);
94+
});
95+
96+
test('NbsDecoder.getTypeIndex() returns an empty array for types not in the decoder', () => {
97+
const fakePacketIndices = decoder.getTypeIndex({ type: 'fakeIndex', subtype: 0 });
98+
assert.equal(fakePacketIndices.length, 0);
99+
});
100+
101+
test('NbsDecoder.getTypeIndex() returns the correct index with the correct packet timestamps', () => {
102+
const pingIndices = decoder.getTypeIndex({ type: pingType, subtype: 0 });
103+
104+
assert.equal(pingIndices[0], { seconds: 1000, nanos: 0 });
105+
assert.equal(pingIndices[1], { seconds: 1003, nanos: 0 });
106+
107+
const lastIndex = pingIndices.length - 1;
108+
assert.equal(pingIndices[lastIndex], { seconds: 1897, nanos: 0 });
109+
});
110+
82111
test('NbsDecoder.getAvailableTypes() returns a list of all the types available in the nbs files', () => {
83112
const types = decoder.getAvailableTypes();
84113

@@ -438,6 +467,78 @@ test('NbsDecoder.getPackets() returns the last packet of each type when given a
438467
});
439468
});
440469

470+
test('NbsDecoder.getPacketByIndex() throws for invalid arguments', () => {
471+
assert.throws(
472+
() => {
473+
decoder.getPacketByIndex();
474+
},
475+
/invalid type for argument `index`: expected integer/,
476+
'NbsDecoder.getPacketByIndex() throws for missing `index` argument'
477+
);
478+
479+
assert.throws(
480+
() => {
481+
decoder.getPacketByIndex(true);
482+
},
483+
/invalid type for argument `index`: expected integer/,
484+
'NbsDecoder.getPacketByIndex() throws for incorrect `index` argument type'
485+
);
486+
487+
assert.throws(
488+
() => {
489+
decoder.getPacketByIndex(0);
490+
},
491+
/invalid type for argument `typeSubtype`: expected object/,
492+
'NbsDecoder.getPacketByIndex() throws for missing `type` argument'
493+
);
494+
495+
assert.throws(
496+
() => {
497+
decoder.getPacketByIndex(0, {});
498+
},
499+
/invalid type for argument `typeSubtype`: expected object with `type` and `subtype` keys/,
500+
'NbsDecoder.getPacketByIndex() throws for invalid `type` argument'
501+
);
502+
});
503+
504+
test('NbsDecoder.getPacketByIndex() returns the correct packet for a valid index', () => {
505+
const packet0 = decoder.getPacketByIndex(0, { type: pingType, subtype: 0 });
506+
const packet1 = decoder.getPacketByIndex(1, { type: pingType, subtype: 0 });
507+
const packetLast = decoder.getPacketByIndex(299, { type: pingType, subtype: 0 });
508+
509+
assert.equal(packet0, {
510+
timestamp: { seconds: 1000, nanos: 0 },
511+
type: pingType,
512+
subtype: 0,
513+
payload: Buffer.from('ping.0', 'utf8'),
514+
});
515+
516+
assert.equal(packet1, {
517+
timestamp: { seconds: 1003, nanos: 0 },
518+
type: pingType,
519+
subtype: 0,
520+
payload: Buffer.from('ping.1', 'utf8'),
521+
});
522+
523+
assert.equal(packetLast, {
524+
timestamp: { seconds: 1897, nanos: 0 },
525+
type: pingType,
526+
subtype: 0,
527+
payload: Buffer.from('ping.699', 'utf8'),
528+
});
529+
});
530+
531+
test('NbsDecoder.getPacketByIndex() returns undefined for indices outside the valid range', () => {
532+
// Negative indices
533+
assert.equal(decoder.getPacketByIndex(-1, { type: pingType, subtype: 0 }), undefined);
534+
535+
// Indices one above the max
536+
assert.equal(decoder.getPacketByIndex(300, { type: pingType, subtype: 0 }), undefined);
537+
assert.equal(decoder.getPacketByIndex(300, { type: pongType, subtype: 0 }), undefined);
538+
assert.equal(decoder.getPacketByIndex(150, { type: pangType, subtype: 100 }), undefined);
539+
assert.equal(decoder.getPacketByIndex(150, { type: pangType, subtype: 200 }), undefined);
540+
});
541+
441542
const multiTypeNextTimestampArray = [
442543
{ type: pongType, subtype: 0 },
443544
{ type: pingType, subtype: 0 },

0 commit comments

Comments
 (0)