Created
June 22, 2021 13:11
-
-
Save teidesu/c36f671ab9bbac5a5d4e62f8cd6bd671 to your computer and use it in GitHub Desktop.
JavaScript BEncode implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is licensed under LGPLv3 | |
// (c) teidesu 2020 | |
class Decoder { | |
pos = 0 | |
constructor (str) { | |
this.str = str | |
} | |
decode () { | |
let prefix = this.str[this.pos] | |
if (prefix === 105) { | |
return this.decodeInt() | |
} else if (prefix === 100) { | |
return this.decodeDict() | |
} else if (prefix === 108) { | |
return this.decodeList() | |
} else if (prefix >= 48 && prefix <= 57) { | |
return this.decodeString() | |
} else { | |
throw Error('Invalid format') | |
} | |
} | |
decodeInt () { | |
++this.pos | |
let val = 0 | |
while (this.str[this.pos] !== 101) { | |
let c = this.str[this.pos++] | |
if (c < 48 || c > 57) throw Error('Invalid number') | |
val = val * 10 + (c - 48) | |
} | |
++this.pos | |
return val | |
} | |
decodeString () { | |
let delim = this.str.indexOf(':', this.pos) | |
if (delim === -1) throw Error('Invalid format') | |
let length = 0 | |
while (this.pos !== delim) { | |
let c = this.str[this.pos++] | |
if (c < 48 || c > 57) throw Error('Invalid number') | |
length = length * 10 + (c - 48) | |
} | |
let value = this.str.subarray(this.pos + 1, this.pos + 1 + length) | |
this.pos += length + 1 | |
return value | |
} | |
decodeList () { | |
++this.pos | |
let list = [] | |
while (this.str[this.pos] !== 101) { | |
list.push(this.decode()) | |
} | |
++this.pos | |
return list | |
} | |
decodeDict () { | |
++this.pos | |
let dict = {} | |
while (this.str[this.pos] !== 101) { | |
let key = this.decodeString().toString() | |
let value = this.decode() | |
dict[key] = value | |
} | |
++this.pos | |
return dict | |
} | |
} | |
function decode (str) { | |
return new Decoder(str).decode() | |
} | |
function _encodeInt (num) { | |
return Buffer.from(`i${num}e`) | |
} | |
function _encodeString (str) { | |
return Buffer.concat([Buffer.from(str.length + ':'), Buffer.from(str)]) | |
} | |
function _encodeArray (arr) { | |
let buf = [Buffer.from([108])] | |
for (let it of arr) { | |
buf.push(encode(it)) | |
} | |
buf.push(Buffer.from([101])) | |
return Buffer.concat(buf) | |
} | |
function _encodeDict (dict) { | |
let buf = [Buffer.from([100])] | |
for (let key of Object.keys(dict).sort()) { | |
buf.push(_encodeString(key)) | |
buf.push(encode(dict[key])) | |
} | |
buf.push(Buffer.from([101])) | |
return Buffer.concat(buf) | |
} | |
function encode (obj) { | |
let t = typeof obj | |
if (t === 'number') return _encodeInt(obj) | |
if (t === 'string' || obj instanceof Buffer) return _encodeString(obj) | |
if (Array.isArray(obj)) return _encodeArray(obj) | |
return _encodeDict(obj) | |
} | |
module.exports = { | |
decode, | |
encode | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment