-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecoder.ts
More file actions
278 lines (221 loc) · 8.03 KB
/
Copy pathdecoder.ts
File metadata and controls
278 lines (221 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
import { logd } from './log'
import { BencodeDict, BencodeInteger, BencodeList, BencodeString, BencodeType } from './type'
import { isUtf8 } from './util'
export class Bdecoder {
private curosr: number = 0
private textDecoder: TextDecoder = new TextDecoder()
/**
* 只有在字典(dict)的key不是一个有效的utf8编码的字符串时,才会返回true
* 判断字符串是否是未解码的字节字符串,例如Unit8Array[1,2,3]这种格式的字符串
*
* 由于字节字符串中可能包含非utf8编码的字符,所以无法转换成字符串,因为转换成字符串后可能会丢失数据,
*
* 所以返回了一个Unit8Array[1,2,3]这种格式的字符串,这个字符串可以通过undecodingByteString2Unit8Array方法转换成Uint8Array
* @param keyOfDict 字典的key
* @returns true or false
*/
static isByteKey(keyOfDict: string) {
if (typeof keyOfDict !== 'string') {
return false
}
// 校验格式
if (!keyOfDict.startsWith('Unit8Array[') || !keyOfDict.endsWith(']')) {
return false
}
// 获取数组元素,每个元素都是一个integer,长度8位,范围0-255
const elements = keyOfDict.replace('Unit8Array[', '').replace(']', '').split(',')
// 校验每个元素是否是integer,并且范围在0-255之间
try {
for (const element of elements) {
const num = parseInt(element)
if (!Number.isInteger(num)) {
return false
}
if (num < 0 || num > 255) {
return false
}
}
} catch (_) {
return false
}
// 校验通过
return true
}
/**
* 将没有解码的字节字符串转换成Uint8Array,例如Unit8Array[1,2,3]这种格式的字符串,'Unit8Array[1,2,3]' => Uint8Array.from([1,2,3])
* @param value Unit8Array[1,2,3]这种格式的字符串
* @returns Uint8Array,如果转换失败,则返回undefined
*/
static byteKeyToUint8Array(value: string): Uint8Array | undefined {
if (!this.isByteKey(value)) {
return undefined
}
const elements = value.replace('Unit8Array[', '').replace(']', '').split(',')
const bytes = new Uint8Array(elements.length)
for (let i = 0; i < elements.length; i++) {
bytes[i] = parseInt(elements[i])
}
return bytes
}
decode(data: Uint8Array): BencodeType | undefined {
// 重置游标
this.curosr = 0
return this.parse(data)
}
/**
* 读取字节数组,默认读取一个字节
* @param length
* @returns
*/
private readBytes(data: Uint8Array, length: number = 1): Uint8Array {
if (this.curosr + length > data.length) {
logd(
`[readBytes] read bytes out of range, curosr is ${this.curosr}, length is ${length}, data length is ${data.length}`
)
return new Uint8Array()
}
const bytes = data.slice(this.curosr, this.curosr + length)
this.curosr += length
return bytes
}
/**
* 回退游标
* @param length
*/
private backCursor(length: number = 1) {
this.curosr -= length
}
private parse(data: Uint8Array): BencodeType | undefined {
// 读取头部字节,用于判断需要解析的数据类型
const bytes = this.readBytes(data)
logd(`[decode] read next byte ${this.textDecoder.decode(bytes)}`)
if (bytes.length === 0) {
logd('[decode] head bytes is null, return null')
return undefined
}
switch (String.fromCharCode(bytes[0])) {
case 'i':
logd(`[decode] start parse integer`)
// 解码整数值
return this.decodeInteger(data)
case 'l':
logd(`[decode] start parse list`)
// 解码列表
return this.decodeList(data)
case 'd':
logd(`[decode] start parse dict`)
// 解码字典
return this.decodeDict(data)
default:
// 除开上面三种类型,剩下的都是字节字符串
logd(`[decode] start parse byte string`)
// 回退游标,因为这里的头字节是长度的一部分或者长度本身,例如5:hello,后面需要用于截取指定长度的字符串
this.backCursor()
// 解码字符串
return this.decodeByteString(data)
}
}
private decodeInteger(data: Uint8Array): BencodeInteger {
logd(`[decodeInteger] start read int bytes length`)
// 读取直到遇到'e'字节为止
const intBuffer = this.readUntil(data, 'e')
// 转换成数字
const integer = parseInt(this.textDecoder.decode(intBuffer))
logd(`[decodeInteger] decoded integer is ${integer}`)
return integer
}
private decodeByteString(data: Uint8Array): BencodeString | Uint8Array {
logd(`[decodeByteString] start read string bytes length`)
// 获取字符串的长度
const lengthBuffer = this.readUntil(data, ':')
logd(`[decodeByteString] readed lengthBuffer value is ${lengthBuffer}`)
// 转换成数字
const length = parseInt(this.textDecoder.decode(lengthBuffer))
logd(`[decodeByteString] next string bytes length is ${length}`)
// 根据长度读取字符串对应的字节数组
const stringBuffer = this.readBytes(data, length)
logd(`[decodeByteString] readed stringBuffer length is ${stringBuffer ? stringBuffer.length : 0}`)
const result = this.textDecoder.decode(stringBuffer)
// 如果是utf8编码,转成字符串,不然后面转成json会是乱码
if (isUtf8(stringBuffer)) {
return result
} else {
logd(`[decodeByteString] string is not utf8, return number array`)
// 否则转换成number数组,由于js中没有byte类型,所以只能用number数组来表示字节
return stringBuffer
}
}
/**
* 解码列表,例如 l5:helloe 或者 l5:helloi123ee,也就是["hello"]或者["hello",123]
* @returns list
*/
private decodeList(data: Uint8Array): BencodeList {
const list = []
while (true) {
// 读取首字节
const bytes = this.readBytes(data)
if (bytes.length === 0 || String.fromCharCode(bytes[0]) === 'e') {
break
}
// 如果读取到的不是'e',回退游标,继续解析
this.backCursor()
const item = this.parse(data)
if (item == null) break
list.push(item)
}
return list
}
/**
* 解码字典
* @returns object
*/
private decodeDict(data: Uint8Array): BencodeDict {
let obj = {} as any
while (true) {
// 读取首字节
const bytes = this.readBytes(data)
if (bytes.length === 0 || String.fromCharCode(bytes[0]) === 'e') {
break
}
// 如果读取到的不是'e',回退游标,继续解析
this.backCursor()
logd(`[decodeDict] start parse dict key`)
// 解析key
let key = this.decodeByteString(data)
// 处理当返回的key是Uint8Array的情况,因为此时的byte string中包含了非utf8编码的字符,导致转换成JS字符串后可能会丢失数据
// 所以这里直接把Unit8Array转换数组
if (key instanceof Uint8Array) {
key = `Unit8Array[${Array.from(key).join(',')}]`
}
logd(`[decodeDict] key is '${key}'`)
logd(`[decodeDict] start parse dict value`)
// 解析value
obj[key] = this.parse(data)
// logd(`[decodeDict] value is '${JSON.stringify(obj[key])}'`)
}
return obj
}
/**
* 从字节流读取直到遇到stop字节为止,返回读取到的字符串
* 注意: stop 字节已经被读取过了,游标已经移动到下一个字节
* @param stop
*/
private readUntil(data: Uint8Array, stop: string): Uint8Array {
logd(`[readUntil] '${stop}'`)
// 用于存储读取到的字节
const buffers: Uint8Array[] = []
while (true) {
// 读取一个字节
const bytes = this.readBytes(data)
logd(`[readUntil] readed bytes is ${bytes}`)
// 如果已经读取到了文件末尾,或者读取到的字节是stop字节,则返回
if (bytes.length === 0 || String.fromCharCode(...bytes) === stop) {
logd(`[readUntil] reached stop char`)
break
}
// 拼接字节
buffers.push(bytes)
}
return Uint8Array.from(Buffer.concat(buffers))
}
}