@thi.ng/bencode
Version:
Bencode binary encoder / decoder with optional UTF8 encoding & floating point support
46 lines (45 loc) • 1.68 kB
JavaScript
import { isArrayLike } from "@thi.ng/checks/is-arraylike";
import { isBoolean } from "@thi.ng/checks/is-boolean";
import { isNumber } from "@thi.ng/checks/is-number";
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
import { isString } from "@thi.ng/checks/is-string";
import { assert } from "@thi.ng/errors/assert";
import { unsupported } from "@thi.ng/errors/unsupported";
import { utf8Length } from "@thi.ng/strings/utf8";
import { bytes, str, u8, u8array } from "@thi.ng/transducers-binary/bytes";
import { mapcat } from "@thi.ng/transducers/mapcat";
const FLOAT_RE = /^[0-9.-]+$/;
const ENCODERS = {
i: (x) => {
__ensureValidNumber(x);
return [str(`i${Math.floor(x)}e`)];
},
f: (x) => {
__ensureValidNumber(x);
assert(
FLOAT_RE.test(x.toString()),
`values requiring exponential notation not allowed (${x})`
);
return [str(`f${x}e`)];
},
b: (buf) => [str(buf.length + ":"), u8array(buf)],
s: (x) => [str(utf8Length(x) + ":" + x)],
l: (x) => [u8(108), ...mapcat(__encodeBin, x), u8(101)],
d: (x) => [
u8(100),
...mapcat(
(k) => __encodeBin(k).concat(__encodeBin(x[k])),
Object.keys(x).sort()
),
u8(101)
]
};
const encode = (x, cap = 1024) => bytes(cap, __encodeBin(x));
const __encodeBin = (x) => ENCODERS[isNumber(x) ? Math.floor(x) !== x ? "f" : "i" : isBoolean(x) ? "i" : isString(x) ? "s" : x instanceof Uint8Array ? "b" : isPlainObject(x) ? "d" : isArrayLike(x) ? "l" : unsupported(`unsupported data type: ${x}`)](x);
const __ensureValidNumber = (x) => {
assert(isFinite(x), `can't encode infinite value`);
assert(!isNaN(x), `can't encode NaN`);
};
export {
encode
};