Skip to content
Draft
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
16 changes: 10 additions & 6 deletions src/Decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,13 +430,13 @@ export class Decoder<ContextType = undefined> {

this.appendBuffer(buffer);

if (isArrayHeaderRequired) {
arrayItemsLeft = this.readArraySize();
isArrayHeaderRequired = false;
this.complete();
}

try {
if (isArrayHeaderRequired) {
arrayItemsLeft = this.readArraySize();
isArrayHeaderRequired = false;
this.complete();
}

while (true) {
yield this.doDecodeSync();
if (--arrayItemsLeft === 0) {
Expand All @@ -451,6 +451,10 @@ export class Decoder<ContextType = undefined> {
}
this.totalPos += this.pos;
}

if (isArrayHeaderRequired) {
throw new RangeError("Insufficient data in parsing array header");
}
} finally {
this.entered = false;
}
Expand Down
48 changes: 48 additions & 0 deletions test/decodeArrayStream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,54 @@ describe("decodeArrayStream", () => {
assert.deepStrictEqual(result, [1, 2, 3]);
});

for (const [name, header] of [
["array16", [0xdc, 0, 1]],
["array32", [0xdd, 0, 0, 0, 1]],
] as const) {
it(`decodes ${name} split at every header boundary`, async () => {
const object = { nested: [1, "value", null] };
const encoded = Uint8Array.from([...header, ...encode(object)]);

for (let split = 1; split < header.length; split++) {
const stream = async function* () {
yield encoded.subarray(0, split);
yield encoded.subarray(split);
};
const result: Array<unknown> = [];
for await (const item of decodeArrayStream(stream())) {
result.push(item);
}
assert.deepStrictEqual(result, [object], `split at ${split}`);
}
});

it(`rejects ${name} truncated in the header`, async () => {
for (let length = 1; length < header.length; length++) {
const stream = async function* () {
yield header.slice(0, length);
yield new Uint8Array(0);
};
await assert.rejects(decodeArrayStream(stream()).next(), RangeError);
}
});

it(`decodes ${name} byte-by-byte with empty chunks`, async () => {
const object = { nested: [1, "value", null] };
const encoded = Uint8Array.from([...header, ...encode(object)]);
const stream = async function* () {
for (const byte of encoded) {
yield new Uint8Array(0);
yield Uint8Array.from([0xff, byte, 0xff]).subarray(1, 2);
}
};
const result: Array<unknown> = [];
for await (const item of decodeArrayStream(stream())) {
result.push(item);
}
assert.deepStrictEqual(result, [object]);
});
}

it("decodes objects array", async () => {
const objectsArrays: Array<any> = [];

Expand Down