1use core::fmt;
4
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7
8use crate::Check;
9#[cfg(any(feature = "check", feature = "cb58"))]
10use crate::CHECKSUM_LEN;
11
12use crate::Alphabet;
13
14#[allow(missing_debug_implementations)]
19pub struct DecodeBuilder<'a, I: AsRef<[u8]>> {
20 input: I,
21 alpha: &'a Alphabet,
22 check: Check,
23}
24
25pub type Result<T> = core::result::Result<T, Error>;
27
28#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30#[non_exhaustive]
31pub enum Error {
32 BufferTooSmall,
34
35 InvalidCharacter {
38 character: char,
40 index: usize,
42 },
43
44 NonAsciiCharacter {
47 index: usize,
50 },
51
52 #[cfg(any(feature = "check", feature = "cb58"))]
53 InvalidChecksum {
55 checksum: [u8; CHECKSUM_LEN],
57 expected_checksum: [u8; CHECKSUM_LEN],
59 },
60
61 #[cfg(any(feature = "check", feature = "cb58"))]
62 InvalidVersion {
64 ver: u8,
66 expected_ver: u8,
68 },
69
70 #[cfg(any(feature = "check", feature = "cb58"))]
71 NoChecksum,
73}
74
75pub trait DecodeTarget {
78 fn decode_with(
82 &mut self,
83 max_len: usize,
84 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
85 ) -> Result<usize>;
86}
87
88impl<T: DecodeTarget + ?Sized> DecodeTarget for &mut T {
89 fn decode_with(
90 &mut self,
91 max_len: usize,
92 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
93 ) -> Result<usize> {
94 T::decode_with(self, max_len, f)
95 }
96}
97
98#[cfg(feature = "alloc")]
99impl DecodeTarget for Vec<u8> {
100 fn decode_with(
101 &mut self,
102 max_len: usize,
103 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
104 ) -> Result<usize> {
105 let original = self.len();
106 self.resize(original + max_len, 0);
107 let len = f(&mut self[original..])?;
108 self.truncate(original + len);
109 Ok(len)
110 }
111}
112
113#[cfg(feature = "smallvec")]
114impl<A: smallvec::Array<Item = u8>> DecodeTarget for smallvec::SmallVec<A> {
115 fn decode_with(
117 &mut self,
118 max_len: usize,
119 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
120 ) -> Result<usize> {
121 let original = self.len();
122 self.resize(original + max_len, 0);
123 let len = f(&mut self[original..])?;
124 self.truncate(original + len);
125 Ok(len)
126 }
127}
128
129#[cfg(feature = "tinyvec")]
130impl<A: tinyvec::Array<Item = u8>> DecodeTarget for tinyvec::ArrayVec<A> {
131 fn decode_with(
132 &mut self,
133 max_len: usize,
134 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
135 ) -> Result<usize> {
136 let _ = max_len;
137 let original = self.len();
138 let len = f(self.grab_spare_slice_mut())?;
139 self.set_len(original + len);
140 Ok(len)
141 }
142}
143
144#[cfg(feature = "tinyvec")]
145impl DecodeTarget for tinyvec::SliceVec<'_, u8> {
146 fn decode_with(
147 &mut self,
148 max_len: usize,
149 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
150 ) -> Result<usize> {
151 let _ = max_len;
152 let original = self.len();
153 let len = f(self.grab_spare_slice_mut())?;
154 self.set_len(original + len);
155 Ok(len)
156 }
157}
158
159#[cfg(all(feature = "tinyvec", feature = "alloc"))]
160impl<A: tinyvec::Array<Item = u8>> DecodeTarget for tinyvec::TinyVec<A> {
161 fn decode_with(
162 &mut self,
163 max_len: usize,
164 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
165 ) -> Result<usize> {
166 let original = self.len();
167 self.resize(original + max_len, 0);
168 let len = f(&mut self[original..])?;
169 self.truncate(original + len);
170 Ok(len)
171 }
172}
173
174impl DecodeTarget for [u8] {
175 fn decode_with(
176 &mut self,
177 max_len: usize,
178 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
179 ) -> Result<usize> {
180 let _ = max_len;
181 f(&mut *self)
182 }
183}
184
185impl<const N: usize> DecodeTarget for [u8; N] {
186 fn decode_with(
187 &mut self,
188 max_len: usize,
189 f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
190 ) -> Result<usize> {
191 let _ = max_len;
192 f(&mut *self)
193 }
194}
195
196impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
197 pub const fn new(input: I, alpha: &'a Alphabet) -> DecodeBuilder<'a, I> {
200 DecodeBuilder {
201 input,
202 alpha,
203 check: Check::Disabled,
204 }
205 }
206
207 pub(crate) const fn from_input(input: I) -> DecodeBuilder<'static, I> {
209 DecodeBuilder {
210 input,
211 alpha: Alphabet::DEFAULT,
212 check: Check::Disabled,
213 }
214 }
215
216 pub const fn with_alphabet(mut self, alpha: &'a Alphabet) -> DecodeBuilder<'a, I> {
229 self.alpha = alpha;
230 self
231 }
232
233 #[cfg(feature = "check")]
252 pub fn with_check(self, expected_ver: Option<u8>) -> DecodeBuilder<'a, I> {
253 let check = Check::Enabled(expected_ver);
254 DecodeBuilder { check, ..self }
255 }
256
257 #[cfg(feature = "cb58")]
276 pub fn as_cb58(self, expected_ver: Option<u8>) -> DecodeBuilder<'a, I> {
277 let check = Check::CB58(expected_ver);
278 DecodeBuilder { check, ..self }
279 }
280 #[cfg(feature = "alloc")]
295 pub fn into_vec(self) -> Result<Vec<u8>> {
296 let mut output = Vec::new();
297 self.onto(&mut output)?;
298 Ok(output)
299 }
300
301 pub fn onto(self, mut output: impl DecodeTarget) -> Result<usize> {
334 let max_decoded_len = self.input.as_ref().len();
335 match self.check {
336 Check::Disabled => output.decode_with(max_decoded_len, |output| {
337 decode_into(self.input.as_ref(), output, self.alpha)
338 }),
339 #[cfg(feature = "check")]
340 Check::Enabled(expected_ver) => output.decode_with(max_decoded_len, |output| {
341 decode_check_into(self.input.as_ref(), output, self.alpha, expected_ver)
342 }),
343 #[cfg(feature = "cb58")]
344 Check::CB58(expected_ver) => output.decode_with(max_decoded_len, |output| {
345 decode_cb58_into(self.input.as_ref(), output, self.alpha, expected_ver)
346 }),
347 }
348 }
349}
350
351impl<'a, 'b> DecodeBuilder<'a, &'b [u8]> {
355 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 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
411fn decode_into(input: &[u8], output: &mut [u8], alpha: &Alphabet) -> Result<usize> {
412 let mut index = 0;
413 let zero = alpha.encode[0];
414
415 for (i, c) in input.iter().enumerate() {
416 if *c > 127 {
417 return Err(Error::NonAsciiCharacter { index: i });
418 }
419
420 let mut val = alpha.decode[*c as usize] as usize;
421 if val == 0xFF {
422 return Err(Error::InvalidCharacter {
423 character: *c as char,
424 index: i,
425 });
426 }
427
428 for byte in &mut output[..index] {
429 val += (*byte as usize) * 58;
430 *byte = (val & 0xFF) as u8;
431 val >>= 8;
432 }
433
434 while val > 0 {
435 let byte = output.get_mut(index).ok_or(Error::BufferTooSmall)?;
436 *byte = (val & 0xFF) as u8;
437 index += 1;
438 val >>= 8
439 }
440 }
441
442 for _ in input.iter().take_while(|c| **c == zero) {
443 let byte = output.get_mut(index).ok_or(Error::BufferTooSmall)?;
444 *byte = 0;
445 index += 1;
446 }
447
448 output[..index].reverse();
449 Ok(index)
450}
451
452#[cfg(feature = "check")]
453fn decode_check_into(
454 input: &[u8],
455 output: &mut [u8],
456 alpha: &Alphabet,
457 expected_ver: Option<u8>,
458) -> Result<usize> {
459 use sha2::{Digest, Sha256};
460
461 let decoded_len = decode_into(input, output, alpha)?;
462 if decoded_len < CHECKSUM_LEN {
463 return Err(Error::NoChecksum);
464 }
465 let checksum_index = decoded_len - CHECKSUM_LEN;
466
467 let expected_checksum = &output[checksum_index..decoded_len];
468
469 let first_hash = Sha256::digest(&output[0..checksum_index]);
470 let second_hash = Sha256::digest(first_hash);
471 let (checksum, _) = second_hash.split_at(CHECKSUM_LEN);
472
473 if checksum == expected_checksum {
474 if let Some(ver) = expected_ver {
475 if output[0] == ver {
476 Ok(checksum_index)
477 } else {
478 Err(Error::InvalidVersion {
479 ver: output[0],
480 expected_ver: ver,
481 })
482 }
483 } else {
484 Ok(checksum_index)
485 }
486 } else {
487 let mut a: [u8; CHECKSUM_LEN] = Default::default();
488 a.copy_from_slice(checksum);
489 let mut b: [u8; CHECKSUM_LEN] = Default::default();
490 b.copy_from_slice(expected_checksum);
491 Err(Error::InvalidChecksum {
492 checksum: a,
493 expected_checksum: b,
494 })
495 }
496}
497
498#[cfg(feature = "cb58")]
499fn decode_cb58_into(
500 input: &[u8],
501 output: &mut [u8],
502 alpha: &Alphabet,
503 expected_ver: Option<u8>,
504) -> Result<usize> {
505 use sha2::{Digest, Sha256};
506
507 let decoded_len = decode_into(input, output, alpha)?;
508 if decoded_len < CHECKSUM_LEN {
509 return Err(Error::NoChecksum);
510 }
511 let checksum_index = decoded_len - CHECKSUM_LEN;
512
513 let expected_checksum = &output[checksum_index..decoded_len];
514
515 let hash = Sha256::digest(&output[0..checksum_index]);
516 let (_, checksum) = hash.split_at(hash.len() - CHECKSUM_LEN);
517
518 if checksum == expected_checksum {
519 if let Some(ver) = expected_ver {
520 if output[0] == ver {
521 Ok(checksum_index)
522 } else {
523 Err(Error::InvalidVersion {
524 ver: output[0],
525 expected_ver: ver,
526 })
527 }
528 } else {
529 Ok(checksum_index)
530 }
531 } else {
532 let mut a: [u8; CHECKSUM_LEN] = Default::default();
533 a.copy_from_slice(checksum);
534 let mut b: [u8; CHECKSUM_LEN] = Default::default();
535 b.copy_from_slice(expected_checksum);
536 Err(Error::InvalidChecksum {
537 checksum: a,
538 expected_checksum: b,
539 })
540 }
541}
542
543const 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 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
606#[cfg(feature = "std")]
607impl std::error::Error for Error {}
608
609impl fmt::Display for Error {
610 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
611 match *self {
612 Error::BufferTooSmall => write!(
613 f,
614 "buffer provided to decode base58 encoded string into was too small"
615 ),
616 Error::InvalidCharacter { character, index } => write!(
617 f,
618 "provided string contained invalid character {:?} at byte {}",
619 character, index
620 ),
621 Error::NonAsciiCharacter { index } => write!(
622 f,
623 "provided string contained non-ascii character starting at byte {}",
624 index
625 ),
626 #[cfg(any(feature = "check", feature = "cb58"))]
627 Error::InvalidChecksum {
628 checksum,
629 expected_checksum,
630 } => write!(
631 f,
632 "invalid checksum, calculated checksum: '{:?}', expected checksum: {:?}",
633 checksum, expected_checksum
634 ),
635 #[cfg(any(feature = "check", feature = "cb58"))]
636 Error::InvalidVersion { ver, expected_ver } => write!(
637 f,
638 "invalid version, payload version: '{:?}', expected version: {:?}",
639 ver, expected_ver
640 ),
641 #[cfg(any(feature = "check", feature = "cb58"))]
642 Error::NoChecksum => write!(f, "provided string is too small to contain a checksum"),
643 }
644 }
645}
646
647impl Error {
648 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}