UNPKG

@dimipay/bqencode

Version:
209 lines (205 loc) 6.09 kB
// libs/base.ts class Base { } class InvalidEncodingError extends Error { constructor() { super("Invalid encoding"); } } // libs/utils.ts function readBigInt56BE(buf, offset = 0) { if (buf[offset] === undefined || buf[offset + 6] === undefined) { throw new RangeError("Cannot access beyond buffer length"); } const first = buf.readUintBE(offset, 3); const last = buf.readUint32BE(offset + 3); return BigInt(first) << 32n | BigInt(last); } function writeBigInt56BE(value, buf, offset = 0) { const first = Number(value >> 32n); buf[offset] = first >> 16; buf[offset + 1] = first >> 8 & 255; buf[offset + 2] = first & 255; const second = Number(value & 0xffffffffn); buf[offset + 3] = second >> 24; buf[offset + 4] = second >> 16 & 255; buf[offset + 5] = second >> 8 & 255; buf[offset + 6] = second & 255; } // libs/base10.ts class Base10 extends Base { byteLength = [3, 5, 8, 10, 13, 15]; encode(input) { let result = ""; const buf = Buffer.isBuffer(input) ? input : Buffer.from(input); for (let i = 0;i < buf.length; i += 7) { if (i + 6 < buf.length) { const val = readBigInt56BE(buf, i); result += String(val).padStart(17, "0"); } else { const size = buf.length - i; const length = this.byteLength[size - 1]; const val = buf.readUintBE(i, size); result += String(val).padStart(length, "0"); } } return result; } decode(str) { if (str.length === 0) { return Buffer.alloc(0); } if (!this.isBase10(str)) { throw new InvalidEncodingError; } const chunk = str.match(/\d{1,17}/g); const lastSize = str.length % 17 ? this.byteLength.indexOf(str.length % 17) + 1 : 7; const result = Buffer.alloc(chunk.length * 7 - (7 - lastSize)); for (let i = 0;i < chunk.length; i++) { if (chunk[i].length === 17) { writeBigInt56BE(BigInt(chunk[i]), result, i * 7); } else { result.writeUintBE(Number(chunk[i]), i * 7, lastSize); } } return result; } isBase10(str) { const r = str.length % 17; return /^\d*$/.test(str) && (r === 0 || this.byteLength.includes(r)); } } var base10 = new Base10; // libs/base45.ts class Base45 extends Base { charset = new Charset("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"); encode(input) { let result = ""; const buf = Buffer.isBuffer(input) ? input : Buffer.from(input); for (let i = 0;i < buf.length; i += 2) { if (i + 1 < buf.length) { let val = buf.readUint16BE(i); for (let j = 0;j < 3; j++) { result += this.charset.charAt(val % 45); val = Math.floor(val / 45); } } else { let val = buf.readUint8(i); for (let j = 0;j < 2; j++) { result += this.charset.charAt(val % 45); val = Math.floor(val / 45); } } } return result; } decode(str) { if (!this.isBase45(str)) { throw new InvalidEncodingError; } if (str.length === 0) { return Buffer.alloc(0); } const input = Array.from(str).map((c) => this.charset.indexOf(c)); const result = []; for (let i = 0;i < str.length; i += 3) { if (i + 2 < str.length) { const val = input[i] + input[i + 1] * 45 + input[i + 2] * 45 ** 2; result.push(val >> 8, val & 255); } else { const val = input[i] + input[i + 1] * 45; result.push(val); } } return Buffer.from(result); } isBase45(str) { const r = str.length % 3; return new RegExp(`^[${this.charset.charset}]*\$`).test(str) && (r === 0 || r === 2); } } class Charset { charset; charsetObj; constructor(charset) { this.charset = charset; this.charsetObj = Object.fromEntries(Array.from(charset).map((c, i) => [c, i])); } charAt(index) { return this.charset[index]; } indexOf(char) { return this.charsetObj[char]; } parse(str) { return Array.from(str).map((c) => this.indexOf(c)); } } var base45 = new Base45; // libs/base94.ts class Base94 extends Base { byteLength = [2, 3, 4]; encode(input) { let result = ""; const buf = Buffer.isBuffer(input) ? input : Buffer.from(input); for (let i = 0;i < buf.length; i += 4) { if (i + 3 < buf.length) { let val = buf.readUint32BE(i); for (let j = 0;j < 5; j++) { result += String.fromCharCode(val % 94 + 33); val = Math.floor(val / 94); } } else { const size = buf.length - i; const length = this.byteLength[size - 1]; let val = buf.readUintBE(i, size); for (let j = 0;j < length; j++) { result += String.fromCharCode(val % 94 + 33); val = Math.floor(val / 94); } } } return result; } decode(str) { if (str.length === 0) { return Buffer.alloc(0); } if (!this.isBase94(str)) { throw new InvalidEncodingError; } const chunk = str.match(/.{1,5}/g); const lastSize = str.length % 5 ? this.byteLength.indexOf(str.length % 5) + 1 : 4; const result = Buffer.alloc(chunk.length * 4 - (4 - lastSize)); for (let i = 0;i < chunk.length; i++) { const buf = Buffer.from(Array.from(chunk[i]).map((c) => c.charCodeAt(0) - 33)); if (chunk[i].length === 5) { const val = buf[0] + buf[1] * 94 + buf[2] * 94 ** 2 + buf[3] * 94 ** 3 + buf[4] * 94 ** 4; result.writeUint32BE(val, i * 4); } else { let val; if (chunk[i].length === 4) { val = buf[0] + buf[1] * 94 + buf[2] * 94 ** 2 + buf[3] * 94 ** 3; } else if (chunk[i].length === 3) { val = buf[0] + buf[1] * 94 + buf[2] * 94 ** 2; } else { val = buf[0] + buf[1] * 94; } result.writeUintBE(val, i * 4, lastSize); } } return result; } isBase94(str) { const r = str.length % 5; return /^[\x21-\x7e]*$/.test(str) && (r === 0 || this.byteLength.includes(r)); } } var base94 = new Base94; export { base94, base45, base10, InvalidEncodingError, Base };