Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 52 additions & 9 deletions lib/binary_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,27 @@ const PRIMITIVE_SIZES: { [key in PrimitiveTypes]: number } = {
doublebe: 8,
};

// Number of bytes consumed by consecutive bit fields, mirroring generateBit
function bitFieldsSize(lengths: number[]): number {
let bytes = 0;
let rem = 0;
lengths.forEach((length, i) => {
if (length > rem) {
let maxBits = 0;
for (let j = i; j < lengths.length; j++) {
if (maxBits + lengths[j] > 32) break;
maxBits += lengths[j];
}
const read = Math.min(4, Math.max(1, Math.ceil((maxBits - rem) / 8)));
bytes += read;
length -= rem;
rem = read * 8;
}
rem -= length;
});
return bytes;
}

const PRIMITIVE_NAMES: { [key in PrimitiveTypes]: string } = {
uint8: "Uint8",
uint16le: "Uint16",
Expand Down Expand Up @@ -873,6 +894,7 @@ export class Parser {

sizeOf(): number {
let size = NaN;
let next = this.next;

if (Object.keys(PRIMITIVE_SIZES).indexOf(this.type) >= 0) {
size = PRIMITIVE_SIZES[this.type as PrimitiveTypes];
Expand All @@ -897,26 +919,47 @@ export class Parser {
typeof this.options.length === "number"
) {
let elementSize = NaN;
if (typeof this.options.type === "string") {
elementSize = PRIMITIVE_SIZES[this.options.type as PrimitiveTypes];
} else if (this.options.type instanceof Parser) {
elementSize = this.options.type.sizeOf();
const type = this.options.type;
if (typeof type === "string" && aliasRegistry.has(type)) {
elementSize = aliasRegistry.get(type)!.sizeOf();
} else if (typeof type === "string") {
elementSize = PRIMITIVE_SIZES[type as PrimitiveTypes];
} else if (type instanceof Parser) {
elementSize = type.sizeOf();
}
size = this.options.length * elementSize;

// if this a skip
} else if (this.type === "seek") {
size = this.options.length as number;
if (typeof this.options.length === "number") {
size = this.options.length;
}

// if this is a nested parser
} else if (this.type === "nest") {
size = (this.options.type as Parser).sizeOf();
} else if (!this.type) {
const type = this.options.type!;
size = (
typeof type === "string" ? aliasRegistry.get(type) : type
)!.sizeOf();

// if this is a run of consecutive bit fields
} else if (this.type === "bit") {
const lengths = [this.options.length as number];
while (next && next.type === "bit") {
lengths.push(next.options.length as number);
next = next.next;
}
size = bitFieldsSize(lengths);
} else if (
!this.type ||
this.type === "pointer" ||
this.type === "saveOffset"
) {
size = 0;
}

if (this.next) {
size += this.next.sizeOf();
if (next) {
size += next.sizeOf();
}

return size;
Expand Down
43 changes: 43 additions & 0 deletions test/composite_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,49 @@ function compositeParserTests(

expect(parser.sizeOf()).toStrictEqual(1 + 4 + 10 + 2 + 3 + 8);
});
it("should count size for bit fields", () => {
const cases = [
[4, 4],
[1],
[3, 7],
[16, 8],
[20, 4, 8],
[30, 4],
[32, 32],
[7, 30, 3],
[12, 12, 12, 12],
];
for (const lengths of cases) {
const parser: any = new Parser();
lengths.forEach((length, i) => parser[`bit${length}`](`b${i}`));
parser.uint8("x").saveOffset("end");
const { end } = parser.parse(new Uint8Array(32));
expect(parser.sizeOf()).toStrictEqual(end);
}
});
it("should count size for seek with non-number length as NaN", () => {
const parser = new Parser().uint8("x").seek("x").uint8("a");
expect(parser.sizeOf()).toBeNaN();
});
it("should count size for aliased nest and array types", () => {
new Parser().namely("sizeOfAlias").uint16("a").uint8("b");
expect(
new Parser().nest("n", { type: "sizeOfAlias" }).sizeOf(),
).toStrictEqual(3);
expect(
new Parser()
.array("arr", { type: "sizeOfAlias", length: 2 })
.sizeOf(),
).toStrictEqual(6);
});
it("should count pointer and saveOffset as zero bytes", () => {
const parser = new Parser()
.uint8("a")
.saveOffset("off")
.pointer("p", { type: "uint8", offset: 0 })
.uint16("b");
expect(parser.sizeOf()).toStrictEqual(3);
});
it("should assert parsed values", () => {
const parser1 = Parser.start().string("msg", {
encoding: "utf8",
Expand Down
Loading