UNPKG

@akala/core

Version:
782 lines 29.9 kB
import ErrorWithStatus, { HttpStatusCode } from "./errorWithStatus.js"; import * as base64 from "./base64.js"; export let defaultContext; export function setDefaultContext(context) { defaultContext = context; } // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-empty-function export function noop() { } export function lazy(factory) { let instance; return function () { return instance || (instance = factory()); }; } export function spread(...args) { const result = {}; for (let i = 0; i < args.length; i++) { const element = args[i]; const descriptors = Object.getOwnPropertyDescriptors(element); Object.defineProperties(result, descriptors); } return result; } export class IsomorphicBuffer { offset; end; buffer; constructor(buffer, offset, end) { this.offset = offset; this.end = end; if (typeof buffer == 'number') this.buffer = new Uint8Array(buffer); else if (Array.isArray(buffer)) this.buffer = new Uint8Array(buffer); else this.buffer = buffer; if (!offset) this.offset = 0; if (!end) this.end = this.buffer.byteLength; this.length = this.end - this.offset; } length; static fromBuffer(buffer) { return new IsomorphicBuffer(buffer); } static fromArrayBuffer(abuffer) { const buffer = new Uint8Array(abuffer.byteLength); const view = new DataView(abuffer); for (let i = 0; i < abuffer.byteLength; i++) { buffer[i] = view.getUint8(i); } return new IsomorphicBuffer(buffer); } copy(source, offset, sourceOffset = 0, length) { if (length === 0 || !length && source.length == 0) return; offset = this.ensureOffset(offset); if (sourceOffset == 0 && (typeof length == 'undefined' || length === source.length)) this.buffer.set(source.toArray(), offset); else this.buffer.set(source.subarray(sourceOffset, sourceOffset + length).toArray(), offset); } toArray() { return this.buffer.slice(this.offset, this.end); } indexOf(value, offset = 0) { return this.buffer.indexOf(value, offset + this.offset) - this.offset; } static concat(buffers) { const totalLength = buffers.reduce((previous, current) => previous + current?.length, 0); const target = new IsomorphicBuffer(totalLength); let offset = 0; for (const buffer of buffers) { if (!buffer?.length) continue; target.copy(buffer, offset); offset += buffer.length; } return target; } static getInitLength(s, encoding = 'utf8') { switch (encoding) { case "ascii": return s.length; case "utf8": case "utf-8": return base64.strUTF8ByteLength(s); case "base64": case "base64url": return base64.base64ByteLength(s); case "hex": return s.length / 2; case "binary": return s.length; } } static from(s, encoding = 'utf8') { switch (encoding) { case "ascii": { const result = new Uint8Array(s.length); for (let i = 0; i < s.length; i++) result[i] = s.charCodeAt(i); return new IsomorphicBuffer(result); } case "utf8": case "utf-8": return new IsomorphicBuffer(base64.strToUTF8Arr(s)); case "base64": return new IsomorphicBuffer(base64.base64DecToArr(s)); case "hex": { const result = new Uint8Array(s.length / 2); for (let i = 0; i < s.length; i++) switch (s[i]) { case '0': break; case '1': result[i / 2] += i % 2 === 0 ? 0x10 : 0x1; break; case '2': result[i / 2] += i % 2 === 0 ? 0x20 : 0x2; break; case '3': result[i / 2] += i % 2 === 0 ? 0x30 : 0x3; break; case '4': result[i / 2] += i % 2 === 0 ? 0x40 : 0x4; break; case '5': result[i / 2] += i % 2 === 0 ? 0x50 : 0x5; break; case '6': result[i / 2] += i % 2 === 0 ? 0x60 : 0x6; break; case '7': result[i / 2] += i % 2 === 0 ? 0x70 : 0x7; break; case '8': result[i / 2] += i % 2 === 0 ? 0x80 : 0x8; break; case '9': result[i / 2] += i % 2 === 0 ? 0x90 : 0x9; break; case 'A': case 'a': result[i / 2] += i % 2 === 0 ? 0xa0 : 0xa; break; case 'B': case 'b': result[i / 2] += i % 2 === 0 ? 0xB0 : 0xB; break; case 'C': case 'c': result[i / 2] += i % 2 === 0 ? 0xC0 : 0xc; break; case 'D': case 'd': result[i / 2] += i % 2 === 0 ? 0xD0 : 0xD; break; case 'E': case 'e': result[i / 2] += i % 2 === 0 ? 0xE0 : 0xE; break; case 'F': case 'f': result[i / 2] += i % 2 === 0 ? 0xF0 : 0xF; break; default: throw new ErrorWithStatus(HttpStatusCode.BadRequest); } return new IsomorphicBuffer(result); } case "base64url": return new IsomorphicBuffer(base64.base64UrlDecToArr(s)); case "binary": return new IsomorphicBuffer(base64.strToUTF8Arr(s)); } } toString(encoding, offset, end) { if (offset) return this.subarray(offset, end).toString(encoding); if (!end) end = this.length; switch (encoding) { case "ascii": { end += this.offset; const result = new Array(end - this.offset); for (let i = this.offset; i < end; i++) result[i - this.offset] = String.fromCharCode(this.buffer[i]); return result.join(''); } case "utf8": case "utf-8": return base64.UTF8IsomorphicBufferToStr(this); case "base64": return base64.base64EncIsomorphicBuffer(this); case "hex": const result = new Array(this.length); for (let i = 0; i < this.length; i++) result[i] = this.buffer[i].toString(16); return result.join(''); case "base64url": return base64.base64UrlEncIsomorphicBuffer(this); case "binary": return base64.UTF8IsomorphicBufferToStr(this); } } toJSON() { return { type: 'Buffer', data: Array.from(this.buffer.subarray(this.offset, this.end)) }; } write(s, offset, length, encoding) { if (typeof length === 'undefined') length = s.length; if (length !== s.length) return this.write(s.substring(0, length), offset, undefined, encoding); this.copy(IsomorphicBuffer.from(s, encoding), offset); } ensureOffset(offset, length = 1) { // console.log(`${offset} + ${length} = ${offset + length} <= ${this.end}`) if (typeof offset == 'undefined') offset = 0; offset += this.offset; if (offset < this.offset || offset + length > this.end) throw new Error('Out of limits'); return offset; } fill(value, start, end) { start = this.ensureOffset(start, end - start); end = this.ensureOffset(typeof end === 'undefined' ? this.length : end, 0); this.buffer.fill(value, start, end); } readInt8(index = 0) { index = this.ensureOffset(index); const val = this.buffer[index]; return val & 0x80 ? val - 0x100 : val; } writeInt8(value, index = 0) { index = this.ensureOffset(index); this.buffer[index] = value & 0xff; } readUInt8(index = 0) { index = this.ensureOffset(index); return this.buffer[index]; } writeUInt8(value, index = 0) { index = this.ensureOffset(index); this.buffer[index] = value & 0xff; } readDoubleBE(index = 0) { index = this.ensureOffset(index, 8); const highWord = (this.buffer[index] << 24) | (this.buffer[index + 1] << 16) | (this.buffer[index + 2] << 8) | this.buffer[index + 3]; const lowWord = (this.buffer[index + 4] << 24) | (this.buffer[index + 5] << 16) | (this.buffer[index + 6] << 8) | this.buffer[index + 7]; // Combine into 64-bit value const bits = BigInt(highWord) * BigInt(0x100000000) + BigInt(lowWord >>> 0); // Handle special cases if (bits === BigInt(0)) return 0; const sign = ((highWord >>> 31) & 0x1) ? -1 : 1; const exponent = ((highWord >>> 20) & 0x7FF) - 1023; const fraction = Number((bits & BigInt(0xFFFFFFFFFFFFF)) | BigInt(0x10000000000000)); return sign * fraction * Math.pow(2, exponent - 52); } writeDoubleBE(value, index = 0) { index = this.ensureOffset(index, 8); // Handle special cases if (value === 0) { for (let i = 0; i < 8; i++) { this.buffer[index + i] = 0; } return; } if (!Number.isFinite(value)) { if (value === Infinity) { this.buffer[index] = 0x7F; this.buffer[index + 1] = 0xF0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0; this.buffer[index + 7] = 0; return; } if (value === -Infinity) { this.buffer[index] = 0xFF; this.buffer[index + 1] = 0xF0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0; this.buffer[index + 7] = 0; return; } // NaN this.buffer[index] = 0x7F; this.buffer[index + 1] = 0xF8; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0; this.buffer[index + 7] = 0; return; } const sign = value < 0 ? 1 : 0; value = Math.abs(value); let exponent = Math.floor(Math.log2(value)); let fraction = value * Math.pow(2, -exponent) - 1; exponent += 1023; fraction = Math.round(fraction * 0x10000000000000); const low = Number(BigInt.asIntN(32, BigInt(fraction))); const high = Number(BigInt.asIntN(32, BigInt(fraction) >> BigInt(32))) | (exponent << 20) | (sign << 31); this.buffer[index] = (high >>> 24) & 0xFF; this.buffer[index + 1] = (high >>> 16) & 0xFF; this.buffer[index + 2] = (high >>> 8) & 0xFF; this.buffer[index + 3] = high & 0xFF; this.buffer[index + 4] = (low >>> 24) & 0xFF; this.buffer[index + 5] = (low >>> 16) & 0xFF; this.buffer[index + 6] = (low >>> 8) & 0xFF; this.buffer[index + 7] = low & 0xFF; } readDoubleLE(index = 0) { index = this.ensureOffset(index, 8); const lowWord = this.buffer[index] | (this.buffer[index + 1] << 8) | (this.buffer[index + 2] << 16) | (this.buffer[index + 3] << 24); const highWord = this.buffer[index + 4] | (this.buffer[index + 5] << 8) | (this.buffer[index + 6] << 16) | (this.buffer[index + 7] << 24); // Combine into 64-bit value const bits = BigInt(highWord) * BigInt(0x100000000) + BigInt(lowWord >>> 0); // Handle special cases if (bits === BigInt(0)) return 0; const sign = ((highWord >>> 31) & 0x1) ? -1 : 1; const exponent = ((highWord >>> 20) & 0x7FF) - 1023; const fraction = Number((bits & BigInt(0xFFFFFFFFFFFFF)) | BigInt(0x10000000000000)); return sign * fraction * Math.pow(2, exponent - 52); } writeDoubleLE(value, index = 0) { index = this.ensureOffset(index, 8); // Handle special cases if (value === 0) { for (let i = 0; i < 8; i++) { this.buffer[index + i] = 0; } return; } if (!Number.isFinite(value)) { if (value === Infinity) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0xF0; this.buffer[index + 7] = 0x7F; return; } if (value === -Infinity) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0xF0; this.buffer[index + 7] = 0xFF; return; } // NaN this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; this.buffer[index + 4] = 0; this.buffer[index + 5] = 0; this.buffer[index + 6] = 0xF8; this.buffer[index + 7] = 0x7F; return; } const sign = value < 0 ? 1 : 0; value = Math.abs(value); let exponent = Math.floor(Math.log2(value)); let fraction = value * Math.pow(2, -exponent) - 1; exponent += 1023; fraction = Math.round(fraction * 0x10000000000000); const low = Number(BigInt.asIntN(32, BigInt(fraction))); const high = Number(BigInt.asIntN(32, BigInt(fraction) >> BigInt(32))) | (exponent << 20) | (sign << 31); this.buffer[index] = low & 0xFF; this.buffer[index + 1] = (low >>> 8) & 0xFF; this.buffer[index + 2] = (low >>> 16) & 0xFF; this.buffer[index + 3] = (low >>> 24) & 0xFF; this.buffer[index + 4] = high & 0xFF; this.buffer[index + 5] = (high >>> 8) & 0xFF; this.buffer[index + 6] = (high >>> 16) & 0xFF; this.buffer[index + 7] = (high >>> 24) & 0xFF; } readFloatBE(index = 0) { index = this.ensureOffset(index, 4); const bytes = (this.buffer[index] << 24) | (this.buffer[index + 1] << 16) | (this.buffer[index + 2] << 8) | this.buffer[index + 3]; // Handle special cases if (bytes === 0) return 0; if (bytes === 0x7F800000) return Infinity; if (bytes === 0xFF800000) return -Infinity; if ((bytes & 0x7F800000) === 0x7F800000) return NaN; const sign = bytes >>> 31 ? -1 : 1; const exponent = ((bytes >>> 23) & 0xFF) - 127; const fraction = (bytes & 0x7FFFFF) | 0x800000; return sign * fraction * Math.pow(2, exponent - 23); } writeFloatBE(value, index = 0) { index = this.ensureOffset(index, 4); // Handle special cases if (value === 0) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; return; } if (!Number.isFinite(value)) { if (value === Infinity) { this.buffer[index] = 0x7F; this.buffer[index + 1] = 0x80; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; return; } if (value === -Infinity) { this.buffer[index] = 0xFF; this.buffer[index + 1] = 0x80; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; return; } // NaN this.buffer[index] = 0x7F; this.buffer[index + 1] = 0xC0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; return; } const sign = value < 0 ? 1 : 0; value = Math.abs(value); let exponent = Math.floor(Math.log2(value)); let fraction = value * Math.pow(2, -exponent) - 1; exponent += 127; fraction = Math.round(fraction * 0x800000); const bytes = (sign << 31) | (exponent << 23) | fraction; this.buffer[index] = (bytes >>> 24) & 0xFF; this.buffer[index + 1] = (bytes >>> 16) & 0xFF; this.buffer[index + 2] = (bytes >>> 8) & 0xFF; this.buffer[index + 3] = bytes & 0xFF; } readFloatLE(index = 0) { index = this.ensureOffset(index, 4); const bytes = this.buffer[index] | (this.buffer[index + 1] << 8) | (this.buffer[index + 2] << 16) | (this.buffer[index + 3] << 24); // Handle special cases if (bytes === 0) return 0; if (bytes === 0x7F800000) return Infinity; if (bytes === 0xFF800000) return -Infinity; if ((bytes & 0x7F800000) === 0x7F800000) return NaN; const sign = bytes >>> 31 ? -1 : 1; const exponent = ((bytes >>> 23) & 0xFF) - 127; const fraction = (bytes & 0x7FFFFF) | 0x800000; return sign * fraction * Math.pow(2, exponent - 23); } writeFloatLE(value, index = 0) { index = this.ensureOffset(index, 4); // Handle special cases if (value === 0) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0; this.buffer[index + 3] = 0; return; } if (!Number.isFinite(value)) { if (value === Infinity) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0x80; this.buffer[index + 3] = 0x7F; return; } if (value === -Infinity) { this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0x80; this.buffer[index + 3] = 0xFF; return; } // NaN this.buffer[index] = 0; this.buffer[index + 1] = 0; this.buffer[index + 2] = 0xC0; this.buffer[index + 3] = 0x7F; return; } const sign = value < 0 ? 1 : 0; value = Math.abs(value); let exponent = Math.floor(Math.log2(value)); let fraction = value * Math.pow(2, -exponent) - 1; exponent += 127; fraction = Math.round(fraction * 0x800000); const bytes = (sign << 31) | (exponent << 23) | fraction; this.buffer[index] = bytes & 0xFF; this.buffer[index + 1] = (bytes >>> 8) & 0xFF; this.buffer[index + 2] = (bytes >>> 16) & 0xFF; this.buffer[index + 3] = (bytes >>> 24) & 0xFF; } readUInt16LE(index = 0) { index = this.ensureOffset(index, 2); return this.buffer[index] | (this.buffer[index + 1] << 8); } writeUInt16LE(value, index = 0) { index = this.ensureOffset(index, 2); this.buffer[index] = value & 0xff; this.buffer[index + 1] = (value >>> 8) & 0xff; } readUInt16BE(index = 0) { index = this.ensureOffset(index, 2); return (this.buffer[index] << 8) | this.buffer[index + 1]; } writeUInt16BE(value, index = 0) { index = this.ensureOffset(index, 2); this.buffer[index] = (value >>> 8) & 0xff; this.buffer[index + 1] = value & 0xff; } readInt16LE(index = 0) { index = this.ensureOffset(index, 2); const val = this.buffer[index] | (this.buffer[index + 1] << 8); return val & 0x8000 ? val - 0x10000 : val; } writeInt16LE(value, index = 0) { index = this.ensureOffset(index, 2); this.buffer[index] = value & 0xff; this.buffer[index + 1] = (value >>> 8) & 0xff; } readInt16BE(index = 0) { index = this.ensureOffset(index, 2); const val = (this.buffer[index] << 8) | this.buffer[index + 1]; return val & 0x8000 ? val - 0x10000 : val; } writeInt16BE(value, index = 0) { index = this.ensureOffset(index, 2); this.buffer[index] = (value >>> 8) & 0xff; this.buffer[index + 1] = value & 0xff; } readUInt32LE(index = 0) { index = this.ensureOffset(index, 4); return ((this.buffer[index + 3] << 24) >>> 0) + ((this.buffer[index + 2] << 16) | (this.buffer[index + 1] << 8) | this.buffer[index]); } writeUInt32LE(value, index = 0) { index = this.ensureOffset(index, 4); this.buffer[index + 3] = (value >>> 24) & 0xff; this.buffer[index + 2] = (value >>> 16) & 0xff; this.buffer[index + 1] = (value >>> 8) & 0xff; this.buffer[index] = value & 0xff; } readUInt32BE(index = 0) { index = this.ensureOffset(index, 4); return ((this.buffer[index] << 24) >>> 0) + ((this.buffer[index + 1] << 16) | (this.buffer[index + 2] << 8) | this.buffer[index + 3]); } writeUInt32BE(value, index = 0) { index = this.ensureOffset(index, 4); this.buffer[index] = (value >>> 24) & 0xff; this.buffer[index + 1] = (value >>> 16) & 0xff; this.buffer[index + 2] = (value >>> 8) & 0xff; this.buffer[index + 3] = value & 0xff; } readInt32BE(index = 0) { index = this.ensureOffset(index, 4); return (this.buffer[index] << 24) | (this.buffer[index + 1] << 16) | (this.buffer[index + 2] << 8) | this.buffer[index + 3]; } writeInt32BE(value, index = 0) { index = this.ensureOffset(index, 4); this.buffer[index] = (value >>> 24) & 0xff; this.buffer[index + 1] = (value >>> 16) & 0xff; this.buffer[index + 2] = (value >>> 8) & 0xff; this.buffer[index + 3] = value & 0xff; } readInt32LE(index = 0) { index = this.ensureOffset(index, 4); return this.buffer[index] | (this.buffer[index + 1] << 8) | (this.buffer[index + 2] << 16) | (this.buffer[index + 3] << 24); } writeInt32LE(value, index = 0) { index = this.ensureOffset(index, 4); this.buffer[index] = value & 0xff; this.buffer[index + 1] = (value >>> 8) & 0xff; this.buffer[index + 2] = (value >>> 16) & 0xff; this.buffer[index + 3] = (value >>> 24) & 0xff; } readBigUInt64LE(index = 0) { index = this.ensureOffset(index, 8); const lo = this.readUInt32LE(index); const hi = this.readUInt32LE(index + 4); return (BigInt(hi) << BigInt(32)) | BigInt(lo); } writeBigUInt64LE(value, index = 0) { index = this.ensureOffset(index, 8); const lo = Number(value & BigInt(0xFFFFFFFF)); const hi = Number(value >> BigInt(32)); this.writeUInt32LE(lo, index); this.writeUInt32LE(hi, index + 4); } readBigUInt64BE(index = 0) { index = this.ensureOffset(index, 8); const hi = this.readUInt32BE(index); const lo = this.readUInt32BE(index + 4); return (BigInt(hi) << BigInt(32)) | BigInt(lo); } writeBigUInt64BE(value, index = 0) { index = this.ensureOffset(index, 8); const lo = Number(value & BigInt(0xFFFFFFFF)); const hi = Number(value >> BigInt(32)); this.writeUInt32BE(hi, index); this.writeUInt32BE(lo, index + 4); } readBigInt64LE(index = 0) { const val = this.readBigUInt64LE(index); return BigInt.asIntN(64, val); } writeBigInt64LE(value, index = 0) { this.writeBigUInt64LE(BigInt.asUintN(64, value), index); } readBigInt64BE(index = 0) { const val = this.readBigUInt64BE(index); return BigInt.asIntN(64, val); } writeBigInt64BE(value, index = 0) { this.writeBigUInt64BE(BigInt.asUintN(64, value), index); } readUIntLE(index, byteLength) { index = this.ensureOffset(index, byteLength); let val = this.buffer[index]; let mul = 1; for (let i = 0; i < byteLength - 1; i++) { mul *= 0x100; val += this.buffer[index + i + 1] * mul; } return val; } writeUIntLE(value, index, byteLength) { index = this.ensureOffset(index, byteLength); let remaining = value; for (let i = 0; i < byteLength; i++) { this.buffer[index + i] = remaining & 0xFF; remaining = Math.floor(remaining / 256); } } readUIntBE(index, byteLength) { index = this.ensureOffset(index, byteLength); let val = this.buffer[index + byteLength - 1]; let mul = 1; for (let i = byteLength - 1; i > 0; i--) { mul *= 0x100; val += this.buffer[index + i - 1] * mul; } return val; } writeUIntBE(value, index, byteLength) { index = this.ensureOffset(index, byteLength); let remaining = value; for (let i = byteLength - 1; i >= 0; i--) { this.buffer[index + i] = remaining & 0xFF; remaining = Math.floor(remaining / 256); } } subarray(start, end) { if (typeof end == 'undefined') end = this.end - this.offset; if (start == 0 && end == this.end) return this; if (end < start) throw new Error('end is before start'); if (start < 0 || this.offset + end > this.end) throw new Error('Out of limits'); return new IsomorphicBuffer(this.buffer, this.offset + start, this.offset + end); } [Symbol.iterator]() { return this.buffer[Symbol.iterator](); } [Symbol.for('nodejs.util.inspect.custom')]() { return { type: 'Buffer', data: Array.from(this.buffer.subarray(this.offset, this.end)) }; } // This is used by util.inspect and assert.deepStrictEqual inspect() { return this[Symbol.for('nodejs.util.inspect.custom')](); } // This is used by assert.deepStrictEqual for comparison [Symbol.for('nodejs.util.inspect.custom.primitive')]() { return this.toJSON(); } valueOf() { return this.toJSON(); } equals(other) { if (!(other instanceof IsomorphicBuffer)) { return false; } if (this.length !== other.length) { return false; } for (let i = 0; i < this.length; i++) { if (this.buffer[this.offset + i] !== other.buffer[other.offset + i]) { return false; } } return true; } } export function throttle(threshold) { if (threshold < 1) throw new Error('Threshold must be greater than 0'); if (threshold == Number.POSITIVE_INFINITY || threshold == Number.MAX_SAFE_INTEGER) return function (handler) { return handler(); }; const open = new Array(threshold); let rr = -1; console.time('throttle'); return function (handler) { rr = (rr + 1) % threshold; if (!open[rr]) { const result = handler(); open[rr] = result; return result; } else { const result = open[rr].then(() => handler()); open[rr] = result; return result; } }; } //# sourceMappingURL=helpers.js.map