@scure/base
Advanced tools
Comparing version 1.2.0 to 1.2.1
140
index.ts
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Utilities | ||
export function assertNumber(n: number) { | ||
if (!Number.isSafeInteger(n)) throw new Error(`Wrong integer: ${n}`); | ||
} | ||
export interface Coder<F, T> { | ||
@@ -21,8 +17,10 @@ encode(from: F): T; | ||
function isArrayOf(type: 'string' | 'number', arr: any[]) { | ||
function isArrayOf(isString: boolean, arr: any[]) { | ||
if (!Array.isArray(arr)) return false; | ||
if (arr.length === 0) return true; | ||
if (type === 'string') return arr.every((item) => typeof item === 'string'); | ||
if (type === 'number') return arr.every((item) => Number.isSafeInteger(item)); | ||
return false; | ||
if (isString) { | ||
return arr.every((item) => typeof item === 'string'); | ||
} else { | ||
return arr.every((item) => Number.isSafeInteger(item)); | ||
} | ||
} | ||
@@ -42,10 +40,16 @@ | ||
function astrArr(label: string, input: string[]): input is string[] { | ||
if (!isArrayOf('string', input)) throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
function anumber(n: number) { | ||
if (!Number.isSafeInteger(n)) throw new Error(`invalid integer: ${n}`); | ||
} | ||
function anumArr(label: string, input: number[]): input is number[] { | ||
if (!isArrayOf('number', input)) throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
export const assertNumber = anumber; | ||
function aArr(input: any[]) { | ||
if (!Array.isArray(input)) throw new Error('array expected'); | ||
} | ||
function astrArr(label: string, input: string[]) { | ||
if (!isArrayOf(true, input)) throw new Error(`${label}: array of strings expected`); | ||
} | ||
function anumArr(label: string, input: number[]) { | ||
if (!isArrayOf(false, input)) throw new Error(`${label}: array of numbers expected`); | ||
} | ||
@@ -68,2 +72,5 @@ // TODO: some recusive type inference so it would check correct order of input/output inside rest? | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>, Output<Last<T>>> { | ||
@@ -83,2 +90,3 @@ const id = (a: any) => a; | ||
* Could also be array of strings. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -95,3 +103,3 @@ function alphabet(letters: string | string[]): Coder<number[], string[]> { | ||
encode: (digits: number[]) => { | ||
if (!Array.isArray(digits)) throw new Error('array expected'); | ||
aArr(digits); | ||
return digits.map((i) => { | ||
@@ -106,3 +114,3 @@ if (!Number.isSafeInteger(i) || i < 0 || i >= len) | ||
decode: (input: string[]): number[] => { | ||
if (!Array.isArray(input)) throw new Error('array expected'); | ||
aArr(input); | ||
return input.map((letter) => { | ||
@@ -118,2 +126,5 @@ astr('alphabet.decode', letter); | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function join(separator = ''): Coder<string[], string> { | ||
@@ -135,5 +146,6 @@ astr('join', separator); | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function padding(bits: number, chr = '='): Coder<string[], string[]> { | ||
assertNumber(bits); | ||
anumber(bits); | ||
astr('padding', chr); | ||
@@ -161,2 +173,5 @@ return { | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function normalize<T>(fn: (val: T) => T): Coder<T, T> { | ||
@@ -172,12 +187,12 @@ afn(fn); | ||
// base 1 is impossible | ||
if (from < 2) throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); | ||
if (to < 2) throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); | ||
if (!Array.isArray(data)) throw new Error('convertRadix: data should be array'); | ||
if (from < 2) throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`); | ||
if (to < 2) throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`); | ||
aArr(data); | ||
if (!data.length) return []; | ||
let pos = 0; | ||
const res = []; | ||
const digits = Array.from(data); | ||
digits.forEach((d) => { | ||
assertNumber(d); | ||
if (d < 0 || d >= from) throw new Error(`Wrong integer: ${d}`); | ||
const digits = Array.from(data, (d) => { | ||
anumber(d); | ||
if (d < 0 || d >= from) throw new Error(`invalid integer: ${d}`); | ||
return d; | ||
}); | ||
@@ -190,11 +205,12 @@ const dlen = digits.length; | ||
const digit = digits[i]!; | ||
const digitBase = from * carry + digit; | ||
const fromCarry = from * carry; | ||
const digitBase = fromCarry + digit; | ||
if ( | ||
!Number.isSafeInteger(digitBase) || | ||
(from * carry) / from !== carry || | ||
digitBase - digit !== from * carry | ||
fromCarry / from !== carry || | ||
digitBase - digit !== fromCarry | ||
) { | ||
throw new Error('convertRadix: carry overflow'); | ||
} | ||
let div = digitBase / to; | ||
const div = digitBase / to; | ||
carry = digitBase % to; | ||
@@ -217,3 +233,3 @@ const rounded = Math.floor(div); | ||
const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b)); | ||
const radix2carry = /*@__NO_SIDE_EFFECTS__ */ (from: number, to: number) => | ||
const radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from: number, to: number) => | ||
from + (to - gcd(from, to)); | ||
@@ -229,3 +245,3 @@ const powers: number[] = /* @__PURE__ */ (() => { | ||
function convertRadix2(data: number[], from: number, to: number, padding: boolean): number[] { | ||
if (!Array.isArray(data)) throw new Error('convertRadix2: data should be array'); | ||
aArr(data); | ||
if (from <= 0 || from > 32) throw new Error(`convertRadix2: wrong from=${from}`); | ||
@@ -244,3 +260,3 @@ if (to <= 0 || to > 32) throw new Error(`convertRadix2: wrong to=${to}`); | ||
for (const n of data) { | ||
assertNumber(n); | ||
anumber(n); | ||
if (n >= max) throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); | ||
@@ -262,4 +278,7 @@ carry = (carry << from) | n; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix(num: number): Coder<Uint8Array, number[]> { | ||
assertNumber(num); | ||
anumber(num); | ||
const _256 = 2 ** 8; | ||
@@ -281,5 +300,6 @@ return { | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix2(bits: number, revPadding = false): Coder<Uint8Array, number[]> { | ||
assertNumber(bits); | ||
anumber(bits); | ||
if (bits <= 0 || bits > 32) throw new Error('radix2: bits should be in (0..32]'); | ||
@@ -314,3 +334,3 @@ if (radix2carry(8, bits) > 32 || radix2carry(bits, 8) > 32) | ||
): Coder<Uint8Array, Uint8Array> { | ||
assertNumber(len); | ||
anumber(len); | ||
afn(fn); | ||
@@ -320,6 +340,6 @@ return { | ||
if (!isBytes(data)) throw new Error('checksum.encode: input should be Uint8Array'); | ||
const checksum = fn(data).slice(0, len); | ||
const sum = fn(data).slice(0, len); | ||
const res = new Uint8Array(data.length + len); | ||
res.set(data); | ||
res.set(checksum, data.length); | ||
res.set(sum, data.length); | ||
return res; | ||
@@ -330,4 +350,4 @@ }, | ||
const payload = data.slice(0, -len); | ||
const oldChecksum = data.slice(-len); | ||
const newChecksum = fn(payload).slice(0, len); | ||
const oldChecksum = data.slice(-len); | ||
for (let i = 0; i < len; i++) | ||
@@ -351,8 +371,4 @@ if (newChecksum[i] !== oldChecksum[i]) throw new Error('Invalid checksum'); | ||
*/ | ||
export const base16: BytesCoder = /* @__PURE__ */ chain( | ||
radix2(4), | ||
alphabet('0123456789ABCDEF'), | ||
join('') | ||
); | ||
export const base32: BytesCoder = /* @__PURE__ */ chain( | ||
export const base16: BytesCoder = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
export const base32: BytesCoder = chain( | ||
radix2(5), | ||
@@ -363,3 +379,3 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), | ||
); | ||
export const base32nopad: BytesCoder = /* @__PURE__ */ chain( | ||
export const base32nopad: BytesCoder = chain( | ||
radix2(5), | ||
@@ -369,3 +385,3 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), | ||
); | ||
export const base32hex: BytesCoder = /* @__PURE__ */ chain( | ||
export const base32hex: BytesCoder = chain( | ||
radix2(5), | ||
@@ -376,3 +392,3 @@ alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), | ||
); | ||
export const base32hexnopad: BytesCoder = /* @__PURE__ */ chain( | ||
export const base32hexnopad: BytesCoder = chain( | ||
radix2(5), | ||
@@ -382,3 +398,3 @@ alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), | ||
); | ||
export const base32crockford: BytesCoder = /* @__PURE__ */ chain( | ||
export const base32crockford: BytesCoder = chain( | ||
radix2(5), | ||
@@ -395,3 +411,3 @@ alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), | ||
*/ | ||
export const base64: BytesCoder = /* @__PURE__ */ chain( | ||
export const base64: BytesCoder = chain( | ||
radix2(6), | ||
@@ -405,3 +421,3 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), | ||
*/ | ||
export const base64nopad: BytesCoder = /* @__PURE__ */ chain( | ||
export const base64nopad: BytesCoder = chain( | ||
radix2(6), | ||
@@ -411,3 +427,3 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), | ||
); | ||
export const base64url: BytesCoder = /* @__PURE__ */ chain( | ||
export const base64url: BytesCoder = chain( | ||
radix2(6), | ||
@@ -418,3 +434,3 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), | ||
); | ||
export const base64urlnopad: BytesCoder = /* @__PURE__ */ chain( | ||
export const base64urlnopad: BytesCoder = chain( | ||
radix2(6), | ||
@@ -427,3 +443,4 @@ alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), | ||
// ----------- | ||
const genBase58 = (abc: string) => chain(radix(58), alphabet(abc), join('')); | ||
const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc: string) => | ||
chain(radix(58), alphabet(abc), join('')); | ||
@@ -434,9 +451,9 @@ /** | ||
*/ | ||
export const base58: BytesCoder = /* @__PURE__ */ genBase58( | ||
export const base58: BytesCoder = genBase58( | ||
'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
); | ||
export const base58flickr: BytesCoder = /* @__PURE__ */ genBase58( | ||
export const base58flickr: BytesCoder = genBase58( | ||
'123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ' | ||
); | ||
export const base58xrp: BytesCoder = /* @__PURE__ */ genBase58( | ||
export const base58xrp: BytesCoder = genBase58( | ||
'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz' | ||
@@ -501,3 +518,3 @@ ); | ||
const BECH_ALPHABET: Coder<number[], string> = /* @__PURE__ */ chain( | ||
const BECH_ALPHABET: Coder<number[], string> = chain( | ||
alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), | ||
@@ -550,2 +567,5 @@ join('') | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function genBech32(encoding: 'bech32' | 'bech32m'): Bech32 { | ||
@@ -585,3 +605,3 @@ const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; | ||
if (slen < 8 || (limit !== false && slen > limit)) | ||
throw new TypeError(`Wrong string length: ${slen} (${str}). Expected (8..${limit})`); | ||
throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`); | ||
// don't allow mixed case | ||
@@ -629,4 +649,4 @@ const lowered = str.toLowerCase(); | ||
*/ | ||
export const bech32: Bech32 = /* @__PURE__ */ genBech32('bech32'); | ||
export const bech32m: Bech32 = /* @__PURE__ */ genBech32('bech32m'); | ||
export const bech32: Bech32 = genBech32('bech32'); | ||
export const bech32m: Bech32 = genBech32('bech32m'); | ||
@@ -653,3 +673,3 @@ declare const TextEncoder: any; | ||
*/ | ||
export const hex: BytesCoder = /* @__PURE__ */ chain( | ||
export const hex: BytesCoder = chain( | ||
radix2(4), | ||
@@ -656,0 +676,0 @@ alphabet('0123456789abcdef'), |
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
export declare function assertNumber(n: number): void; | ||
export interface Coder<F, T> { | ||
@@ -11,2 +10,4 @@ encode(from: F): T; | ||
} | ||
declare function anumber(n: number): void; | ||
export declare const assertNumber: typeof anumber; | ||
type Chain = [Coder<any, any>, ...Coder<any, any>[]]; | ||
@@ -21,2 +22,5 @@ type Input<F> = F extends Coder<infer T, any> ? T : never; | ||
}; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>, Output<Last<T>>>; | ||
@@ -26,7 +30,12 @@ /** | ||
* Could also be array of strings. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function alphabet(letters: string | string[]): Coder<number[], string[]>; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function join(separator?: string): Coder<string[], string>; | ||
/** | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -42,2 +51,5 @@ declare function padding(bits: number, chr?: string): Coder<string[], string[]>; | ||
declare function convertRadix2(data: number[], from: number, to: number, padding: boolean): number[]; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function radix(num: number): Coder<Uint8Array, number[]>; | ||
@@ -47,2 +59,3 @@ /** | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -49,0 +62,0 @@ declare function radix2(bits: number, revPadding?: boolean): Coder<Uint8Array, number[]>; |
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Utilities | ||
export function assertNumber(n) { | ||
if (!Number.isSafeInteger(n)) | ||
throw new Error(`Wrong integer: ${n}`); | ||
} | ||
function isBytes(a) { | ||
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'); | ||
} | ||
function isArrayOf(type, arr) { | ||
function isArrayOf(isString, arr) { | ||
if (!Array.isArray(arr)) | ||
@@ -15,7 +10,8 @@ return false; | ||
return true; | ||
if (type === 'string') | ||
if (isString) { | ||
return arr.every((item) => typeof item === 'string'); | ||
if (type === 'number') | ||
} | ||
else { | ||
return arr.every((item) => Number.isSafeInteger(item)); | ||
return false; | ||
} | ||
} | ||
@@ -33,12 +29,22 @@ // no abytes: seems to have 10% slowdown. Why?! | ||
} | ||
function anumber(n) { | ||
if (!Number.isSafeInteger(n)) | ||
throw new Error(`invalid integer: ${n}`); | ||
} | ||
export const assertNumber = anumber; | ||
function aArr(input) { | ||
if (!Array.isArray(input)) | ||
throw new Error('array expected'); | ||
} | ||
function astrArr(label, input) { | ||
if (!isArrayOf('string', input)) | ||
if (!isArrayOf(true, input)) | ||
throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
} | ||
function anumArr(label, input) { | ||
if (!isArrayOf('number', input)) | ||
throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
if (!isArrayOf(false, input)) | ||
throw new Error(`${label}: array of numbers expected`); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function chain(...args) { | ||
@@ -57,2 +63,3 @@ const id = (a) => a; | ||
* Could also be array of strings. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -68,4 +75,3 @@ function alphabet(letters) { | ||
encode: (digits) => { | ||
if (!Array.isArray(digits)) | ||
throw new Error('array expected'); | ||
aArr(digits); | ||
return digits.map((i) => { | ||
@@ -78,4 +84,3 @@ if (!Number.isSafeInteger(i) || i < 0 || i >= len) | ||
decode: (input) => { | ||
if (!Array.isArray(input)) | ||
throw new Error('array expected'); | ||
aArr(input); | ||
return input.map((letter) => { | ||
@@ -91,2 +96,5 @@ astr('alphabet.decode', letter); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function join(separator = '') { | ||
@@ -107,5 +115,6 @@ astr('join', separator); | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function padding(bits, chr = '=') { | ||
assertNumber(bits); | ||
anumber(bits); | ||
astr('padding', chr); | ||
@@ -134,2 +143,5 @@ return { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function normalize(fn) { | ||
@@ -145,7 +157,6 @@ afn(fn); | ||
if (from < 2) | ||
throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); | ||
throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`); | ||
if (to < 2) | ||
throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); | ||
if (!Array.isArray(data)) | ||
throw new Error('convertRadix: data should be array'); | ||
throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`); | ||
aArr(data); | ||
if (!data.length) | ||
@@ -155,7 +166,7 @@ return []; | ||
const res = []; | ||
const digits = Array.from(data); | ||
digits.forEach((d) => { | ||
assertNumber(d); | ||
const digits = Array.from(data, (d) => { | ||
anumber(d); | ||
if (d < 0 || d >= from) | ||
throw new Error(`Wrong integer: ${d}`); | ||
throw new Error(`invalid integer: ${d}`); | ||
return d; | ||
}); | ||
@@ -168,9 +179,10 @@ const dlen = digits.length; | ||
const digit = digits[i]; | ||
const digitBase = from * carry + digit; | ||
const fromCarry = from * carry; | ||
const digitBase = fromCarry + digit; | ||
if (!Number.isSafeInteger(digitBase) || | ||
(from * carry) / from !== carry || | ||
digitBase - digit !== from * carry) { | ||
fromCarry / from !== carry || | ||
digitBase - digit !== fromCarry) { | ||
throw new Error('convertRadix: carry overflow'); | ||
} | ||
let div = digitBase / to; | ||
const div = digitBase / to; | ||
carry = digitBase % to; | ||
@@ -197,3 +209,3 @@ const rounded = Math.floor(div); | ||
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); | ||
const radix2carry = /*@__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
const radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
const powers = /* @__PURE__ */ (() => { | ||
@@ -209,4 +221,3 @@ let res = []; | ||
function convertRadix2(data, from, to, padding) { | ||
if (!Array.isArray(data)) | ||
throw new Error('convertRadix2: data should be array'); | ||
aArr(data); | ||
if (from <= 0 || from > 32) | ||
@@ -225,3 +236,3 @@ throw new Error(`convertRadix2: wrong from=${from}`); | ||
for (const n of data) { | ||
assertNumber(n); | ||
anumber(n); | ||
if (n >= max) | ||
@@ -249,4 +260,7 @@ throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix(num) { | ||
assertNumber(num); | ||
anumber(num); | ||
const _256 = 2 ** 8; | ||
@@ -268,5 +282,6 @@ return { | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix2(bits, revPadding = false) { | ||
assertNumber(bits); | ||
anumber(bits); | ||
if (bits <= 0 || bits > 32) | ||
@@ -298,3 +313,3 @@ throw new Error('radix2: bits should be in (0..32]'); | ||
function checksum(len, fn) { | ||
assertNumber(len); | ||
anumber(len); | ||
afn(fn); | ||
@@ -305,6 +320,6 @@ return { | ||
throw new Error('checksum.encode: input should be Uint8Array'); | ||
const checksum = fn(data).slice(0, len); | ||
const sum = fn(data).slice(0, len); | ||
const res = new Uint8Array(data.length + len); | ||
res.set(data); | ||
res.set(checksum, data.length); | ||
res.set(sum, data.length); | ||
return res; | ||
@@ -316,4 +331,4 @@ }, | ||
const payload = data.slice(0, -len); | ||
const oldChecksum = data.slice(-len); | ||
const newChecksum = fn(payload).slice(0, len); | ||
const oldChecksum = data.slice(-len); | ||
for (let i = 0; i < len; i++) | ||
@@ -335,8 +350,8 @@ if (newChecksum[i] !== oldChecksum[i]) | ||
*/ | ||
export const base16 = /* @__PURE__ */ chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
export const base32 = /* @__PURE__ */ chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); | ||
export const base32nopad = /* @__PURE__ */ chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), join('')); | ||
export const base32hex = /* @__PURE__ */ chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); | ||
export const base32hexnopad = /* @__PURE__ */ chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), join('')); | ||
export const base32crockford = /* @__PURE__ */ chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'))); | ||
export const base16 = chain(radix2(4), alphabet('0123456789ABCDEF'), join('')); | ||
export const base32 = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), padding(5), join('')); | ||
export const base32nopad = chain(radix2(5), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), join('')); | ||
export const base32hex = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), padding(5), join('')); | ||
export const base32hexnopad = chain(radix2(5), alphabet('0123456789ABCDEFGHIJKLMNOPQRSTUV'), join('')); | ||
export const base32crockford = chain(radix2(5), alphabet('0123456789ABCDEFGHJKMNPQRSTVWXYZ'), join(''), normalize((s) => s.toUpperCase().replace(/O/g, '0').replace(/[IL]/g, '1'))); | ||
/** | ||
@@ -348,12 +363,12 @@ * base64 with padding. For no padding, use `base64nopad`. | ||
*/ | ||
export const base64 = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); | ||
export const base64 = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), padding(6), join('')); | ||
/** | ||
* base64 without padding. | ||
*/ | ||
export const base64nopad = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), join('')); | ||
export const base64url = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); | ||
export const base64urlnopad = /* @__PURE__ */ chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), join('')); | ||
export const base64nopad = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'), join('')); | ||
export const base64url = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), padding(6), join('')); | ||
export const base64urlnopad = chain(radix2(6), alphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'), join('')); | ||
// base58 code | ||
// ----------- | ||
const genBase58 = (abc) => chain(radix(58), alphabet(abc), join('')); | ||
const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => chain(radix(58), alphabet(abc), join('')); | ||
/** | ||
@@ -363,5 +378,5 @@ * Base58: base64 without characters +, /, 0, O, I, l. | ||
*/ | ||
export const base58 = /* @__PURE__ */ genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); | ||
export const base58flickr = /* @__PURE__ */ genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); | ||
export const base58xrp = /* @__PURE__ */ genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); | ||
export const base58 = genBase58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'); | ||
export const base58flickr = genBase58('123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'); | ||
export const base58xrp = genBase58('rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz'); | ||
// Data len (index) -> encoded block len | ||
@@ -404,3 +419,3 @@ const XMR_BLOCK_LEN = [0, 2, 3, 5, 6, 7, 9, 10, 11]; | ||
export const base58check = createBase58check; | ||
const BECH_ALPHABET = /* @__PURE__ */ chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; | ||
@@ -435,2 +450,5 @@ function bech32Polymod(pre) { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function genBech32(encoding) { | ||
@@ -461,3 +479,3 @@ const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; | ||
if (slen < 8 || (limit !== false && slen > limit)) | ||
throw new TypeError(`Wrong string length: ${slen} (${str}). Expected (8..${limit})`); | ||
throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`); | ||
// don't allow mixed case | ||
@@ -502,4 +520,4 @@ const lowered = str.toLowerCase(); | ||
*/ | ||
export const bech32 = /* @__PURE__ */ genBech32('bech32'); | ||
export const bech32m = /* @__PURE__ */ genBech32('bech32m'); | ||
export const bech32 = genBech32('bech32'); | ||
export const bech32m = genBech32('bech32m'); | ||
/** | ||
@@ -521,3 +539,3 @@ * UTF-8-to-byte decoder. Uses built-in TextDecoder / TextEncoder. | ||
*/ | ||
export const hex = /* @__PURE__ */ chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => { | ||
export const hex = chain(radix2(4), alphabet('0123456789abcdef'), join(''), normalize((s) => { | ||
if (typeof s !== 'string' || s.length % 2 !== 0) | ||
@@ -524,0 +542,0 @@ throw new TypeError(`hex.decode: expected string, got ${typeof s} with length ${s.length}`); |
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
export declare function assertNumber(n: number): void; | ||
export interface Coder<F, T> { | ||
@@ -11,2 +10,4 @@ encode(from: F): T; | ||
} | ||
declare function anumber(n: number): void; | ||
export declare const assertNumber: typeof anumber; | ||
type Chain = [Coder<any, any>, ...Coder<any, any>[]]; | ||
@@ -21,2 +22,5 @@ type Input<F> = F extends Coder<infer T, any> ? T : never; | ||
}; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>, Output<Last<T>>>; | ||
@@ -26,7 +30,12 @@ /** | ||
* Could also be array of strings. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function alphabet(letters: string | string[]): Coder<number[], string[]>; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function join(separator?: string): Coder<string[], string>; | ||
/** | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -42,2 +51,5 @@ declare function padding(bits: number, chr?: string): Coder<string[], string[]>; | ||
declare function convertRadix2(data: number[], from: number, to: number, padding: boolean): number[]; | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
declare function radix(num: number): Coder<Uint8Array, number[]>; | ||
@@ -47,2 +59,3 @@ /** | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -49,0 +62,0 @@ declare function radix2(bits: number, revPadding?: boolean): Coder<Uint8Array, number[]>; |
109
lib/index.js
"use strict"; | ||
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.createBase58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64urlnopad = exports.base64url = exports.base64nopad = exports.base64 = exports.base32crockford = exports.base32hexnopad = exports.base32hex = exports.base32nopad = exports.base32 = exports.base16 = exports.utils = void 0; | ||
exports.assertNumber = assertNumber; | ||
// Utilities | ||
function assertNumber(n) { | ||
if (!Number.isSafeInteger(n)) | ||
throw new Error(`Wrong integer: ${n}`); | ||
} | ||
exports.bytes = exports.stringToBytes = exports.str = exports.bytesToString = exports.hex = exports.utf8 = exports.bech32m = exports.bech32 = exports.base58check = exports.createBase58check = exports.base58xmr = exports.base58xrp = exports.base58flickr = exports.base58 = exports.base64urlnopad = exports.base64url = exports.base64nopad = exports.base64 = exports.base32crockford = exports.base32hexnopad = exports.base32hex = exports.base32nopad = exports.base32 = exports.base16 = exports.utils = exports.assertNumber = void 0; | ||
function isBytes(a) { | ||
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array'); | ||
} | ||
function isArrayOf(type, arr) { | ||
function isArrayOf(isString, arr) { | ||
if (!Array.isArray(arr)) | ||
@@ -19,7 +13,8 @@ return false; | ||
return true; | ||
if (type === 'string') | ||
if (isString) { | ||
return arr.every((item) => typeof item === 'string'); | ||
if (type === 'number') | ||
} | ||
else { | ||
return arr.every((item) => Number.isSafeInteger(item)); | ||
return false; | ||
} | ||
} | ||
@@ -37,12 +32,22 @@ // no abytes: seems to have 10% slowdown. Why?! | ||
} | ||
function anumber(n) { | ||
if (!Number.isSafeInteger(n)) | ||
throw new Error(`invalid integer: ${n}`); | ||
} | ||
exports.assertNumber = anumber; | ||
function aArr(input) { | ||
if (!Array.isArray(input)) | ||
throw new Error('array expected'); | ||
} | ||
function astrArr(label, input) { | ||
if (!isArrayOf('string', input)) | ||
if (!isArrayOf(true, input)) | ||
throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
} | ||
function anumArr(label, input) { | ||
if (!isArrayOf('number', input)) | ||
throw new Error(`${label}: array of strings expected`); | ||
return true; | ||
if (!isArrayOf(false, input)) | ||
throw new Error(`${label}: array of numbers expected`); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function chain(...args) { | ||
@@ -61,2 +66,3 @@ const id = (a) => a; | ||
* Could also be array of strings. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
@@ -72,4 +78,3 @@ function alphabet(letters) { | ||
encode: (digits) => { | ||
if (!Array.isArray(digits)) | ||
throw new Error('array expected'); | ||
aArr(digits); | ||
return digits.map((i) => { | ||
@@ -82,4 +87,3 @@ if (!Number.isSafeInteger(i) || i < 0 || i >= len) | ||
decode: (input) => { | ||
if (!Array.isArray(input)) | ||
throw new Error('array expected'); | ||
aArr(input); | ||
return input.map((letter) => { | ||
@@ -95,2 +99,5 @@ astr('alphabet.decode', letter); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function join(separator = '') { | ||
@@ -111,5 +118,6 @@ astr('join', separator); | ||
* Pad strings array so it has integer number of bits | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function padding(bits, chr = '=') { | ||
assertNumber(bits); | ||
anumber(bits); | ||
astr('padding', chr); | ||
@@ -138,2 +146,5 @@ return { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function normalize(fn) { | ||
@@ -149,7 +160,6 @@ afn(fn); | ||
if (from < 2) | ||
throw new Error(`convertRadix: wrong from=${from}, base cannot be less than 2`); | ||
throw new Error(`convertRadix: invalid from=${from}, base cannot be less than 2`); | ||
if (to < 2) | ||
throw new Error(`convertRadix: wrong to=${to}, base cannot be less than 2`); | ||
if (!Array.isArray(data)) | ||
throw new Error('convertRadix: data should be array'); | ||
throw new Error(`convertRadix: invalid to=${to}, base cannot be less than 2`); | ||
aArr(data); | ||
if (!data.length) | ||
@@ -159,7 +169,7 @@ return []; | ||
const res = []; | ||
const digits = Array.from(data); | ||
digits.forEach((d) => { | ||
assertNumber(d); | ||
const digits = Array.from(data, (d) => { | ||
anumber(d); | ||
if (d < 0 || d >= from) | ||
throw new Error(`Wrong integer: ${d}`); | ||
throw new Error(`invalid integer: ${d}`); | ||
return d; | ||
}); | ||
@@ -172,9 +182,10 @@ const dlen = digits.length; | ||
const digit = digits[i]; | ||
const digitBase = from * carry + digit; | ||
const fromCarry = from * carry; | ||
const digitBase = fromCarry + digit; | ||
if (!Number.isSafeInteger(digitBase) || | ||
(from * carry) / from !== carry || | ||
digitBase - digit !== from * carry) { | ||
fromCarry / from !== carry || | ||
digitBase - digit !== fromCarry) { | ||
throw new Error('convertRadix: carry overflow'); | ||
} | ||
let div = digitBase / to; | ||
const div = digitBase / to; | ||
carry = digitBase % to; | ||
@@ -201,3 +212,3 @@ const rounded = Math.floor(div); | ||
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); | ||
const radix2carry = /*@__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
const radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to)); | ||
const powers = /* @__PURE__ */ (() => { | ||
@@ -213,4 +224,3 @@ let res = []; | ||
function convertRadix2(data, from, to, padding) { | ||
if (!Array.isArray(data)) | ||
throw new Error('convertRadix2: data should be array'); | ||
aArr(data); | ||
if (from <= 0 || from > 32) | ||
@@ -229,3 +239,3 @@ throw new Error(`convertRadix2: wrong from=${from}`); | ||
for (const n of data) { | ||
assertNumber(n); | ||
anumber(n); | ||
if (n >= max) | ||
@@ -253,4 +263,7 @@ throw new Error(`convertRadix2: invalid data word=${n} from=${from}`); | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix(num) { | ||
assertNumber(num); | ||
anumber(num); | ||
const _256 = 2 ** 8; | ||
@@ -272,5 +285,6 @@ return { | ||
* there is a linear algorithm. For now we have implementation for power-of-two bases only. | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function radix2(bits, revPadding = false) { | ||
assertNumber(bits); | ||
anumber(bits); | ||
if (bits <= 0 || bits > 32) | ||
@@ -302,3 +316,3 @@ throw new Error('radix2: bits should be in (0..32]'); | ||
function checksum(len, fn) { | ||
assertNumber(len); | ||
anumber(len); | ||
afn(fn); | ||
@@ -309,6 +323,6 @@ return { | ||
throw new Error('checksum.encode: input should be Uint8Array'); | ||
const checksum = fn(data).slice(0, len); | ||
const sum = fn(data).slice(0, len); | ||
const res = new Uint8Array(data.length + len); | ||
res.set(data); | ||
res.set(checksum, data.length); | ||
res.set(sum, data.length); | ||
return res; | ||
@@ -320,4 +334,4 @@ }, | ||
const payload = data.slice(0, -len); | ||
const oldChecksum = data.slice(-len); | ||
const newChecksum = fn(payload).slice(0, len); | ||
const oldChecksum = data.slice(-len); | ||
for (let i = 0; i < len; i++) | ||
@@ -360,3 +374,3 @@ if (newChecksum[i] !== oldChecksum[i]) | ||
// ----------- | ||
const genBase58 = (abc) => chain(radix(58), alphabet(abc), join('')); | ||
const genBase58 = /* @__NO_SIDE_EFFECTS__ */ (abc) => chain(radix(58), alphabet(abc), join('')); | ||
/** | ||
@@ -407,3 +421,3 @@ * Base58: base64 without characters +, /, 0, O, I, l. | ||
exports.base58check = exports.createBase58check; | ||
const BECH_ALPHABET = /* @__PURE__ */ chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const BECH_ALPHABET = chain(alphabet('qpzry9x8gf2tvdw0s3jn54khce6mua7l'), join('')); | ||
const POLYMOD_GENERATORS = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; | ||
@@ -438,2 +452,5 @@ function bech32Polymod(pre) { | ||
} | ||
/** | ||
* @__NO_SIDE_EFFECTS__ | ||
*/ | ||
function genBech32(encoding) { | ||
@@ -464,3 +481,3 @@ const ENCODING_CONST = encoding === 'bech32' ? 1 : 0x2bc830a3; | ||
if (slen < 8 || (limit !== false && slen > limit)) | ||
throw new TypeError(`Wrong string length: ${slen} (${str}). Expected (8..${limit})`); | ||
throw new TypeError(`invalid string length: ${slen} (${str}). Expected (8..${limit})`); | ||
// don't allow mixed case | ||
@@ -467,0 +484,0 @@ const lowered = str.toLowerCase(); |
{ | ||
"name": "@scure/base", | ||
"version": "1.2.0", | ||
"version": "1.2.1", | ||
"description": "Secure, audited & 0-dep implementation of base64, bech32, base58, base32 & base16", | ||
@@ -5,0 +5,0 @@ "files": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2031
134771