mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-22 13:15:49 +00:00
94 lines
3.6 KiB
TypeScript
94 lines
3.6 KiB
TypeScript
import { randomBytes as cryptoRandomBytes } from 'crypto'
|
|
import type { BytesToHexFn, HexToBytesFn, RandomBytesFn } from './types'
|
|
import { HexToStringFn, StringToHexFn } from './types'
|
|
import { HEX_REGEX } from './shared'
|
|
|
|
const OriginalBuffer = Symbol('OriginalBuffer')
|
|
|
|
/**
|
|
* An extended Uint8Array that incorporates a reference to the original Node.js Buffer.
|
|
*
|
|
* When converting a Node.js Buffer to a Uint8Array, there's an optimization that shares
|
|
* the memory of the original Buffer with the resulting Uint8Array instead of copying data.
|
|
* The Uint8ArrayWithReference interface is used to attach a reference to the original Buffer, ensuring
|
|
* its persistence in memory (preventing garbage collection) as long as the Uint8Array exists.
|
|
* This strategy upholds the ownership semantics of the slice of the ArrayBuffer.
|
|
*/
|
|
interface Uint8ArrayWithReference extends Uint8Array {
|
|
[OriginalBuffer]: Buffer
|
|
}
|
|
|
|
/**
|
|
* Converts a Node.js Buffer to a Uint8Array for uniform behavior with browser implementations.
|
|
*
|
|
* Choices:
|
|
* 1. Directly returning the Buffer:
|
|
* - Operation: Return Buffer as is (a Buffer *IS* an instanceof Uint8Array).
|
|
* - Pros: Most memory and performance efficient.
|
|
* - Cons: Violates strict Uint8Array typing and may lead to issues where Buffer-specific features are [ab]used.
|
|
*
|
|
* 2. Using `new Uint8Array(buffer)` or `Uint8Array.from(buffer)`:
|
|
* - Operation: Copies the buffer's data into a new Uint8Array.
|
|
* - Pros: Ensures data isolation; memory-safe.
|
|
* - Cons: Less performant due to data duplication.
|
|
*
|
|
* 3. Using buf.buffer slice:
|
|
* - Operation: Shares memory between Buffer and Uint8Array.
|
|
* - Pros: Performant.
|
|
* - Cons: Risks with shared memory and potential for invalid references.
|
|
*
|
|
* 4. Using buf.buffer slice and keeping a Buffer reference for ownership semantics:
|
|
* - Operation: Shares memory and associates the original Buffer with the resulting Uint8Array.
|
|
* - Pros: Performant while ensuring the original Buffer isn't garbage collected.
|
|
* - Cons: Risks with shared memory but mitigates potential for invalid references.
|
|
*
|
|
* The chosen method (4) prioritizes performance by sharing memory while ensuring buffer ownership.
|
|
*
|
|
* @param {Buffer} buffer - The Node.js Buffer to convert.
|
|
* @returns {Uint8Array} Resulting Uint8Array sharing the same memory as the Buffer and maintaining a reference to it.
|
|
*/
|
|
function toUint8Array(buffer: Buffer): Uint8Array {
|
|
const u8Array = new Uint8Array(
|
|
buffer.buffer.slice(
|
|
buffer.byteOffset,
|
|
buffer.byteOffset + buffer.byteLength,
|
|
),
|
|
) as Uint8ArrayWithReference
|
|
u8Array[OriginalBuffer] = buffer
|
|
return u8Array
|
|
}
|
|
|
|
/* eslint-disable func-style -- Typed to ensure uniformity between node and browser implementations and docs */
|
|
export const bytesToHex: typeof BytesToHexFn = (bytes) => {
|
|
const buf = Buffer.from(bytes)
|
|
return buf.toString('hex').toUpperCase()
|
|
}
|
|
|
|
export const hexToBytes: typeof HexToBytesFn = (hex) => {
|
|
if (!HEX_REGEX.test(hex)) {
|
|
throw new Error('Invalid hex string')
|
|
}
|
|
return toUint8Array(Buffer.from(hex, 'hex'))
|
|
}
|
|
|
|
export const randomBytes: typeof RandomBytesFn = (size) => {
|
|
return toUint8Array(cryptoRandomBytes(size))
|
|
}
|
|
|
|
export const hexToString: typeof HexToStringFn = (
|
|
hex: string,
|
|
encoding = 'utf8',
|
|
): string => {
|
|
if (!HEX_REGEX.test(hex)) {
|
|
throw new Error('Invalid hex string')
|
|
}
|
|
return new TextDecoder(encoding).decode(hexToBytes(hex))
|
|
}
|
|
|
|
export const stringToHex: typeof StringToHexFn = (string: string): string => {
|
|
return bytesToHex(new TextEncoder().encode(string))
|
|
}
|
|
/* eslint-enable func-style */
|
|
|
|
export * from './shared'
|