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

support io (read/write) without core #98

Merged
merged 1 commit into from
Oct 29, 2021
Merged
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
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
94 changes: 63 additions & 31 deletions src/cid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R: io::Read>(mut r: R) -> Result<u64> {
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;
Expand Down Expand Up @@ -93,8 +129,7 @@ impl<S: Size> Cid<S> {
}

/// Reads the bytes from a byte stream.
#[cfg(feature = "std")]
pub fn read_bytes<R: std::io::Read>(mut r: R) -> Result<Self> {
pub fn read_bytes<R: io::Read>(mut r: R) -> Result<Self> {
let version = varint_read_u64(&mut r)?;
let codec = varint_read_u64(&mut r)?;
// CIDv0 has the fixed `0x12 0x20` prefix
Expand All @@ -110,8 +145,7 @@ impl<S: Size> Cid<S> {
}
}

#[cfg(feature = "std")]
fn write_bytes_v1<W: std::io::Write>(&self, mut w: W) -> Result<()> {
fn write_bytes_v1<W: io::Write>(&self, mut w: W) -> Result<()> {
let mut version_buf = varint_encode::u64_buffer();
let version = varint_encode::u64(self.version.into(), &mut version_buf);

Expand All @@ -125,8 +159,7 @@ impl<S: Size> Cid<S> {
}

/// Writes the bytes to a byte stream.
#[cfg(feature = "std")]
pub fn write_bytes<W: std::io::Write>(&self, w: W) -> Result<()> {
pub fn write_bytes<W: io::Write>(&self, w: W) -> Result<()> {
match self.version {
Version::V0 => self.hash.write(w)?,
Version::V1 => self.write_bytes_v1(w)?,
Expand All @@ -135,19 +168,19 @@ impl<S: Size> Cid<S> {
}

/// Returns the encoded bytes of the `Cid`.
#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = vec![];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to add #![cfg_attr(feature = "alloc", macro_use)] to lib.rs and keep this? It seems fine either way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's easier to just use the constructor. Honestly, the macro was mostly useful as an efficient way to allocate vectors with a slice-like syntax before const generics.

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())
}
Expand All @@ -167,7 +200,7 @@ impl<S: Size> Cid<S> {
/// 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<String> {
match self.version {
Version::V0 => {
Expand All @@ -192,9 +225,9 @@ impl<S: Size> Default for Cid<S> {
}
}

#[cfg(feature = "std")]
impl<S: Size> std::fmt::Display for Cid<S> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
#[cfg(feature = "alloc")]
impl<S: Size> core::fmt::Display for Cid<S> {
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(),
Expand Down Expand Up @@ -222,16 +255,16 @@ impl<S: Size> std::fmt::Debug for Cid<S> {
}
}

#[cfg(feature = "std")]
impl<S: Size> std::str::FromStr for Cid<S> {
#[cfg(feature = "alloc")]
impl<S: Size> core::str::FromStr for Cid<S> {
type Err = Error;

fn from_str(cid_str: &str) -> Result<Self> {
Self::try_from(cid_str)
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl<S: Size> TryFrom<String> for Cid<S> {
type Error = Error;

Expand All @@ -240,7 +273,7 @@ impl<S: Size> TryFrom<String> for Cid<S> {
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl<S: Size> TryFrom<&str> for Cid<S> {
type Error = Error;

Expand All @@ -267,7 +300,7 @@ impl<S: Size> TryFrom<&str> for Cid<S> {
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl<S: Size> TryFrom<Vec<u8>> for Cid<S> {
type Error = Error;

Expand All @@ -276,7 +309,6 @@ impl<S: Size> TryFrom<Vec<u8>> for Cid<S> {
}
}

#[cfg(feature = "std")]
impl<S: Size> TryFrom<&[u8]> for Cid<S> {
type Error = Error;

Expand All @@ -294,31 +326,31 @@ where
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl<S: Size> From<Cid<S>> for Vec<u8> {
fn from(cid: Cid<S>) -> Self {
cid.to_bytes()
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl<S: Size> From<Cid<S>> for String {
fn from(cid: Cid<S>) -> Self {
cid.to_string()
}
}

#[cfg(feature = "std")]
impl<'a, S: Size> From<Cid<S>> for std::borrow::Cow<'a, Cid<S>> {
#[cfg(feature = "alloc")]
impl<'a, S: Size> From<Cid<S>> for borrow::Cow<'a, Cid<S>> {
fn from(from: Cid<S>) -> Self {
std::borrow::Cow::Owned(from)
borrow::Cow::Owned(from)
}
}

#[cfg(feature = "std")]
impl<'a, S: Size> From<&'a Cid<S>> for std::borrow::Cow<'a, Cid<S>> {
impl<'a, S: Size> From<&'a Cid<S>> for borrow::Cow<'a, Cid<S>> {
fn from(from: &'a Cid<S>) -> Self {
std::borrow::Cow::Borrowed(from)
borrow::Cow::Borrowed(from)
}
}

Expand Down
17 changes: 10 additions & 7 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -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<T> = core::result::Result<T, Error>;

Expand All @@ -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")]
Expand All @@ -42,15 +47,14 @@ 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),
};

f.write_str(error)
}
}

#[cfg(feature = "std")]
#[cfg(feature = "alloc")]
impl From<multibase::Error> for Error {
fn from(_: multibase::Error) -> Error {
Error::ParsingError
Expand Down Expand Up @@ -80,9 +84,8 @@ impl From<unsigned_varint::io::ReadError> for Error {
}
}

#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down