Skip to content

Instantly share code, notes, and snippets.

@teidesu
Created June 22, 2021 13:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save teidesu/c36f671ab9bbac5a5d4e62f8cd6bd671 to your computer and use it in GitHub Desktop.
Save teidesu/c36f671ab9bbac5a5d4e62f8cd6bd671 to your computer and use it in GitHub Desktop.
JavaScript BEncode implementation
// 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