New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@foxglove/cdr

Package Overview
Dependencies
Maintainers
4
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@foxglove/cdr - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

dist/isBigEndian.d.ts

27

dist/CdrReader.d.ts

@@ -6,2 +6,3 @@ export declare class CdrReader {

private littleEndian;
private hostLittleEndian;
private textDecoder;

@@ -23,4 +24,30 @@ get data(): Uint8Array;

sequenceLength(): number;
int8Array(count?: number): Int8Array;
uint8Array(count?: number): Uint8Array;
int16Array(count?: number): Int16Array;
uint16Array(count?: number): Uint16Array;
int32Array(count?: number): Int32Array;
uint32Array(count?: number): Uint32Array;
int64Array(count?: number): BigInt64Array;
uint64Array(count?: number): BigUint64Array;
float32Array(count?: number): Float32Array;
float64Array(count?: number): Float64Array;
stringArray(count?: number): string[];
/**
* Seek the current read pointer a number of bytes relative to the current position. Note that
* seeking before the four-byte header is invalid
* @param relativeOffset A positive or negative number of bytes to seek
*/
seek(relativeOffset: number): void;
/**
* Seek to an absolute byte position in the data. Note that seeking before the four-byte header is
* invalid
* @param offset An absolute byte offset in the range of [4-byteLength)
*/
seekTo(offset: number): void;
private align;
private typedArray;
private typedArrayUnaligned;
private typedArraySlow;
}
//# sourceMappingURL=CdrReader.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdrReader = void 0;
const isBigEndian_1 = require("./isBigEndian");
class CdrReader {
constructor(data) {
this.textDecoder = new TextDecoder("utf8");
this.hostLittleEndian = !isBigEndian_1.isBigEndian();
if (data.byteLength < 4) {

@@ -93,2 +95,77 @@ throw new Error(`Invalid CDR data size ${data.byteLength}, must contain at least a 4-byte header`);

}
int8Array(count) {
count ?? (count = this.sequenceLength());
const array = new Int8Array(this.data.buffer, this.data.byteOffset + this.offset, count);
this.offset += count;
return array;
}
uint8Array(count) {
count ?? (count = this.sequenceLength());
const array = new Uint8Array(this.data.buffer, this.data.byteOffset + this.offset, count);
this.offset += count;
return array;
}
int16Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Int16Array, "getInt16", count);
}
uint16Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Uint16Array, "getUint16", count);
}
int32Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Int32Array, "getInt32", count);
}
uint32Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Uint32Array, "getUint32", count);
}
int64Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(BigInt64Array, "getBigInt64", count);
}
uint64Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(BigUint64Array, "getBigUint64", count);
}
float32Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Float32Array, "getFloat32", count);
}
float64Array(count) {
count ?? (count = this.sequenceLength());
return this.typedArray(Float64Array, "getFloat64", count);
}
stringArray(count) {
count ?? (count = this.sequenceLength());
const output = [];
for (let i = 0; i < count; i++) {
output.push(this.string());
}
return output;
}
/**
* Seek the current read pointer a number of bytes relative to the current position. Note that
* seeking before the four-byte header is invalid
* @param relativeOffset A positive or negative number of bytes to seek
*/
seek(relativeOffset) {
const newOffset = this.offset + relativeOffset;
if (newOffset < 4 || newOffset >= this.data.byteLength) {
throw new Error(`seek(${relativeOffset}) failed, ${newOffset} is outside the data range`);
}
this.offset = newOffset;
}
/**
* Seek to an absolute byte position in the data. Note that seeking before the four-byte header is
* invalid
* @param offset An absolute byte offset in the range of [4-byteLength)
*/
seekTo(offset) {
if (offset < 4 || offset >= this.data.byteLength) {
throw new Error(`seekTo(${offset}) failed, value is outside the data range`);
}
this.offset = offset;
}
align(size) {

@@ -100,4 +177,44 @@ const alignment = (this.offset - 4) % size;

}
// Reads a given count of numeric values into a typed array.
typedArray(TypedArrayConstructor, getter, count) {
this.align(TypedArrayConstructor.BYTES_PER_ELEMENT);
const totalOffset = this.data.byteOffset + this.offset;
if (this.littleEndian !== this.hostLittleEndian) {
// Slowest path
return this.typedArraySlow(TypedArrayConstructor, getter, count);
}
else if (totalOffset % TypedArrayConstructor.BYTES_PER_ELEMENT === 0) {
// Fastest path
return new TypedArrayConstructor(this.data.buffer, totalOffset, count);
}
else {
// Slower path
return this.typedArrayUnaligned(TypedArrayConstructor, getter, count);
}
}
typedArrayUnaligned(TypedArrayConstructor, getter, count) {
// Benchmarks indicate for count < ~10 doing each individually is faster than copy
if (count < 10) {
return this.typedArraySlow(TypedArrayConstructor, getter, count);
}
// If the length is > 10, then doing a copy of the data to align it is faster
// using _set_ is slightly faster than slice on the array buffer according to today's benchmarks
const byteLength = TypedArrayConstructor.BYTES_PER_ELEMENT * count;
const copy = new Uint8Array(byteLength);
copy.set(new Uint8Array(this.view.buffer, this.view.byteOffset + this.offset, byteLength));
this.offset += byteLength;
return new TypedArrayConstructor(copy.buffer, copy.byteOffset, count);
}
typedArraySlow(TypedArrayConstructor, getter, count) {
const array = new TypedArrayConstructor(count);
let offset = this.offset;
for (let i = 0; i < count; i++) {
array[i] = this.view[getter](offset, this.littleEndian);
offset += TypedArrayConstructor.BYTES_PER_ELEMENT;
}
this.offset = offset;
return array;
}
}
exports.CdrReader = CdrReader;
//# sourceMappingURL=CdrReader.js.map

78

dist/CdrReader.test.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const CdrReader_1 = require("./CdrReader");
const CdrWriter_1 = require("./CdrWriter");
// Example tf2_msgs/TFMessage
const tf2_msg__TFMessage = "0001000001000000cce0d158f08cf9060a000000626173655f6c696e6b000000060000007261646172000000ae47e17a14ae0e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f03f";
describe("CdrReader", () => {
it("parses an example message", () => {
// Example tf2_msgs/TFMessage
const tf2_msg__TFMessage = "0001000001000000cce0d158f08cf9060a000000626173655f6c696e6b000000060000007261646172000000ae47e17a14ae0e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f03f";
const data = Uint8Array.from(Buffer.from(tf2_msg__TFMessage, "hex"));

@@ -29,3 +30,76 @@ const reader = new CdrReader_1.CdrReader(data);

});
it("seeks to absolute and relative positions", () => {
const data = Uint8Array.from(Buffer.from(tf2_msg__TFMessage, "hex"));
const reader = new CdrReader_1.CdrReader(data);
reader.seekTo(4 + 4 + 4 + 4 + 4 + 10 + 4 + 6);
expect(reader.float64()).toBeCloseTo(3.835);
// This works due to aligned reads
reader.seekTo(4 + 4 + 4 + 4 + 4 + 10 + 4 + 3);
expect(reader.float64()).toBeCloseTo(3.835);
reader.seek(-8);
expect(reader.float64()).toBeCloseTo(3.835);
expect(reader.float64()).toBeCloseTo(0);
});
it.each([
["int8Array", "int8", [-128, 127, 3]],
["uint8Array", "uint8", [0, 255, 3]],
["int16Array", "int16", [-32768, 32767, -3]],
["uint16Array", "uint16", [0, 65535, 3]],
["int32Array", "int32", [-2147483648, 2147483647, 3]],
["uint32Array", "uint32", [0, 4294967295, 3]],
])("reads %s", (getter, setter, expected) => {
const writer = new CdrWriter_1.CdrWriter();
writeArray(writer, setter, expected);
const reader = new CdrReader_1.CdrReader(writer.data);
const array = reader[getter](reader.sequenceLength());
expect(Array.from(array.values())).toEqual(expected);
});
it.each([
["float32Array", "float32", [-3.835, 0, Math.PI], 6],
["float64Array", "float64", [-3.835, 0, Math.PI], 15],
["float64Array", "float64", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -0.123456789121212121212], 15],
])("reads %s", (getter, setter, expected, numDigits) => {
const writer = new CdrWriter_1.CdrWriter();
writeArray(writer, setter, expected);
const reader = new CdrReader_1.CdrReader(writer.data);
const array = reader[getter](reader.sequenceLength());
expectToBeCloseToArray(Array.from(array.values()), expected, numDigits);
});
it.each([
["int64Array", "int64", [-9223372036854775808n, 9223372036854775807n, 3n]],
["uint64Array", "uint64", [0n, 18446744073709551615n, 3n]],
["uint64Array", "uint64", [1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n, 11n, 12n]],
])("reads %s", (getter, setter, expected) => {
const writer = new CdrWriter_1.CdrWriter();
writeBigArray(writer, setter, expected);
const reader = new CdrReader_1.CdrReader(writer.data);
const array = reader[getter](reader.sequenceLength());
expect(Array.from(array.values())).toEqual(expected);
});
it("reads stringArray", () => {
const writer = new CdrWriter_1.CdrWriter();
writer.sequenceLength(3);
writer.string("abc");
writer.string("");
writer.string("test string");
const reader = new CdrReader_1.CdrReader(writer.data);
expect(reader.stringArray(reader.sequenceLength())).toEqual(["abc", "", "test string"]);
});
});
function writeArray(writer, setter, array) {
writer.sequenceLength(array.length);
for (const value of array) {
writer[setter](value);
}
}
function writeBigArray(writer, setter, array) {
writer.sequenceLength(array.length);
for (const value of array) {
writer[setter](value);
}
}
function expectToBeCloseToArray(actual, expected, numDigits) {
expect(actual.length).toBe(expected.length);
actual.forEach((x, i) => expect(x).toBeCloseTo(expected[i], numDigits));
}
//# sourceMappingURL=CdrReader.test.js.map

@@ -8,3 +8,5 @@ export declare type CdrWriterOpts = {

static DEFAULT_CAPACITY: number;
static BUFFER_COPY_THRESHOLD: number;
private littleEndian;
private hostLittleEndian;
private buffer;

@@ -30,2 +32,12 @@ private array;

sequenceLength(value: number): CdrWriter;
int8Array(value: Int8Array | number[], writeLength?: boolean): CdrWriter;
uint8Array(value: Uint8Array | number[], writeLength?: boolean): CdrWriter;
int16Array(value: Int16Array | number[], writeLength?: boolean): CdrWriter;
uint16Array(value: Uint16Array | number[], writeLength?: boolean): CdrWriter;
int32Array(value: Int32Array | number[], writeLength?: boolean): CdrWriter;
uint32Array(value: Uint32Array | number[], writeLength?: boolean): CdrWriter;
int64Array(value: BigInt64Array | bigint[] | number[], writeLength?: boolean): CdrWriter;
uint64Array(value: BigUint64Array | bigint[] | number[], writeLength?: boolean): CdrWriter;
float32Array(value: Float32Array | number[], writeLength?: boolean): CdrWriter;
float64Array(value: Float64Array | number[], writeLength?: boolean): CdrWriter;
private align;

@@ -32,0 +44,0 @@ private resizeIfNeeded;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdrWriter = void 0;
const isBigEndian_1 = require("./isBigEndian");
class CdrWriter {

@@ -17,2 +18,3 @@ constructor(options = {}) {

this.littleEndian = !(options.bigEndian === true);
this.hostLittleEndian = !isBigEndian_1.isBigEndian();
this.array = new Uint8Array(this.buffer);

@@ -109,9 +111,172 @@ this.view = new DataView(this.buffer);

}
int8Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
this.resizeIfNeeded(value.length);
this.array.set(value, this.offset);
this.offset += value.length;
return this;
}
uint8Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
this.resizeIfNeeded(value.length);
this.array.set(value, this.offset);
this.offset += value.length;
return this;
}
int16Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Int16Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.int16(entry);
}
}
return this;
}
uint16Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Uint16Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.uint16(entry);
}
}
return this;
}
int32Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Int32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.int32(entry);
}
}
return this;
}
uint32Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Uint32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.uint32(entry);
}
}
return this;
}
int64Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof BigInt64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.int64(BigInt(entry));
}
}
return this;
}
uint64Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof BigUint64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.uint64(BigInt(entry));
}
}
return this;
}
float32Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Float32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.float32(entry);
}
}
return this;
}
float64Array(value, writeLength) {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (value instanceof Float64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
}
else {
for (const entry of value) {
this.float64(entry);
}
}
return this;
}
// Calculate the capacity needed to hold the given number of aligned bytes,
// resize if needed, and write padding bytes for alignment
align(size) {
align(size, bytesToWrite) {
bytesToWrite ?? (bytesToWrite = size);
// The four byte header is not considered for alignment
const alignment = (this.offset - 4) % size;
const padding = alignment > 0 ? size - alignment : 0;
this.resizeIfNeeded(padding + size);
this.resizeIfNeeded(padding + bytesToWrite);
// Write padding bytes

@@ -143,2 +308,3 @@ this.array.fill(0, this.offset, this.offset + padding);

CdrWriter.DEFAULT_CAPACITY = 16;
CdrWriter.BUFFER_COPY_THRESHOLD = 10;
//# sourceMappingURL=CdrWriter.js.map

@@ -78,3 +78,17 @@ "use strict";

});
it("round trips all array types", () => {
const writer = new CdrWriter_1.CdrWriter();
writer.int8Array([-128, 127, 3], true);
writer.uint8Array([0, 255, 3], true);
writer.int16Array([-32768, 32767, -3], true);
writer.uint16Array([0, 65535, 3], true);
writer.int32Array([-2147483648, 2147483647, 3], true);
writer.uint32Array([0, 4294967295, 3], true);
writer.int64Array([-9223372036854775808n, 9223372036854775807n, 3n], true);
writer.uint64Array([0n, 18446744073709551615n, 3n], true);
const reader = new CdrReader_1.CdrReader(writer.data);
expect(Array.from(reader.int8Array().values())).toEqual([-128, 127, 3]);
expect(Array.from(reader.uint8Array().values())).toEqual([0, 255, 3]);
});
});
//# sourceMappingURL=CdrWriter.test.js.map

2

package.json
{
"name": "@foxglove/cdr",
"version": "0.1.0",
"version": "0.2.0",
"description": "Common Data Representation serialization and deserialization library",

@@ -5,0 +5,0 @@ "license": "MIT",

import { CdrReader } from "./CdrReader";
import { CdrWriter } from "./CdrWriter";
type ArrayGetter =
| "int8Array"
| "uint8Array"
| "int16Array"
| "uint16Array"
| "int32Array"
| "uint32Array"
| "float32Array"
| "float64Array";
type Setter = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "float32" | "float64";
// Example tf2_msgs/TFMessage
const tf2_msg__TFMessage =
"0001000001000000cce0d158f08cf9060a000000626173655f6c696e6b000000060000007261646172000000ae47e17a14ae0e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f03f";
describe("CdrReader", () => {
it("parses an example message", () => {
// Example tf2_msgs/TFMessage
const tf2_msg__TFMessage =
"0001000001000000cce0d158f08cf9060a000000626173655f6c696e6b000000060000007261646172000000ae47e17a14ae0e4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f03f";
const data = Uint8Array.from(Buffer.from(tf2_msg__TFMessage, "hex"));

@@ -30,2 +43,90 @@ const reader = new CdrReader(data);

});
it("seeks to absolute and relative positions", () => {
const data = Uint8Array.from(Buffer.from(tf2_msg__TFMessage, "hex"));
const reader = new CdrReader(data);
reader.seekTo(4 + 4 + 4 + 4 + 4 + 10 + 4 + 6);
expect(reader.float64()).toBeCloseTo(3.835);
// This works due to aligned reads
reader.seekTo(4 + 4 + 4 + 4 + 4 + 10 + 4 + 3);
expect(reader.float64()).toBeCloseTo(3.835);
reader.seek(-8);
expect(reader.float64()).toBeCloseTo(3.835);
expect(reader.float64()).toBeCloseTo(0);
});
it.each([
["int8Array", "int8", [-128, 127, 3]],
["uint8Array", "uint8", [0, 255, 3]],
["int16Array", "int16", [-32768, 32767, -3]],
["uint16Array", "uint16", [0, 65535, 3]],
["int32Array", "int32", [-2147483648, 2147483647, 3]],
["uint32Array", "uint32", [0, 4294967295, 3]],
])("reads %s", (getter: string, setter: string, expected: number[]) => {
const writer = new CdrWriter();
writeArray(writer, setter as Setter, expected);
const reader = new CdrReader(writer.data);
const array = reader[getter as ArrayGetter](reader.sequenceLength());
expect(Array.from(array.values())).toEqual(expected);
});
it.each([
["float32Array", "float32", [-3.835, 0, Math.PI], 6],
["float64Array", "float64", [-3.835, 0, Math.PI], 15],
["float64Array", "float64", [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -0.123456789121212121212], 15],
])("reads %s", (getter: string, setter: string, expected: number[], numDigits: number) => {
const writer = new CdrWriter();
writeArray(writer, setter as Setter, expected);
const reader = new CdrReader(writer.data);
const array = reader[getter as ArrayGetter](reader.sequenceLength());
expectToBeCloseToArray(Array.from(array.values()), expected, numDigits);
});
it.each([
["int64Array", "int64", [-9223372036854775808n, 9223372036854775807n, 3n]],
["uint64Array", "uint64", [0n, 18446744073709551615n, 3n]],
["uint64Array", "uint64", [1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n, 11n, 12n]],
])("reads %s", (getter: string, setter: string, expected: bigint[]) => {
const writer = new CdrWriter();
writeBigArray(writer, setter as "int64" | "uint64", expected);
const reader = new CdrReader(writer.data);
const array = reader[getter as "int64Array" | "uint64Array"](reader.sequenceLength());
expect(Array.from(array.values())).toEqual(expected);
});
it("reads stringArray", () => {
const writer = new CdrWriter();
writer.sequenceLength(3);
writer.string("abc");
writer.string("");
writer.string("test string");
const reader = new CdrReader(writer.data);
expect(reader.stringArray(reader.sequenceLength())).toEqual(["abc", "", "test string"]);
});
});
function writeArray(writer: CdrWriter, setter: Setter, array: number[]): void {
writer.sequenceLength(array.length);
for (const value of array) {
writer[setter](value);
}
}
function writeBigArray(writer: CdrWriter, setter: "int64" | "uint64", array: bigint[]): void {
writer.sequenceLength(array.length);
for (const value of array) {
writer[setter](value);
}
}
function expectToBeCloseToArray(actual: number[], expected: number[], numDigits: number): void {
expect(actual.length).toBe(expected.length);
actual.forEach((x, i) => expect(x).toBeCloseTo(expected[i]!, numDigits));
}

@@ -0,1 +1,25 @@

import { isBigEndian } from "./isBigEndian";
interface Indexable {
[index: number]: unknown;
}
interface TypedArrayConstructor<T> {
new (length?: number): T;
new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): T;
BYTES_PER_ELEMENT: number;
}
type ArrayValueGetter =
| "getInt8"
| "getUint8"
| "getInt16"
| "getUint16"
| "getInt32"
| "getUint32"
| "getBigInt64"
| "getBigUint64"
| "getFloat32"
| "getFloat64";
export class CdrReader {

@@ -6,2 +30,3 @@ private array: Uint8Array;

private littleEndian: boolean;
private hostLittleEndian: boolean;
private textDecoder = new TextDecoder("utf8");

@@ -18,2 +43,4 @@

constructor(data: Uint8Array) {
this.hostLittleEndian = !isBigEndian();
if (data.byteLength < 4) {

@@ -114,2 +141,90 @@ throw new Error(

int8Array(count?: number): Int8Array {
count ??= this.sequenceLength();
const array = new Int8Array(this.data.buffer, this.data.byteOffset + this.offset, count);
this.offset += count;
return array;
}
uint8Array(count?: number): Uint8Array {
count ??= this.sequenceLength();
const array = new Uint8Array(this.data.buffer, this.data.byteOffset + this.offset, count);
this.offset += count;
return array;
}
int16Array(count?: number): Int16Array {
count ??= this.sequenceLength();
return this.typedArray(Int16Array, "getInt16", count);
}
uint16Array(count?: number): Uint16Array {
count ??= this.sequenceLength();
return this.typedArray(Uint16Array, "getUint16", count);
}
int32Array(count?: number): Int32Array {
count ??= this.sequenceLength();
return this.typedArray(Int32Array, "getInt32", count);
}
uint32Array(count?: number): Uint32Array {
count ??= this.sequenceLength();
return this.typedArray(Uint32Array, "getUint32", count);
}
int64Array(count?: number): BigInt64Array {
count ??= this.sequenceLength();
return this.typedArray(BigInt64Array, "getBigInt64", count);
}
uint64Array(count?: number): BigUint64Array {
count ??= this.sequenceLength();
return this.typedArray(BigUint64Array, "getBigUint64", count);
}
float32Array(count?: number): Float32Array {
count ??= this.sequenceLength();
return this.typedArray(Float32Array, "getFloat32", count);
}
float64Array(count?: number): Float64Array {
count ??= this.sequenceLength();
return this.typedArray(Float64Array, "getFloat64", count);
}
stringArray(count?: number): string[] {
count ??= this.sequenceLength();
const output: string[] = [];
for (let i = 0; i < count; i++) {
output.push(this.string());
}
return output;
}
/**
* Seek the current read pointer a number of bytes relative to the current position. Note that
* seeking before the four-byte header is invalid
* @param relativeOffset A positive or negative number of bytes to seek
*/
seek(relativeOffset: number): void {
const newOffset = this.offset + relativeOffset;
if (newOffset < 4 || newOffset >= this.data.byteLength) {
throw new Error(`seek(${relativeOffset}) failed, ${newOffset} is outside the data range`);
}
this.offset = newOffset;
}
/**
* Seek to an absolute byte position in the data. Note that seeking before the four-byte header is
* invalid
* @param offset An absolute byte offset in the range of [4-byteLength)
*/
seekTo(offset: number): void {
if (offset < 4 || offset >= this.data.byteLength) {
throw new Error(`seekTo(${offset}) failed, value is outside the data range`);
}
this.offset = offset;
}
private align(size: number): void {

@@ -121,2 +236,56 @@ const alignment = (this.offset - 4) % size;

}
// Reads a given count of numeric values into a typed array.
private typedArray<T extends Indexable>(
TypedArrayConstructor: TypedArrayConstructor<T>,
getter: ArrayValueGetter,
count: number,
) {
this.align(TypedArrayConstructor.BYTES_PER_ELEMENT);
const totalOffset = this.data.byteOffset + this.offset;
if (this.littleEndian !== this.hostLittleEndian) {
// Slowest path
return this.typedArraySlow(TypedArrayConstructor, getter, count);
} else if (totalOffset % TypedArrayConstructor.BYTES_PER_ELEMENT === 0) {
// Fastest path
return new TypedArrayConstructor(this.data.buffer, totalOffset, count);
} else {
// Slower path
return this.typedArrayUnaligned(TypedArrayConstructor, getter, count);
}
}
private typedArrayUnaligned<T extends Indexable>(
TypedArrayConstructor: TypedArrayConstructor<T>,
getter: ArrayValueGetter,
count: number,
) {
// Benchmarks indicate for count < ~10 doing each individually is faster than copy
if (count < 10) {
return this.typedArraySlow(TypedArrayConstructor, getter, count);
}
// If the length is > 10, then doing a copy of the data to align it is faster
// using _set_ is slightly faster than slice on the array buffer according to today's benchmarks
const byteLength = TypedArrayConstructor.BYTES_PER_ELEMENT * count;
const copy = new Uint8Array(byteLength);
copy.set(new Uint8Array(this.view.buffer, this.view.byteOffset + this.offset, byteLength));
this.offset += byteLength;
return new TypedArrayConstructor(copy.buffer, copy.byteOffset, count);
}
private typedArraySlow<T extends Indexable>(
TypedArrayConstructor: TypedArrayConstructor<T>,
getter: ArrayValueGetter,
count: number,
) {
const array = new TypedArrayConstructor(count);
let offset = this.offset;
for (let i = 0; i < count; i++) {
array[i] = this.view[getter](offset, this.littleEndian);
offset += TypedArrayConstructor.BYTES_PER_ELEMENT;
}
this.offset = offset;
return array;
}
}

@@ -84,2 +84,18 @@ import { CdrReader } from "./CdrReader";

});
it("round trips all array types", () => {
const writer = new CdrWriter();
writer.int8Array([-128, 127, 3], true);
writer.uint8Array([0, 255, 3], true);
writer.int16Array([-32768, 32767, -3], true);
writer.uint16Array([0, 65535, 3], true);
writer.int32Array([-2147483648, 2147483647, 3], true);
writer.uint32Array([0, 4294967295, 3], true);
writer.int64Array([-9223372036854775808n, 9223372036854775807n, 3n], true);
writer.uint64Array([0n, 18446744073709551615n, 3n], true);
const reader = new CdrReader(writer.data);
expect(Array.from(reader.int8Array().values())).toEqual([-128, 127, 3]);
expect(Array.from(reader.uint8Array().values())).toEqual([0, 255, 3]);
});
});

@@ -0,1 +1,3 @@

import { isBigEndian } from "./isBigEndian";
export type CdrWriterOpts = {

@@ -9,4 +11,6 @@ buffer?: ArrayBuffer;

static DEFAULT_CAPACITY = 16;
static BUFFER_COPY_THRESHOLD = 10;
private littleEndian: boolean;
private hostLittleEndian: boolean;
private buffer: ArrayBuffer;

@@ -36,2 +40,3 @@ private array: Uint8Array;

this.littleEndian = !(options.bigEndian === true);
this.hostLittleEndian = !isBigEndian();
this.array = new Uint8Array(this.buffer);

@@ -136,9 +141,190 @@ this.view = new DataView(this.buffer);

int8Array(value: Int8Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
this.resizeIfNeeded(value.length);
this.array.set(value, this.offset);
this.offset += value.length;
return this;
}
uint8Array(value: Uint8Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
this.resizeIfNeeded(value.length);
this.array.set(value, this.offset);
this.offset += value.length;
return this;
}
int16Array(value: Int16Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Int16Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.int16(entry);
}
}
return this;
}
uint16Array(value: Uint16Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Uint16Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.uint16(entry);
}
}
return this;
}
int32Array(value: Int32Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Int32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.int32(entry);
}
}
return this;
}
uint32Array(value: Uint32Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Uint32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.uint32(entry);
}
}
return this;
}
int64Array(value: BigInt64Array | bigint[] | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof BigInt64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.int64(BigInt(entry));
}
}
return this;
}
uint64Array(value: BigUint64Array | bigint[] | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof BigUint64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.uint64(BigInt(entry));
}
}
return this;
}
float32Array(value: Float32Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Float32Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.float32(entry);
}
}
return this;
}
float64Array(value: Float64Array | number[], writeLength?: boolean): CdrWriter {
if (writeLength === true) {
this.sequenceLength(value.length);
}
if (
value instanceof Float64Array &&
this.littleEndian === this.hostLittleEndian &&
value.length >= CdrWriter.BUFFER_COPY_THRESHOLD
) {
this.align(value.BYTES_PER_ELEMENT, value.byteLength);
this.array.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength), this.offset);
this.offset += value.byteLength;
} else {
for (const entry of value) {
this.float64(entry);
}
}
return this;
}
// Calculate the capacity needed to hold the given number of aligned bytes,
// resize if needed, and write padding bytes for alignment
private align(size: number): void {
private align(size: number, bytesToWrite?: number): void {
bytesToWrite ??= size;
// The four byte header is not considered for alignment
const alignment = (this.offset - 4) % size;
const padding = alignment > 0 ? size - alignment : 0;
this.resizeIfNeeded(padding + size);
this.resizeIfNeeded(padding + bytesToWrite);
// Write padding bytes

@@ -145,0 +331,0 @@ this.array.fill(0, this.offset, this.offset + padding);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc