You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
249 lines
6.7 KiB
249 lines
6.7 KiB
import { BaseError } from '../../errors/base.js'
|
|
import type { ErrorType } from '../../errors/utils.js'
|
|
import type { ByteArray, Hex } from '../../types/misc.js'
|
|
import { type IsHexErrorType, isHex } from '../data/isHex.js'
|
|
import { type PadErrorType, pad } from '../data/pad.js'
|
|
|
|
import { type AssertSizeErrorType, assertSize } from './fromHex.js'
|
|
import {
|
|
type NumberToHexErrorType,
|
|
type NumberToHexOpts,
|
|
numberToHex,
|
|
} from './toHex.js'
|
|
|
|
const encoder = /*#__PURE__*/ new TextEncoder()
|
|
|
|
export type ToBytesParameters = {
|
|
/** Size of the output bytes. */
|
|
size?: number | undefined
|
|
}
|
|
|
|
export type ToBytesErrorType =
|
|
| NumberToBytesErrorType
|
|
| BoolToBytesErrorType
|
|
| HexToBytesErrorType
|
|
| StringToBytesErrorType
|
|
| IsHexErrorType
|
|
| ErrorType
|
|
|
|
/**
|
|
* Encodes a UTF-8 string, hex value, bigint, number or boolean to a byte array.
|
|
*
|
|
* - Docs: https://viem.sh/docs/utilities/toBytes
|
|
* - Example: https://viem.sh/docs/utilities/toBytes#usage
|
|
*
|
|
* @param value Value to encode.
|
|
* @param opts Options.
|
|
* @returns Byte array value.
|
|
*
|
|
* @example
|
|
* import { toBytes } from 'viem'
|
|
* const data = toBytes('Hello world')
|
|
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
|
*
|
|
* @example
|
|
* import { toBytes } from 'viem'
|
|
* const data = toBytes(420)
|
|
* // Uint8Array([1, 164])
|
|
*
|
|
* @example
|
|
* import { toBytes } from 'viem'
|
|
* const data = toBytes(420, { size: 4 })
|
|
* // Uint8Array([0, 0, 1, 164])
|
|
*/
|
|
export function toBytes(
|
|
value: string | bigint | number | boolean | Hex,
|
|
opts: ToBytesParameters = {},
|
|
): ByteArray {
|
|
if (typeof value === 'number' || typeof value === 'bigint')
|
|
return numberToBytes(value, opts)
|
|
if (typeof value === 'boolean') return boolToBytes(value, opts)
|
|
if (isHex(value)) return hexToBytes(value, opts)
|
|
return stringToBytes(value, opts)
|
|
}
|
|
|
|
export type BoolToBytesOpts = {
|
|
/** Size of the output bytes. */
|
|
size?: number | undefined
|
|
}
|
|
|
|
export type BoolToBytesErrorType =
|
|
| AssertSizeErrorType
|
|
| PadErrorType
|
|
| ErrorType
|
|
|
|
/**
|
|
* Encodes a boolean into a byte array.
|
|
*
|
|
* - Docs: https://viem.sh/docs/utilities/toBytes#booltobytes
|
|
*
|
|
* @param value Boolean value to encode.
|
|
* @param opts Options.
|
|
* @returns Byte array value.
|
|
*
|
|
* @example
|
|
* import { boolToBytes } from 'viem'
|
|
* const data = boolToBytes(true)
|
|
* // Uint8Array([1])
|
|
*
|
|
* @example
|
|
* import { boolToBytes } from 'viem'
|
|
* const data = boolToBytes(true, { size: 32 })
|
|
* // Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
|
|
*/
|
|
export function boolToBytes(value: boolean, opts: BoolToBytesOpts = {}) {
|
|
const bytes = new Uint8Array(1)
|
|
bytes[0] = Number(value)
|
|
if (typeof opts.size === 'number') {
|
|
assertSize(bytes, { size: opts.size })
|
|
return pad(bytes, { size: opts.size })
|
|
}
|
|
return bytes
|
|
}
|
|
|
|
// We use very optimized technique to convert hex string to byte array
|
|
const charCodeMap = {
|
|
zero: 48,
|
|
nine: 57,
|
|
A: 65,
|
|
F: 70,
|
|
a: 97,
|
|
f: 102,
|
|
} as const
|
|
|
|
function charCodeToBase16(char: number) {
|
|
if (char >= charCodeMap.zero && char <= charCodeMap.nine)
|
|
return char - charCodeMap.zero
|
|
if (char >= charCodeMap.A && char <= charCodeMap.F)
|
|
return char - (charCodeMap.A - 10)
|
|
if (char >= charCodeMap.a && char <= charCodeMap.f)
|
|
return char - (charCodeMap.a - 10)
|
|
return undefined
|
|
}
|
|
|
|
export type HexToBytesOpts = {
|
|
/** Size of the output bytes. */
|
|
size?: number | undefined
|
|
}
|
|
|
|
export type HexToBytesErrorType = AssertSizeErrorType | PadErrorType | ErrorType
|
|
|
|
/**
|
|
* Encodes a hex string into a byte array.
|
|
*
|
|
* - Docs: https://viem.sh/docs/utilities/toBytes#hextobytes
|
|
*
|
|
* @param hex Hex string to encode.
|
|
* @param opts Options.
|
|
* @returns Byte array value.
|
|
*
|
|
* @example
|
|
* import { hexToBytes } from 'viem'
|
|
* const data = hexToBytes('0x48656c6c6f20776f726c6421')
|
|
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
|
|
*
|
|
* @example
|
|
* import { hexToBytes } from 'viem'
|
|
* const data = hexToBytes('0x48656c6c6f20776f726c6421', { size: 32 })
|
|
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
*/
|
|
export function hexToBytes(hex_: Hex, opts: HexToBytesOpts = {}): ByteArray {
|
|
let hex = hex_
|
|
if (opts.size) {
|
|
assertSize(hex, { size: opts.size })
|
|
hex = pad(hex, { dir: 'right', size: opts.size })
|
|
}
|
|
|
|
let hexString = hex.slice(2) as string
|
|
if (hexString.length % 2) hexString = `0${hexString}`
|
|
|
|
const length = hexString.length / 2
|
|
const bytes = new Uint8Array(length)
|
|
for (let index = 0, j = 0; index < length; index++) {
|
|
const nibbleLeft = charCodeToBase16(hexString.charCodeAt(j++))
|
|
const nibbleRight = charCodeToBase16(hexString.charCodeAt(j++))
|
|
if (nibbleLeft === undefined || nibbleRight === undefined) {
|
|
throw new BaseError(
|
|
`Invalid byte sequence ("${hexString[j - 2]}${
|
|
hexString[j - 1]
|
|
}" in "${hexString}").`,
|
|
)
|
|
}
|
|
bytes[index] = nibbleLeft * 16 + nibbleRight
|
|
}
|
|
return bytes
|
|
}
|
|
|
|
export type NumberToBytesErrorType =
|
|
| NumberToHexErrorType
|
|
| HexToBytesErrorType
|
|
| ErrorType
|
|
|
|
/**
|
|
* Encodes a number into a byte array.
|
|
*
|
|
* - Docs: https://viem.sh/docs/utilities/toBytes#numbertobytes
|
|
*
|
|
* @param value Number to encode.
|
|
* @param opts Options.
|
|
* @returns Byte array value.
|
|
*
|
|
* @example
|
|
* import { numberToBytes } from 'viem'
|
|
* const data = numberToBytes(420)
|
|
* // Uint8Array([1, 164])
|
|
*
|
|
* @example
|
|
* import { numberToBytes } from 'viem'
|
|
* const data = numberToBytes(420, { size: 4 })
|
|
* // Uint8Array([0, 0, 1, 164])
|
|
*/
|
|
export function numberToBytes(
|
|
value: bigint | number,
|
|
opts?: NumberToHexOpts | undefined,
|
|
) {
|
|
const hex = numberToHex(value, opts)
|
|
return hexToBytes(hex)
|
|
}
|
|
|
|
export type StringToBytesOpts = {
|
|
/** Size of the output bytes. */
|
|
size?: number | undefined
|
|
}
|
|
|
|
export type StringToBytesErrorType =
|
|
| AssertSizeErrorType
|
|
| PadErrorType
|
|
| ErrorType
|
|
|
|
/**
|
|
* Encodes a UTF-8 string into a byte array.
|
|
*
|
|
* - Docs: https://viem.sh/docs/utilities/toBytes#stringtobytes
|
|
*
|
|
* @param value String to encode.
|
|
* @param opts Options.
|
|
* @returns Byte array value.
|
|
*
|
|
* @example
|
|
* import { stringToBytes } from 'viem'
|
|
* const data = stringToBytes('Hello world!')
|
|
* // Uint8Array([72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33])
|
|
*
|
|
* @example
|
|
* import { stringToBytes } from 'viem'
|
|
* const data = stringToBytes('Hello world!', { size: 32 })
|
|
* // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
*/
|
|
export function stringToBytes(
|
|
value: string,
|
|
opts: StringToBytesOpts = {},
|
|
): ByteArray {
|
|
const bytes = encoder.encode(value)
|
|
if (typeof opts.size === 'number') {
|
|
assertSize(bytes, { size: opts.size })
|
|
return pad(bytes, { dir: 'right', size: opts.size })
|
|
}
|
|
return bytes
|
|
}
|