From 2bda23d1129e5eb04844ff746fa21eaad6bbc36b Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 28 Oct 2021 12:19:41 -0700 Subject: [PATCH] support io (read/write) without core This: 1. Uses core2 when std isn't supported. 2. Adds an alloc feature for building with and without the alloc crate. Uses https://github.com/multiformats/rust-multihash/pull/146. Unfortunately, we can't do multibase formatting yet due to https://github.com/multiformats/rust-multibase/issues/33. But we can fix that later. --- Cargo.toml | 12 +++++-- src/cid.rs | 94 +++++++++++++++++++++++++++++++++++----------------- src/error.rs | 17 ++++++---- src/lib.rs | 2 +- 4 files changed, 83 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6222078..b21f45b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,20 +15,26 @@ no-dev-version = true [features] default = ["std", "multihash/default"] -std = ["multibase", "multihash/std", "unsigned-varint/std"] +std = ["multihash/std", "unsigned-varint/std", "alloc", "multibase/std"] +alloc = ["multibase", "multihash/alloc"] arb = ["quickcheck", "rand", "multihash/arb"] scale-codec = ["parity-scale-codec", "multihash/scale-codec"] serde-codec = ["serde", "multihash/serde-codec"] [dependencies] -multihash = { version = "0.14.0", default-features = false } +multihash = { version = "0.15.0", default-features = false } unsigned-varint = { version = "0.7.0", default-features = false } -multibase = { version = "0.9.1", optional = true } +multibase = { version = "0.9.1", optional = true, default-features = false } parity-scale-codec = { version = "2.1.1", default-features = false, features = ["derive"], optional = true } quickcheck = { version = "0.9.2", optional = true } rand = { version = "0.7.3", optional = true } serde = { version = "1.0.116", optional = true } +core2 = { version = "0.3", default-features = false, features = ["alloc"] } + [dev-dependencies] serde_json = "1.0.59" + +[patch.crates-io] +multihash = { git = "https://github.com/yatima-inc/rust-multihash", branch = "sb/core-io" } \ No newline at end of file diff --git a/src/cid.rs b/src/cid.rs index 4d8de82..2fd0c44 100644 --- a/src/cid.rs +++ b/src/cid.rs @@ -5,14 +5,50 @@ //! //! As a library author that works with CIDs that should support hashes of anysize, you would //! import the `Cid` type from this module. -#[cfg(feature = "std")] -use std::convert::TryFrom; +use core::convert::TryFrom; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] use multibase::{encode as base_encode, Base}; + use multihash::{MultihashGeneric as Multihash, Size}; +use unsigned_varint::encode as varint_encode; + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::{ + borrow, + string::{String, ToString}, + vec::Vec, +}; + +#[cfg(feature = "std")] +pub(crate) use unsigned_varint::io::read_u64 as varint_read_u64; + +/// Reads 64 bits from a byte array into a u64 +/// Adapted from unsigned-varint's generated read_u64 function at +/// https://github.com/paritytech/unsigned-varint/blob/master/src/io.rs +#[cfg(not(feature = "std"))] +pub(crate) fn varint_read_u64(mut r: R) -> Result { + use unsigned_varint::decode; + let mut b = varint_encode::u64_buffer(); + for i in 0..b.len() { + let n = r.read(&mut (b[i..i + 1]))?; + if n == 0 { + return Err(Error::VarIntDecodeError); + } else if decode::is_last(b[i]) { + return Ok(decode::u64(&b[..=i]).unwrap().0); + } + } + Err(Error::VarIntDecodeError) +} + #[cfg(feature = "std")] -use unsigned_varint::{encode as varint_encode, io::read_u64 as varint_read_u64}; +use std::io; + +#[cfg(not(feature = "std"))] +use core2::io; use crate::error::{Error, Result}; use crate::version::Version; @@ -93,8 +129,7 @@ impl Cid { } /// Reads the bytes from a byte stream. - #[cfg(feature = "std")] - pub fn read_bytes(mut r: R) -> Result { + pub fn read_bytes(mut r: R) -> Result { let version = varint_read_u64(&mut r)?; let codec = varint_read_u64(&mut r)?; // CIDv0 has the fixed `0x12 0x20` prefix @@ -110,8 +145,7 @@ impl Cid { } } - #[cfg(feature = "std")] - fn write_bytes_v1(&self, mut w: W) -> Result<()> { + fn write_bytes_v1(&self, mut w: W) -> Result<()> { let mut version_buf = varint_encode::u64_buffer(); let version = varint_encode::u64(self.version.into(), &mut version_buf); @@ -125,8 +159,7 @@ impl Cid { } /// Writes the bytes to a byte stream. - #[cfg(feature = "std")] - pub fn write_bytes(&self, w: W) -> Result<()> { + pub fn write_bytes(&self, w: W) -> Result<()> { match self.version { Version::V0 => self.hash.write(w)?, Version::V1 => self.write_bytes_v1(w)?, @@ -135,19 +168,19 @@ impl Cid { } /// Returns the encoded bytes of the `Cid`. - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] pub fn to_bytes(&self) -> Vec { - let mut bytes = vec![]; + let mut bytes = Vec::new(); self.write_bytes(&mut bytes).unwrap(); bytes } - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] fn to_string_v0(&self) -> String { Base::Base58Btc.encode(self.hash.to_bytes()) } - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] fn to_string_v1(&self) -> String { multibase::encode(Base::Base32Lower, self.to_bytes().as_slice()) } @@ -167,7 +200,7 @@ impl Cid { /// let encoded = cid.to_string_of_base(Base::Base64).unwrap(); /// assert_eq!(encoded, "mAVUSICwmtGto/8aP+ZtFPB0wQTQTQi1wZIO/oPmKXohiZueu"); /// ``` - #[cfg(feature = "std")] + #[cfg(feature = "alloc")] pub fn to_string_of_base(&self, base: Base) -> Result { match self.version { Version::V0 => { @@ -192,9 +225,9 @@ impl Default for Cid { } } -#[cfg(feature = "std")] -impl std::fmt::Display for Cid { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +#[cfg(feature = "alloc")] +impl core::fmt::Display for Cid { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { let output = match self.version { Version::V0 => self.to_string_v0(), Version::V1 => self.to_string_v1(), @@ -222,8 +255,8 @@ impl std::fmt::Debug for Cid { } } -#[cfg(feature = "std")] -impl std::str::FromStr for Cid { +#[cfg(feature = "alloc")] +impl core::str::FromStr for Cid { type Err = Error; fn from_str(cid_str: &str) -> Result { @@ -231,7 +264,7 @@ impl std::str::FromStr for Cid { } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl TryFrom for Cid { type Error = Error; @@ -240,7 +273,7 @@ impl TryFrom for Cid { } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl TryFrom<&str> for Cid { type Error = Error; @@ -267,7 +300,7 @@ impl TryFrom<&str> for Cid { } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl TryFrom> for Cid { type Error = Error; @@ -276,7 +309,6 @@ impl TryFrom> for Cid { } } -#[cfg(feature = "std")] impl TryFrom<&[u8]> for Cid { type Error = Error; @@ -294,31 +326,31 @@ where } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl From> for Vec { fn from(cid: Cid) -> Self { cid.to_bytes() } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl From> for String { fn from(cid: Cid) -> Self { cid.to_string() } } -#[cfg(feature = "std")] -impl<'a, S: Size> From> for std::borrow::Cow<'a, Cid> { +#[cfg(feature = "alloc")] +impl<'a, S: Size> From> for borrow::Cow<'a, Cid> { fn from(from: Cid) -> Self { - std::borrow::Cow::Owned(from) + borrow::Cow::Owned(from) } } #[cfg(feature = "std")] -impl<'a, S: Size> From<&'a Cid> for std::borrow::Cow<'a, Cid> { +impl<'a, S: Size> From<&'a Cid> for borrow::Cow<'a, Cid> { fn from(from: &'a Cid) -> Self { - std::borrow::Cow::Borrowed(from) + borrow::Cow::Borrowed(from) } } diff --git a/src/error.rs b/src/error.rs index 8d8bb08..624d058 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,11 @@ use core::fmt; +#[cfg(feature = "std")] +use std::io; + +#[cfg(not(feature = "std"))] +use core2::io; + /// Type alias to use this library's [`Error`] type in a `Result`. pub type Result = core::result::Result; @@ -23,8 +29,7 @@ pub enum Error { /// Varint decode failure. VarIntDecodeError, /// Io error. - #[cfg(feature = "std")] - Io(std::io::Error), + Io(io::Error), } #[cfg(feature = "std")] @@ -42,7 +47,6 @@ impl fmt::Display for Error { InvalidCidV0Multihash => "CIDv0 requires a Sha-256 multihash", InvalidCidV0Base => "CIDv0 requires a Base58 base", VarIntDecodeError => "Failed to decode unsigned varint format", - #[cfg(feature = "std")] Io(err) => return write!(f, "{}", err), }; @@ -50,7 +54,7 @@ impl fmt::Display for Error { } } -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] impl From for Error { fn from(_: multibase::Error) -> Error { Error::ParsingError @@ -80,9 +84,8 @@ impl From for Error { } } -#[cfg(feature = "std")] -impl From for Error { - fn from(err: std::io::Error) -> Self { +impl From for Error { + fn from(err: io::Error) -> Self { Self::Io(err) } } diff --git a/src/lib.rs b/src/lib.rs index 04776ff..d9b27e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ pub use self::cid::Cid as CidGeneric; pub use self::error::{Error, Result}; pub use self::version::Version; -#[cfg(feature = "std")] +#[cfg(feature = "alloc")] pub use multibase; pub use multihash;