Skip to content

Commit 7501809

Browse files
committed
implementing encode and decode
1 parent 7facf8c commit 7501809

File tree

14 files changed

+487
-111
lines changed

14 files changed

+487
-111
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules/
22
dist/
3+
build/
34
package-lock.json
45

package.json

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,35 @@
44
"description": "MessagePack implementation in JavaScript",
55
"main": "index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "TS_NODE_FILES=true mocha 'test/**/*.test.ts'"
88
},
99
"repository": {
1010
"type": "git",
1111
"url": "git+ssh://git@github.com/msgpack/msgpack-javascript.git"
1212
},
1313
"keywords": [
14-
"msgpack"
14+
"msgpack",
15+
"MessagePack",
16+
"serialization"
1517
],
1618
"author": "The MessagePack community",
1719
"license": "ISC",
1820
"bugs": {
1921
"url": "https://github.com/msgpack/msgpack-javascript/issues"
2022
},
21-
"homepage": "https://github.com/msgpack/msgpack-javascript#readme",
23+
"homepage": "https://github.com/msgpack/msgpack-javascript",
2224
"devDependencies": {
2325
"@types/mocha": "^5.2.6",
26+
"@types/node": "^11.13.8",
27+
"ieee754": "^1.1.13",
28+
"long": "^4.0.0",
2429
"mocha": "^6.1.4",
30+
"msgpack-lite": "^0.1.26",
31+
"msgpack-test-js": "^1.0.0",
2532
"ts-node": "^8.1.0",
2633
"typescript": "^3.4.5"
34+
},
35+
"dependencies": {
36+
"tslib": "^1.9.3"
2737
}
2838
}

src/BufferType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11

2-
export type BufferType = ArrayBuffer | Uint8Array | ArrayLike<number>;
2+
export type BufferType = ArrayLike<number>;

src/decode.ts

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
import { prettyByte } from "./utils/prettyByte";
2+
3+
export type InputBufferType = ReadonlyArray<number> | Uint8Array;
4+
5+
export function decode(blob: InputBufferType): unknown {
6+
const context = new DecodeContext(blob);
7+
return context.decode();
8+
}
9+
10+
class DecodeContext {
11+
readonly buffer: InputBufferType;
12+
pos = 0;
13+
14+
constructor(buffer: InputBufferType) {
15+
this.buffer = buffer;
16+
}
17+
18+
decode() {
19+
const type = this.next8();
20+
21+
if (type >= 0xe0) {
22+
// negative fixint (111x xxxx) 0xe0 - 0xff
23+
return type - 0x100;
24+
} else if (type < 0xc0) {
25+
if (type < 0x80) {
26+
// positive fixint (0xxx xxxx) 0x00 - 0x7f
27+
return type;
28+
} else if (type < 0x90) {
29+
// fixmap (1000 xxxx) 0x80 - 0x8f
30+
const size = type - 0x80;
31+
return this.decodeMap(size);
32+
} else if (type < 0xa0) {
33+
// fixarray (1001 xxxx) 0x90 - 0x9f
34+
const size = type - 0x90;
35+
return this.decodeArray(size);
36+
} else {
37+
// fixstr (101x xxxx) 0xa0 - 0xbf
38+
const length = type - 0xa0;
39+
return this.decodeUtf8String(length);
40+
}
41+
}
42+
43+
if (type === 0xc0) {
44+
// nil
45+
return null;
46+
} else if (type === 0xc2) {
47+
// false
48+
return false;
49+
} else if (type === 0xc3) {
50+
// true
51+
return true;
52+
} else if (type === 0xc4) {
53+
// bin 8
54+
const size = this.next8();
55+
return this.decodeBinary(size);
56+
} else if (type === 0xc5) {
57+
// bin 16
58+
const size = this.next16();
59+
return this.decodeBinary(size);
60+
} else if (type === 0xc6) {
61+
// bin 32
62+
const size = this.next32();
63+
return this.decodeBinary(size);
64+
} else if (type === 0xca) {
65+
// float 32
66+
return this.decodeFloat(23, 4);
67+
} else if (type === 0xcb) {
68+
// float 64
69+
return this.decodeFloat(52, 8);
70+
} else if (type === 0xcc) {
71+
// uint 8
72+
return this.next8();
73+
} else if (type === 0xcd) {
74+
// uint 16
75+
return this.next16();
76+
} else if (type === 0xce) {
77+
// uint 32
78+
return this.next32();
79+
} else if (type === 0xcf) {
80+
// uint 64
81+
return this.next64();
82+
} else if (type === 0xd0) {
83+
// int 8
84+
const v = this.next8();
85+
return v < 0x80 ? v : v - 0x100;
86+
} else if (type === 0xd1) {
87+
// int 16
88+
const v = this.next16();
89+
return v < 0x8000 ? v : v - 0x10000;
90+
} else if (type === 0xd2) {
91+
// int 32
92+
const v = this.next32();
93+
return v < 0x80000000 ? v : v - 0x100000000;
94+
} else if (type === 0xd3) {
95+
// int 64
96+
const b1 = this.next8();
97+
const b2 = this.next8();
98+
const b3 = this.next8();
99+
const b4 = this.next8();
100+
const b5 = this.next8();
101+
const b6 = this.next8();
102+
const b7 = this.next8();
103+
const b8 = this.next8();
104+
105+
if (b1 & 0x80) {
106+
// to avoid overflow
107+
return -(
108+
(b1 ^ 0xff) * 0x100000000000000 +
109+
(b2 ^ 0xff) * 0x1000000000000 +
110+
(b3 ^ 0xff) * 0x10000000000 +
111+
(b4 ^ 0xff) * 0x100000000 +
112+
(b5 ^ 0xff) * 0x1000000 +
113+
(b6 ^ 0xff) * 0x10000 +
114+
(b7 ^ 0xff) * 0x100 +
115+
(b8 ^ 0xff) +
116+
1
117+
);
118+
}
119+
return (
120+
b1 * 0x100000000000000 +
121+
b2 * 0x1000000000000 +
122+
b3 * 0x10000000000 +
123+
b4 * 0x100000000 +
124+
b5 * 0x1000000 +
125+
b6 * 0x10000 +
126+
b7 * 0x100 +
127+
b8
128+
);
129+
} else if (type === 0xd9) {
130+
// str 8
131+
const length = this.next8();
132+
return this.decodeUtf8String(length);
133+
} else if (type === 0xda) {
134+
// str 16
135+
const length = this.next16();
136+
return this.decodeUtf8String(length);
137+
} else if (type === 0xdb) {
138+
// str 32
139+
const length = this.next32();
140+
return this.decodeUtf8String(length);
141+
} else if (type === 0xdc) {
142+
// array 16
143+
const size = this.next16();
144+
return this.decodeArray(size);
145+
} else if (type === 0xdd) {
146+
// array 32
147+
const size = this.next32();
148+
return this.decodeArray(size);
149+
} else if (type === 0xde) {
150+
// map 16
151+
const size = this.next16();
152+
return this.decodeMap(size);
153+
} else if (type === 0xdf) {
154+
// map 32
155+
const size = this.next32();
156+
return this.decodeMap(size);
157+
}
158+
}
159+
160+
decodeBinary(size: number): ArrayLike<number> {
161+
const start = this.pos;
162+
this.pos += size;
163+
return this.buffer.slice(start, start + size);
164+
}
165+
166+
decodeFloat(mLen: number, nBytes: number): number {
167+
const eLen = nBytes * 8 - mLen - 1;
168+
const eMax = (1 << eLen) - 1;
169+
const eBias = eMax >> 1;
170+
let nBits = -7;
171+
const byte = this.next8();
172+
const sign = byte >> -nBits;
173+
let exp = byte & ((1 << -nBits) - 1);
174+
175+
nBits += eLen;
176+
while (nBits > 0) {
177+
exp = exp * 256 + this.next8();
178+
nBits -= 8;
179+
}
180+
181+
let frac = exp & ((1 << -nBits) - 1);
182+
exp >>= -nBits;
183+
nBits += mLen;
184+
185+
while (nBits > 0) {
186+
frac = frac * 256 + this.next8();
187+
nBits -= 8;
188+
}
189+
190+
if (exp === 0) {
191+
exp = 1 - eBias;
192+
} else if (exp === eMax) {
193+
return frac ? NaN : sign ? -Infinity : Infinity;
194+
} else {
195+
frac = frac + Math.pow(2, mLen);
196+
exp = exp - eBias;
197+
}
198+
const value = frac * Math.pow(2, exp - mLen);
199+
return sign ? -value : value;
200+
}
201+
202+
decodeUtf8String(length: number): string {
203+
const out: Array<number> = [];
204+
205+
const end = this.pos + length;
206+
while (this.pos < end) {
207+
const byte1 = this.next8();
208+
209+
if (byte1 == null) {
210+
throw new Error(`Invalid null at ${this.pos} in decoding ${length} bytes of buffer`);
211+
}
212+
213+
if ((byte1 & 0x80) === 0) {
214+
// 1 byte
215+
out.push(byte1);
216+
} else if ((byte1 & 0xe0) === 0xc0) {
217+
// 2 bytes
218+
const byte2 = this.next8() & 0x3f;
219+
out.push(((byte1 & 0x1f) << 6) | byte2);
220+
} else if ((byte1 & 0xf0) === 0xe0) {
221+
// 3 bytes
222+
const byte2 = this.next8() & 0x3f;
223+
const byte3 = this.next8() & 0x3f;
224+
out.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
225+
} else if ((byte1 & 0xf8) === 0xf0) {
226+
// 4 bytes
227+
const byte2 = this.next8() & 0x3f;
228+
const byte3 = this.next8() & 0x3f;
229+
const byte4 = this.next8() & 0x3f;
230+
231+
let codepoint = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
232+
if (codepoint > 0xffff) {
233+
codepoint -= 0x10000;
234+
out.push(((codepoint >>> 10) & 0x3ff) | 0xd800);
235+
codepoint = 0xdc00 | (codepoint & 0x3ff);
236+
}
237+
out.push(codepoint);
238+
} else {
239+
throw new Error(`Invalid UTF-8 byte ${prettyByte(byte1)} at ${this.pos}`);
240+
}
241+
}
242+
243+
return String.fromCharCode(...out);
244+
}
245+
246+
decodeMap(size: number): Record<string, any> {
247+
const result: Record<string, any> = {};
248+
for (let i = 0; i < size; i++) {
249+
const key = this.decode();
250+
if (typeof key !== "string") {
251+
throw new Error(`Unsupported map key type: ${typeof key}`);
252+
}
253+
const value = this.decode();
254+
result[key] = value;
255+
}
256+
return result;
257+
}
258+
259+
decodeArray(size: number): Array<any> {
260+
const result = new Array<any>(size);
261+
for (let i = 0; i < size; i++) {
262+
result[i] = this.decode();
263+
}
264+
return result;
265+
}
266+
267+
next8(): number {
268+
return this.buffer[this.pos++];
269+
}
270+
271+
next16(): number {
272+
const b1 = this.next8();
273+
const b2 = this.next8();
274+
return (b1 << 8) + b2;
275+
}
276+
277+
next32(): number {
278+
const b1 = this.next8();
279+
const b2 = this.next8();
280+
const b3 = this.next8();
281+
const b4 = this.next8();
282+
return b1 * 0x1000000 + (b2 << 16) + (b3 << 8) + b4;
283+
}
284+
285+
next64(): number {
286+
const high = this.next32();
287+
const low = this.next32();
288+
289+
return high * 0x100000000 + low;
290+
}
291+
}

0 commit comments

Comments
 (0)