58 lines
1.9 KiB
TypeScript
58 lines
1.9 KiB
TypeScript
/**
|
|
* Bencode encoder — serializes JS values to bencode bytes.
|
|
* Used primarily for UDP tracker connect/announce requests.
|
|
*/
|
|
|
|
/**
|
|
* Encoder-only type hierarchy — mirrors BencodeValue but adds `string` at every level.
|
|
* The *decoder* never returns strings (bencode byte-strings are always Buffer), so
|
|
* BencodeValue intentionally excludes string. The encoder is more permissive: callers
|
|
* can pass plain JS strings, and they are serialized as bencode byte-strings.
|
|
*/
|
|
export type EncodableDict = { [key: string]: EncodableValue };
|
|
export type EncodableList = EncodableValue[];
|
|
export type EncodableValue = number | string | Buffer | EncodableList | EncodableDict;
|
|
|
|
export function encode(value: EncodableValue): Buffer {
|
|
if (typeof value === "number") {
|
|
return Buffer.from(`i${Math.trunc(value)}e`, "ascii");
|
|
}
|
|
|
|
if (Buffer.isBuffer(value)) {
|
|
return Buffer.concat([Buffer.from(`${value.length}:`, "ascii"), value]);
|
|
}
|
|
|
|
if (typeof value === "string") {
|
|
const strBuf = Buffer.from(value, "utf8");
|
|
return Buffer.concat([Buffer.from(`${strBuf.length}:`, "ascii"), strBuf]);
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
return encodeList(value);
|
|
}
|
|
|
|
return encodeDict(value);
|
|
}
|
|
|
|
function encodeList(list: EncodableList): Buffer {
|
|
const parts: Buffer[] = [Buffer.from("l", "ascii")];
|
|
for (const item of list) {
|
|
parts.push(encode(item));
|
|
}
|
|
parts.push(Buffer.from("e", "ascii"));
|
|
return Buffer.concat(parts);
|
|
}
|
|
|
|
function encodeDict(dict: EncodableDict): Buffer {
|
|
const parts: Buffer[] = [Buffer.from("d", "ascii")];
|
|
// Keys must be sorted lexicographically
|
|
const keys = Object.keys(dict).sort();
|
|
for (const key of keys) {
|
|
const keyBuf = Buffer.from(key, "utf8");
|
|
parts.push(Buffer.from(`${keyBuf.length}:`, "ascii"), keyBuf);
|
|
parts.push(encode(dict[key]));
|
|
}
|
|
parts.push(Buffer.from("e", "ascii"));
|
|
return Buffer.concat(parts);
|
|
}
|