]> git.codecow.com Git - libnemo.git/commitdiff
Extract conversion functions into dedicated directory.
authorChris Duncan <chris@codecow.com>
Mon, 29 Jun 2026 21:59:31 +0000 (14:59 -0700)
committerChris Duncan <chris@codecow.com>
Mon, 29 Jun 2026 21:59:31 +0000 (14:59 -0700)
src/lib/convert.ts [deleted file]
src/lib/convert/base32.ts [new file with mode: 0644]
src/lib/convert/bin.ts [new file with mode: 0644]
src/lib/convert/bytes.ts [new file with mode: 0644]
src/lib/convert/dec.ts [new file with mode: 0644]
src/lib/convert/hex.ts [new file with mode: 0644]
src/lib/convert/index.ts [new file with mode: 0644]
src/lib/convert/obj.ts [new file with mode: 0644]
src/lib/convert/utf8.ts [new file with mode: 0644]

diff --git a/src/lib/convert.ts b/src/lib/convert.ts
deleted file mode 100644 (file)
index 801f96e..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>\r
-//! SPDX-License-Identifier: GPL-3.0-or-later\r
-\r
-import { ALPHABET } from './constants'\r
-\r
-export class base32 {\r
-       /**\r
-       * Convert a base32 string to a Uint8Array of bytes.\r
-       *\r
-       * @param {string} base32 - String to convert\r
-       * @returns {Uint8Array} Byte array representation of the input string\r
-       */\r
-       static toBytes (base32: string): Uint8Array<ArrayBuffer> {\r
-               const leftover = (base32.length * 5) % 8\r
-               const offset = leftover === 0\r
-                       ? 0\r
-                       : 8 - leftover\r
-               let bits = 0\r
-               let value = 0\r
-               let letter = -1\r
-               let index = 0\r
-               let output = new Uint8Array(Math.ceil((base32.length * 5) / 8))\r
-               for (let i = 0; i < base32.length; i++) {\r
-                       letter = ALPHABET.indexOf(base32[i])\r
-                       if (letter < 0) {\r
-                               throw new RangeError('Invalid base32 character', { cause: base32[i] })\r
-                       }\r
-                       value = (value << 5) | letter\r
-                       bits += 5\r
-                       if (bits >= 8) {\r
-                               output[index++] = (value >>> (bits + offset - 8)) & 255\r
-                               bits -= 8\r
-                       }\r
-               }\r
-               if (bits > 0) {\r
-                       output[index++] = (value << (bits + offset - 8)) & 255\r
-               }\r
-               if (leftover !== 0) {\r
-                       output = output.slice(1)\r
-               }\r
-               return output\r
-       }\r
-\r
-       /**\r
-       * Convert a base32 string to an ArrayBuffer.\r
-       *\r
-       * @param {string} base32 - String to convert\r
-       * @returns {Uint8Array} Byte array representation of the input string\r
-       */\r
-       static toBuffer (base32: string): ArrayBuffer {\r
-               return this.toBytes(base32).buffer\r
-       }\r
-}\r
-\r
-export class bin {\r
-       /**\r
-       * Convert a binary string to a Uint8Array of bytes.\r
-       *\r
-       * @param {string} bin - String to convert\r
-       * @returns {Uint8Array} Byte array representation of the input string\r
-       */\r
-       static toBytes (bin: string): Uint8Array<ArrayBuffer> {\r
-               const bytes: number[] = []\r
-               while (bin.length > 0) {\r
-                       const bits = bin.substring(0, 8)\r
-                       bytes.push(parseInt(bits, 2))\r
-                       bin = bin.substring(8)\r
-               }\r
-               return new Uint8Array(bytes)\r
-       }\r
-}\r
-\r
-export class bytes {\r
-       static decoder: TextDecoder = new TextDecoder()\r
-\r
-       /**\r
-       * Write zeroes to memory to erase bytes and then transfers the buffer to\r
-       * render it inaccessible to any process.\r
-       *\r
-       * @param bytes - Buffer or bytes to erase\r
-       */\r
-       static erase (bytes?: ArrayBuffer | Uint8Array<ArrayBuffer> | null): void {\r
-               if (bytes == null) return\r
-               if (bytes instanceof ArrayBuffer && bytes.byteLength === 0) return\r
-               if (bytes instanceof Uint8Array && bytes.buffer.byteLength === 0) return\r
-               bytes = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes\r
-               bytes.fill(0)\r
-       }\r
-\r
-       /**\r
-       * Convert a Uint8Aarray of bytes to a base32 string.\r
-       *\r
-       * @param {Uint8Array} bytes - Byte array to convert\r
-       * @returns {string} Base32 string representation of the input bytes\r
-       */\r
-       static toBase32 (bytes: ArrayBuffer | Uint8Array): string {\r
-               if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes)\r
-               const leftover = (bytes.length * 8) % 5\r
-               const offset = leftover === 0\r
-                       ? 0\r
-                       : 5 - leftover\r
-               let value = 0\r
-               let output = ''\r
-               let bits = 0\r
-               for (let i = 0; i < bytes.length; i++) {\r
-                       value = (value << 8) | bytes[i]\r
-                       bits += 8\r
-                       while (bits >= 5) {\r
-                               output += ALPHABET[(value >>> (bits + offset - 5)) & 31]\r
-                               bits -= 5\r
-                       }\r
-               }\r
-               if (bits > 0) {\r
-                       output += ALPHABET[(value << (5 - (bits + offset))) & 31]\r
-               }\r
-               return output\r
-       }\r
-\r
-       /**\r
-       * Convert a Uint8Array of bytes to a binary string.\r
-       *\r
-       * @param {Uint8Array} bytes - Byte array to convert\r
-       * @returns {string} Binary string representation of the input value\r
-       */\r
-       static toBin (bytes: Uint8Array<ArrayBuffer>): string {\r
-               return [...bytes].map(b => b.toString(2).padStart(8, '0')).join('')\r
-       }\r
-\r
-       /**\r
-       * Sum an array of bytes to a decimal integer. If the result is larger than\r
-       * Number.MAX_SAFE_INTEGER, it will be returned as a bigint.\r
-       *\r
-       * @param {Uint8Array} bytes - Byte array to convert\r
-       * @returns {bigint|number} Decimal sum of the literal byte values\r
-       */\r
-       static toDec (bytes: Uint8Array): bigint | number {\r
-               let decimal = 0n\r
-               for (let i = bytes.byteLength; i > 0; i--) {\r
-                       decimal += BigInt(bytes[i - 1]) << (BigInt(bytes.byteLength - i) * 8n)\r
-               }\r
-               if (decimal > 9007199254740991n) {\r
-                       return decimal\r
-               } else {\r
-                       return Number(decimal)\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Convert a Uint8Array of bytes to a hexadecimal string.\r
-       *\r
-       * @param {Uint8Array} bytes - Byte array to convert\r
-       * @returns {string} Hexadecimal string representation of the input bytes\r
-       */\r
-       static toHex (bytes: ArrayBuffer | Uint8Array): string {\r
-               if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes)\r
-               if (bytes.buffer instanceof ArrayBuffer && bytes.buffer.byteLength === 0) return ''\r
-               return [...bytes]\r
-                       .map(byte => byte.toString(16).padStart(2, '0'))\r
-                       .join('')\r
-                       .toUpperCase()\r
-       }\r
-\r
-       /**\r
-       * Convert a Uint8Array of bytes to a UTF-8 text string.\r
-       *\r
-       * @param {Uint8Array} bytes - Byte array to convert\r
-       * @returns {string} UTF-8 encoded text string\r
-       */\r
-       static toUtf8 (bytes: Uint8Array<ArrayBuffer>): string {\r
-               return this.decoder.decode(bytes)\r
-       }\r
-}\r
-\r
-export class dec {\r
-       /**\r
-       * Convert a decimal integer to a binary string.\r
-       *\r
-       * @param {bigint|number|string} decimal - Integer to convert\r
-       * @param {number} [padding=0] - Minimum length of the resulting string padded as necessary with starting zeroes\r
-       * @returns {string} Binary string representation of the input decimal\r
-       */\r
-       static toBin (decimal: bigint | number | string, padding: number = 0): string {\r
-               if (typeof padding !== 'number') {\r
-                       throw new TypeError('Invalid padding')\r
-               }\r
-               try {\r
-                       return BigInt(decimal)\r
-                               .toString(2)\r
-                               .padStart(padding, '0')\r
-               } catch (err) {\r
-                       throw new RangeError('Invalid decimal integer')\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Convert a decimal integer to a Uint8Array of bytes. Fractional part is truncated.\r
-       *\r
-       * @param {bigint|number|string} decimal - Integer to convert\r
-       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes\r
-       * @returns {Uint8Array} Byte array representation of the input decimal\r
-       */\r
-       static toBytes (decimal: bigint | number | string, padding: number = 0): Uint8Array<ArrayBuffer> {\r
-               if (decimal == null) {\r
-                       throw new TypeError(`Failed to convert '${decimal}' from decimal to bytes`)\r
-               }\r
-               if (typeof padding !== 'number') {\r
-                       throw new TypeError('Invalid padding')\r
-               }\r
-               let integer = BigInt(decimal)\r
-               const bytes: number[] = (integer === 0n) ? [0] : []\r
-               while (integer > 0) {\r
-                       const lsb = BigInt.asUintN(8, integer)\r
-                       bytes.push(Number(lsb))\r
-                       integer >>= 8n\r
-               }\r
-               const result = new Uint8Array(Math.max(padding, bytes.length))\r
-               result.set(bytes)\r
-               return (result.reverse())\r
-       }\r
-\r
-       /**\r
-       * Convert a decimal integer to a hexadecimal string.\r
-       *\r
-       * @param {(bigint|number|string)} decimal - Integer to convert\r
-       * @param {number} [padding=0] - Minimum length of the resulting string padded as necessary with starting zeroes\r
-       * @returns {string} Hexadecimal string representation of the input decimal\r
-       */\r
-       static toHex (decimal: bigint | number | string, padding: number = 0): string {\r
-               if (decimal == null) {\r
-                       throw new TypeError(`Failed to convert '${decimal}' from decimal to hex`)\r
-               }\r
-               if (typeof padding !== 'number') {\r
-                       throw new TypeError('Invalid padding')\r
-               }\r
-               try {\r
-                       return BigInt(decimal)\r
-                               .toString(16)\r
-                               .padStart(padding, '0')\r
-                               .toUpperCase()\r
-               } catch (err) {\r
-                       throw new RangeError('Invalid decimal integer')\r
-               }\r
-       }\r
-}\r
-\r
-export class hex {\r
-       /**\r
-       * Convert a hexadecimal string to an array of decimal byte values.\r
-       *\r
-       * @param {string} hex - Hexadecimal number string to convert\r
-       * @param {number}[padding=0] - Minimum length of the resulting array padded as necessary with starting 0 values\r
-       * @returns {number[]} Decimal array representation of the input value\r
-       */\r
-       static toArray (hex: string, padding: number = 0): number[] {\r
-               if (typeof hex !== 'string' || !/^[A-Fa-f0-9]+$/i.test(hex)) {\r
-                       throw new TypeError('Invalid string when converting hex to array', { cause: hex })\r
-               }\r
-               if (typeof padding !== 'number') {\r
-                       throw new TypeError('Invalid padding when converting hex to array', { cause: padding })\r
-               }\r
-               if ((hex.length & 1) !== 0) hex = `0${hex}`\r
-               const hexArray = hex.match(/.{2}/g)\r
-               if (hexArray == null) {\r
-                       throw new RangeError('Invalid hex string when converting to array', { cause: hexArray })\r
-               }\r
-               const diff = padding - hexArray.length\r
-               const pad = new Array(diff > 0 ? diff : 0).fill(0)\r
-               return pad.concat(hexArray.map(v => parseInt(v, 16)))\r
-       }\r
-\r
-       /**\r
-       * Convert a hexadecimal string to a binary string.\r
-       *\r
-       * @param {string} hex - Hexadecimal number string to convert\r
-       * @returns {string} Binary string representation of the input value\r
-       */\r
-       static toBin (hex: string): string {\r
-               return [...hex].map(c => dec.toBin(parseInt(c, 16), 4)).join('')\r
-       }\r
-\r
-       /**\r
-       * Convert a hexadecimal string to an ArrayBuffer.\r
-       *\r
-       * @param {string} hex - Hexadecimal number string to convert\r
-       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes\r
-       * @returns {ArrayBuffer} Buffer representation of the input value\r
-       */\r
-       static toBuffer (hex: string, padding: number = 0): ArrayBuffer {\r
-               return this.toBytes(hex, padding).buffer\r
-       }\r
-\r
-       /**\r
-       * Convert a hexadecimal string to a Uint8Array of bytes.\r
-       *\r
-       * @param {string} hex - Hexadecimal number string to convert\r
-       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes\r
-       * @returns {Uint8Array} Byte array representation of the input value\r
-       */\r
-       static toBytes (hex: string, padding: number = 0): Uint8Array<ArrayBuffer> {\r
-               return new Uint8Array(this.toArray(hex, padding))\r
-       }\r
-}\r
-\r
-export class obj {\r
-       /**\r
-       * Convert a numerically-indexed object of 8-bit values to a Uint8Array of bytes.\r
-       *\r
-       * @param {object} obj - Object to convert\r
-       * @returns {Uint8Array} Byte array representation of the input object\r
-       */\r
-       static toBytes (obj: { [key: number]: number }): Uint8Array<ArrayBuffer> {\r
-               const values = Object.keys(obj)\r
-                       .map(key => +key)\r
-                       .sort((a, b) => a - b)\r
-                       .map(i => obj[i])\r
-               return new Uint8Array(values)\r
-       }\r
-}\r
-\r
-export class utf8 {\r
-       static encoder: TextEncoder = new TextEncoder()\r
-\r
-       /**\r
-       * Convert a UTF-8 text string to an ArrayBuffer.\r
-       *\r
-       * @param {string} utf8 - String to convert\r
-       * @returns {ArrayBuffer} Buffer representation of the input string\r
-       */\r
-       static toBuffer (utf8: string): ArrayBuffer {\r
-               return this.toBytes(utf8).buffer\r
-       }\r
-\r
-       /**\r
-       * Convert a UTF-8 text string to a Uint8Array of bytes.\r
-       *\r
-       * @param {string} utf8 - String to convert\r
-       * @returns {Uint8Array} Byte array representation of the input string\r
-       */\r
-       static toBytes (utf8: string): Uint8Array<ArrayBuffer> {\r
-               return this.encoder.encode(utf8) as Uint8Array<ArrayBuffer>\r
-       }\r
-\r
-       /**\r
-       * Convert a string to a hexadecimal representation\r
-       *\r
-       * @param {string} utf8 - String to convert\r
-       * @returns {string} Hexadecimal representation of the input string\r
-       */\r
-       static toHex (utf8: string): string {\r
-               return bytes.toHex(this.toBytes(utf8))\r
-       }\r
-}\r
-\r
-export default `\r
-       const base32 = ${base32}\r
-       const bin = ${bin}\r
-       const bytes = ${bytes}\r
-       const dec = ${dec}\r
-       const hex = ${hex}\r
-       const obj = ${obj}\r
-       const utf8 = ${utf8}\r
-`\r
diff --git a/src/lib/convert/base32.ts b/src/lib/convert/base32.ts
new file mode 100644 (file)
index 0000000..93cb99c
--- /dev/null
@@ -0,0 +1,53 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { ALPHABET } from '../constants'
+
+export class base32 {
+       /**
+       * Convert a base32 string to a Uint8Array of bytes.
+       *
+       * @param {string} base32 - String to convert
+       * @returns {Uint8Array} Byte array representation of the input string
+       */
+       static toBytes (base32: string): Uint8Array<ArrayBuffer> {
+               const leftover = (base32.length * 5) % 8
+               const offset = leftover === 0
+                       ? 0
+                       : 8 - leftover
+               let bits = 0
+               let value = 0
+               let letter = -1
+               let index = 0
+               let output = new Uint8Array(Math.ceil((base32.length * 5) / 8))
+               for (let i = 0; i < base32.length; i++) {
+                       letter = ALPHABET.indexOf(base32[i])
+                       if (letter < 0) {
+                               throw new RangeError('Invalid base32 character', { cause: base32[i] })
+                       }
+                       value = (value << 5) | letter
+                       bits += 5
+                       if (bits >= 8) {
+                               output[index++] = (value >>> (bits + offset - 8)) & 255
+                               bits -= 8
+                       }
+               }
+               if (bits > 0) {
+                       output[index++] = (value << (bits + offset - 8)) & 255
+               }
+               if (leftover !== 0) {
+                       output = output.slice(1)
+               }
+               return output
+       }
+
+       /**
+       * Convert a base32 string to an ArrayBuffer.
+       *
+       * @param {string} base32 - String to convert
+       * @returns {Uint8Array} Byte array representation of the input string
+       */
+       static toBuffer (base32: string): ArrayBuffer {
+               return this.toBytes(base32).buffer
+       }
+}
diff --git a/src/lib/convert/bin.ts b/src/lib/convert/bin.ts
new file mode 100644 (file)
index 0000000..548d7e3
--- /dev/null
@@ -0,0 +1,20 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+export class bin {
+       /**
+       * Convert a binary string to a Uint8Array of bytes.
+       *
+       * @param {string} bin - String to convert
+       * @returns {Uint8Array} Byte array representation of the input string
+       */
+       static toBytes (bin: string): Uint8Array<ArrayBuffer> {
+               const bytes: number[] = []
+               while (bin.length > 0) {
+                       const bits = bin.substring(0, 8)
+                       bytes.push(parseInt(bits, 2))
+                       bin = bin.substring(8)
+               }
+               return new Uint8Array(bytes)
+       }
+}
diff --git a/src/lib/convert/bytes.ts b/src/lib/convert/bytes.ts
new file mode 100644 (file)
index 0000000..fe63fc1
--- /dev/null
@@ -0,0 +1,105 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { ALPHABET } from '../constants'
+
+export class bytes {
+       static decoder: TextDecoder = new TextDecoder()
+
+       /**
+       * Write zeroes to memory to erase bytes and then transfers the buffer to
+       * render it inaccessible to any process.
+       *
+       * @param bytes - Buffer or bytes to erase
+       */
+       static erase (bytes?: ArrayBuffer | Uint8Array<ArrayBuffer> | null): void {
+               if (bytes == null) return
+               if (bytes instanceof ArrayBuffer && bytes.byteLength === 0) return
+               if (bytes instanceof Uint8Array && bytes.buffer.byteLength === 0) return
+               bytes = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes
+               bytes.fill(0)
+       }
+
+       /**
+       * Convert a Uint8Aarray of bytes to a base32 string.
+       *
+       * @param {Uint8Array} bytes - Byte array to convert
+       * @returns {string} Base32 string representation of the input bytes
+       */
+       static toBase32 (bytes: ArrayBuffer | Uint8Array): string {
+               if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes)
+               const leftover = (bytes.length * 8) % 5
+               const offset = leftover === 0
+                       ? 0
+                       : 5 - leftover
+               let value = 0
+               let output = ''
+               let bits = 0
+               for (let i = 0; i < bytes.length; i++) {
+                       value = (value << 8) | bytes[i]
+                       bits += 8
+                       while (bits >= 5) {
+                               output += ALPHABET[(value >>> (bits + offset - 5)) & 31]
+                               bits -= 5
+                       }
+               }
+               if (bits > 0) {
+                       output += ALPHABET[(value << (5 - (bits + offset))) & 31]
+               }
+               return output
+       }
+
+       /**
+       * Convert a Uint8Array of bytes to a binary string.
+       *
+       * @param {Uint8Array} bytes - Byte array to convert
+       * @returns {string} Binary string representation of the input value
+       */
+       static toBin (bytes: Uint8Array<ArrayBuffer>): string {
+               return [...bytes].map(b => b.toString(2).padStart(8, '0')).join('')
+       }
+
+       /**
+       * Sum an array of bytes to a decimal integer. If the result is larger than
+       * Number.MAX_SAFE_INTEGER, it will be returned as a bigint.
+       *
+       * @param {Uint8Array} bytes - Byte array to convert
+       * @returns {bigint|number} Decimal sum of the literal byte values
+       */
+       static toDec (bytes: Uint8Array): bigint | number {
+               let decimal = 0n
+               for (let i = bytes.byteLength; i > 0; i--) {
+                       decimal += BigInt(bytes[i - 1]) << (BigInt(bytes.byteLength - i) * 8n)
+               }
+               if (decimal > 9007199254740991n) {
+                       return decimal
+               } else {
+                       return Number(decimal)
+               }
+       }
+
+       /**
+       * Convert a Uint8Array of bytes to a hexadecimal string.
+       *
+       * @param {Uint8Array} bytes - Byte array to convert
+       * @returns {string} Hexadecimal string representation of the input bytes
+       */
+       static toHex (bytes: ArrayBuffer | Uint8Array): string {
+               if (bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes)
+               if (bytes.buffer instanceof ArrayBuffer && bytes.buffer.byteLength === 0) return ''
+               return [...bytes]
+                       .map(byte => byte.toString(16).padStart(2, '0'))
+                       .join('')
+                       .toUpperCase()
+       }
+
+       /**
+       * Convert a Uint8Array of bytes to a UTF-8 text string.
+       *
+       * @param {Uint8Array} bytes - Byte array to convert
+       * @returns {string} UTF-8 encoded text string
+       */
+       static toUtf8 (bytes: Uint8Array<ArrayBuffer>): string {
+               return this.decoder.decode(bytes)
+       }
+}
diff --git a/src/lib/convert/dec.ts b/src/lib/convert/dec.ts
new file mode 100644 (file)
index 0000000..15016cf
--- /dev/null
@@ -0,0 +1,74 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+export class dec {
+       /**
+       * Convert a decimal integer to a binary string.
+       *
+       * @param {bigint|number|string} decimal - Integer to convert
+       * @param {number} [padding=0] - Minimum length of the resulting string padded as necessary with starting zeroes
+       * @returns {string} Binary string representation of the input decimal
+       */
+       static toBin (decimal: bigint | number | string, padding: number = 0): string {
+               if (typeof padding !== 'number') {
+                       throw new TypeError('Invalid padding')
+               }
+               try {
+                       return BigInt(decimal)
+                               .toString(2)
+                               .padStart(padding, '0')
+               } catch (err) {
+                       throw new RangeError('Invalid decimal integer')
+               }
+       }
+
+       /**
+       * Convert a decimal integer to a Uint8Array of bytes. Fractional part is truncated.
+       *
+       * @param {bigint|number|string} decimal - Integer to convert
+       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes
+       * @returns {Uint8Array} Byte array representation of the input decimal
+       */
+       static toBytes (decimal: bigint | number | string, padding: number = 0): Uint8Array<ArrayBuffer> {
+               if (decimal == null) {
+                       throw new TypeError(`Failed to convert '${decimal}' from decimal to bytes`)
+               }
+               if (typeof padding !== 'number') {
+                       throw new TypeError('Invalid padding')
+               }
+               let integer = BigInt(decimal)
+               const bytes: number[] = (integer === 0n) ? [0] : []
+               while (integer > 0) {
+                       const lsb = BigInt.asUintN(8, integer)
+                       bytes.push(Number(lsb))
+                       integer >>= 8n
+               }
+               const result = new Uint8Array(Math.max(padding, bytes.length))
+               result.set(bytes)
+               return (result.reverse())
+       }
+
+       /**
+       * Convert a decimal integer to a hexadecimal string.
+       *
+       * @param {(bigint|number|string)} decimal - Integer to convert
+       * @param {number} [padding=0] - Minimum length of the resulting string padded as necessary with starting zeroes
+       * @returns {string} Hexadecimal string representation of the input decimal
+       */
+       static toHex (decimal: bigint | number | string, padding: number = 0): string {
+               if (decimal == null) {
+                       throw new TypeError(`Failed to convert '${decimal}' from decimal to hex`)
+               }
+               if (typeof padding !== 'number') {
+                       throw new TypeError('Invalid padding')
+               }
+               try {
+                       return BigInt(decimal)
+                               .toString(16)
+                               .padStart(padding, '0')
+                               .toUpperCase()
+               } catch (err) {
+                       throw new RangeError('Invalid decimal integer')
+               }
+       }
+}
diff --git a/src/lib/convert/hex.ts b/src/lib/convert/hex.ts
new file mode 100644 (file)
index 0000000..adb072d
--- /dev/null
@@ -0,0 +1,62 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { dec } from './dec'
+
+export class hex {
+       /**
+       * Convert a hexadecimal string to an array of decimal byte values.
+       *
+       * @param {string} hex - Hexadecimal number string to convert
+       * @param {number}[padding=0] - Minimum length of the resulting array padded as necessary with starting 0 values
+       * @returns {number[]} Decimal array representation of the input value
+       */
+       static toArray (hex: string, padding: number = 0): number[] {
+               if (typeof hex !== 'string' || !/^[A-Fa-f0-9]+$/i.test(hex)) {
+                       throw new TypeError('Invalid string when converting hex to array', { cause: hex })
+               }
+               if (typeof padding !== 'number') {
+                       throw new TypeError('Invalid padding when converting hex to array', { cause: padding })
+               }
+               if ((hex.length & 1) !== 0) hex = `0${hex}`
+               const hexArray = hex.match(/.{2}/g)
+               if (hexArray == null) {
+                       throw new RangeError('Invalid hex string when converting to array', { cause: hexArray })
+               }
+               const diff = padding - hexArray.length
+               const pad = new Array(diff > 0 ? diff : 0).fill(0)
+               return pad.concat(hexArray.map(v => parseInt(v, 16)))
+       }
+
+       /**
+       * Convert a hexadecimal string to a binary string.
+       *
+       * @param {string} hex - Hexadecimal number string to convert
+       * @returns {string} Binary string representation of the input value
+       */
+       static toBin (hex: string): string {
+               return [...hex].map(c => dec.toBin(parseInt(c, 16), 4)).join('')
+       }
+
+       /**
+       * Convert a hexadecimal string to an ArrayBuffer.
+       *
+       * @param {string} hex - Hexadecimal number string to convert
+       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes
+       * @returns {ArrayBuffer} Buffer representation of the input value
+       */
+       static toBuffer (hex: string, padding: number = 0): ArrayBuffer {
+               return this.toBytes(hex, padding).buffer
+       }
+
+       /**
+       * Convert a hexadecimal string to a Uint8Array of bytes.
+       *
+       * @param {string} hex - Hexadecimal number string to convert
+       * @param {number} [padding=0] - Minimum length of the resulting array padded as necessary with starting 0x00 bytes
+       * @returns {Uint8Array} Byte array representation of the input value
+       */
+       static toBytes (hex: string, padding: number = 0): Uint8Array<ArrayBuffer> {
+               return new Uint8Array(this.toArray(hex, padding))
+       }
+}
diff --git a/src/lib/convert/index.ts b/src/lib/convert/index.ts
new file mode 100644 (file)
index 0000000..32f761a
--- /dev/null
@@ -0,0 +1,21 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>\r
+//! SPDX-License-Identifier: GPL-3.0-or-later\r
+\r
+import { base32 } from './base32'\r
+import { bin } from './bin'\r
+import { bytes } from './bytes'\r
+import { dec } from './dec'\r
+import { hex } from './hex'\r
+import { obj } from './obj'\r
+import { utf8 } from './utf8'\r
+\r
+export { base32, bin, bytes, dec, hex, obj, utf8 }\r
+export default `\r
+       const base32 = ${base32}\r
+       const bin = ${bin}\r
+       const bytes = ${bytes}\r
+       const dec = ${dec}\r
+       const hex = ${hex}\r
+       const obj = ${obj}\r
+       const utf8 = ${utf8}\r
+`\r
diff --git a/src/lib/convert/obj.ts b/src/lib/convert/obj.ts
new file mode 100644 (file)
index 0000000..55cb994
--- /dev/null
@@ -0,0 +1,18 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+export class obj {
+       /**
+       * Convert a numerically-indexed object of 8-bit values to a Uint8Array of bytes.
+       *
+       * @param {object} obj - Object to convert
+       * @returns {Uint8Array} Byte array representation of the input object
+       */
+       static toBytes (obj: { [key: number]: number }): Uint8Array<ArrayBuffer> {
+               const values = Object.keys(obj)
+                       .map(key => +key)
+                       .sort((a, b) => a - b)
+                       .map(i => obj[i])
+               return new Uint8Array(values)
+       }
+}
diff --git a/src/lib/convert/utf8.ts b/src/lib/convert/utf8.ts
new file mode 100644 (file)
index 0000000..6c40c2b
--- /dev/null
@@ -0,0 +1,38 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { bytes } from './bytes'
+
+export class utf8 {
+       static encoder: TextEncoder = new TextEncoder()
+
+       /**
+       * Convert a UTF-8 text string to an ArrayBuffer.
+       *
+       * @param {string} utf8 - String to convert
+       * @returns {ArrayBuffer} Buffer representation of the input string
+       */
+       static toBuffer (utf8: string): ArrayBuffer {
+               return this.toBytes(utf8).buffer
+       }
+
+       /**
+       * Convert a UTF-8 text string to a Uint8Array of bytes.
+       *
+       * @param {string} utf8 - String to convert
+       * @returns {Uint8Array} Byte array representation of the input string
+       */
+       static toBytes (utf8: string): Uint8Array<ArrayBuffer> {
+               return this.encoder.encode(utf8) as Uint8Array<ArrayBuffer>
+       }
+
+       /**
+       * Convert a string to a hexadecimal representation
+       *
+       * @param {string} utf8 - String to convert
+       * @returns {string} Hexadecimal representation of the input string
+       */
+       static toHex (utf8: string): string {
+               return bytes.toHex(this.toBytes(utf8))
+       }
+}