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.

310 lines
8.7 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

import * as Bytes from './Bytes.js'
import type * as Errors from './Errors.js'
import * as Hex from './Hex.js'
const encoder = /*#__PURE__*/ new TextEncoder()
const decoder = /*#__PURE__*/ new TextDecoder()
const integerToCharacter = /*#__PURE__*/ Object.fromEntries(
Array.from(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
).map((a, i) => [i, a.charCodeAt(0)]),
)
const characterToInteger = /*#__PURE__*/ {
...Object.fromEntries(
Array.from(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
).map((a, i) => [a.charCodeAt(0), i]),
),
['='.charCodeAt(0)]: 0,
['-'.charCodeAt(0)]: 62,
['_'.charCodeAt(0)]: 63,
} as Record<number, number>
/**
* Encodes a {@link ox#Bytes.Bytes} to a Base64-encoded string (with optional padding and/or URL-safe characters).
*
* @example
* ```ts twoslash
* import { Base64, Bytes } from 'ox'
*
* const value = Base64.fromBytes(Bytes.fromString('hello world'))
* // @log: 'aGVsbG8gd29ybGQ='
* ```
*
* @example
* ### No Padding
*
* Turn off [padding of encoded data](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) with the `pad` option:
*
* ```ts twoslash
* import { Base64, Bytes } from 'ox'
*
* const value = Base64.fromBytes(Bytes.fromString('hello world'), { pad: false })
* // @log: 'aGVsbG8gd29ybGQ'
* ```
*
* ### URL-safe Encoding
*
* Turn on [URL-safe encoding](https://datatracker.ietf.org/doc/html/rfc4648#section-5) (Base64 URL) with the `url` option:
*
* ```ts twoslash
* import { Base64, Bytes } from 'ox'
*
* const value = Base64.fromBytes(Bytes.fromString('hello wod'), { url: true })
* // @log: 'aGVsbG8gd29_77-9ZA=='
* ```
*
* @param value - The byte array to encode.
* @param options - Encoding options.
* @returns The Base64 encoded string.
*/
export function fromBytes(value: Bytes.Bytes, options: fromBytes.Options = {}) {
const { pad = true, url = false } = options
const encoded = new Uint8Array(Math.ceil(value.length / 3) * 4)
for (let i = 0, j = 0; j < value.length; i += 4, j += 3) {
const y = (value[j]! << 16) + (value[j + 1]! << 8) + (value[j + 2]! | 0)
encoded[i] = integerToCharacter[y >> 18]!
encoded[i + 1] = integerToCharacter[(y >> 12) & 0x3f]!
encoded[i + 2] = integerToCharacter[(y >> 6) & 0x3f]!
encoded[i + 3] = integerToCharacter[y & 0x3f]!
}
const k = value.length % 3
const end = Math.floor(value.length / 3) * 4 + (k && k + 1)
let base64 = decoder.decode(new Uint8Array(encoded.buffer, 0, end))
if (pad && k === 1) base64 += '=='
if (pad && k === 2) base64 += '='
if (url) base64 = base64.replaceAll('+', '-').replaceAll('/', '_')
return base64
}
export declare namespace fromBytes {
type Options = {
/**
* Whether to [pad](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) the Base64 encoded string.
*
* @default true
*/
pad?: boolean | undefined
/**
* Whether to Base64 encode with [URL safe characters](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
*
* @default false
*/
url?: boolean | undefined
}
type ErrorType = Errors.GlobalErrorType
}
/**
* Encodes a {@link ox#Hex.Hex} to a Base64-encoded string (with optional padding and/or URL-safe characters).
*
* @example
* ```ts twoslash
* import { Base64, Hex } from 'ox'
*
* const value = Base64.fromHex(Hex.fromString('hello world'))
* // @log: 'aGVsbG8gd29ybGQ='
* ```
*
* @example
* ### No Padding
*
* Turn off [padding of encoded data](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) with the `pad` option:
*
* ```ts twoslash
* import { Base64, Hex } from 'ox'
*
* const value = Base64.fromHex(Hex.fromString('hello world'), { pad: false })
* // @log: 'aGVsbG8gd29ybGQ'
* ```
*
* ### URL-safe Encoding
*
* Turn on [URL-safe encoding](https://datatracker.ietf.org/doc/html/rfc4648#section-5) (Base64 URL) with the `url` option:
*
* ```ts twoslash
* import { Base64, Hex } from 'ox'
*
* const value = Base64.fromHex(Hex.fromString('hello wod'), { url: true })
* // @log: 'aGVsbG8gd29_77-9ZA=='
* ```
*
* @param value - The hex value to encode.
* @param options - Encoding options.
* @returns The Base64 encoded string.
*/
export function fromHex(value: Hex.Hex, options: fromHex.Options = {}) {
return fromBytes(Bytes.fromHex(value), options)
}
export declare namespace fromHex {
type Options = {
/**
* Whether to [pad](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) the Base64 encoded string.
*
* @default true
*/
pad?: boolean | undefined
/**
* Whether to Base64 encode with [URL safe characters](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
*
* @default false
*/
url?: boolean | undefined
}
type ErrorType = fromBytes.ErrorType | Errors.GlobalErrorType
}
/**
* Encodes a string to a Base64-encoded string (with optional padding and/or URL-safe characters).
*
* @example
* ```ts twoslash
* import { Base64 } from 'ox'
*
* const value = Base64.fromString('hello world')
* // @log: 'aGVsbG8gd29ybGQ='
* ```
*
* @example
* ### No Padding
*
* Turn off [padding of encoded data](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) with the `pad` option:
*
* ```ts twoslash
* import { Base64 } from 'ox'
*
* const value = Base64.fromString('hello world', { pad: false })
* // @log: 'aGVsbG8gd29ybGQ'
* ```
*
* ### URL-safe Encoding
*
* Turn on [URL-safe encoding](https://datatracker.ietf.org/doc/html/rfc4648#section-5) (Base64 URL) with the `url` option:
*
* ```ts twoslash
* import { Base64 } from 'ox'
*
* const value = Base64.fromString('hello wod', { url: true })
* // @log: 'aGVsbG8gd29_77-9ZA=='
* ```
*
* @param value - The string to encode.
* @param options - Encoding options.
* @returns The Base64 encoded string.
*/
export function fromString(value: string, options: fromString.Options = {}) {
return fromBytes(Bytes.fromString(value), options)
}
export declare namespace fromString {
type Options = {
/**
* Whether to [pad](https://datatracker.ietf.org/doc/html/rfc4648#section-3.2) the Base64 encoded string.
*
* @default true
*/
pad?: boolean | undefined
/**
* Whether to Base64 encode with [URL safe characters](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
*
* @default false
*/
url?: boolean | undefined
}
type ErrorType = fromBytes.ErrorType | Errors.GlobalErrorType
}
/**
* Decodes a Base64-encoded string (with optional padding and/or URL-safe characters) to {@link ox#Bytes.Bytes}.
*
* @example
* ```ts twoslash
* import { Base64, Bytes } from 'ox'
*
* const value = Base64.toBytes('aGVsbG8gd29ybGQ=')
* // @log: Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])
* ```
*
* @param value - The string, hex value, or byte array to encode.
* @returns The Base64 decoded {@link ox#Bytes.Bytes}.
*/
export function toBytes(value: string): Bytes.Bytes {
const base64 = value.replace(/=+$/, '')
const size = base64.length
const decoded = new Uint8Array(size + 3)
encoder.encodeInto(base64 + '===', decoded)
for (let i = 0, j = 0; i < base64.length; i += 4, j += 3) {
const x =
(characterToInteger[decoded[i]!]! << 18) +
(characterToInteger[decoded[i + 1]!]! << 12) +
(characterToInteger[decoded[i + 2]!]! << 6) +
characterToInteger[decoded[i + 3]!]!
decoded[j] = x >> 16
decoded[j + 1] = (x >> 8) & 0xff
decoded[j + 2] = x & 0xff
}
const decodedSize = (size >> 2) * 3 + (size % 4 && (size % 4) - 1)
return new Uint8Array(decoded.buffer, 0, decodedSize)
}
export declare namespace toBytes {
type ErrorType = Errors.GlobalErrorType
}
/**
* Decodes a Base64-encoded string (with optional padding and/or URL-safe characters) to {@link ox#Hex.Hex}.
*
* @example
* ```ts twoslash
* import { Base64, Hex } from 'ox'
*
* const value = Base64.toHex('aGVsbG8gd29ybGQ=')
* // @log: 0x68656c6c6f20776f726c64
* ```
*
* @param value - The string, hex value, or byte array to encode.
* @returns The Base64 decoded {@link ox#Hex.Hex}.
*/
export function toHex(value: string): Hex.Hex {
return Hex.fromBytes(toBytes(value))
}
export declare namespace toHex {
type ErrorType = toBytes.ErrorType | Errors.GlobalErrorType
}
/**
* Decodes a Base64-encoded string (with optional padding and/or URL-safe characters) to a string.
*
* @example
* ```ts twoslash
* import { Base64 } from 'ox'
*
* const value = Base64.toString('aGVsbG8gd29ybGQ=')
* // @log: 'hello world'
* ```
*
* @param value - The string, hex value, or byte array to encode.
* @returns The Base64 decoded string.
*/
export function toString(value: string): string {
return Bytes.toString(toBytes(value))
}
export declare namespace toString {
type ErrorType = toBytes.ErrorType | Errors.GlobalErrorType
}