@@ -18,16 +18,6 @@ function abytes(b: Uint8Array | undefined): void {
1818 if ( ! isBytes ( b ) ) throw new Error ( 'Uint8Array expected' ) ;
1919}
2020
21- function isArrayOf ( isString : boolean , arr : any [ ] ) {
22- if ( ! Array . isArray ( arr ) ) return false ;
23- if ( arr . length === 0 ) return true ;
24- if ( isString ) {
25- return arr . every ( ( item ) => typeof item === 'string' ) ;
26- } else {
27- return arr . every ( ( item ) => Number . isSafeInteger ( item ) ) ;
28- }
29- }
30-
3121function afn ( input : Function ) : input is Function {
3222 if ( typeof input !== 'function' ) throw new Error ( 'function expected' ) ;
3323 return true ;
@@ -42,13 +32,6 @@ function anumber(n: number): void {
4232 if ( ! Number . isSafeInteger ( n ) ) throw new Error ( `invalid integer: ${ n } ` ) ;
4333}
4434
45- function aArr ( input : any [ ] ) {
46- if ( ! Array . isArray ( input ) ) throw new Error ( 'array expected' ) ;
47- }
48- function astrArr ( label : string , input : string [ ] ) {
49- if ( ! isArrayOf ( true , input ) ) throw new Error ( `${ label } : array of strings expected` ) ;
50- }
51-
5235// TODO: some recusive type inference so it would check correct order of input/output inside rest?
5336// like <string, number>, <number, bytes>, <bytes, float>
5437type Chain = [ Coder < any , any > , ...Coder < any , any > [ ] ] ;
@@ -80,15 +63,16 @@ function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>,
8063}
8164
8265/**
83- * Encodes integer radix representation to array of strings using alphabet and back.
84- * Could also be array of strings.
66+ * Encodes integer radix representation in Uint8Array to a string in alphabet and back, with optional padding
8567 * @__NO_SIDE_EFFECTS__
8668 */
87- function alphabet ( letters : string | string [ ] ) : Coder < Uint8Array , string [ ] > {
69+ function alphabet ( letters : string , paddingBits : number = 0 , paddingChr = '=' ) : Coder < Uint8Array , string > {
8870 // mapping 1 to "b"
89- const lettersA = typeof letters === 'string' ? letters . split ( '' ) : letters ;
71+ astr ( 'alphabet' , letters ) ;
72+ const lettersA = letters . split ( '' ) ;
9073 const len = lettersA . length ;
91- astrArr ( 'alphabet' , lettersA ) ;
74+ const paddingCode = paddingChr . codePointAt ( 0 ) ! ;
75+ if ( paddingChr . length !== 1 || paddingCode > 128 ) throw new Error ( 'Wrong padding char' ) ;
9276
9377 // mapping "b" to 1
9478 const indexes = new Int8Array ( 256 ) . fill ( - 1 ) ;
@@ -98,79 +82,46 @@ function alphabet(letters: string | string[]): Coder<Uint8Array, string[]> {
9882 indexes [ code ] = i ;
9983 } ) ;
10084 return {
101- encode : ( digits : Uint8Array ) : string [ ] => {
85+ encode : ( digits : Uint8Array ) : string => {
10286 abytes ( digits ) ;
10387 const out = [ ]
10488 for ( const i of digits ) {
10589 if ( i >= len )
10690 throw new Error (
10791 `alphabet.encode: digit index outside alphabet "${ i } ". Allowed: ${ letters } `
10892 ) ;
109- out . push ( letters [ i ] ! ) ;
93+ out . push ( lettersA [ i ] ! ) ;
11094 }
111- return out ;
95+ if ( paddingBits > 0 ) {
96+ while ( ( out . length * paddingBits ) % 8 ) out . push ( paddingChr ) ;
97+ }
98+ return out . join ( '' ) ;
11299 } ,
113- decode : ( input : string [ ] ) : Uint8Array => {
114- aArr ( input ) ;
115- const out = new Uint8Array ( input . length ) ;
100+ decode : ( str : string ) : Uint8Array => {
101+ astr ( 'alphabet.decode' , str ) ;
102+ let end = str . length ;
103+ if ( paddingBits > 0 ) {
104+ if ( ( end * paddingBits ) % 8 )
105+ throw new Error ( 'padding: invalid, string should have whole number of bytes' ) ;
106+ for ( ; end > 0 && str . charCodeAt ( end - 1 ) === paddingCode ; end -- ) {
107+ const last = end - 1 ;
108+ const byte = last * paddingBits ;
109+ if ( byte % 8 === 0 ) throw new Error ( 'padding: invalid, string has too much padding' ) ;
110+ }
111+ }
112+ const out = new Uint8Array ( end ) ;
116113 let at = 0
117- for ( const letter of input ) {
118- astr ( 'alphabet.decode' , letter ) ;
119- const c = letter . codePointAt ( 0 ) ! ;
114+ for ( let j = 0 ; j < end ; j ++ ) {
115+ const c = str . charCodeAt ( j ) ! ;
120116 const i = indexes [ c ] ! ;
121- if ( letter . length !== 1 || c > 127 || i < 0 ) throw new Error ( `Unknown letter: "${ letter } ". Allowed: ${ letters } ` ) ;
117+ if ( c > 127 || i < 0 ) throw new Error ( `Unknown letter: "${ String . fromCharCode ( c ) } ". Allowed: ${ letters } ` ) ;
122118 out [ at ++ ] = i ;
123119 }
124120 return out ;
125121 } ,
126122 } ;
127123}
128124
129- /**
130- * @__NO_SIDE_EFFECTS__
131- */
132- function join ( separator = '' ) : Coder < string [ ] , string > {
133- astr ( 'join' , separator ) ;
134- return {
135- encode : ( from ) => {
136- astrArr ( 'join.decode' , from ) ;
137- return from . join ( separator ) ;
138- } ,
139- decode : ( to ) => {
140- astr ( 'join.decode' , to ) ;
141- return to . split ( separator ) ;
142- } ,
143- } ;
144- }
145-
146- /**
147- * Pad strings array so it has integer number of bits
148- * @__NO_SIDE_EFFECTS__
149- */
150- function padding ( bits : number , chr = '=' ) : Coder < string [ ] , string [ ] > {
151- anumber ( bits ) ;
152- astr ( 'padding' , chr ) ;
153- return {
154- encode ( data : string [ ] ) : string [ ] {
155- astrArr ( 'padding.encode' , data ) ;
156- while ( ( data . length * bits ) % 8 ) data . push ( chr ) ;
157- return data ;
158- } ,
159- decode ( input : string [ ] ) : string [ ] {
160- astrArr ( 'padding.decode' , input ) ;
161- let end = input . length ;
162- if ( ( end * bits ) % 8 )
163- throw new Error ( 'padding: invalid, string should have whole number of bytes' ) ;
164- for ( ; end > 0 && input [ end - 1 ] === chr ; end -- ) {
165- const last = end - 1 ;
166- const byte = last * bits ;
167- if ( byte % 8 === 0 ) throw new Error ( 'padding: invalid, string has too much padding' ) ;
168- }
169- return input . slice ( 0 , end ) ;
170- } ,
171- } ;
172- }
173-
174125/**
175126 * @__NO_SIDE_EFFECTS__
176127 */
@@ -347,8 +298,8 @@ function checksum(
347298}
348299
349300// prettier-ignore
350- export const utils : { alphabet : typeof alphabet ; chain : typeof chain ; checksum : typeof checksum ; convertRadix : typeof convertRadix ; convertRadix2 : typeof convertRadix2 ; radix : typeof radix ; radix2 : typeof radix2 ; join : typeof join ; padding : typeof padding ; } = {
351- alphabet, chain, checksum, convertRadix, convertRadix2, radix, radix2, join , padding ,
301+ export const utils : { alphabet : typeof alphabet ; chain : typeof chain ; checksum : typeof checksum ; convertRadix : typeof convertRadix ; convertRadix2 : typeof convertRadix2 ; radix : typeof radix ; radix2 : typeof radix2 ; } = {
302+ alphabet, chain, checksum, convertRadix, convertRadix2, radix, radix2,
352303} ;
353304
354305// RFC 4648 aka RFC 3548
@@ -362,7 +313,7 @@ export const utils: { alphabet: typeof alphabet; chain: typeof chain; checksum:
362313 * // => '12AB'
363314 * ```
364315 */
365- export const base16 : BytesCoder = chain ( radix2 ( 4 ) , alphabet ( '0123456789ABCDEF' ) , join ( '' ) ) ;
316+ export const base16 : BytesCoder = chain ( radix2 ( 4 ) , alphabet ( '0123456789ABCDEF' ) ) ;
366317
367318/**
368319 * base32 encoding from RFC 4648. Has padding.
@@ -378,9 +329,7 @@ export const base16: BytesCoder = chain(radix2(4), alphabet('0123456789ABCDEF'),
378329 */
379330export const base32 : BytesCoder = chain (
380331 radix2 ( 5 ) ,
381- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ) ,
382- padding ( 5 ) ,
383- join ( '' )
332+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' , 5 )
384333) ;
385334
386335/**
@@ -397,8 +346,7 @@ export const base32: BytesCoder = chain(
397346 */
398347export const base32nopad : BytesCoder = chain (
399348 radix2 ( 5 ) ,
400- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' ) ,
401- join ( '' )
349+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' )
402350) ;
403351/**
404352 * base32 encoding from RFC 4648. Padded. Compared to ordinary `base32`, slightly different alphabet.
@@ -413,9 +361,7 @@ export const base32nopad: BytesCoder = chain(
413361 */
414362export const base32hex : BytesCoder = chain (
415363 radix2 ( 5 ) ,
416- alphabet ( '0123456789ABCDEFGHIJKLMNOPQRSTUV' ) ,
417- padding ( 5 ) ,
418- join ( '' )
364+ alphabet ( '0123456789ABCDEFGHIJKLMNOPQRSTUV' , 5 )
419365) ;
420366
421367/**
@@ -431,8 +377,7 @@ export const base32hex: BytesCoder = chain(
431377 */
432378export const base32hexnopad : BytesCoder = chain (
433379 radix2 ( 5 ) ,
434- alphabet ( '0123456789ABCDEFGHIJKLMNOPQRSTUV' ) ,
435- join ( '' )
380+ alphabet ( '0123456789ABCDEFGHIJKLMNOPQRSTUV' )
436381) ;
437382/**
438383 * base32 encoding from RFC 4648. Doug Crockford's version.
@@ -448,7 +393,6 @@ export const base32hexnopad: BytesCoder = chain(
448393export const base32crockford : BytesCoder = chain (
449394 radix2 ( 5 ) ,
450395 alphabet ( '0123456789ABCDEFGHJKMNPQRSTVWXYZ' ) ,
451- join ( '' ) ,
452396 normalize ( ( s : string ) => s . toUpperCase ( ) . replace ( / O / g, '0' ) . replace ( / [ I L ] / g, '1' ) )
453397) ;
454398
@@ -489,9 +433,7 @@ export const base64: BytesCoder = hasBase64Builtin ? {
489433 decode ( s ) { return decodeBase64Builtin ( s , false ) ; } ,
490434} : chain (
491435 radix2 ( 6 ) ,
492- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' ) ,
493- padding ( 6 ) ,
494- join ( '' )
436+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' , 6 )
495437) ;
496438/**
497439 * base64 from RFC 4648. No padding.
@@ -506,8 +448,7 @@ export const base64: BytesCoder = hasBase64Builtin ? {
506448 */
507449export const base64nopad : BytesCoder = chain (
508450 radix2 ( 6 ) ,
509- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' ) ,
510- join ( '' )
451+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' )
511452) ;
512453
513454/**
@@ -528,9 +469,7 @@ export const base64url: BytesCoder = hasBase64Builtin ? {
528469 decode ( s ) { return decodeBase64Builtin ( s , true ) ; } ,
529470} : chain (
530471 radix2 ( 6 ) ,
531- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' ) ,
532- padding ( 6 ) ,
533- join ( '' )
472+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' , 6 )
534473) ;
535474
536475/**
@@ -546,14 +485,13 @@ export const base64url: BytesCoder = hasBase64Builtin ? {
546485 */
547486export const base64urlnopad : BytesCoder = chain (
548487 radix2 ( 6 ) ,
549- alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' ) ,
550- join ( '' )
488+ alphabet ( 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' )
551489) ;
552490
553491// base58 code
554492// -----------
555493const genBase58 = /* @__NO_SIDE_EFFECTS__ */ ( abc : string ) =>
556- chain ( radix ( 58 ) , alphabet ( abc ) , join ( '' ) ) ;
494+ chain ( radix ( 58 ) , alphabet ( abc ) ) ;
557495
558496/**
559497 * base58: base64 without ambigous characters +, /, 0, O, I, l.
@@ -642,8 +580,7 @@ export interface Bech32DecodedWithArray<Prefix extends string = string> {
642580}
643581
644582const BECH_ALPHABET : Coder < Uint8Array , string > = chain (
645- alphabet ( 'qpzry9x8gf2tvdw0s3jn54khce6mua7l' ) ,
646- join ( '' )
583+ alphabet ( 'qpzry9x8gf2tvdw0s3jn54khce6mua7l' )
647584) ;
648585
649586const POLYMOD_GENERATORS = [ 0x3b6a57b2 , 0x26508e6d , 0x1ea119fa , 0x3d4233dd , 0x2a1462b3 ] ;
@@ -822,7 +759,6 @@ export const hex: BytesCoder = hasHexBuiltin
822759 : chain (
823760 radix2 ( 4 ) ,
824761 alphabet ( '0123456789abcdef' ) ,
825- join ( '' ) ,
826762 normalize ( ( s : string ) => {
827763 if ( typeof s !== 'string' || s . length % 2 !== 0 )
828764 throw new TypeError (
0 commit comments