UNPKG

ox

Version:

Ethereum Standard Library

112 lines 4.23 kB
import * as Errors from '../../Errors.js'; /** @internal */ const hexes = /*#__PURE__*/ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, '0')); // Char-code → 4-bit nibble lookup table. Sentinel `0xff` = invalid. const nibbleTable = /*#__PURE__*/ (() => { const table = new Uint8Array(256).fill(0xff); for (let i = 0; i < 10; i++) table[48 + i] = i; // '0'-'9' for (let i = 0; i < 6; i++) { table[65 + i] = 10 + i; // 'A'-'F' table[97 + i] = 10 + i; // 'a'-'f' } return table; })(); // Phase 2 native fast-path detection: `Uint8Array.prototype.toHex` lands in // Node 22+, Safari 18+, Firefox 133+. Falls back to `Buffer` on Node, then to // the JS loop. const _Buffer = globalThis.Buffer; const nativeToHex = Uint8Array.prototype.toHex; const nativeFromHex = Uint8Array.fromHex; /** * Encodes a `Uint8Array` into a `0x`-prefixed lowercase hex string. Uses the * native `Uint8Array.prototype.toHex` (Node 22+, Safari 18+, Firefox 133+) or * Node's `Buffer` when available; otherwise a tight JS loop. * * @internal */ export function bytesToHex(value) { if (nativeToHex) return `0x${nativeToHex.call(value)}`; if (_Buffer) return `0x${_Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString('hex')}`; const length = value.length; const parts = Array.from({ length }); for (let i = 0; i < length; i++) parts[i] = hexes[value[i]]; return `0x${parts.join('')}`; } /** * Strictly decodes a `0x`-prefixed even-length hex string into a `Uint8Array`. * Uses the native `Uint8Array.fromHex` (Node 22+, Safari 18+, Firefox 133+) or * Node's `Buffer` when available; otherwise a tight JS loop. * * @internal */ export function hexToBytes(value) { if (typeof value !== 'string' || value.length < 2 || value.charCodeAt(0) !== 48 /* '0' */ || value.charCodeAt(1) !== 120 /* 'x' */) throw new InvalidHexValueError(value); const body = value.length === 2 ? '' : value.slice(2); if ((body.length & 1) !== 0) throw new InvalidLengthError(value); if (nativeFromHex) { try { return nativeFromHex(body); } catch { throw new InvalidHexValueError(value); } } if (_Buffer && body.length > 0) { // Buffer.from with 'hex' silently truncates on invalid chars; verify // byteLength matches expectations to detect malformed input. const expected = body.length >> 1; const buf = _Buffer.from(body, 'hex'); if (buf.length !== expected) throw new InvalidHexValueError(value); // Copy out of Buffer pool: callers may rely on `.buffer` being a // standalone ArrayBuffer (e.g. WebAuthn attestationObject round-trips). const out = new Uint8Array(buf.byteLength); out.set(buf); return out; } const length = body.length >> 1; const out = new Uint8Array(length); for (let i = 0, j = 0; i < length; i++) { const hi = nibbleTable[body.charCodeAt(j++)]; const lo = nibbleTable[body.charCodeAt(j++)]; if (hi === 0xff || lo === 0xff) throw new InvalidHexValueError(value); out[i] = (hi << 4) | lo; } return out; } /** @internal */ export function charCodeToBase16(char) { const v = nibbleTable[char]; return v === 0xff ? undefined : v; } /** Thrown when a value is not a valid `0x`-prefixed hex string. */ export class InvalidHexValueError extends Errors.BaseError { name = 'Hex.InvalidHexValueError'; constructor(value) { super(`Value \`${value}\` is an invalid hex value.`, { metaMessages: [ 'Hex values must start with `"0x"` and contain only hexadecimal characters (0-9, a-f, A-F).', ], }); } } /** Thrown when a hex string has an odd nibble count. */ export class InvalidLengthError extends Errors.BaseError { name = 'Hex.InvalidLengthError'; constructor(value) { super(`Hex value \`"${value}"\` is an odd length (${value.length - 2} nibbles).`, { metaMessages: ['It must be an even length.'], }); } } //# sourceMappingURL=hex.js.map