From d0881a84c77a1bbdd9e149be4d89db355b6c5558 Mon Sep 17 00:00:00 2001 From: mriise Date: Mon, 4 Jul 2022 12:34:08 -0400 Subject: [PATCH 1/6] generic over container type, and move code to associated const --- Cargo.toml | 5 +- src/base.rs | 57 +++++++-------- src/impls.rs | 192 +++++++++++++++++++++++++++++++++++---------------- src/lib.rs | 8 +-- 4 files changed, 167 insertions(+), 95 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f61304..70c2230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,11 @@ keywords = ["ipld", "ipfs", "multihash", "cid", "no_std"] [features] default = ["std"] -std = ["data-encoding/std"] +std = ["data-encoding/std", "base-x"] [dependencies] -base-x = { version = "0.2.7", default-features = false } +base-x = { version = "0.2.7", default-features = false, optional = true } +smol-base-x = {version = "0.1.0"} data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] } data-encoding-macro = "0.1.9" diff --git a/src/base.rs b/src/base.rs index c6a734b..3779ed3 100644 --- a/src/base.rs +++ b/src/base.rs @@ -5,7 +5,7 @@ use crate::impls::*; use alloc::{string::String, vec::Vec}; macro_rules! build_base_enum { - ( $(#[$attr:meta] $code:expr => $base:ident,)* ) => { + ( $(#[$attr:meta] $base:ident,)* ) => { /// List of types currently supported in the multibase spec. /// /// Not all base types are supported by this library. @@ -13,12 +13,11 @@ macro_rules! build_base_enum { pub enum Base { $( #[$attr] $base, )* } - impl Base { /// Convert a number to the matching base algorithm, or `Error` if no algorithm is matching. pub fn from_code(code: char) -> Result { match code { - $( $code => Ok(Self::$base), )* + $( $base::CODE => Ok(Self::$base), )* _ => Err(Error::UnknownBase(code)), } } @@ -26,14 +25,14 @@ macro_rules! build_base_enum { /// Get the code corresponding to the base algorithm. pub fn code(&self) -> char { match self { - $( Self::$base => $code, )* + $( Self::$base => $base::CODE, )* } } /// Encode the given byte slice to base string. pub fn encode>(&self, input: I) -> String { match self { - $( Self::$base => $base::encode(input), )* + $( Self::$base => $base::encode(input).unwrap(), )* // encode wont panic for String } } @@ -44,54 +43,56 @@ macro_rules! build_base_enum { } } } + + } } build_base_enum! { /// 8-bit binary (encoder and decoder keeps data unmodified). - '\x00' => Identity, + Identity, /// Base2 (alphabet: 01). - '0' => Base2, + Base2, /// Base8 (alphabet: 01234567). - '7' => Base8, + Base8, /// Base10 (alphabet: 0123456789). - '9' => Base10, + Base10, /// Base16 lower hexadecimal (alphabet: 0123456789abcdef). - 'f' => Base16Lower, + Base16Lower, /// Base16 upper hexadecimal (alphabet: 0123456789ABCDEF). - 'F' => Base16Upper, + Base16Upper, /// Base32, rfc4648 no padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). - 'b' => Base32Lower, + Base32Lower, /// Base32, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). - 'B' => Base32Upper, + Base32Upper, /// Base32, rfc4648 with padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). - 'c' => Base32PadLower, + Base32PadLower, /// Base32, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). - 'C' => Base32PadUpper, + Base32PadUpper, /// Base32hex, rfc4648 no padding (alphabet: 0123456789abcdefghijklmnopqrstuv). - 'v' => Base32HexLower, + Base32HexLower, /// Base32hex, rfc4648 no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). - 'V' => Base32HexUpper, + Base32HexUpper, /// Base32hex, rfc4648 with padding (alphabet: 0123456789abcdefghijklmnopqrstuv). - 't' => Base32HexPadLower, + Base32HexPadLower, /// Base32hex, rfc4648 with padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). - 'T' => Base32HexPadUpper, + Base32HexPadUpper, /// z-base-32 (used by Tahoe-LAFS) (alphabet: ybndrfg8ejkmcpqxot1uwisza345h769). - 'h' => Base32Z, + Base32Z, /// Base36, [0-9a-z] no padding (alphabet: 0123456789abcdefghijklmnopqrstuvwxyz). - 'k' => Base36Lower, + Base36Lower, /// Base36, [0-9A-Z] no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ). - 'K' => Base36Upper, + Base36Upper, /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). - 'Z' => Base58Flickr, + Base58Flickr, /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). - 'z' => Base58Btc, + Base58Btc, /// Base64, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). - 'm' => Base64, + Base64, /// Base64, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). - 'M' => Base64Pad, + Base64Pad, /// Base64 url, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). - 'u' => Base64Url, + Base64Url, /// Base64 url, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). - 'U' => Base64UrlPad, + Base64UrlPad, } diff --git a/src/impls.rs b/src/impls.rs index 2d55652..bd38cc4 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,42 +1,53 @@ use crate::encoding; use crate::error::Result; +pub use big::*; + #[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; macro_rules! derive_base_encoding { - ( $(#[$doc:meta] $type:ident, $encoding:expr;)* ) => { + ( $(#[$doc:meta] $code:literal => $type:ident, $encoding:expr;)* ) => { $( #[$doc] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct $type; - impl BaseCodec for $type { - fn encode>(input: I) -> String { - $encoding.encode(input.as_ref()) + impl BaseCodec> for $type { + const CODE: char = $code; + + type Error = crate::Error; + + fn encode(input: impl AsRef<[u8]>) -> Result { + Ok($encoding.encode(input.as_ref())) } - fn decode>(input: I) -> Result> { + fn decode(input: impl AsRef) -> Result> { Ok($encoding.decode(input.as_ref().as_bytes())?) } } + )* }; } macro_rules! derive_base_x { - ( $(#[$doc:meta] $type:ident, $encoding:expr;)* ) => { + ( $(#[$doc:meta] $code:literal => $type:ident, $encoding:expr;)* ) => { $( #[$doc] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct $type; - impl BaseCodec for $type { - fn encode>(input: I) -> String { - base_x::encode($encoding, input.as_ref()) + impl BaseCodec> for $type { + const CODE: char = $code; + + type Error = crate::Error; + + fn encode(input: impl AsRef<[u8]>) -> Result { + Ok(base_x::encode($encoding, input.as_ref())) } - fn decode>(input: I) -> Result> { + fn decode(input: impl AsRef) -> Result> { Ok(base_x::decode($encoding, input.as_ref())?) } } @@ -44,102 +55,161 @@ macro_rules! derive_base_x { }; } -pub(crate) trait BaseCodec { +pub(crate) trait BaseCodec { + const CODE: char; + + /// dont matter xd + type Error; + /// Encode with the given byte slice. - fn encode>(input: I) -> String; + fn encode(input: impl AsRef<[u8]>) -> core::result::Result; + /// Decode with the given string (as slice). + fn decode(input: impl AsRef) -> core::result::Result; - /// Decode with the given string. - fn decode>(input: I) -> Result>; + fn code(&self) -> char { + Self::CODE + } } /// Identity, 8-bit binary (encoder and decoder keeps data unmodified). #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct Identity; -impl BaseCodec for Identity { - fn encode>(input: I) -> String { - String::from_utf8(input.as_ref().to_vec()).expect("input must be valid UTF-8 bytes") +impl BaseCodec> for Identity { + const CODE: char = '\x00'; + + type Error = crate::Error; + + fn encode(input: impl AsRef<[u8]>) -> Result { + String::from_utf8(input.as_ref().to_vec()).map_err(|e| crate::Error::InvalidBaseString) } - fn decode>(input: I) -> Result> { + fn decode(input: impl AsRef) -> Result> { Ok(input.as_ref().as_bytes().to_vec()) } } derive_base_encoding! { /// Base2 (alphabet: 01). - Base2, encoding::BASE2; + '0' => Base2, encoding::BASE2; /// Base8 (alphabet: 01234567). - Base8, encoding::BASE8; + '7' => Base8, encoding::BASE8; /// Base16 lower hexadecimal (alphabet: 0123456789abcdef). - Base16Lower, encoding::BASE16_LOWER; + 'f' => Base16Lower, encoding::BASE16_LOWER; /// Base16 upper hexadecimal (alphabet: 0123456789ABCDEF). - Base16Upper, encoding::BASE16_UPPER; + 'F' => Base16Upper, encoding::BASE16_UPPER; /// Base32, rfc4648 no padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). - Base32Lower, encoding::BASE32_NOPAD_LOWER; + 'b' => Base32Lower, encoding::BASE32_NOPAD_LOWER; /// Base32, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). - Base32Upper, encoding::BASE32_NOPAD_UPPER; + 'B' => Base32Upper, encoding::BASE32_NOPAD_UPPER; /// Base32, rfc4648 with padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). - Base32PadLower, encoding::BASE32_PAD_LOWER; + 'c' => Base32PadLower, encoding::BASE32_PAD_LOWER; /// Base32, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). - Base32PadUpper, encoding::BASE32_PAD_UPPER; + 'C' => Base32PadUpper, encoding::BASE32_PAD_UPPER; /// Base32hex, rfc4648 no padding (alphabet: 0123456789abcdefghijklmnopqrstuv). - Base32HexLower, encoding::BASE32HEX_NOPAD_LOWER; + 'v' => Base32HexLower, encoding::BASE32HEX_NOPAD_LOWER; /// Base32hex, rfc4648 no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). - Base32HexUpper, encoding::BASE32HEX_NOPAD_UPPER; + 'V' => Base32HexUpper, encoding::BASE32HEX_NOPAD_UPPER; /// Base32hex, rfc4648 with padding (alphabet: 0123456789abcdefghijklmnopqrstuv). - Base32HexPadLower, encoding::BASE32HEX_PAD_LOWER; + 't' => Base32HexPadLower, encoding::BASE32HEX_PAD_LOWER; /// Base32hex, rfc4648 with padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). - Base32HexPadUpper, encoding::BASE32HEX_PAD_UPPER; + 'T' => Base32HexPadUpper, encoding::BASE32HEX_PAD_UPPER; /// z-base-32 (used by Tahoe-LAFS) (alphabet: ybndrfg8ejkmcpqxot1uwisza345h769). - Base32Z, encoding::BASE32Z; + 'h' => Base32Z, encoding::BASE32Z; /// Base64, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). - Base64, encoding::BASE64_NOPAD; + 'm' => Base64, encoding::BASE64_NOPAD; /// Base64, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). - Base64Pad, encoding::BASE64_PAD; + 'M' => Base64Pad, encoding::BASE64_PAD; /// Base64 url, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). - Base64Url, encoding::BASE64URL_NOPAD; + 'u' => Base64Url, encoding::BASE64URL_NOPAD; /// Base64 url, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). - Base64UrlPad, encoding::BASE64URL_PAD; + 'U' => Base64UrlPad, encoding::BASE64URL_PAD; } derive_base_x! { /// Base10 (alphabet: 0123456789). - Base10, encoding::BASE10; + '9' => Base10, encoding::BASE10; /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). - Base58Flickr, encoding::BASE58_FLICKR; + 'Z' => Base58Flickr, encoding::BASE58_FLICKR; /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). - Base58Btc, encoding::BASE58_BITCOIN; + 'z' => Base58Btc, encoding::BASE58_BITCOIN; } -/// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub(crate) struct Base36Lower; -impl BaseCodec for Base36Lower { - fn encode>(input: I) -> String { - base_x::encode(encoding::BASE36_LOWER, input.as_ref()) - } +mod big { + use base_x; - fn decode>(input: I) -> Result> { - // The input is case insensitive, hence lowercase it - let lowercased = input.as_ref().to_ascii_lowercase(); - Ok(base_x::decode(encoding::BASE36_LOWER, &lowercased)?) - } -} + use crate::encoding; -/// Base36, [0-9A-Z] no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789). -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub(crate) struct Base36Upper; + use super::{BaseCodec, Result}; + /// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub(crate) struct Base36Lower; + + impl BaseCodec> for Base36Lower { + const CODE: char = 'k'; + + type Error = crate::Error; + + fn encode(input: impl AsRef<[u8]>) -> Result { + Ok(base_x::encode(encoding::BASE36_LOWER, input.as_ref())) + } -impl BaseCodec for Base36Upper { - fn encode>(input: I) -> String { - base_x::encode(encoding::BASE36_UPPER, input.as_ref()) + fn decode(input: impl AsRef) -> Result> { + // The input is case insensitive, hence lowercase it + let lowercased = input.as_ref().to_ascii_lowercase(); + Ok(base_x::decode(encoding::BASE36_LOWER, &lowercased)?) + } } - fn decode>(input: I) -> Result> { - // The input is case insensitive, hence uppercase it - let uppercased = input.as_ref().to_ascii_uppercase(); - Ok(base_x::decode(encoding::BASE36_UPPER, &uppercased)?) + /// Base36, [0-9A-Z] no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789). + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub(crate) struct Base36Upper; + + impl BaseCodec> for Base36Upper { + const CODE: char = 'K'; + + type Error = crate::Error; + + fn encode(input: impl AsRef<[u8]>) -> Result { + Ok(base_x::encode(encoding::BASE36_UPPER, input.as_ref())) + } + + fn decode(input: impl AsRef) -> Result> { + // The input is case insensitive, hence uppercase it + let uppercased = input.as_ref().to_ascii_uppercase(); + Ok(base_x::decode(encoding::BASE36_UPPER, &uppercased)?) + } } } + +// mod smol { +// use smol_base_x::Base; + +// use super::BaseCodec; +// use super::Result; +// /// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). +// #[derive(PartialEq, Eq, Clone, Copy, Debug)] +// pub(crate) struct Base36Lower; + +// impl Base<36> for Base36Lower { +// const ALPHABET: [u8; 36] = *b"abcdefghijklmnopqrstuvwxyz0123456789"; +// } + +// impl BaseCodec for Base36Lower { +// fn encode>(input: I) -> String { +// let mut buf = [0u8; S]; +// let written = Base36Lower::encode_mut(input, &mut buf).unwrap(); +// String::from_utf8(buf[..written].to_vec()).unwrap() +// } + +// fn decode>(input: I) -> Result> { +// // The input is case insensitive, hence lowercase it +// let lowercased = input.as_ref().to_ascii_lowercase(); +// let mut buf = [0u8; S]; +// let written = Base36Lower::decode_mut(input.as_ref(), &mut buf).unwrap(); + +// Ok(buf[..written].to_owned()) +// } +// } +// } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3b7960d..cc54a9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ mod encoding; mod error; mod impls; -pub use self::base::Base; +pub use self::base::Base as BaseEnum; pub use self::error::{Error, Result}; /// Decode the base string. @@ -31,10 +31,10 @@ pub use self::error::{Error, Result}; /// (Base::Base58Btc, b"hello".to_vec()) /// ); /// ``` -pub fn decode>(input: T) -> Result<(Base, Vec)> { +pub fn decode>(input: T) -> Result<(BaseEnum, Vec)> { let input = input.as_ref(); let code = input.chars().next().ok_or(Error::InvalidBaseString)?; - let base = Base::from_code(code)?; + let base = BaseEnum::from_code(code)?; let decoded = base.decode(&input[code.len_utf8()..])?; Ok((base, decoded)) } @@ -48,7 +48,7 @@ pub fn decode>(input: T) -> Result<(Base, Vec)> { /// /// assert_eq!(encode(Base::Base58Btc, b"hello"), "zCn8eVZg"); /// ``` -pub fn encode>(base: Base, input: T) -> String { +pub fn encode>(base: BaseEnum, input: T) -> String { let input = input.as_ref(); let mut encoded = base.encode(input.as_ref()); encoded.insert(0, base.code()); From 5e5ea1005652a88cf59181cb0dbdeb208b5ec3d9 Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 12 Jul 2022 13:53:43 +0000 Subject: [PATCH 2/6] Add stack based en/de coding - add seperate enum for stack based encoding - add helper functions for encoding and decoding on the stack - add alloc feature - split codec code out from base codec trait --- Cargo.toml | 5 +- src/base.rs | 82 +++++++++++++++++++++++++- src/error.rs | 3 + src/impls.rs | 160 ++++++++++++++++++++++++++++++++------------------- src/lib.rs | 62 ++++++++++++++++++-- 5 files changed, 244 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70c2230..618a967 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,14 @@ keywords = ["ipld", "ipfs", "multihash", "cid", "no_std"] [features] default = ["std"] -std = ["data-encoding/std", "base-x"] +std = ["data-encoding/std", "base-x", "alloc"] +alloc = [] +unstable = ["smol-base-x/unstable"] [dependencies] base-x = { version = "0.2.7", default-features = false, optional = true } smol-base-x = {version = "0.1.0"} +heapless = "0.7.14" data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] } data-encoding-macro = "0.1.9" diff --git a/src/base.rs b/src/base.rs index 3779ed3..2a2728b 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,9 +1,13 @@ +use core::convert::TryFrom; +use core::fmt; + use crate::error::{Error, Result}; use crate::impls::*; -#[cfg(not(feature = "std"))] +#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::{string::String, vec::Vec}; +#[cfg(feature = "alloc")] macro_rules! build_base_enum { ( $(#[$attr:meta] $base:ident,)* ) => { /// List of types currently supported in the multibase spec. @@ -44,10 +48,11 @@ macro_rules! build_base_enum { } } - + } } +#[cfg(feature = "alloc")] build_base_enum! { /// 8-bit binary (encoder and decoder keeps data unmodified). Identity, @@ -96,3 +101,76 @@ build_base_enum! { /// Base64 url, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). Base64UrlPad, } + +macro_rules! build_base_const_enum { + ( $(#[$attr:meta] $base:ident,)* ) => { + /// List of types currently supported in the multibase spec. + /// + /// Not all base types are supported by this. + /// + /// TODO doc some more things + /// + /// ``` + /// use smol_base_x::util::encoded_arr_size; + /// use multibase::StackBase; + /// // type of + /// type AAA = StackBase<32, {encoded_arr_size(10, 32)}>; + /// ``` + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub enum StackBase { + $( #[$attr] $base, )* + } + impl StackBase { + /// Convert a number to the matching base algorithm, or `Error` if no algorithm is matching. + pub fn from_code(code: char) -> Result { + match code { + $( $base::CODE => Ok(Self::$base), )* + _ => Err(Error::UnknownBase(code)), + } + } + + /// Get the code corresponding to the base algorithm. + pub fn code(&self) -> char { + match self { + $( Self::$base => $base::CODE, )* + } + } + + /// Encode the given byte slice to base string. + pub fn encode>(&self, input: I) -> Result> { + match self { + $( Self::$base => <$base as BaseCodec, heapless::Vec>>::encode(input), )* + } + } + + /// Decode the base string. + pub fn decode>(&self, input: I) -> Result> { + match self { + $( Self::$base => <$base as BaseCodec, heapless::Vec>>::decode(input), )* + } + } + } + + + } +} + +use smol::{Base10S, Base58BtcS, Base58FlickrS}; + +build_base_const_enum! { + /// Base10 (alphabet: 0123456789). + Base10S, + /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). + Base58FlickrS, + /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). + Base58BtcS, +} + +#[cfg(feature = "alloc")] +impl TryFrom for Base { + type Error = Error; + + fn try_from(value: StackBase) -> Result { + Base::from_code(value.code()) + } +} diff --git a/src/error.rs b/src/error.rs index 0c18c1b..5884edd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,8 @@ pub enum Error { UnknownBase(char), /// Invalid string. InvalidBaseString, + /// Container being written into is too small. + ContainerTooSmall, } impl fmt::Display for Error { @@ -17,6 +19,7 @@ impl fmt::Display for Error { match self { Error::UnknownBase(code) => write!(f, "Unknown base code: {}", code), Error::InvalidBaseString => write!(f, "Invalid base string"), + Error::ContainerTooSmall => write!(f, "Output container too small"), } } } diff --git a/src/impls.rs b/src/impls.rs index bd38cc4..3f21873 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -13,11 +13,11 @@ macro_rules! derive_base_encoding { #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct $type; - impl BaseCodec> for $type { + impl CodecCode for $type { const CODE: char = $code; + } - type Error = crate::Error; - + impl BaseCodec> for $type { fn encode(input: impl AsRef<[u8]>) -> Result { Ok($encoding.encode(input.as_ref())) } @@ -26,7 +26,7 @@ macro_rules! derive_base_encoding { Ok($encoding.decode(input.as_ref().as_bytes())?) } } - + )* }; } @@ -38,11 +38,11 @@ macro_rules! derive_base_x { #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct $type; - impl BaseCodec> for $type { + impl CodecCode for $type { const CODE: char = $code; + } - type Error = crate::Error; - + impl BaseCodec> for $type { fn encode(input: impl AsRef<[u8]>) -> Result { Ok(base_x::encode($encoding, input.as_ref())) } @@ -55,33 +55,30 @@ macro_rules! derive_base_x { }; } -pub(crate) trait BaseCodec { +pub(crate) trait CodecCode { const CODE: char; - - /// dont matter xd - type Error; - +} + +/// Trait codecs use for encoding and decoding, generic over their output continer +/// output container of encode must have the ability to prepend one byte for the codec code +pub(crate) trait BaseCodec: CodecCode { /// Encode with the given byte slice. - fn encode(input: impl AsRef<[u8]>) -> core::result::Result; + fn encode(input: impl AsRef<[u8]>) -> Result; /// Decode with the given string (as slice). - fn decode(input: impl AsRef) -> core::result::Result; - - fn code(&self) -> char { - Self::CODE - } + fn decode(input: impl AsRef) -> Result; } /// Identity, 8-bit binary (encoder and decoder keeps data unmodified). #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct Identity; -impl BaseCodec> for Identity { +impl CodecCode for Identity { const CODE: char = '\x00'; +} - type Error = crate::Error; - +impl BaseCodec> for Identity { fn encode(input: impl AsRef<[u8]>) -> Result { - String::from_utf8(input.as_ref().to_vec()).map_err(|e| crate::Error::InvalidBaseString) + String::from_utf8(input.as_ref().to_vec()).map_err(|_| crate::Error::InvalidBaseString) } fn decode(input: impl AsRef) -> Result> { @@ -135,22 +132,21 @@ derive_base_x! { 'z' => Base58Btc, encoding::BASE58_BITCOIN; } - mod big { use base_x; use crate::encoding; - use super::{BaseCodec, Result}; + use super::{BaseCodec, CodecCode, Result}; /// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct Base36Lower; - impl BaseCodec> for Base36Lower { + impl CodecCode for Base36Lower { const CODE: char = 'k'; + } - type Error = crate::Error; - + impl BaseCodec> for Base36Lower { fn encode(input: impl AsRef<[u8]>) -> Result { Ok(base_x::encode(encoding::BASE36_LOWER, input.as_ref())) } @@ -166,11 +162,11 @@ mod big { #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub(crate) struct Base36Upper; - impl BaseCodec> for Base36Upper { + impl CodecCode for Base36Upper { const CODE: char = 'K'; + } - type Error = crate::Error; - + impl BaseCodec> for Base36Upper { fn encode(input: impl AsRef<[u8]>) -> Result { Ok(base_x::encode(encoding::BASE36_UPPER, input.as_ref())) } @@ -183,33 +179,77 @@ mod big { } } -// mod smol { -// use smol_base_x::Base; - -// use super::BaseCodec; -// use super::Result; -// /// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). -// #[derive(PartialEq, Eq, Clone, Copy, Debug)] -// pub(crate) struct Base36Lower; - -// impl Base<36> for Base36Lower { -// const ALPHABET: [u8; 36] = *b"abcdefghijklmnopqrstuvwxyz0123456789"; -// } - -// impl BaseCodec for Base36Lower { -// fn encode>(input: I) -> String { -// let mut buf = [0u8; S]; -// let written = Base36Lower::encode_mut(input, &mut buf).unwrap(); -// String::from_utf8(buf[..written].to_vec()).unwrap() -// } - -// fn decode>(input: I) -> Result> { -// // The input is case insensitive, hence lowercase it -// let lowercased = input.as_ref().to_ascii_lowercase(); -// let mut buf = [0u8; S]; -// let written = Base36Lower::decode_mut(input.as_ref(), &mut buf).unwrap(); - -// Ok(buf[..written].to_owned()) -// } -// } -// } \ No newline at end of file +pub mod smol { + + // TODO not unwrap + + use heapless::{String, Vec}; + use smol_base_x::Base; + + use crate::Error; + + use super::BaseCodec; + use super::CodecCode; + use super::Result; + + // it sucks a lot that I can't + // impl BaseCodec, Vec> for T where + // T: Base + CodecCode + + macro_rules! derive_smol_base_x { + ( $(#[$doc:meta] $code:literal => $base:literal:$type:ident, $encoding:expr;)* ) => { + $( + #[$doc] + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub(crate) struct $type; + + impl Base<$base> for $type { + const ALPHABET: [u8; $base] = $encoding; + } + + impl CodecCode for $type { + const CODE: char = $code; + } + + impl BaseCodec, Vec> for $type { + fn encode(input: impl AsRef<[u8]>) -> Result> { + let input = input.as_ref(); + let mut s = String::::default(); + + // SAFETY: trait Base should only contain ascii chars (otherwise would be a borken base implementation) + unsafe { + let vec = s.as_mut_vec(); + // resize is a safe operation + let len = crate::base_x_encoded_size(Self::BASE, input.len()); + vec.resize(len, 0) + .map_err(|_| Error::ContainerTooSmall)?; + // skips first byte to leave room for multibase code + $type::encode_mut(input, &mut vec[1..]).unwrap(); + } + Ok(s) + } + + fn decode(input: impl AsRef) -> Result> { + let input = input.as_ref(); + let mut buf = Vec::::default(); + let len = crate::base_x_decoded_size(Self::BASE, input.len()); + buf.resize(len, 0) + .map_err(|_| Error::ContainerTooSmall)?; + println!("{}", input); + $type::decode_mut(input, &mut buf).unwrap(); + Ok(buf) + } + } + )* + }; + } + + derive_smol_base_x! { + /// Base10 (alphabet: 0123456789). + '9' => 10:Base10S, *b"0123456789"; + /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). + 'z' => 58:Base58BtcS, *b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + /// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ). + 'Z' => 58:Base58FlickrS, *b"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; + } +} diff --git a/src/lib.rs b/src/lib.rs index cc54a9e..fa8551f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ mod encoding; mod error; mod impls; -pub use self::base::Base as BaseEnum; +pub use self::base::{Base, StackBase}; pub use self::error::{Error, Result}; /// Decode the base string. @@ -31,10 +31,10 @@ pub use self::error::{Error, Result}; /// (Base::Base58Btc, b"hello".to_vec()) /// ); /// ``` -pub fn decode>(input: T) -> Result<(BaseEnum, Vec)> { +pub fn decode>(input: T) -> Result<(Base, Vec)> { let input = input.as_ref(); let code = input.chars().next().ok_or(Error::InvalidBaseString)?; - let base = BaseEnum::from_code(code)?; + let base = Base::from_code(code)?; let decoded = base.decode(&input[code.len_utf8()..])?; Ok((base, decoded)) } @@ -48,9 +48,61 @@ pub fn decode>(input: T) -> Result<(BaseEnum, Vec)> { /// /// assert_eq!(encode(Base::Base58Btc, b"hello"), "zCn8eVZg"); /// ``` -pub fn encode>(base: BaseEnum, input: T) -> String { +pub fn encode>(base: Base, input: T) -> String { let input = input.as_ref(); - let mut encoded = base.encode(input.as_ref()); + let mut encoded = base.encode(input); encoded.insert(0, base.code()); encoded } + +/// Encode with the given byte slice to *stack allocated* base string. +/// +/// # Examples +/// +/// ``` +/// use multibase::{StackBase, encode_arr}; +/// +/// assert_eq!(encode_arr::<32>(StackBase::Base58BtcS, b"hello").unwrap(), "zCn8eVZg"); +/// ``` +pub fn encode_arr( + base: StackBase<0, S>, + input: &[u8], +) -> Result> { + let mut out = base.encode(input)?; + // encode() leaves an open byte in the begining (for stack implementations) + // SAFETY: this trusts that and all (implemented) multibase codes are ascii + unsafe { out.as_mut_vec()[0] = base.code() as u8 }; + Ok(out) +} + +/// Decode the given byte slice to *stack allocated* output buffer. +/// +/// # Examples +/// +/// ``` +/// use multibase::{StackBase, decode_arr}; +/// +/// assert_eq!(decode_arr::<32>("zCn8eVZg").unwrap().1.as_slice(), "hello".as_bytes()); +/// ``` +pub fn decode_arr(input: &str) -> Result<(StackBase, heapless::Vec)> { + let code = input.chars().next().ok_or(Error::InvalidBaseString)?; + let base = StackBase::from_code(code)?; + let decoded = base.decode(&input[code.len_utf8()..])?; + Ok((base, decoded)) +} + +/// generates encoded size of a given base and input length, useful for determining max encoded size +/// +/// given as `smol_base_x::encoded_size + 1` +pub fn base_x_encoded_size(base: usize, bytes_len: usize) -> usize { + use smol_base_x::util::encoded_size; + encoded_size(base, bytes_len) + 1 +} + +/// generates decoded size of a given base and input byte length, useful for determining decoded size +/// +/// given as `smol_base_x::decoded_size - 1` +pub fn base_x_decoded_size(base: usize, bytes_len: usize) -> usize { + use smol_base_x::util::decoded_size; + decoded_size(base, bytes_len) - 1 +} From 735d4d0930660c513a4a864eea980aa75f3a84ee Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 12 Jul 2022 14:17:29 +0000 Subject: [PATCH 3/6] import cleanup --- Cargo.toml | 4 ++-- src/base.rs | 4 +++- src/error.rs | 1 + src/impls.rs | 48 +++++++++++++++++++++++------------------------- src/lib.rs | 11 ++++++++--- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 618a967..412fc43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ keywords = ["ipld", "ipfs", "multihash", "cid", "no_std"] [features] default = ["std"] -std = ["data-encoding/std", "base-x", "alloc"] -alloc = [] +std = ["data-encoding/std", "alloc"] +alloc = ["base-x"] unstable = ["smol-base-x/unstable"] [dependencies] diff --git a/src/base.rs b/src/base.rs index 2a2728b..38b50a0 100644 --- a/src/base.rs +++ b/src/base.rs @@ -1,5 +1,4 @@ use core::convert::TryFrom; -use core::fmt; use crate::error::{Error, Result}; use crate::impls::*; @@ -7,6 +6,9 @@ use crate::impls::*; #[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::{string::String, vec::Vec}; +#[cfg(feature = "alloc")] +use crate::impls::alloc::*; + #[cfg(feature = "alloc")] macro_rules! build_base_enum { ( $(#[$attr:meta] $base:ident,)* ) => { diff --git a/src/error.rs b/src/error.rs index 5884edd..bd46223 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,6 +27,7 @@ impl fmt::Display for Error { #[cfg(feature = "std")] impl std::error::Error for Error {} +#[cfg(feature = "alloc")] impl From for Error { fn from(_: base_x::DecodeError) -> Self { Self::InvalidBaseString diff --git a/src/impls.rs b/src/impls.rs index 3f21873..f96a4c1 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,9 +1,7 @@ use crate::encoding; use crate::error::Result; -pub use big::*; - -#[cfg(not(feature = "std"))] +#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::{string::String, vec::Vec}; macro_rules! derive_base_encoding { @@ -68,24 +66,7 @@ pub(crate) trait BaseCodec: CodecCode { fn decode(input: impl AsRef) -> Result; } -/// Identity, 8-bit binary (encoder and decoder keeps data unmodified). -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub(crate) struct Identity; - -impl CodecCode for Identity { - const CODE: char = '\x00'; -} - -impl BaseCodec> for Identity { - fn encode(input: impl AsRef<[u8]>) -> Result { - String::from_utf8(input.as_ref().to_vec()).map_err(|_| crate::Error::InvalidBaseString) - } - - fn decode(input: impl AsRef) -> Result> { - Ok(input.as_ref().as_bytes().to_vec()) - } -} - +#[cfg(feature = "alloc")] derive_base_encoding! { /// Base2 (alphabet: 01). '0' => Base2, encoding::BASE2; @@ -123,6 +104,7 @@ derive_base_encoding! { 'U' => Base64UrlPad, encoding::BASE64URL_PAD; } +#[cfg(feature = "alloc")] derive_base_x! { /// Base10 (alphabet: 0123456789). '9' => Base10, encoding::BASE10; @@ -132,11 +114,28 @@ derive_base_x! { 'z' => Base58Btc, encoding::BASE58_BITCOIN; } -mod big { - use base_x; - +#[cfg(feature = "alloc")] +pub(crate) mod alloc { use crate::encoding; + /// Identity, 8-bit binary (encoder and decoder keeps data unmodified). + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + pub(crate) struct Identity; + + impl CodecCode for Identity { + const CODE: char = '\x00'; + } + + impl BaseCodec> for Identity { + fn encode(input: impl AsRef<[u8]>) -> Result { + String::from_utf8(input.as_ref().to_vec()).map_err(|_| crate::Error::InvalidBaseString) + } + + fn decode(input: impl AsRef) -> Result> { + Ok(input.as_ref().as_bytes().to_vec()) + } + } + use super::{BaseCodec, CodecCode, Result}; /// Base36, [0-9a-z] no padding (alphabet: abcdefghijklmnopqrstuvwxyz0123456789). #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -235,7 +234,6 @@ pub mod smol { let len = crate::base_x_decoded_size(Self::BASE, input.len()); buf.resize(len, 0) .map_err(|_| Error::ContainerTooSmall)?; - println!("{}", input); $type::decode_mut(input, &mut buf).unwrap(); Ok(buf) } diff --git a/src/lib.rs b/src/lib.rs index fa8551f..fea2ecf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,10 +5,10 @@ #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] +#[cfg(all(feature = "alloc", not(feature = "std")))] extern crate alloc; -#[cfg(not(feature = "std"))] +#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::{string::String, vec::Vec}; mod base; @@ -16,9 +16,13 @@ mod encoding; mod error; mod impls; -pub use self::base::{Base, StackBase}; +#[cfg(feature = "alloc")] +pub use self::base::Base; + +pub use self::base::StackBase; pub use self::error::{Error, Result}; +#[cfg(feature = "alloc")] /// Decode the base string. /// /// # Examples @@ -39,6 +43,7 @@ pub fn decode>(input: T) -> Result<(Base, Vec)> { Ok((base, decoded)) } +#[cfg(feature = "alloc")] /// Encode with the given byte slice to base string. /// /// # Examples From 5e57694b9cca57404fad1e6994fd8190e9346f5d Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 12 Jul 2022 14:51:22 +0000 Subject: [PATCH 4/6] export heapless vec and string --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fea2ecf..8da97ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub use self::base::Base; pub use self::base::StackBase; pub use self::error::{Error, Result}; +pub use heapless::{Vec as StackVec, String as StackString}; #[cfg(feature = "alloc")] /// Decode the base string. @@ -72,7 +73,7 @@ pub fn encode>(base: Base, input: T) -> String { pub fn encode_arr( base: StackBase<0, S>, input: &[u8], -) -> Result> { +) -> Result> { let mut out = base.encode(input)?; // encode() leaves an open byte in the begining (for stack implementations) // SAFETY: this trusts that and all (implemented) multibase codes are ascii @@ -89,7 +90,7 @@ pub fn encode_arr( /// /// assert_eq!(decode_arr::<32>("zCn8eVZg").unwrap().1.as_slice(), "hello".as_bytes()); /// ``` -pub fn decode_arr(input: &str) -> Result<(StackBase, heapless::Vec)> { +pub fn decode_arr(input: &str) -> Result<(StackBase, StackVec)> { let code = input.chars().next().ok_or(Error::InvalidBaseString)?; let base = StackBase::from_code(code)?; let decoded = base.decode(&input[code.len_utf8()..])?; From c045c48ab9a9fa56441d13145cf48b59a69fdb4a Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 12 Jul 2022 18:22:58 +0000 Subject: [PATCH 5/6] impl stack en/de coding for all `data_encoding` bits --- src/base.rs | 34 ++++++++++++++++++++++++++++++++++ src/impls.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/base.rs b/src/base.rs index 38b50a0..48f8496 100644 --- a/src/base.rs +++ b/src/base.rs @@ -166,6 +166,40 @@ build_base_const_enum! { Base58FlickrS, /// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz). Base58BtcS, + /// Base2 (alphabet: 01). + Base2, + /// Base8 (alphabet: 01234567). + Base8, + /// Base16 lower hexadecimal (alphabet: 0123456789abcdef). + Base16Lower, + /// Base16 upper hexadecimal (alphabet: 0123456789ABCDEF). + Base16Upper, + /// Base32, rfc4648 no padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). + Base32Lower, + /// Base32, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). + Base32Upper, + /// Base32, rfc4648 with padding (alphabet: abcdefghijklmnopqrstuvwxyz234567). + Base32PadLower, + /// Base32, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). + Base32PadUpper, + /// Base32hex, rfc4648 no padding (alphabet: 0123456789abcdefghijklmnopqrstuv). + Base32HexLower, + /// Base32hex, rfc4648 no padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). + Base32HexUpper, + /// Base32hex, rfc4648 with padding (alphabet: 0123456789abcdefghijklmnopqrstuv). + Base32HexPadLower, + /// Base32hex, rfc4648 with padding (alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUV). + Base32HexPadUpper, + /// z-base-32 (used by Tahoe-LAFS) (alphabet: ybndrfg8ejkmcpqxot1uwisza345h769). + Base32Z, + /// Base64, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). + Base64, + /// Base64, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/). + Base64Pad, + /// Base64 url, rfc4648 no padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). + Base64Url, + /// Base64 url, rfc4648 with padding (alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_). + Base64UrlPad, } #[cfg(feature = "alloc")] diff --git a/src/impls.rs b/src/impls.rs index f96a4c1..d8ee940 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,4 +1,4 @@ -use crate::encoding; +use crate::{encoding, Error}; use crate::error::Result; #[cfg(all(feature = "alloc", not(feature = "std")))] @@ -25,6 +25,36 @@ macro_rules! derive_base_encoding { } } + impl BaseCodec, heapless::Vec> for $type { + fn encode(input: impl AsRef<[u8]>) -> Result> { + let input = input.as_ref(); + let mut s = heapless::String::::default(); + + // SAFETY: trait Base should only contain ascii chars (otherwise would be a borken base implementation) + unsafe { + let vec = s.as_mut_vec(); + // resize is a safe operation + let len = $encoding.encode_len(input.len()); + + vec.resize(len, 0) + .map_err(|_| Error::ContainerTooSmall)?; + // skips first byte to leave room for multibase code + $encoding.encode_mut(input, &mut vec[1..]); + } + Ok(s) + } + + fn decode(input: impl AsRef) -> Result> { + let input = input.as_ref(); + let mut buf = heapless::Vec::::default(); + let len = $encoding.decode_len(input.len())?; + buf.resize(len, 0) + .map_err(|_| Error::ContainerTooSmall)?; + $encoding.decode_mut(input.as_bytes(), &mut buf).unwrap(); + Ok(buf) + } + } + )* }; } From a2f5b9e8e0d95bf562925b37c57f11baaad5a9f8 Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 12 Jul 2022 18:34:17 +0000 Subject: [PATCH 6/6] put feature flag above alloc things, not the whole base encoding macro --- src/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/impls.rs b/src/impls.rs index d8ee940..d9db2a8 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -15,6 +15,7 @@ macro_rules! derive_base_encoding { const CODE: char = $code; } + #[cfg(feature = "alloc")] impl BaseCodec> for $type { fn encode(input: impl AsRef<[u8]>) -> Result { Ok($encoding.encode(input.as_ref())) @@ -96,7 +97,6 @@ pub(crate) trait BaseCodec: CodecCode { fn decode(input: impl AsRef) -> Result; } -#[cfg(feature = "alloc")] derive_base_encoding! { /// Base2 (alphabet: 01). '0' => Base2, encoding::BASE2;