@substrate-system/bencode
Version:
Bencode de/encoder
145 lines (114 loc) • 3.74 kB
text/typescript
import { concat, text2arr, type TypedArray } from '@substrate-system/uint8-util'
import { getType } from './util.js'
/**
* Encodes data in bencode.
*
* @param {Uint8Array|Array|String|Object|Number|Boolean} data
* @return {Uint8Array}
*/
export function encode (
data?:TypedArray|any[]|string|number|boolean|object|null,
buffer?:Uint8Array,
offset?:number
):Uint8Array {
const buffers = []
encode._encode(buffers, data)
const result:Uint8Array = concat(buffers)
encode.bytes = result.length
if (ArrayBuffer.isView(buffer)) {
buffer.set(result, offset)
return buffer
}
return result
}
encode.bytes = -1
encode._floatConversionDetected = false
encode._encode = function (buffers, data) {
if (data == null) { return }
switch (getType(data)) {
case 'object': encode.dict(buffers, data); break
case 'map': encode.dictMap(buffers, data); break
case 'array': encode.list(buffers, data); break
case 'set': encode.listSet(buffers, data); break
case 'string': encode.string(buffers, data); break
case 'number': encode.number(buffers, data); break
case 'boolean': encode.number(buffers, data); break
case 'arraybufferview': encode.buffer(buffers, new Uint8Array(data.buffer, data.byteOffset, data.byteLength)); break
case 'arraybuffer': encode.buffer(buffers, new Uint8Array(data)); break
}
}
const buffE = new Uint8Array([0x65])
const buffD = new Uint8Array([0x64])
const buffL = new Uint8Array([0x6C])
encode.buffer = function (buffers, data) {
buffers.push(text2arr(data.length + ':'), data)
}
encode.string = function (buffers, data) {
buffers.push(text2arr(text2arr(data).byteLength + ':' + data))
}
encode.number = function (buffers, data) {
if (Number.isInteger(data)) return buffers.push(text2arr('i' + BigInt(data) + 'e'))
const maxLo = 0x80000000
const hi = (data / maxLo) << 0
const lo = (data % maxLo) << 0
const val = hi * maxLo + lo
buffers.push(text2arr('i' + val + 'e'))
if (val !== data && !encode._floatConversionDetected) {
encode._floatConversionDetected = true
console.warn(
'WARNING: Possible data corruption detected with value "' + data + '":',
'Bencoding only defines support for integers, value was converted to "' + val + '"'
)
console.trace()
}
}
encode.dict = function (buffers, data) {
buffers.push(buffD)
let j = 0
let k
// fix for issue #13 - sorted dicts
const keys = Object.keys(data).sort()
const kl = keys.length
for (; j < kl; j++) {
k = keys[j]
if (data[k] == null) continue
encode.string(buffers, k)
encode._encode(buffers, data[k])
}
buffers.push(buffE)
}
encode.dictMap = function (buffers, data) {
buffers.push(buffD)
const keys = Array.from(data.keys()).sort()
for (const key of keys) {
if (data.get(key) == null) {
continue
}
if (ArrayBuffer.isView(key)) {
encode._encode(buffers, key)
} else {
encode.string(buffers, String(key))
}
encode._encode(buffers, data.get(key))
}
buffers.push(buffE)
}
encode.list = function (buffers, data) {
let i = 0
const c = data.length
buffers.push(buffL)
for (; i < c; i++) {
if (data[i] == null) continue
encode._encode(buffers, data[i])
}
buffers.push(buffE)
}
encode.listSet = function (buffers, data) {
buffers.push(buffL)
for (const item of data) {
if (item == null) continue
encode._encode(buffers, item)
}
buffers.push(buffE)
}
export default encode