UNPKG

@substrate-system/bencode

Version:
145 lines (114 loc) 3.74 kB
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