@@ -196,7 +196,7 @@ impl<const N: usize> DecodeTarget for [u8; N] {
196
196
impl < ' a , I : AsRef < [ u8 ] > > DecodeBuilder < ' a , I > {
197
197
/// Setup decoder for the given string using the given alphabet.
198
198
/// Preferably use [`bs58::decode`](crate::decode()) instead of this directly.
199
- pub fn new ( input : I , alpha : & ' a Alphabet ) -> DecodeBuilder < ' a , I > {
199
+ pub const fn new ( input : I , alpha : & ' a Alphabet ) -> DecodeBuilder < ' a , I > {
200
200
DecodeBuilder {
201
201
input,
202
202
alpha,
@@ -205,7 +205,7 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
205
205
}
206
206
207
207
/// Setup decoder for the given string using default prepared alphabet.
208
- pub ( crate ) fn from_input ( input : I ) -> DecodeBuilder < ' static , I > {
208
+ pub ( crate ) const fn from_input ( input : I ) -> DecodeBuilder < ' static , I > {
209
209
DecodeBuilder {
210
210
input,
211
211
alpha : Alphabet :: DEFAULT ,
@@ -225,8 +225,9 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
225
225
/// .into_vec()?);
226
226
/// # Ok::<(), bs58::decode::Error>(())
227
227
/// ```
228
- pub fn with_alphabet ( self , alpha : & ' a Alphabet ) -> DecodeBuilder < ' a , I > {
229
- DecodeBuilder { alpha, ..self }
228
+ pub const fn with_alphabet ( mut self , alpha : & ' a Alphabet ) -> DecodeBuilder < ' a , I > {
229
+ self . alpha = alpha;
230
+ self
230
231
}
231
232
232
233
/// Expect and check checksum using the [Base58Check][] algorithm when
@@ -276,7 +277,6 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
276
277
let check = Check :: CB58 ( expected_ver) ;
277
278
DecodeBuilder { check, ..self }
278
279
}
279
-
280
280
/// Decode into a new vector of bytes.
281
281
///
282
282
/// See the documentation for [`bs58::decode`](crate::decode()) for an
@@ -348,6 +348,66 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
348
348
}
349
349
}
350
350
351
+ /// For `const` compatibility we are restricted to using a concrete input and output type, as
352
+ /// `const` trait implementations and `&mut` are unstable. These methods will eventually be
353
+ /// deprecated once the primary interfaces can be converted into `const fn` directly.
354
+ impl < ' a , ' b > DecodeBuilder < ' a , & ' b [ u8 ] > {
355
+ /// Decode into a new array.
356
+ ///
357
+ /// Returns the decoded array as bytes.
358
+ ///
359
+ /// See the documentation for [`bs58::decode`](crate::decode())
360
+ /// for an explanation of the errors that may occur.
361
+ ///
362
+ /// # Examples
363
+ ///
364
+ /// ```rust
365
+ /// const _: () = {
366
+ /// let Ok(output) = bs58::decode(b"EUYUqQf".as_slice()).into_array_const::<5>() else {
367
+ /// panic!()
368
+ /// };
369
+ /// assert!(matches!(&output, b"world"));
370
+ /// };
371
+ /// ```
372
+ pub const fn into_array_const < const N : usize > ( self ) -> Result < [ u8 ; N ] > {
373
+ assert ! (
374
+ matches!( self . check, Check :: Disabled ) ,
375
+ "checksums in const aren't supported (why are you using this API at runtime)" ,
376
+ ) ;
377
+ decode_into_const ( self . input , self . alpha )
378
+ }
379
+
380
+ /// [`Self::into_array_const`] but the result will be unwrapped, turning any error into a panic
381
+ /// message via [`Error::unwrap_const`], as a simple `into_array_const().unwrap()` isn't
382
+ /// possible yet.
383
+ ///
384
+ /// # Examples
385
+ ///
386
+ /// ```rust
387
+ /// const _: () = {
388
+ /// let output: [u8; 5] = bs58::decode(b"EUYUqQf".as_slice()).into_array_const_unwrap();
389
+ /// assert!(matches!(&output, b"world"));
390
+ /// };
391
+ /// ```
392
+ ///
393
+ /// ```rust
394
+ /// const _: () = {
395
+ /// assert!(matches!(
396
+ /// bs58::decode(b"he11owor1d".as_slice())
397
+ /// .with_alphabet(bs58::Alphabet::RIPPLE)
398
+ /// .into_array_const_unwrap(),
399
+ /// [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78],
400
+ /// ));
401
+ /// };
402
+ /// ```
403
+ pub const fn into_array_const_unwrap < const N : usize > ( self ) -> [ u8 ; N ] {
404
+ match self . into_array_const ( ) {
405
+ Ok ( result) => result,
406
+ Err ( err) => err. unwrap_const ( ) ,
407
+ }
408
+ }
409
+ }
410
+
351
411
fn decode_into ( input : & [ u8 ] , output : & mut [ u8 ] , alpha : & Alphabet ) -> Result < usize > {
352
412
let mut index = 0 ;
353
413
let zero = alpha. encode [ 0 ] ;
@@ -480,6 +540,69 @@ fn decode_cb58_into(
480
540
}
481
541
}
482
542
543
+ const fn decode_into_const < const N : usize > ( input : & [ u8 ] , alpha : & Alphabet ) -> Result < [ u8 ; N ] > {
544
+ let mut output = [ 0u8 ; N ] ;
545
+ let mut index = 0 ;
546
+ let zero = alpha. encode [ 0 ] ;
547
+
548
+ let mut i = 0 ;
549
+ while i < input. len ( ) {
550
+ let c = input[ i] ;
551
+ if c > 127 {
552
+ return Err ( Error :: NonAsciiCharacter { index : i } ) ;
553
+ }
554
+
555
+ let mut val = alpha. decode [ c as usize ] as usize ;
556
+ if val == 0xFF {
557
+ return Err ( Error :: InvalidCharacter {
558
+ character : c as char ,
559
+ index : i,
560
+ } ) ;
561
+ }
562
+
563
+ let mut j = 0 ;
564
+ while j < index {
565
+ let byte = output[ j] ;
566
+ val += ( byte as usize ) * 58 ;
567
+ output[ j] = ( val & 0xFF ) as u8 ;
568
+ val >>= 8 ;
569
+ j += 1 ;
570
+ }
571
+
572
+ while val > 0 {
573
+ if index >= output. len ( ) {
574
+ return Err ( Error :: BufferTooSmall ) ;
575
+ }
576
+ output[ index] = ( val & 0xFF ) as u8 ;
577
+ index += 1 ;
578
+ val >>= 8
579
+ }
580
+ i += 1 ;
581
+ }
582
+
583
+ let mut i = 0 ;
584
+ while i < input. len ( ) && input[ i] == zero {
585
+ if index >= output. len ( ) {
586
+ return Err ( Error :: BufferTooSmall ) ;
587
+ }
588
+ output[ index] = 0 ;
589
+ index += 1 ;
590
+ i += 1 ;
591
+ }
592
+
593
+ // reverse
594
+ let mut i = 0 ;
595
+ let n = index / 2 ;
596
+ while i < n {
597
+ let x = output[ i] ;
598
+ output[ i] = output[ index - 1 - i] ;
599
+ output[ index - 1 - i] = x;
600
+ i += 1 ;
601
+ }
602
+
603
+ Ok ( output)
604
+ }
605
+
483
606
#[ cfg( feature = "std" ) ]
484
607
impl std:: error:: Error for Error { }
485
608
@@ -520,3 +643,25 @@ impl fmt::Display for Error {
520
643
}
521
644
}
522
645
}
646
+
647
+ impl Error {
648
+ /// Panic with an error message based on this error. This cannot include any of the dynamic
649
+ /// content because formatting in `const` is not yet possible.
650
+ pub const fn unwrap_const ( self ) -> ! {
651
+ match self {
652
+ Error :: BufferTooSmall => {
653
+ panic ! ( "buffer provided to decode base58 encoded string into was too small" )
654
+ }
655
+ Error :: InvalidCharacter { .. } => panic ! ( "provided string contained invalid character" ) ,
656
+ Error :: NonAsciiCharacter { .. } => {
657
+ panic ! ( "provided string contained non-ascii character" )
658
+ }
659
+ #[ cfg( any( feature = "check" , feature = "cb58" ) ) ]
660
+ Error :: InvalidChecksum { .. } => panic ! ( "invalid checksum" ) ,
661
+ #[ cfg( any( feature = "check" , feature = "cb58" ) ) ]
662
+ Error :: InvalidVersion { .. } => panic ! ( "invalid version" ) ,
663
+ #[ cfg( any( feature = "check" , feature = "cb58" ) ) ]
664
+ Error :: NoChecksum => panic ! ( "provided string is too small to contain a checksum" ) ,
665
+ }
666
+ }
667
+ }
0 commit comments