Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack allocated encoding/decoding #70

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ keywords = ["ipld", "ipfs", "multihash", "cid", "no_std"]

[features]
default = ["std"]
std = ["data-encoding/std"]
std = ["data-encoding/std", "alloc"]
alloc = ["base-x"]
unstable = ["smol-base-x/unstable"]

[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"}
heapless = "0.7.14"
data-encoding = { version = "2.3.1", default-features = false, features = ["alloc"] }
data-encoding-macro = "0.1.9"

Expand Down
173 changes: 144 additions & 29 deletions src/base.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
use core::convert::TryFrom;

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")]
use crate::impls::alloc::*;

#[cfg(feature = "alloc")]
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.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
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<Self> {
match code {
$( $code => Ok(Self::$base), )*
$( $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 => $code, )*
$( Self::$base => $base::CODE, )*
}
}

/// Encode the given byte slice to base string.
pub fn encode<I: AsRef<[u8]>>(&self, input: I) -> String {
match self {
$( Self::$base => $base::encode(input), )*
$( Self::$base => $base::encode(input).unwrap(), )* // encode wont panic for String
}
}

Expand All @@ -44,54 +49,164 @@ macro_rules! build_base_enum {
}
}
}


}
}

#[cfg(feature = "alloc")]
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).
Base58Flickr,
/// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz).
Base58Btc,
/// 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,
}

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 <decoded size, fn(lowest efficiency encoding algorithm, decoded size) >
/// type AAA = StackBase<32, {encoded_arr_size(10, 32)}>;
/// ```
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum StackBase<const DECODED_LEN: usize = 0, const ENCODED_LEN: usize = 0> {
$( #[$attr] $base, )*
}
impl<const D: usize, const E: usize> StackBase<D, E> {
/// Convert a number to the matching base algorithm, or `Error` if no algorithm is matching.
pub fn from_code(code: char) -> Result<Self> {
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<I: AsRef<[u8]>>(&self, input: I) -> Result<heapless::String<E>> {
match self {
$( Self::$base => <$base as BaseCodec<heapless::String<E>, heapless::Vec<u8, D>>>::encode(input), )*
}
}

/// Decode the base string.
pub fn decode<I: AsRef<str>>(&self, input: I) -> Result<heapless::Vec<u8, D>> {
match self {
$( Self::$base => <$base as BaseCodec<heapless::String<E>, heapless::Vec<u8, D>>>::decode(input), )*
}
}
}


}
}

use smol::{Base10S, Base58BtcS, Base58FlickrS};

build_base_const_enum! {
/// Base10 (alphabet: 0123456789).
Base10S,
/// Base58 flicker (alphabet: 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ).
'Z' => Base58Flickr,
Base58FlickrS,
/// Base58 bitcoin (alphabet: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz).
'z' => Base58Btc,
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+/).
'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,
}

#[cfg(feature = "alloc")]
impl TryFrom<StackBase> for Base {
type Error = Error;

fn try_from(value: StackBase) -> Result<Self> {
Base::from_code(value.code())
}
}
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ pub enum Error {
UnknownBase(char),
/// Invalid string.
InvalidBaseString,
/// Container being written into is too small.
ContainerTooSmall,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
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"),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

#[cfg(feature = "alloc")]
impl From<base_x::DecodeError> for Error {
fn from(_: base_x::DecodeError) -> Self {
Self::InvalidBaseString
Expand Down
Loading