mirror of
https://git.v0l.io/Kieran/dtan.git
synced 2025-01-31 10:01:32 +00:00
185 lines
4.6 KiB
TypeScript
185 lines
4.6 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
|
function getType(value: any) {
|
|
if (ArrayBuffer.isView(value)) return "arraybufferview";
|
|
if (Array.isArray(value)) return "array";
|
|
if (value instanceof Number) return "number";
|
|
if (value instanceof Boolean) return "boolean";
|
|
if (value instanceof Set) return "set";
|
|
if (value instanceof Map) return "map";
|
|
if (value instanceof String) return "string";
|
|
if (value instanceof ArrayBuffer) return "arraybuffer";
|
|
return typeof value;
|
|
}
|
|
|
|
function text2arr(data: string) {
|
|
return new TextEncoder().encode(data);
|
|
}
|
|
|
|
function concat(arrays: Uint8Array[]): Uint8Array {
|
|
// Calculate the total length of all arrays
|
|
const totalLength = arrays.reduce((acc, value) => acc + value.length, 0);
|
|
|
|
// Create a new array with total length and fill it with elements of the arrays
|
|
const result = new Uint8Array(totalLength);
|
|
|
|
// Copy each array into the result
|
|
let length = 0;
|
|
for (const array of arrays) {
|
|
result.set(array, length);
|
|
length += array.length;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Encodes data in bencode.
|
|
*
|
|
* @param {Uint8Array|Array|String|Object|Number|Boolean} data
|
|
* @return {Uint8Array}
|
|
*/
|
|
export function encode(data: any, outBuffer?: Uint8Array, offset?: number) {
|
|
const buffers = [] as Array<Uint8Array>;
|
|
let result = null;
|
|
|
|
encode._encode(buffers, data);
|
|
result = concat(buffers);
|
|
encode.bytes = result.length;
|
|
|
|
if (ArrayBuffer.isView(outBuffer)) {
|
|
outBuffer.set(result, offset);
|
|
return outBuffer;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
encode.bytes = -1;
|
|
encode._floatConversionDetected = false;
|
|
|
|
encode._encode = function (buffers: Array<Uint8Array>, data: any) {
|
|
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: Array<Uint8Array>, data: any) {
|
|
buffers.push(text2arr(data.length + ":"), data);
|
|
};
|
|
|
|
encode.string = function (buffers: Array<Uint8Array>, data: any) {
|
|
buffers.push(text2arr(text2arr(data).byteLength + ":" + data));
|
|
};
|
|
|
|
encode.number = function (buffers: Array<Uint8Array>, data: any) {
|
|
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: Array<Uint8Array>, data: any) {
|
|
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: Array<Uint8Array>, data: any) {
|
|
buffers.push(buffD);
|
|
|
|
const keys = Array.from(data.keys()).sort();
|
|
|
|
for (const key of keys) {
|
|
if (data.get(key) == null) continue;
|
|
ArrayBuffer.isView(key) ? encode._encode(buffers, key) : encode.string(buffers, String(key));
|
|
encode._encode(buffers, data.get(key));
|
|
}
|
|
|
|
buffers.push(buffE);
|
|
};
|
|
|
|
encode.list = function (buffers: Array<Uint8Array>, data: any) {
|
|
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: Array<Uint8Array>, data: any) {
|
|
buffers.push(buffL);
|
|
|
|
for (const item of data) {
|
|
if (item == null) continue;
|
|
encode._encode(buffers, item);
|
|
}
|
|
|
|
buffers.push(buffE);
|
|
};
|