rosbag
Version:
`rosbag` is a node.js & browser compatible module for reading [rosbag](http://wiki.ros.org/rosbag) binary data files.
136 lines (119 loc) • 4.71 kB
text/typescript
// Copyright (c) 2018-present, Cruise LLC
// This source code is licensed under the Apache License, Version 2.0,
// found in the LICENSE file in the root directory of this source tree.
// You may not use this file except in compliance with the License.
import BagReader from "./BagReader";
import { BagHeader } from "./record";
function int64Buffer(number: number) {
const buff = Buffer.alloc(8);
buff.writeBigInt64LE(BigInt(number), 0);
return buff;
}
function int32Buffer(number: number) {
const buff = Buffer.alloc(4);
buff.writeInt32LE(number, 0);
return buff;
}
function makeHeaderKeyValuePairBuffer(key: string, value: Buffer) {
const indexFieldName = Buffer.from(`${key}=`, "ascii");
const combined = Buffer.concat([indexFieldName, value]);
const lengthBuffer = int32Buffer(combined.byteLength);
return Buffer.concat([lengthBuffer, combined]);
}
class FakeHeaderFilelike {
preamble = "#ROSBAG V2.0\n";
indexPosition = 0;
connectionCount = 0;
chunkCount = 0;
// eslint-disable-next-line class-methods-use-this
size() {
return 4096;
}
read(offset: number, _: number, callback: any): void {
if (offset === 0) {
return callback(null, Buffer.from(this.preamble, "utf8"));
}
if (offset === 13) {
const resultBuffer = Buffer.alloc(4096, 0);
// build a list of key value pair buffers
const kvpBuffers = [
makeHeaderKeyValuePairBuffer("op", Buffer.from([0x03])),
makeHeaderKeyValuePairBuffer("index_pos", int64Buffer(this.indexPosition)),
makeHeaderKeyValuePairBuffer("conn_count", int32Buffer(this.connectionCount)),
makeHeaderKeyValuePairBuffer("chunk_count", int32Buffer(this.chunkCount)),
];
const bagHeaderHeader = Buffer.concat(kvpBuffers);
// start at the beginning of the output buffer
let currentOffset = 0;
// write header length
resultBuffer.writeInt32LE(bagHeaderHeader.byteLength, currentOffset);
currentOffset += 4;
// copy bag header record's "header" section to the overall buffer
// right after the length byte
bagHeaderHeader.copy(resultBuffer, currentOffset);
currentOffset += bagHeaderHeader.byteLength;
// we're currently header_len (4) + header bytes into the buffer
const remainingBytes = resultBuffer.byteLength - currentOffset;
// write the data_len which is the remaining 4096 bytes
resultBuffer.writeInt32LE(remainingBytes, currentOffset);
currentOffset += 4;
// According to rosbag documentation,
// header record is padded out by filling with ASCII space (0x20).
resultBuffer.fill(0x20, currentOffset);
return callback(null, resultBuffer);
}
return callback(new Error(`Unexpected read position: ${offset}`));
}
}
describe("BagReader", () => {
describe("parsing header", () => {
it("calls back with an error if the header has an invalid preamble", (done) => {
const filelike = new FakeHeaderFilelike();
filelike.preamble = "#ROSBAG V1.0\n";
const reader = new BagReader(filelike);
const callback: any = (err?: Error, header?: BagHeader) => {
expect(err).not.toBeUndefined();
expect(header).toBeUndefined();
done();
};
reader.readHeader(callback);
});
it("parses header correctly with small int32 values", (done: (error?: Error) => void) => {
const filelike = new FakeHeaderFilelike();
filelike.indexPosition = 1;
filelike.connectionCount = 2;
filelike.chunkCount = 3;
const reader = new BagReader(filelike);
const callback: any = (err: Error | undefined, header: BagHeader) => {
if (err) {
done(err);
return;
}
expect(header.indexPosition).toEqual(1);
expect(header.connectionCount).toEqual(2);
expect(header.chunkCount).toEqual(3);
done();
};
reader.readHeader(callback);
});
it("parses header correctly with large int32 values", (done: (error?: Error) => void) => {
const filelike = new FakeHeaderFilelike();
// 100000, 200000, 300000 etc overflow an Int16, but fit in Int32.
filelike.indexPosition = 100000;
filelike.connectionCount = 200000;
filelike.chunkCount = 300000;
const reader = new BagReader(filelike);
const callback: any = (err: Error | undefined, header: BagHeader) => {
if (err) {
done(err);
return;
}
expect(header.indexPosition).toEqual(100000);
expect(header.connectionCount).toEqual(200000);
expect(header.chunkCount).toEqual(300000);
done();
};
reader.readHeader(callback);
});
});
});