UNPKG

bufferfy

Version:
1,579 lines (1,558 loc) 76.6 kB
import { concat, compare } from 'uint8array-tools'; import deepEqual from 'fast-deep-equal'; import { base64url, base64, base58, base32 } from '@scure/base'; // src/utilities/Error.ts var BufferfyError = class extends Error { constructor(message, codecName, offset, context) { super(message); this.codecName = codecName; this.offset = offset; this.context = context; this.name = "BufferfyError"; } }; var BufferfyByteLengthError = class extends BufferfyError { constructor(required, available, offset) { const message = required !== void 0 && available !== void 0 ? `Buffer too short: need ${required} bytes, have ${available} at offset ${offset ?? 0}` : "Buffer is not of sufficient byteLength to decode."; super(message, void 0, offset); this.name = "BufferfyByteLengthError"; } }; var BufferfyValidationError = class extends BufferfyError { constructor(codecName, value) { super(`Value does not match codec ${codecName}`, codecName, void 0, { value }); this.name = "BufferfyValidationError"; } }; var BufferfyUnionError = class extends BufferfyError { constructor(value, attemptedCodecs) { super( `Value does not match any codec in union: tried [${attemptedCodecs.join(", ")}]`, "UnionCodec", void 0, { value, attempted: attemptedCodecs } ); this.name = "BufferfyUnionError"; } }; var BufferfyRangeError = class extends BufferfyError { constructor(message, codecName, value, limit, offset) { super(message, codecName, offset, { value, limit }); this.name = "BufferfyRangeError"; } }; // src/utilities/Reader.ts var Reader = class { constructor(buffer, offset = 0) { this.buffer = buffer; this.offset = offset; this.view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); } get position() { return this.offset; } get remaining() { return this.buffer.byteLength - this.offset; } get bytes() { return this.buffer; } ensureBytes(count) { if (this.remaining < count) { throw new BufferfyByteLengthError(); } } readByte() { this.ensureBytes(1); return this.buffer[this.offset++]; } readBytes(count) { this.ensureBytes(count); const bytes = this.buffer.subarray(this.offset, this.offset + count); this.offset += count; return bytes; } skipBytes(count) { this.ensureBytes(count); const offset = this.offset; this.offset += count; return offset; } readUint16(littleEndian) { this.ensureBytes(2); const value = this.view.getUint16(this.offset, littleEndian); this.offset += 2; return value; } readUint24(littleEndian) { this.ensureBytes(3); let high; let low; if (littleEndian) { low = this.view.getUint8(this.offset); high = this.view.getUint16(this.offset + 1, true); } else { high = this.view.getUint16(this.offset, false); low = this.view.getUint8(this.offset + 2); } this.offset += 3; return high << 8 | low; } readUint32(littleEndian) { this.ensureBytes(4); const value = this.view.getUint32(this.offset, littleEndian); this.offset += 4; return value; } readUint40(littleEndian) { this.ensureBytes(5); let high; let low; if (littleEndian) { low = this.view.getUint32(this.offset, true); high = this.view.getUint8(this.offset + 4); } else { high = this.view.getUint8(this.offset); low = this.view.getUint32(this.offset + 1, false); } this.offset += 5; return high * 4294967296 + low; } readUint48(littleEndian) { this.ensureBytes(6); let high; let low; if (littleEndian) { low = this.view.getUint32(this.offset, true); high = this.view.getUint16(this.offset + 4, true); } else { high = this.view.getUint16(this.offset, false); low = this.view.getUint32(this.offset + 2, false); } this.offset += 6; return high * 4294967296 + low; } readFloat32(littleEndian) { this.ensureBytes(4); const value = this.view.getFloat32(this.offset, littleEndian); this.offset += 4; return value; } readFloat64(littleEndian) { this.ensureBytes(8); const value = this.view.getFloat64(this.offset, littleEndian); this.offset += 8; return value; } readBigUint64(littleEndian) { this.ensureBytes(8); const value = this.view.getBigUint64(this.offset, littleEndian); this.offset += 8; return value; } peekBytes(start, end) { return this.buffer.subarray(start, end); } }; // src/utilities/Writer.ts var Writer = class { constructor(buffer, initialSize = 1024) { this.buffer = buffer || new Uint8Array(initialSize); this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength); this.offset = 0; } get position() { return this.offset; } // Growth swaps the underlying array; a caller holding this reference across a write that grows the buffer must re-read the getter. get bytes() { return this.buffer; } ensureCapacity(additionalBytes) { const required = this.offset + additionalBytes; if (required <= this.buffer.byteLength) return; let newSize = this.buffer.byteLength * 2; while (newSize < required) newSize *= 2; const newBuffer = new Uint8Array(newSize); newBuffer.set(this.buffer); this.buffer = newBuffer; this.view = new DataView(this.buffer.buffer, this.buffer.byteOffset, this.buffer.byteLength); } toBuffer() { return this.buffer.subarray(0, this.offset); } reset() { this.offset = 0; } writeByte(value) { this.ensureCapacity(1); this.buffer[this.offset++] = value; } writeBytes(bytes) { this.ensureCapacity(bytes.byteLength); this.buffer.set(bytes, this.offset); this.offset += bytes.byteLength; } reserve(byteLength) { this.ensureCapacity(byteLength); const offset = this.offset; this.offset += byteLength; return offset; } writeUint16(value, littleEndian) { this.ensureCapacity(2); this.view.setUint16(this.offset, value, littleEndian); this.offset += 2; } writeUint24(value, littleEndian) { this.ensureCapacity(3); const high = value >>> 8; const low = value & 255; if (littleEndian) { this.view.setUint8(this.offset, low); this.view.setUint16(this.offset + 1, high, true); } else { this.view.setUint16(this.offset, high, false); this.view.setUint8(this.offset + 2, low); } this.offset += 3; } writeUint32(value, littleEndian) { this.ensureCapacity(4); this.view.setUint32(this.offset, value, littleEndian); this.offset += 4; } writeUint40(value, littleEndian) { this.ensureCapacity(5); const high = Math.floor(value / 4294967296); const low = value % 4294967296; if (littleEndian) { this.view.setUint32(this.offset, low, true); this.view.setUint8(this.offset + 4, high); } else { this.view.setUint8(this.offset, high); this.view.setUint32(this.offset + 1, low, false); } this.offset += 5; } writeUint48(value, littleEndian) { this.ensureCapacity(6); const high = Math.floor(value / 4294967296); const low = value % 4294967296; if (littleEndian) { this.view.setUint32(this.offset, low, true); this.view.setUint16(this.offset + 4, high, true); } else { this.view.setUint16(this.offset, high, false); this.view.setUint32(this.offset + 2, low, false); } this.offset += 6; } writeFloat32(value, littleEndian) { this.ensureCapacity(4); this.view.setFloat32(this.offset, value, littleEndian); this.offset += 4; } writeFloat64(value, littleEndian) { this.ensureCapacity(8); this.view.setFloat64(this.offset, value, littleEndian); this.offset += 8; } writeBigUint64(value, littleEndian) { this.ensureCapacity(8); this.view.setBigUint64(this.offset, value, littleEndian); this.offset += 8; } }; var WARN_BUFFER_SIZE = 10 * 1024 * 1024; var hasWarned = false; var DecodeTransformStream = class extends TransformStream { constructor(codec) { super({ transform: async (chunk, controller) => { this._valueBytes = concat([this._valueBytes, chunk]); if (!hasWarned && this._valueBytes.byteLength > WARN_BUFFER_SIZE) { console.warn( `DecodeTransformStream buffer exceeded ${WARN_BUFFER_SIZE} bytes (${this._valueBytes.byteLength} bytes accumulated). This may indicate a DOS attack or incorrect codec usage. Consider validating input size upstream.` ); hasWarned = true; } try { while (this._valueBytes.byteLength) { const value = codec.decode(this._valueBytes); controller.enqueue(value); const byteLength = codec.byteLength(value); if (byteLength === 0) { controller.error(new BufferfyError("Codec returned zero byteLength, cannot make progress")); return; } this._valueBytes = this._valueBytes.subarray(byteLength, this._valueBytes.byteLength); } } catch (error) { if (error instanceof BufferfyByteLengthError) return; controller.error(error); } } }); this._valueBytes = Uint8Array.from([]); } }; // src/Codecs/Abstract/EncodeTransform.ts var EncodeTransformStream = class extends TransformStream { constructor(codec) { super({ async transform(value, controller) { try { const chunk = codec.encode(value); controller.enqueue(chunk); } catch (error) { controller.error(error); } } }); } }; // src/Codecs/Abstract/index.ts var sharedWriter = new Writer(); var sharedWriterInUse = false; var AbstractCodec = class { /** * Encodes a value of this codecs type into a buffer. * * **Note:** This method does NOT validate the value before encoding. * Call `isValid()` first if you need to verify the value is encodable. * Encoding invalid values may result in undefined behavior or runtime errors. * * @param {Value} value - Value of this codec's type. * @param {Uint8Array} [target] - A target buffer to write into (uses byteLength for sizing). * @param {number} [offset=0] - Offset at which to write into the target. * @return {Uint8Array} Buffer encoding of value. * */ encode(value, target, offset = 0) { if (target) { const buffer = offset ? new Uint8Array(target.buffer, target.byteOffset + offset) : target; const writer = new Writer(buffer); this._encode(value, writer); return writer.toBuffer(); } if (sharedWriterInUse) { const writer = new Writer(); this._encode(value, writer); return writer.toBuffer(); } sharedWriterInUse = true; try { this._encode(value, sharedWriter); return sharedWriter.toBuffer().slice(); } finally { sharedWriter.reset(); sharedWriterInUse = false; } } Encoder() { return new EncodeTransformStream(this); } /** * Decodes a buffer to a value of this codecs type. * * @param {Uint8Array} buffer - The buffer to be decoded. * @param {number} [offset=0] - Offset at which to read at. * @return {Value} Value decoded from the buffer * */ decode(source, offset = 0) { const reader = new Reader(source, offset); return this._decode(reader); } Decoder() { return new DecodeTransformStream(this); } }; // src/Codecs/UInt/index.ts var buildNumberValidators = (codecName, options) => { const minimum = options?.minimum; const maximum = options?.maximum; const mode = options?.validationMode ?? "both"; const hasRange = minimum !== void 0 || maximum !== void 0; return { validateEncode: hasRange && (mode === "both" || mode === "encode") ? (value) => { if (minimum !== void 0 && value < minimum) throw new BufferfyRangeError(`Encoded value ${value} is less than minimum ${minimum}`, codecName, value, minimum); if (maximum !== void 0 && value > maximum) throw new BufferfyRangeError(`Encoded value ${value} exceeds maximum ${maximum}`, codecName, value, maximum); } : null, validateDecode: hasRange && (mode === "both" || mode === "decode") ? (value, position) => { if (minimum !== void 0 && value < minimum) throw new BufferfyRangeError(`Decoded value ${value} is less than minimum ${minimum}`, codecName, value, minimum, position); if (maximum !== void 0 && value > maximum) throw new BufferfyRangeError(`Decoded value ${value} exceeds maximum ${maximum}`, codecName, value, maximum, position); } : null }; }; var createUIntCodec = (bits = 48, endianness = "BE", options) => { switch (bits) { case 8: return new UInt8Codec(options); case 16: return new UInt16Codec(endianness, options); case 24: return new UInt24Codec(endianness, options); case 32: return new UInt32Codec(endianness, options); case 40: return new UInt40Codec(endianness, options); case 48: return new UInt48Codec(endianness, options); } }; var _UInt8Codec = class _UInt8Codec extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildNumberValidators("UInt8Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 256) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt8Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeByte(value); } _decode(reader) { const value = reader.readByte(); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt8Codec.BYTE_LENGTH = 1; var UInt8Codec = _UInt8Codec; var _UInt16Codec = class _UInt16Codec extends AbstractCodec { constructor(endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("UInt16Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 65536) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt16Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint16(value, this._littleEndian); } _decode(reader) { const value = reader.readUint16(this._littleEndian); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt16Codec.BYTE_LENGTH = 2; var UInt16Codec = _UInt16Codec; var _UInt24Codec = class _UInt24Codec extends AbstractCodec { constructor(endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("UInt24Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 16777216) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt24Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint24(value, this._littleEndian); } _decode(reader) { const value = reader.readUint24(this._littleEndian); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt24Codec.BYTE_LENGTH = 3; var UInt24Codec = _UInt24Codec; var _UInt32Codec = class _UInt32Codec extends AbstractCodec { constructor(endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("UInt32Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 4294967296) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt32Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint32(value, this._littleEndian); } _decode(reader) { const value = reader.readUint32(this._littleEndian); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt32Codec.BYTE_LENGTH = 4; var UInt32Codec = _UInt32Codec; var _UInt40Codec = class _UInt40Codec extends AbstractCodec { constructor(endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("UInt40Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 1099511627776) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt40Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint40(value, this._littleEndian); } _decode(reader) { const value = reader.readUint40(this._littleEndian); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt40Codec.BYTE_LENGTH = 5; var UInt40Codec = _UInt40Codec; var _UInt48Codec = class _UInt48Codec extends AbstractCodec { constructor(endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("UInt48Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= 281474976710656) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _UInt48Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint48(value, this._littleEndian); } _decode(reader) { const value = reader.readUint48(this._littleEndian); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _UInt48Codec.BYTE_LENGTH = 6; var UInt48Codec = _UInt48Codec; // src/Codecs/VarInt/VarInt60.ts var MAX_SAFE_VARINT60 = 281474976710656; var VARINT60_THRESHOLDS = [32, 8192, 2097152, 536870912, 137438953472, 35184372088832]; var POW256 = [1, 256, 65536, 16777216, 4294967296, 1099511627776, 281474976710656]; var VarInt60Codec = class extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildNumberValidators("VarInt60Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value >= MAX_SAFE_VARINT60 || value > Number.MAX_SAFE_INTEGER) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength(value) { for (let i = 0; i < VARINT60_THRESHOLDS.length; i++) { if (value < VARINT60_THRESHOLDS[i]) return i + 1; } return 7; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); let byteLength = 7; for (let i = 0; i < VARINT60_THRESHOLDS.length; i++) { if (value < VARINT60_THRESHOLDS[i]) { byteLength = i + 1; break; } } const offset = writer.reserve(byteLength); const bytes = writer.bytes; if (byteLength === 1) { bytes[offset] = value; return; } const remainingBytes = byteLength - 1; bytes[offset] = remainingBytes << 5 | Math.floor(value / POW256[remainingBytes]) & 31; let position = offset + 1; for (let i = remainingBytes - 1; i >= 0; i--) { bytes[position++] = Math.floor(value / POW256[i]) & 255; } } _decode(reader) { const firstByte = reader.readByte(); const remainingBytes = (firstByte & 224) / 32; let value; if (remainingBytes === 0) { value = firstByte; } else { value = (firstByte & 31) * POW256[remainingBytes]; for (let i = remainingBytes - 1; i >= 0; i--) { value += reader.readByte() * POW256[i]; } } if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; // src/Codecs/Bytes/Variable.ts var BytesVariableCodec = class extends AbstractCodec { constructor(lengthCodec = new VarInt60Codec()) { super(); this.lengthCodec = lengthCodec; } isValid(value) { return value instanceof Uint8Array; } byteLength(value) { return this.lengthCodec.byteLength(value.byteLength) + value.byteLength; } _encode(value, writer) { this.lengthCodec._encode(value.byteLength, writer); writer.writeBytes(value); } _decode(reader) { const byteLength = this.lengthCodec._decode(reader); return reader.readBytes(byteLength); } }; // src/Codecs/Any/index.ts var createAnyCodec = (options) => new AnyCodec(options); var AnyCodec = class extends AbstractCodec { constructor(options) { super(); this._encodeValue = options?.encode || ((value) => new TextEncoder().encode(JSON.stringify(value))); this._decodeValue = options?.decode || ((buffer) => JSON.parse(new TextDecoder().decode(buffer))); this.lengthCodec = options?.lengthCodec || new VarInt60Codec(); this._bytesCodec = new BytesVariableCodec(this.lengthCodec); } isValid(_value) { return true; } byteLength(value) { const byteLength = this._encodeValue(value).byteLength; return this.lengthCodec.byteLength(byteLength) + byteLength; } _encode(value, writer) { const valueBuffer = this._encodeValue(value); return this._bytesCodec._encode(valueBuffer, writer); } _decode(reader) { const valueBuffer = this._bytesCodec._decode(reader); return this._decodeValue(valueBuffer); } }; // src/Codecs/Array/Fixed.ts var ArrayFixedCodec = class extends AbstractCodec { constructor(length, itemCodec) { super(); this.length = length; this.itemCodec = itemCodec; } isValid(value) { if (!Array.isArray(value) || value.length !== this.length) return false; for (let i = 0; i < value.length; i++) if (!this.itemCodec.isValid(value[i])) return false; return true; } byteLength(value) { let byteLength = 0; for (let i = 0; i < value.length; i++) byteLength += this.itemCodec.byteLength(value[i]); return byteLength; } _encode(value, writer) { for (let i = 0; i < value.length; i++) this.itemCodec._encode(value[i], writer); } _decode(reader) { const value = Array(this.length); for (let i = 0; i < this.length; i++) value[i] = this.itemCodec._decode(reader); return value; } }; // src/Codecs/Array/Variable.ts var ArrayVariableCodec = class extends AbstractCodec { constructor(itemCodec, lengthCodec = new VarInt60Codec()) { super(); this.itemCodec = itemCodec; this.lengthCodec = lengthCodec; } isValid(value) { if (!Array.isArray(value)) return false; for (let i = 0; i < value.length; i++) if (!this.itemCodec.isValid(value[i])) return false; return true; } byteLength(value) { let byteLength = this.lengthCodec.byteLength(value.length); for (let i = 0; i < value.length; i++) byteLength += this.itemCodec.byteLength(value[i]); return byteLength; } _encode(value, writer) { this.lengthCodec._encode(value.length, writer); for (let i = 0; i < value.length; i++) this.itemCodec._encode(value[i], writer); } _decode(reader) { const length = this.lengthCodec._decode(reader); const value = Array(length); for (let i = 0; i < length; i++) value[i] = this.itemCodec._decode(reader); return value; } }; // src/Codecs/BigUInt/index.ts var buildBigUIntValidators = (codecName, options) => { const minimum = options?.minimum; const maximum = options?.maximum; const mode = options?.validationMode ?? "both"; const hasRange = minimum !== void 0 || maximum !== void 0; return { validateEncode: hasRange && (mode === "both" || mode === "encode") ? (value) => { if (minimum !== void 0 && value < minimum) throw new BufferfyRangeError(`Encoded value ${value} is less than minimum ${minimum}`, codecName, value, void 0); if (maximum !== void 0 && value > maximum) throw new BufferfyRangeError(`Encoded value ${value} exceeds maximum ${maximum}`, codecName, value, void 0); } : null, validateDecode: hasRange && (mode === "both" || mode === "decode") ? (value, position) => { if (minimum !== void 0 && value < minimum) throw new BufferfyRangeError(`Decoded value ${value} is less than minimum ${minimum}`, codecName, value, void 0, position); if (maximum !== void 0 && value > maximum) throw new BufferfyRangeError(`Decoded value ${value} exceeds maximum ${maximum}`, codecName, value, void 0, position); } : null }; }; var createBigUIntCodec = (endianness = "BE", options) => { switch (endianness) { case "BE": { return new BigUIntBECodec(options); } case "LE": { return new BigUIntLECodec(options); } } }; var _BigUIntBECodec = class _BigUIntBECodec extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildBigUIntValidators(this.constructor.name, options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "bigint") return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _BigUIntBECodec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeBigUint64(value, false); } _decode(reader) { const value = reader.readBigUint64(false); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _BigUIntBECodec.BYTE_LENGTH = 8; var BigUIntBECodec = _BigUIntBECodec; var BigUIntLECodec = class extends BigUIntBECodec { _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeBigUint64(value, true); } _decode(reader) { const value = reader.readBigUint64(true); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; // src/Codecs/BitField/index.ts var BIT_MAP = [128, 64, 32, 16, 8, 4, 2, 1]; var createBitFieldCodec = (keys, byteLength) => new BitFieldCodec(keys, byteLength); var BitFieldCodec = class extends AbstractCodec { constructor(keys, _byteLength = Math.ceil(keys.length / 8)) { super(); this.keys = keys; this._byteLength = _byteLength; } isValid(value) { if (!value || typeof value !== "object") return false; for (const key of this.keys) if (typeof value[key] !== "boolean") return false; return true; } byteLength() { return this._byteLength; } _encode(value, writer) { for (let i = 0; i < this._byteLength; i++) { let byte = 0; const offset = i * 8; const bits = Math.min(this.keys.length - offset, 8); for (let j = 0; j < bits; j++) { if (value[this.keys[offset + j]] === true) byte |= BIT_MAP[j]; } writer.writeByte(byte); } } _decode(reader) { const value = {}; for (let i = 0; i < this._byteLength; i++) { const byte = reader.readByte(); const offset = i * 8; const bits = Math.min(this.keys.length - offset, 8); for (let j = 0; j < bits; j++) { value[this.keys[offset + j]] = (byte & BIT_MAP[j]) > 0; } } return value; } }; // src/Codecs/Boolean/index.ts var createBooleanCodec = () => new BooleanCodec(); var BooleanCodec = class extends AbstractCodec { isValid(value) { return typeof value === "boolean"; } byteLength() { return 1; } _encode(value, writer) { writer.writeByte(value ? 1 : 0); } _decode(reader) { return reader.readByte() === 1; } }; // src/Codecs/Bytes/Fixed.ts var BytesFixedCodec = class extends AbstractCodec { constructor(byteLength) { super(); this._byteLength = byteLength; } isValid(value) { return value instanceof Uint8Array && value.byteLength === this._byteLength; } byteLength() { return this._byteLength; } _encode(value, writer) { writer.writeBytes(value); } _decode(reader) { return reader.readBytes(this._byteLength); } }; // src/Codecs/Bytes/Constant.ts var constantTimeCompare = (a, b) => { if (a.byteLength !== b.byteLength) return false; let result = 0; for (let i = 0; i < a.byteLength; i++) { result |= a[i] ^ b[i]; } return result === 0; }; var BytesConstantCodec = class extends BytesFixedCodec { constructor(bytes, constantTime = false) { super(bytes.byteLength); this.bytes = bytes; this.constantTime = constantTime; } isValid(value) { if (!(value instanceof Uint8Array)) return false; if (this.constantTime) { return constantTimeCompare(value, this.bytes); } return compare(value, this.bytes) === 0; } _encode(_, writer) { writer.writeBytes(this.bytes); } _decode(reader) { reader.readBytes(this._byteLength); return this.bytes; } }; var createConstantCodec = (value) => { if (typeof value === "object" && value !== null) return new DeepConstantCodec(value); return new ConstantCodec(value); }; var ConstantCodec = class extends AbstractCodec { constructor(value) { super(); this.value = value; } isValid(value) { return value === this.value; } byteLength() { return 0; } _encode(_value, _writer) { } _decode(_reader) { return this.value; } }; var DeepConstantCodec = class extends ConstantCodec { constructor(value) { super(value); this.value = value; } isValid(value) { return deepEqual(value, this.value); } }; // src/Codecs/Float/index.ts var createFloatCodec = (bits = 64, endianness = "BE", options) => { switch (endianness) { case "BE": { switch (bits) { case 32: { return new Float32BECodec(options); } case 64: { return new Float64BECodec(options); } } } case "LE": { switch (bits) { case 32: { return new Float32LECodec(options); } case 64: { return new Float64LECodec(options); } } } } }; var _Float32BECodec = class _Float32BECodec extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildNumberValidators(this.constructor.name, options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number") return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Float32BECodec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeFloat32(value, false); } _decode(reader) { const value = reader.readFloat32(false); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Float32BECodec.BYTE_LENGTH = 4; var Float32BECodec = _Float32BECodec; var Float32LECodec = class extends Float32BECodec { _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeFloat32(value, true); } _decode(reader) { const value = reader.readFloat32(true); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; var _Float64BECodec = class _Float64BECodec extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildNumberValidators(this.constructor.name, options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number") return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Float64BECodec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeFloat64(value, false); } _decode(reader) { const value = reader.readFloat64(false); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Float64BECodec.BYTE_LENGTH = 8; var Float64BECodec = _Float64BECodec; var Float64LECodec = class extends Float64BECodec { _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeFloat64(value, true); } _decode(reader) { const value = reader.readFloat64(true); if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; // src/Codecs/Int/index.ts var createIntCodec = (bits = 48, endianness = "BE", options) => { switch (bits) { case 8: return new Int8Codec(options); case 16: return new Int16Codec(bits, endianness, options); case 24: return new Int24Codec(bits, endianness, options); case 32: return new Int32Codec(bits, endianness, options); case 40: return new Int40Codec(bits, endianness, options); case 48: return new Int48Codec(bits, endianness, options); } }; var _Int8Codec = class _Int8Codec extends AbstractCodec { constructor(options) { super(); this.options = options; const validators = buildNumberValidators("Int8Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int8Codec.MIN_VALUE || value > _Int8Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int8Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeByte(value + _Int8Codec.OFFSET); } _decode(reader) { const value = reader.readByte() - _Int8Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int8Codec.BYTE_LENGTH = 1; _Int8Codec.OFFSET = 128; _Int8Codec.MIN_VALUE = -128; _Int8Codec.MAX_VALUE = 127; var Int8Codec = _Int8Codec; var _Int16Codec = class _Int16Codec extends AbstractCodec { constructor(_bits, endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("Int16Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int16Codec.MIN_VALUE || value > _Int16Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int16Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint16(value + _Int16Codec.OFFSET, this._littleEndian); } _decode(reader) { const value = reader.readUint16(this._littleEndian) - _Int16Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int16Codec.BYTE_LENGTH = 2; _Int16Codec.OFFSET = 32768; _Int16Codec.MIN_VALUE = -32768; _Int16Codec.MAX_VALUE = 32767; var Int16Codec = _Int16Codec; var _Int24Codec = class _Int24Codec extends AbstractCodec { constructor(_bits, endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("Int24Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int24Codec.MIN_VALUE || value > _Int24Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int24Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint24(value + _Int24Codec.OFFSET, this._littleEndian); } _decode(reader) { const value = reader.readUint24(this._littleEndian) - _Int24Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int24Codec.BYTE_LENGTH = 3; _Int24Codec.OFFSET = 8388608; _Int24Codec.MIN_VALUE = -8388608; _Int24Codec.MAX_VALUE = 8388607; var Int24Codec = _Int24Codec; var _Int32Codec = class _Int32Codec extends AbstractCodec { constructor(_bits, endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("Int32Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int32Codec.MIN_VALUE || value > _Int32Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int32Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint32(value + _Int32Codec.OFFSET, this._littleEndian); } _decode(reader) { const value = reader.readUint32(this._littleEndian) - _Int32Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int32Codec.BYTE_LENGTH = 4; _Int32Codec.OFFSET = 2147483648; _Int32Codec.MIN_VALUE = -2147483648; _Int32Codec.MAX_VALUE = 2147483647; var Int32Codec = _Int32Codec; var _Int40Codec = class _Int40Codec extends AbstractCodec { constructor(_bits, endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("Int40Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int40Codec.MIN_VALUE || value > _Int40Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int40Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint40(value + _Int40Codec.OFFSET, this._littleEndian); } _decode(reader) { const value = reader.readUint40(this._littleEndian) - _Int40Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int40Codec.BYTE_LENGTH = 5; _Int40Codec.OFFSET = 549755813888; _Int40Codec.MIN_VALUE = -549755813888; _Int40Codec.MAX_VALUE = 549755813887; var Int40Codec = _Int40Codec; var _Int48Codec = class _Int48Codec extends AbstractCodec { constructor(_bits, endianness = "BE", options) { super(); this.options = options; this._littleEndian = endianness === "LE"; const validators = buildNumberValidators("Int48Codec", options); this._validateEncode = validators.validateEncode; this._validateDecode = validators.validateDecode; } isValid(value) { if (typeof value !== "number" || !Number.isInteger(value) || value < _Int48Codec.MIN_VALUE || value > _Int48Codec.MAX_VALUE) return false; if (this.options?.minimum !== void 0 && value < this.options.minimum) return false; if (this.options?.maximum !== void 0 && value > this.options.maximum) return false; return true; } byteLength() { return _Int48Codec.BYTE_LENGTH; } _encode(value, writer) { if (this._validateEncode !== null) this._validateEncode(value); writer.writeUint48(value + _Int48Codec.OFFSET, this._littleEndian); } _decode(reader) { const value = reader.readUint48(this._littleEndian) - _Int48Codec.OFFSET; if (this._validateDecode !== null) this._validateDecode(value, reader.position); return value; } }; _Int48Codec.BYTE_LENGTH = 6; _Int48Codec.OFFSET = 140737488355328; _Int48Codec.MIN_VALUE = -140737488355328; _Int48Codec.MAX_VALUE = 140737488355327; var Int48Codec = _Int48Codec; // src/utilities/hex.ts var BYTE_TO_HEX = []; for (let byte = 0; byte < 256; byte++) { BYTE_TO_HEX.push(byte.toString(16).padStart(2, "0")); } var CHAR_TO_NIBBLE = new Int8Array(128).fill(-1); for (let digit = 0; digit < 10; digit++) { CHAR_TO_NIBBLE[48 + digit] = digit; } for (let letter = 0; letter < 6; letter++) { CHAR_TO_NIBBLE[97 + letter] = 10 + letter; CHAR_TO_NIBBLE[65 + letter] = 10 + letter; } function encodeHex(bytes) { let result = ""; for (let index = 0; index < bytes.length; index++) { result += BYTE_TO_HEX[bytes[index]]; } return result; } function decodeHex(value) { if (value.length % 2 !== 0) throw new Error(`hex string expected, got unpadded hex of length ${value.length}`); const bytes = new Uint8Array(value.length / 2); for (let index = 0; index < bytes.length; index++) { const high = value.charCodeAt(index * 2); const low = value.charCodeAt(index * 2 + 1); const highNibble = high < 128 ? CHAR_TO_NIBBLE[high] : -1; const lowNibble = low < 128 ? CHAR_TO_NIBBLE[low] : -1; if (highNibble === -1 || lowNibble === -1) throw new Error(`hex string expected, got non-hex character at index ${highNibble === -1 ? index * 2 : index * 2 + 1}`); bytes[index] = highNibble << 4 | lowNibble; } return bytes; } function hexByteLength(value) { return value.length / 2; } // src/utilities/utf8.ts var textEncoder = new TextEncoder(); var textDecoder = new TextDecoder(); var SHORT_STRING_THRESHOLD = 48; var DECODE_THRESHOLD = 48; function utf8ByteLength(value) { const length = value.length; let byteLength = 0; for (let index = 0; index < length; index++) { const code = value.charCodeAt(index); if (code < 128) { byteLength += 1; } else if (code < 2048) { byteLength += 2; } else if (code >= 55296 && code <= 56319) { const next = value.charCodeAt(index + 1); if (next >= 56320 && next <= 57343) { byteLength += 4; index++; } else { byteLength += 3; } } else if (code >= 56320 && code <= 57343) { byteLength += 3; } else { byteLength += 3; } } return byteLength; } function encodeUtf8Into(value, buffer, offset) { const length = value.length; if (length > SHORT_STRING_THRESHOLD) { return textEncoder.encodeInto(value, buffer.subarray(offset)).written; } let position = offset; for (let index = 0; index < length; index++) { let code = value.charCodeAt(index); if (code < 128) { buffer[position++] = code; } else if (code < 2048) { buffer[position++] = 192 | code >> 6; buffer[position++] = 128 | code & 63; } else if (code >= 55296 && code <= 56319) { const next = value.charCodeAt(index + 1); if (next >= 56320 && next <= 57343) { code = 65536 + (code - 55296 << 10) + (next - 56320); index++; buffer[position++] = 240 | code >> 18; buffer[position++] = 128 | code >> 12 & 63; buffer[position++] = 128 | code >> 6 & 63; buffer[position++] = 128 | code & 63; } else { buffer[position++] = 239; buffer[position++] = 191; buffer[position++] = 189; } } else if (code >= 56320 && code <= 57343) { buffer[position++] = 239; buffer[position++] = 191; buffer[position++] = 189; } else { buffer[position++] = 224 | code >> 12; buffer[position++] = 128 | code >> 6 & 63; buffer[position++] = 128 | code & 63; } } return position - offset; } function decodeUtf8(buffer, start, end) { if (end - start > DECODE_THRESHOLD) { return textDecoder.decode(buffer.subarray(start, end)); } for (let index = start; index < end; index++) { if (buffer[index] >= 128) { return textDecoder.decode(buffer.subarray(start, end)); } } let result = ""; for (let index = start; index < end; index++) { result += String.fromCharCode(buffer[index]); } return result; } // src/Codecs/String/Fixed.ts var textEncoder2 = new TextEncoder(); var StringFixedCodec = class extends AbstractCodec { constructor(byteLength, encoding = "utf8") { super(); this.encoding = encoding; this._byteLength = byteLength; this._bufferCodec = new BytesFixedCodec(byteLength); if (encoding === "utf8") { this._encoder = (value, writer) => { if (value.length <= SHORT_STRING_THRESHOLD) { const byteLength2 = utf8ByteLength(value); if (byteLength2 <= this._byteLength) { const offset = writer.reserve(byteLength2); encodeUtf8Into(value, writer.bytes, offset); return; } } writer.writeBytes(textEncoder2.encode(value).subarray(0, this._byteLength)); }; this._decoder = (reader) => { const start = reader.skipBytes(this._byteLength); return decodeUtf8(reader.bytes, start, start + this._byteLength); }; return; } let encoder; let decoder; switch (encoding) { case "hex": { encoder = decodeHex; decoder = encodeHex; break; } case "base32": { encoder = base32.decode; decoder = base32.encode; break; } case "base58": { encoder = base58.decode; decoder = base58.encode; break; } case "base64": { encoder = base64.decode; decoder = base64.encode; break; } case "base64url": { en