Skip to content

Commit

Permalink
Update to use prepared Alphabet instances only
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemo157 committed Nov 6, 2020
1 parent d170cba commit 32f6449
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 145 deletions.
42 changes: 15 additions & 27 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -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,
}
}
Expand All @@ -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));
Expand All @@ -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.
Expand All @@ -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())?;
}
Expand Down
87 changes: 45 additions & 42 deletions src/alphabet.rs
Original file line number Diff line number Diff line change
@@ -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 <https://en.bitcoin.it/wiki/Base58Check_encoding#Base58_symbol_chart>
pub const BITCOIN: &'static Self =
&Self::new(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: &'static Self =
&Self::new(b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");

/// Ripple's alphabet as defined in their wiki.
///
/// See <https://wiki.ripple.com/Encodings>
pub const RIPPLE: &'static Self =
&Self::new(b"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");

/// Flickr's alphabet for creating short urls from photo ids.
///
/// See <https://www.flickr.com/groups/api/discuss/72157616713786392/>
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.
Expand All @@ -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;
};
44 changes: 11 additions & 33 deletions src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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,
}
}
Expand All @@ -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,
}
}
Expand All @@ -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 }
}

Expand Down Expand Up @@ -210,12 +192,8 @@ impl<'a, I: AsRef<[u8]>> DecodeBuilder<'a, I> {
}
}

fn decode_into(input: &[u8], output: &mut [u8], alpha: &AlphabetCow) -> Result<usize> {
fn decode_into(input: &[u8], output: &mut [u8], alpha: &Alphabet) -> Result<usize> {
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() {
Expand Down Expand Up @@ -259,7 +237,7 @@ fn decode_into(input: &[u8], output: &mut [u8], alpha: &AlphabetCow) -> Result<u
fn decode_check_into(
input: &[u8],
output: &mut [u8],
alpha: &AlphabetCow,
alpha: &Alphabet,
expected_ver: Option<u8>,
) -> Result<usize> {
use sha2::{Digest, Sha256};
Expand Down
41 changes: 9 additions & 32 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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,
}
}
Expand All @@ -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,
}
}
Expand All @@ -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 }
}

Expand Down Expand Up @@ -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<usize>
fn encode_into<'a, I>(input: I, output: &mut [u8], alpha: &Alphabet) -> Result<usize>
where
I: Clone + IntoIterator<Item = &'a u8>,
{
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;
Expand Down Expand Up @@ -396,7 +373,7 @@ where
fn encode_check_into(
input: &[u8],
output: &mut [u8],
alpha: &AlphabetCow,
alpha: &Alphabet,
version: Option<u8>,
) -> Result<usize> {
use sha2::{Digest, Sha256};
Expand Down
Loading

0 comments on commit 32f6449

Please sign in to comment.