Skip to content

Commit 4774df4

Browse files
committed
feat(json-pack): 🎸 add BencodeDecoder implementation
1 parent 7a27d1e commit 4774df4

File tree

2 files changed

+422
-0
lines changed

2 files changed

+422
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {Reader} from '../../util/buffers/Reader';
2+
import type {BinaryJsonDecoder, PackValue} from '../types';
3+
4+
export class BencodeDecoder implements BinaryJsonDecoder {
5+
public reader = new Reader();
6+
7+
public read(uint8: Uint8Array): unknown {
8+
this.reader.reset(uint8);
9+
return this.readAny();
10+
}
11+
12+
public decode(uint8: Uint8Array): unknown {
13+
this.reader.reset(uint8);
14+
return this.readAny();
15+
}
16+
17+
public readAny(): unknown {
18+
const reader = this.reader;
19+
const x = reader.x;
20+
const uint8 = reader.uint8;
21+
const char = uint8[x];
22+
switch (char) {
23+
case 0x69: // i
24+
return this.readNum();
25+
case 0x64: // d
26+
return this.readObj();
27+
case 0x6c: // l
28+
return this.readArr();
29+
case 0x66: // f
30+
return this.readFalse();
31+
case 0x74: // t
32+
return this.readTrue();
33+
case 110: // n
34+
return this.readNull();
35+
case 117: // u
36+
return this.readUndef();
37+
default:
38+
if (char >= 48 && char <= 57) return this.readBin();
39+
}
40+
throw new Error('INVALID_BENCODE');
41+
}
42+
43+
public readNull(): null {
44+
if (this.reader.u8() !== 0x6e) throw new Error('INVALID_BENCODE');
45+
return null;
46+
}
47+
48+
public readUndef(): undefined {
49+
if (this.reader.u8() !== 117) throw new Error('INVALID_BENCODE');
50+
return undefined;
51+
}
52+
53+
public readTrue(): true {
54+
if (this.reader.u8() !== 0x74) throw new Error('INVALID_BENCODE');
55+
return true;
56+
}
57+
58+
public readFalse(): false {
59+
if (this.reader.u8() !== 0x66) throw new Error('INVALID_BENCODE');
60+
return false;
61+
}
62+
63+
public readBool(): unknown {
64+
const reader = this.reader;
65+
switch (reader.uint8[reader.x]) {
66+
case 0x66: // f
67+
return this.readFalse();
68+
case 0x74: // t
69+
return this.readTrue();
70+
default:
71+
throw new Error('INVALID_BENCODE');
72+
}
73+
}
74+
75+
public readNum(): number {
76+
const reader = this.reader;
77+
const startChar = reader.u8();
78+
if (startChar !== 0x69) throw new Error('INVALID_BENCODE');
79+
const u8 = reader.uint8;
80+
let x = reader.x;
81+
let numStr = '';
82+
let c = u8[x++];
83+
let i = 0;
84+
while (c !== 0x65) {
85+
numStr += String.fromCharCode(c);
86+
c = u8[x++];
87+
if (i > 25) throw new Error('INVALID_BENCODE');
88+
i++;
89+
}
90+
if (!numStr) throw new Error('INVALID_BENCODE');
91+
reader.x = x;
92+
return +numStr;
93+
}
94+
95+
public readStr(): string {
96+
const bin = this.readBin();
97+
return new TextDecoder().decode(bin);
98+
}
99+
100+
public readBin(): Uint8Array {
101+
const reader = this.reader;
102+
const u8 = reader.uint8;
103+
let lenStr = '';
104+
let x = reader.x;
105+
let c = u8[x++];
106+
let i = 0;
107+
while (c !== 0x3a) {
108+
if (c < 48 || c > 57) throw new Error('INVALID_BENCODE');
109+
lenStr += String.fromCharCode(c);
110+
c = u8[x++];
111+
if (i > 10) throw new Error('INVALID_BENCODE');
112+
i++;
113+
}
114+
reader.x = x;
115+
const len = +lenStr;
116+
const bin = reader.buf(len);
117+
return bin;
118+
}
119+
120+
public readArr(): unknown[] {
121+
const reader = this.reader;
122+
if (reader.u8() !== 0x6c) throw new Error('INVALID_BENCODE');
123+
const arr: unknown[] = [];
124+
const uint8 = reader.uint8;
125+
while (true) {
126+
const char = uint8[reader.x];
127+
if (char === 0x65) {
128+
reader.x++;
129+
return arr;
130+
}
131+
arr.push(this.readAny());
132+
}
133+
}
134+
135+
public readObj(): PackValue | Record<string, unknown> | unknown {
136+
const reader = this.reader;
137+
if (reader.u8() !== 0x64) throw new Error('INVALID_BENCODE');
138+
const obj: Record<string, unknown> = {};
139+
const uint8 = reader.uint8;
140+
while (true) {
141+
let char = uint8[reader.x];
142+
if (char === 0x65) {
143+
reader.x++;
144+
return obj;
145+
}
146+
const key = this.readStr();
147+
if (key === '__proto__') throw new Error('INVALID_KEY');
148+
obj[key] = this.readAny();
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)