diff --git a/cli/src/main.rs b/cli/src/main.rs index cfb10fd..5cad0f7 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,25 +1,26 @@ -use anyhow::anyhow; +use anyhow::{anyhow, Context}; use std::{ - fmt, + convert::TryInto, io::{self, Read, Write}, str::FromStr, }; +#[derive(Debug)] enum Alphabet { Bitcoin, Monero, Ripple, Flickr, - Custom([u8; 58]), + Custom(bs58::Alphabet), } impl Alphabet { - fn as_bytes(&self) -> &[u8; 58] { + fn as_alphabet(&self) -> &bs58::Alphabet { match self { - Alphabet::Bitcoin => bs58::alphabet::BITCOIN, - Alphabet::Monero => bs58::alphabet::MONERO, - Alphabet::Ripple => bs58::alphabet::RIPPLE, - Alphabet::Flickr => bs58::alphabet::FLICKR, + Alphabet::Bitcoin => bs58::Alphabet::BITCOIN, + Alphabet::Monero => bs58::Alphabet::MONERO, + Alphabet::Ripple => bs58::Alphabet::RIPPLE, + Alphabet::Flickr => bs58::Alphabet::FLICKR, Alphabet::Custom(custom) => custom, } } @@ -40,11 +41,10 @@ impl FromStr for Alphabet { if bytes.iter().any(|&c| c > 128) { return Err(anyhow!("custom alphabet must be ASCII characters only")); } - if bytes.len() != 58 { - return Err(anyhow!("custom alphabet is not 58 characters long")); - } - let ptr = bytes.as_ptr() as *const [u8; 58]; - Alphabet::Custom(unsafe { *ptr }) + let bytes = bytes + .try_into() + .context("custom alphabet is not 58 characters long")?; + Alphabet::Custom(bs58::Alphabet::new(bytes)) } other => { return Err(anyhow!("'{}' is not a known alphabet", other)); @@ -53,18 +53,6 @@ impl FromStr for Alphabet { } } -impl fmt::Debug for Alphabet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Alphabet::Bitcoin => f.debug_tuple("Bitcoin").finish(), - Alphabet::Monero => f.debug_tuple("Bitcoin").finish(), - Alphabet::Ripple => f.debug_tuple("Bitcoin").finish(), - Alphabet::Flickr => f.debug_tuple("Bitcoin").finish(), - Alphabet::Custom(custom) => f.debug_tuple("Custom").field(&&custom[..]).finish(), - } - } -} - #[derive(Debug, structopt::StructOpt)] #[structopt(name = "bs58", setting = structopt::clap::AppSettings::ColoredHelp)] /// A utility for encoding/decoding base58 encoded data. @@ -87,14 +75,14 @@ fn main(args: Args) -> anyhow::Result<()> { io::stdin().read_to_string(&mut input)?; let trimmed = input.trim_end(); let output = bs58::decode(trimmed) - .with_alphabet(args.alphabet.as_bytes()) + .with_alphabet(args.alphabet.as_alphabet()) .into_vec()?; io::stdout().write_all(&output)?; } else { let mut input = Vec::with_capacity(INITIAL_INPUT_CAPACITY); io::stdin().read_to_end(&mut input)?; let output = bs58::encode(input) - .with_alphabet(args.alphabet.as_bytes()) + .with_alphabet(args.alphabet.as_alphabet()) .into_string(); io::stdout().write_all(output.as_bytes())?; } diff --git a/src/alphabet.rs b/src/alphabet.rs index b78c806..14d9bd9 100644 --- a/src/alphabet.rs +++ b/src/alphabet.rs @@ -1,49 +1,50 @@ -//! Commonly used Base58 alphabets. - -/// Bitcoin's alphabet as defined in their Base58Check encoding. -/// -/// See https://en.bitcoin.it/wiki/Base58Check_encoding#Base58_symbol_chart. -pub const BITCOIN: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - -/// Monero's alphabet as defined in this forum post. -/// -/// See https://forum.getmonero.org/4/academic-and-technical/221/creating-a-standard-for-physical-coins -pub const MONERO: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - -/// Ripple's alphabet as defined in their wiki. -/// -/// See https://wiki.ripple.com/Encodings -pub const RIPPLE: &[u8; 58] = b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; - -/// Flickr's alphabet for creating short urls from photo ids. -/// -/// See https://www.flickr.com/groups/api/discuss/72157616713786392/ -pub const FLICKR: &[u8; 58] = b"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; - -/// The default alphabet used if none is given. Currently is the -/// [`BITCOIN`](constant.BITCOIN.html) alphabet. -pub const DEFAULT: &[u8; 58] = BITCOIN; +use core::fmt; /// Prepared Alphabet for [`EncodeBuilder`](crate::encode::EncodeBuilder) and /// [`DecodeBuilder`](crate::decode::DecodeBuilder). #[derive(Clone, Copy)] -#[allow(missing_debug_implementations)] pub struct Alphabet { pub(crate) encode: [u8; 58], pub(crate) decode: [u8; 128], } +impl fmt::Debug for Alphabet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Ok(s) = core::str::from_utf8(&self.encode) { + f.debug_tuple("Alphabet").field(&s).finish() + } else { + f.debug_tuple("Alphabet").field(&self.encode).finish() + } + } +} + impl Alphabet { - /// Bitcoin's prepared alphabet. - pub const BITCOIN: &'static Self = &Self::new(BITCOIN); - /// Monero's prepared alphabet. - pub const MONERO: &'static Self = &Self::new(MONERO); - /// Ripple's prepared alphabet. - pub const RIPPLE: &'static Self = &Self::new(RIPPLE); - /// Flickr's prepared alphabet. - pub const FLICKR: &'static Self = &Self::new(FLICKR); - /// The default prepared alphabet used if none is given. Currently is the - /// [`Alphabet::Bitcoin`](Alphabet::BITCOIN) alphabet. + /// Bitcoin's alphabet as defined in their Base58Check encoding. + /// + /// See + pub const BITCOIN: &'static Self = + &Self::new(b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); + + /// Monero's alphabet as defined in this forum post. + /// + /// See + pub const MONERO: &'static Self = + &Self::new(b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); + + /// Ripple's alphabet as defined in their wiki. + /// + /// See + pub const RIPPLE: &'static Self = + &Self::new(b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); + + /// Flickr's alphabet for creating short urls from photo ids. + /// + /// See + pub const FLICKR: &'static Self = + &Self::new(b"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); + + /// The default alphabet used if none is given. Currently is the + /// [`BITCOIN`](Self::BITCOIN) alphabet. pub const DEFAULT: &'static Self = Self::BITCOIN; /// Create prepared alphabet. @@ -62,9 +63,11 @@ impl Alphabet { } } -/// `std::borrow::Cow` alternative. -#[allow(variant_size_differences)] -pub(crate) enum AlphabetCow<'a> { - Borrowed(&'a Alphabet), - Owned(Alphabet), -} +// Force evaluation of the associated constants to make sure they don't error +const _: () = { + let _ = Alphabet::BITCOIN; + let _ = Alphabet::MONERO; + let _ = Alphabet::RIPPLE; + let _ = Alphabet::FLICKR; + let _ = Alphabet::DEFAULT; +}; diff --git a/src/decode.rs b/src/decode.rs index 0175789..3e5a259 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -9,16 +9,16 @@ use crate::Check; #[cfg(feature = "check")] use crate::CHECKSUM_LEN; -use crate::alphabet::{Alphabet, AlphabetCow}; +use crate::Alphabet; /// A builder for setting up the alphabet and output of a base58 decode. /// -/// See the documentation for [`bs58::decode`](../fn.decode.html) for a more +/// See the documentation for [`bs58::decode`](crate::decode()) for a more /// high level view of how to use this. #[allow(missing_debug_implementations)] pub struct DecodeBuilder<'a, I: AsRef<[u8]>> { input: I, - alpha: AlphabetCow<'a>, + alpha: &'a Alphabet, check: Check, } @@ -77,12 +77,11 @@ pub enum Error { impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> { /// Setup decoder for the given string using the given alphabet. - /// Preferably use [`bs58::decode`](../fn.decode.html) instead of this - /// directly. - pub fn new(input: I, alpha: &'a [u8; 58]) -> DecodeBuilder<'a, I> { + /// Preferably use [`bs58::decode`](crate::decode()) instead of this directly. + pub fn new(input: I, alpha: &'a Alphabet) -> DecodeBuilder<'a, I> { DecodeBuilder { input, - alpha: AlphabetCow::Owned(Alphabet::new(alpha)), + alpha, check: Check::Disabled, } } @@ -91,7 +90,7 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> { pub(crate) fn from_input(input: I) -> DecodeBuilder<'static, I> { DecodeBuilder { input, - alpha: AlphabetCow::Borrowed(Alphabet::DEFAULT), + alpha: Alphabet::DEFAULT, check: Check::Disabled, } } @@ -104,28 +103,11 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> { /// assert_eq!( /// vec![0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78], /// bs58::decode("he11owor1d") - /// .with_alphabet(bs58::alphabet::RIPPLE) + /// .with_alphabet(bs58::Alphabet::RIPPLE) /// .into_vec()?); /// # Ok::<(), bs58::decode::Error>(()) /// ``` - pub fn with_alphabet(self, alpha: &'a [u8; 58]) -> DecodeBuilder<'a, I> { - let alpha = AlphabetCow::Owned(Alphabet::new(alpha)); - DecodeBuilder { alpha, ..self } - } - - /// Change the alphabet that will be used for decoding. - /// - /// # Examples - /// - /// ```rust - /// assert_eq!( - /// vec![0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78], - /// bs58::decode("he11owor1d") - /// .with_prepared_alphabet(bs58::Alphabet::RIPPLE) - /// .into_vec().unwrap()); - /// ``` - pub fn with_prepared_alphabet(self, alpha: &'a Alphabet) -> DecodeBuilder<'a, I> { - let alpha = AlphabetCow::Borrowed(alpha); + pub fn with_alphabet(self, alpha: &'a Alphabet) -> DecodeBuilder<'a, I> { DecodeBuilder { alpha, ..self } } @@ -210,12 +192,8 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> { } } -fn decode_into(input: &[u8], output: &mut [u8], alpha: &AlphabetCow) -> Result { +fn decode_into(input: &[u8], output: &mut [u8], alpha: &Alphabet) -> Result { let mut index = 0; - let alpha = match alpha { - AlphabetCow::Borrowed(alpha) => alpha, - AlphabetCow::Owned(ref alpha) => alpha, - }; let zero = alpha.encode[0]; for (i, c) in input.iter().enumerate() { @@ -259,7 +237,7 @@ fn decode_into(input: &[u8], output: &mut [u8], alpha: &AlphabetCow) -> Result, ) -> Result { use sha2::{Digest, Sha256}; diff --git a/src/encode.rs b/src/encode.rs index 47d7c56..761f35a 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -9,13 +9,13 @@ use crate::Check; #[cfg(feature = "check")] use crate::CHECKSUM_LEN; -use crate::alphabet::{Alphabet, AlphabetCow}; +use crate::Alphabet; /// A builder for setting up the alphabet and output of a base58 encode. #[allow(missing_debug_implementations)] pub struct EncodeBuilder<'a, I: AsRef<[u8]>> { input: I, - alpha: AlphabetCow<'a>, + alpha: &'a Alphabet, check: Check, } @@ -138,10 +138,10 @@ impl<'a, I: AsRef<[u8]>> EncodeBuilder<'a, I> { /// Setup encoder for the given string using the given alphabet. /// Preferably use [`bs58::encode`](crate::encode()) instead of this /// directly. - pub fn new(input: I, alpha: &'a [u8; 58]) -> EncodeBuilder<'a, I> { + pub fn new(input: I, alpha: &'a Alphabet) -> EncodeBuilder<'a, I> { EncodeBuilder { input, - alpha: AlphabetCow::Owned(Alphabet::new(alpha)), + alpha, check: Check::Disabled, } } @@ -150,7 +150,7 @@ impl<'a, I: AsRef<[u8]>> EncodeBuilder<'a, I> { pub(crate) fn from_input(input: I) -> EncodeBuilder<'static, I> { EncodeBuilder { input, - alpha: AlphabetCow::Borrowed(Alphabet::DEFAULT), + alpha: Alphabet::DEFAULT, check: Check::Disabled, } } @@ -164,28 +164,10 @@ impl<'a, I: AsRef<[u8]>> EncodeBuilder<'a, I> { /// assert_eq!( /// "he11owor1d", /// bs58::encode(input) - /// .with_alphabet(bs58::alphabet::RIPPLE) + /// .with_alphabet(bs58::Alphabet::RIPPLE) /// .into_string()); /// ``` - pub fn with_alphabet(self, alpha: &'a [u8; 58]) -> EncodeBuilder<'a, I> { - let alpha = AlphabetCow::Owned(Alphabet::new(alpha)); - EncodeBuilder { alpha, ..self } - } - - /// Change the alphabet that will be used for decoding. - /// - /// # Examples - /// - /// ```rust - /// let input = [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78]; - /// assert_eq!( - /// "he11owor1d", - /// bs58::encode(input) - /// .with_prepared_alphabet(bs58::Alphabet::RIPPLE) - /// .into_string()); - /// ``` - pub fn with_prepared_alphabet(self, alpha: &'a Alphabet) -> EncodeBuilder<'a, I> { - let alpha = AlphabetCow::Borrowed(alpha); + pub fn with_alphabet(self, alpha: &'a Alphabet) -> EncodeBuilder<'a, I> { EncodeBuilder { alpha, ..self } } @@ -349,15 +331,10 @@ impl<'a, I: AsRef<[u8]>> EncodeBuilder<'a, I> { } } -fn encode_into<'a, I>(input: I, output: &mut [u8], alpha: &AlphabetCow) -> Result +fn encode_into<'a, I>(input: I, output: &mut [u8], alpha: &Alphabet) -> Result where I: Clone + IntoIterator, { - let alpha = match alpha { - AlphabetCow::Borrowed(alpha) => alpha, - AlphabetCow::Owned(ref alpha) => alpha, - }; - let mut index = 0; for &val in input.clone() { let mut carry = val as usize; @@ -396,7 +373,7 @@ where fn encode_check_into( input: &[u8], output: &mut [u8], - alpha: &AlphabetCow, + alpha: &Alphabet, version: Option, ) -> Result { use sha2::{Digest, Sha256}; diff --git a/src/lib.rs b/src/lib.rs index 5f0efbb..e7564d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,10 +53,10 @@ //! //! ```rust //! let decoded = bs58::decode("he11owor1d") -//! .with_alphabet(bs58::alphabet::RIPPLE) +//! .with_alphabet(bs58::Alphabet::RIPPLE) //! .into_vec()?; //! let encoded = bs58::encode(decoded) -//! .with_alphabet(bs58::alphabet::FLICKR) +//! .with_alphabet(bs58::Alphabet::FLICKR) //! .into_string(); //! assert_eq!("4DSSNaN1SC", encoded); //! # Ok::<(), bs58::decode::Error>(()) @@ -78,7 +78,7 @@ extern crate std; #[cfg(feature = "alloc")] extern crate alloc; -pub mod alphabet; +mod alphabet; pub use alphabet::Alphabet; pub mod decode; @@ -94,9 +94,7 @@ enum Check { Enabled(Option), } -/// Setup decoder for the given string using the [default alphabet][]. -/// -/// [default alphabet]: alphabet/constant.DEFAULT.html +/// Setup decoder for the given string using the [default alphabet][Alphabet::DEFAULT]. /// /// # Examples /// @@ -115,7 +113,7 @@ enum Check { /// assert_eq!( /// vec![0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78], /// bs58::decode("he11owor1d") -/// .with_alphabet(bs58::alphabet::RIPPLE) +/// .with_alphabet(bs58::Alphabet::RIPPLE) /// .into_vec()?); /// # Ok::<(), bs58::decode::Error>(()) /// ``` @@ -165,9 +163,7 @@ pub fn decode>(input: I) -> decode::DecodeBuilder<'static, I> { decode::DecodeBuilder::from_input(input) } -/// Setup encoder for the given bytes using the [default alphabet][]. -/// -/// [default alphabet]: alphabet/constant.DEFAULT.html +/// Setup encoder for the given bytes using the [default alphabet][Alphabet::DEFAULT]. /// /// # Examples /// @@ -185,7 +181,7 @@ pub fn decode>(input: I) -> decode::DecodeBuilder<'static, I> { /// assert_eq!( /// "he11owor1d", /// bs58::encode(input) -/// .with_alphabet(bs58::alphabet::RIPPLE) +/// .with_alphabet(bs58::Alphabet::RIPPLE) /// .into_string()); /// ``` ///