Skip to content

Commit b9739e3

Browse files
authored
Merge pull request #173 from msgpack/decoder-recover-after-errors
reset Decoder's state to recover from broken input
2 parents afd1885 + 5295681 commit b9739e3

File tree

3 files changed

+57
-14
lines changed

3 files changed

+57
-14
lines changed

src/Decoder.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class DecodeError extends Error {
7676
}
7777
}
7878

79-
export class Decoder<ContextType> {
79+
export class Decoder<ContextType = undefined> {
8080
private totalPos = 0;
8181
private pos = 0;
8282

@@ -99,6 +99,9 @@ export class Decoder<ContextType> {
9999
private reinitializeState() {
100100
this.totalPos = 0;
101101
this.headByte = HEAD_BYTE_REQUIRED;
102+
this.stack.length = 0;
103+
104+
// view, bytes, and pos will be re-initialized in setBuffer()
102105
}
103106

104107
private setBuffer(buffer: ArrayLike<number> | BufferSource): void {

src/Encoder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { ExtData } from "./ExtData";
77
export const DEFAULT_MAX_DEPTH = 100;
88
export const DEFAULT_INITIAL_BUFFER_SIZE = 2048;
99

10-
export class Encoder<ContextType> {
10+
export class Encoder<ContextType = undefined> {
1111
private pos = 0;
1212
private view = new DataView(new ArrayBuffer(this.initialBufferSize));
1313
private bytes = new Uint8Array(this.view.buffer);

test/edge-cases.test.ts

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,150 @@
1+
// kind of hand-written fuzzing data
2+
// any errors should not break Encoder/Decoder instance states
13
import assert from "assert";
2-
import { encode, decode, decodeAsync } from "../src";
4+
import { encode, decode, decodeAsync, Encoder, Decoder } from "../src";
35
import { DataViewIndexOutOfBoundsError } from "../src/Decoder";
46

7+
function testEncoder(encoder: Encoder): void {
8+
const object = {
9+
foo: 1,
10+
bar: 2,
11+
baz: ["one", "two", "three"],
12+
};
13+
assert.deepStrictEqual(decode(encoder.encode(object)), object);
14+
}
15+
16+
function testDecoder(decoder: Decoder): void {
17+
const object = {
18+
foo: 1,
19+
bar: 2,
20+
baz: ["one", "two", "three"],
21+
};
22+
assert.deepStrictEqual(decoder.decode(encode(object)), object);
23+
}
24+
525
describe("edge cases", () => {
626
context("try to encode cyclic refs", () => {
727
it("throws errors on arrays", () => {
28+
const encoder = new Encoder();
829
const cyclicRefs: Array<any> = [];
930
cyclicRefs.push(cyclicRefs);
1031
assert.throws(() => {
11-
encode(cyclicRefs);
32+
encoder.encode(cyclicRefs);
1233
}, /too deep/i);
34+
testEncoder(encoder);
1335
});
1436

1537
it("throws errors on objects", () => {
38+
const encoder = new Encoder();
1639
const cyclicRefs: Record<string, any> = {};
1740
cyclicRefs["foo"] = cyclicRefs;
1841
assert.throws(() => {
19-
encode(cyclicRefs);
42+
encoder.encode(cyclicRefs);
2043
}, /too deep/i);
44+
testEncoder(encoder);
2145
});
2246
});
2347

24-
context("try to encode non-encodable objects", () => {
48+
context("try to encode unrecognized objects", () => {
2549
it("throws errors", () => {
50+
const encoder = new Encoder();
2651
assert.throws(() => {
2752
encode(() => {});
2853
}, /unrecognized object/i);
54+
testEncoder(encoder);
2955
});
3056
});
3157

3258
context("try to decode a map with non-string keys (asynchronous)", () => {
3359
it("throws errors", async () => {
60+
const decoder = new Decoder();
3461
const createStream = async function* () {
3562
yield [0x81]; // fixmap size=1
3663
yield encode(null);
3764
yield encode(null);
3865
};
3966

4067
await assert.rejects(async () => {
41-
await decodeAsync(createStream());
68+
await decoder.decodeAsync(createStream());
4269
}, /The type of key must be string/i);
70+
testDecoder(decoder);
4371
});
4472
});
4573

46-
context("try to decode invlid MessagePack binary", () => {
74+
context("try to decode invalid MessagePack binary", () => {
4775
it("throws errors", () => {
76+
const decoder = new Decoder();
4877
const TYPE_NEVER_USED = 0xc1;
4978

5079
assert.throws(() => {
51-
decode([TYPE_NEVER_USED]);
80+
decoder.decode([TYPE_NEVER_USED]);
5281
}, /unrecognized type byte/i);
82+
testDecoder(decoder);
5383
});
5484
});
5585

5686
context("try to decode insufficient data", () => {
5787
it("throws errors (synchronous)", () => {
88+
const decoder = new Decoder();
5889
assert.throws(() => {
59-
decode([
90+
decoder.decode([
6091
0x92, // fixarray size=2
6192
0xc0, // nil
6293
]);
6394
// [IE11] A raw error thrown by DataView
6495
}, DataViewIndexOutOfBoundsError);
96+
testDecoder(decoder);
6597
});
6698

6799
it("throws errors (asynchronous)", async () => {
100+
const decoder = new Decoder();
68101
const createStream = async function* () {
69102
yield [0x92]; // fixarray size=2
70103
yield encode(null);
71104
};
72105

73106
await assert.rejects(async () => {
74-
await decodeAsync(createStream());
107+
await decoder.decodeAsync(createStream());
75108
}, RangeError);
109+
testDecoder(decoder);
76110
});
77111
});
78112

79113
context("try to decode data with extra bytes", () => {
80114
it("throws errors (synchronous)", () => {
115+
const decoder = new Decoder();
81116
assert.throws(() => {
82-
decode([
117+
decoder.decode([
83118
0x90, // fixarray size=0
84119
...encode(null),
85120
]);
86121
}, RangeError);
122+
testDecoder(decoder);
87123
});
88124

89125
it("throws errors (asynchronous)", async () => {
126+
const decoder = new Decoder();
90127
const createStream = async function* () {
91128
yield [0x90]; // fixarray size=0
92129
yield encode(null);
93130
};
94131

95132
await assert.rejects(async () => {
96-
await decodeAsync(createStream());
133+
await decoder.decodeAsync(createStream());
97134
}, RangeError);
135+
testDecoder(decoder);
98136
});
99137

100138
it("throws errors (asynchronous)", async () => {
139+
const decoder = new Decoder();
101140
const createStream = async function* () {
102141
yield [0x90, ...encode(null)]; // fixarray size=0 + nil
103142
};
104143

105144
await assert.rejects(async () => {
106-
await decodeAsync(createStream());
145+
await decoder.decodeAsync(createStream());
107146
}, RangeError);
147+
testDecoder(decoder);
108148
});
109149
});
110150
});

0 commit comments

Comments
 (0)