From f693c4bcd24cdf3d4081b34fadd4306d3a00d3fd Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 5 May 2021 16:00:33 +0200 Subject: [PATCH 01/14] new literacy::{Read, Write} traits to handle std/no_std --- .github/workflows/rust.yml | 1 + Cargo.toml | 2 + embedded/src/main.rs | 3 +- src/{std_impls.rs => impls.rs} | 50 ++++++----- src/lib.rs | 5 +- src/literacy.rs | 148 +++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+), 29 deletions(-) rename src/{std_impls.rs => impls.rs} (72%) create mode 100644 src/literacy.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ea7beb0..405fb09 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -63,6 +63,7 @@ jobs: - 1.29.0 - beta - stable + fail-fast: false steps: - name: Checkout Crate uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 294df8f..47980b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,12 @@ default = [ "std" ] std = [] serde-std = ["serde/std"] unstable = [] # for benchmarking +use-core2 = ["core2"] [dependencies] serde = { version = "1.0", default-features = false, optional = true } schemars = { version = "0.8.0", optional = true } +core2 = { version="0.3.0-alpha.1", optional= true, default-features = false } [dev-dependencies] serde_test = "1.0" diff --git a/embedded/src/main.rs b/embedded/src/main.rs index 6b5940a..c21d5ae 100644 --- a/embedded/src/main.rs +++ b/embedded/src/main.rs @@ -15,6 +15,7 @@ use cortex_m::asm; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use panic_halt as _; +use crate::bitcoin_hashes::literacy::Write; hash_newtype!(TestType, sha256::Hash, 32, doc = "test"); @@ -29,7 +30,7 @@ fn main() -> ! { unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } let mut engine = TestType::engine(); - engine.input(b"abc"); + engine.write(b"abc"); let hash = TestType::from_engine(engine); let hash_check = diff --git a/src/std_impls.rs b/src/impls.rs similarity index 72% rename from src/std_impls.rs rename to src/impls.rs index aa87c30..3a29f7c 100644 --- a/src/std_impls.rs +++ b/src/impls.rs @@ -16,62 +16,61 @@ //! //! impls of traits defined in `std` and not `core` -use std::{error, io}; +use {sha1, sha256, sha512, ripemd160, siphash24}; +use ::{HashEngine, literacy}; -use {hex, sha1, sha256, sha512, ripemd160, siphash24}; -use HashEngine; -use Error; - -impl error::Error for Error { - fn cause(&self) -> Option<&error::Error> { None } +#[cfg(any(test, feature = "std"))] +impl ::std::error::Error for ::Error { + fn cause(&self) -> Option<&::std::error::Error> { None } fn description(&self) -> &str { "`std::error::description` is deprecated" } } -impl error::Error for hex::Error { - fn cause(&self) -> Option<&error::Error> { None } +#[cfg(any(test, feature = "std"))] +impl ::std::error::Error for ::hex::Error { + fn cause(&self) -> Option<&::std::error::Error> { None } fn description(&self) -> &str { "`std::error::description` is deprecated" } } -impl io::Write for sha1::HashEngine { - fn flush(&mut self) -> io::Result<()> { Ok(()) } +impl literacy::Write for sha1::HashEngine { + fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> io::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } -impl io::Write for sha256::HashEngine { - fn flush(&mut self) -> io::Result<()> { Ok(()) } +impl literacy::Write for sha256::HashEngine { + fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> io::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } -impl io::Write for sha512::HashEngine { - fn flush(&mut self) -> io::Result<()> { Ok(()) } +impl literacy::Write for sha512::HashEngine { + fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> io::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } -impl io::Write for ripemd160::HashEngine { - fn flush(&mut self) -> io::Result<()> { Ok(()) } +impl literacy::Write for ripemd160::HashEngine { + fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> io::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } -impl io::Write for siphash24::HashEngine { - fn flush(&mut self) -> io::Result<()> { Ok(()) } +impl literacy::Write for siphash24::HashEngine { + fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> io::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } @@ -79,8 +78,7 @@ impl io::Write for siphash24::HashEngine { #[cfg(test)] mod tests { - use std::io::Write; - + use ::literacy::Write; use {sha1, sha256, sha256d, sha512, ripemd160, hash160, siphash24}; use Hash; diff --git a/src/lib.rs b/src/lib.rs index 11a56b5..3be36b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #![deny(non_camel_case_types)] #![deny(non_snake_case)] #![deny(unused_mut)] -#![deny(missing_docs)] +//#![deny(missing_docs)] // In general, rust is absolutely horrid at supporting users doing things like, // for example, compiling Rust code for real environments. Disable useless lints @@ -53,7 +53,7 @@ pub mod _export { #[macro_use] mod util; #[macro_use] pub mod serde_macros; -#[cfg(any(test, feature = "std"))] mod std_impls; +mod impls; pub mod error; pub mod hex; pub mod hash160; @@ -66,6 +66,7 @@ pub mod sha256t; pub mod siphash24; pub mod sha512; pub mod cmp; +pub mod literacy; use core::{borrow, fmt, hash, ops}; diff --git a/src/literacy.rs b/src/literacy.rs new file mode 100644 index 0000000..2fbacd0 --- /dev/null +++ b/src/literacy.rs @@ -0,0 +1,148 @@ +#[cfg(all(feature = "std", feature = "use-core2"))] +compile_error!("feature \"std\" and \"use-core2\" cannot be enabled together."); + +#[derive(Debug)] +pub enum Error { + #[cfg(feature = "std")] + Std(::std::io::Error), + + #[cfg(feature = "use-core2")] + Core2(core2::io::Error), + + // Needed for write_all blanket implementation + WriteZero, + Interrupted, + + Other, +} + +pub trait Read{ + fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; +} + +pub trait Write { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result; + fn flush(&mut self) -> ::core::result::Result<(), Error>; + + fn write_all(&mut self, mut buf: &[u8]) -> ::core::result::Result<(), Error> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::WriteZero); + } + Ok(n) => buf = &buf[n..], + Err(Error::Interrupted) => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +#[cfg(feature = "std")] +mod std_impl { + use super::{Read, Write, Error}; + + impl From<::std::io::Error> for Error { + fn from(error: ::std::io::Error) -> Self { + if let ::std::io::ErrorKind::Interrupted = error.kind() { + Error::Interrupted + } else { + Error::Std(error) + } + } + } + + impl Read for R { + fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(::read(self, buf)?) + } + } + + impl Write for W { + fn write(&mut self, buf: &[u8]) -> Result { + Ok(::write(self, buf)?) + } + + fn flush(&mut self) -> Result<(), Error> { + Ok(::flush(self)?) + } + } +} + +#[cfg(feature = "use-core2")] +mod core2_impl { + use super::{Read, Write, Error}; + + impl From for Error { + fn from(error: core2::io::Error) -> Self { + if let core2::io::ErrorKind::Interrupted = error.kind() { + Error::Interrupted + } else { + Error::Core2(error) + } + } + } + + impl Read for R { + fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(::read(self, buf)?) + } + } + + impl Write for W { + fn write(&mut self, buf: &[u8]) -> Result { + Ok(::write(self, buf)?) + } + + fn flush(&mut self) -> Result<(), Error> { + Ok(::flush(self)?) + } + } +} + +#[cfg(test)] +mod tests { + + #[cfg(feature = "std")] + mod std_test { + use ::literacy::{Read, Write}; + + #[test] + fn test_std_read() { + let mut cursor = ::std::io::Cursor::new(vec![10u8]); + let mut buf = [0u8; 1]; + cursor.read(&mut buf).unwrap(); + assert_eq!(buf, [10u8]); + } + + #[test] + fn test_std_write() { + let mut cursor = ::std::io::Cursor::new(vec![]); + let mut buf = [10u8; 1]; + cursor.write(&mut buf).unwrap(); + assert_eq!(cursor.into_inner(), vec![10u8]); + } + } + + #[cfg(feature = "use-core2")] + mod tests { + use ::literacy::{Read, Write}; + + #[test] + fn test_core2_read() { + let mut cursor = core2::io::Cursor::new(vec![10u8]); + let mut buf = [0u8; 1]; + cursor.read(&mut buf).unwrap(); + assert_eq!(buf, [10u8]); + } + + #[test] + fn test_core2_write() { + let mut cursor = core2::io::Cursor::new(vec![]); + let mut buf = [10u8; 1]; + cursor.write(&mut buf).unwrap(); + assert_eq!(cursor.into_inner(), vec![10u8]); + } + } +} From 41e04db03864fb9ff5f952824a998f2ec169bfb6 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Thu, 6 May 2021 11:51:26 +0200 Subject: [PATCH 02/14] use associated type in literacy Read/Write trait --- embedded/src/main.rs | 6 ++-- src/impls.rs | 30 ++++++++++------ src/literacy.rs | 81 +++++++++++++++----------------------------- 3 files changed, 51 insertions(+), 66 deletions(-) diff --git a/embedded/src/main.rs b/embedded/src/main.rs index c21d5ae..957c02a 100644 --- a/embedded/src/main.rs +++ b/embedded/src/main.rs @@ -8,14 +8,14 @@ extern crate bitcoin_hashes; extern crate alloc; use alloc_cortex_m::CortexMHeap; -use bitcoin_hashes::{sha256, Hash, HashEngine}; +use bitcoin_hashes::{sha256, Hash}; +use bitcoin_hashes::literacy::Write; use core::alloc::Layout; use core::str::FromStr; use cortex_m::asm; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use panic_halt as _; -use crate::bitcoin_hashes::literacy::Write; hash_newtype!(TestType, sha256::Hash, 32, doc = "test"); @@ -30,7 +30,7 @@ fn main() -> ! { unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } let mut engine = TestType::engine(); - engine.write(b"abc"); + engine.write(b"abc").unwrap(); let hash = TestType::from_engine(engine); let hash_check = diff --git a/src/impls.rs b/src/impls.rs index 3a29f7c..72d840a 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -32,45 +32,55 @@ impl ::std::error::Error for ::hex::Error { } impl literacy::Write for sha1::HashEngine { - fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } + type Error = (); - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } + + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } impl literacy::Write for sha256::HashEngine { - fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } + type Error = (); + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } impl literacy::Write for sha512::HashEngine { - fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } + type Error = (); + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } impl literacy::Write for ripemd160::HashEngine { - fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } + type Error = (); - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } + + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } } impl literacy::Write for siphash24::HashEngine { - fn flush(&mut self) -> ::core::result::Result<(), literacy::Error> { Ok(()) } + type Error = (); + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } diff --git a/src/literacy.rs b/src/literacy.rs index 2fbacd0..d8d89c0 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -1,37 +1,24 @@ #[cfg(all(feature = "std", feature = "use-core2"))] compile_error!("feature \"std\" and \"use-core2\" cannot be enabled together."); -#[derive(Debug)] -pub enum Error { - #[cfg(feature = "std")] - Std(::std::io::Error), - - #[cfg(feature = "use-core2")] - Core2(core2::io::Error), - - // Needed for write_all blanket implementation - WriteZero, - Interrupted, - - Other, -} pub trait Read{ - fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; + type Error; + fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; } pub trait Write { - fn write(&mut self, buf: &[u8]) -> ::core::result::Result; - fn flush(&mut self) -> ::core::result::Result<(), Error>; - - fn write_all(&mut self, mut buf: &[u8]) -> ::core::result::Result<(), Error> { + type Error; + fn write(&mut self, buf: &[u8]) -> ::core::result::Result; + fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; + fn write_all(&mut self, mut buf: &[u8]) -> ::core::result::Result<(), Self::Error> { while !buf.is_empty() { match self.write(buf) { - Ok(0) => { - return Err(Error::WriteZero); - } + /*Ok(0) => { + return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); + }*/ Ok(n) => buf = &buf[n..], - Err(Error::Interrupted) => {} + //Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(e) => return Err(e), } } @@ -41,61 +28,49 @@ pub trait Write { #[cfg(feature = "std")] mod std_impl { - use super::{Read, Write, Error}; - - impl From<::std::io::Error> for Error { - fn from(error: ::std::io::Error) -> Self { - if let ::std::io::ErrorKind::Interrupted = error.kind() { - Error::Interrupted - } else { - Error::Std(error) - } - } - } + use super::{Read, Write}; impl Read for R { - fn read(&mut self, buf: &mut [u8]) -> Result { + type Error = ::std::io::Error; + + fn read(&mut self, buf: &mut [u8]) -> Result { Ok(::read(self, buf)?) } } impl Write for W { - fn write(&mut self, buf: &[u8]) -> Result { - Ok(::write(self, buf)?) + type Error = ::std::io::Error; + + fn write(&mut self, buf: &[u8]) -> Result { + ::write(self, buf) } - fn flush(&mut self) -> Result<(), Error> { - Ok(::flush(self)?) + fn flush(&mut self) -> Result<(), Self::Error> { + ::flush(self) } } } #[cfg(feature = "use-core2")] mod core2_impl { - use super::{Read, Write, Error}; - - impl From for Error { - fn from(error: core2::io::Error) -> Self { - if let core2::io::ErrorKind::Interrupted = error.kind() { - Error::Interrupted - } else { - Error::Core2(error) - } - } - } + use super::{Read, Write}; impl Read for R { - fn read(&mut self, buf: &mut [u8]) -> Result { + type Error = core2::io::Error; + + fn read(&mut self, buf: &mut [u8]) -> Result { Ok(::read(self, buf)?) } } impl Write for W { - fn write(&mut self, buf: &[u8]) -> Result { + type Error = core2::io::Error; + + fn write(&mut self, buf: &[u8]) -> Result { Ok(::write(self, buf)?) } - fn flush(&mut self) -> Result<(), Error> { + fn flush(&mut self) -> Result<(), Self::Error> { Ok(::flush(self)?) } } From 4291b4bca84e690aa5a18f64cfe88d2453e7b680 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 11 May 2021 13:56:57 +0200 Subject: [PATCH 03/14] add default impl for vec and slice, remove blanket impl --- src/impls.rs | 45 ++++++++++++++++----- src/lib.rs | 3 ++ src/literacy.rs | 103 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index 72d840a..bd7aca4 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -34,56 +34,81 @@ impl ::std::error::Error for ::hex::Error { impl literacy::Write for sha1::HashEngine { type Error = (); - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } } impl literacy::Write for sha256::HashEngine { type Error = (); - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } } impl literacy::Write for sha512::HashEngine { type Error = (); - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } } impl literacy::Write for ripemd160::HashEngine { type Error = (); - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } } impl literacy::Write for siphash24::HashEngine { type Error = (); - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 3be36b0..f1e57b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,9 @@ pub mod _export { #[cfg(feature = "schemars")] extern crate schemars; +#[cfg(all(not(feature = "use-core2"), not(feature = "std")))] +extern crate alloc; + #[macro_use] mod util; #[macro_use] pub mod serde_macros; mod impls; diff --git a/src/literacy.rs b/src/literacy.rs index d8d89c0..f4a155c 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -1,29 +1,18 @@ #[cfg(all(feature = "std", feature = "use-core2"))] compile_error!("feature \"std\" and \"use-core2\" cannot be enabled together."); - -pub trait Read{ +pub trait Read { type Error; fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; + fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>; } pub trait Write { type Error; fn write(&mut self, buf: &[u8]) -> ::core::result::Result; + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error>; fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; - fn write_all(&mut self, mut buf: &[u8]) -> ::core::result::Result<(), Self::Error> { - while !buf.is_empty() { - match self.write(buf) { - /*Ok(0) => { - return Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")); - }*/ - Ok(n) => buf = &buf[n..], - //Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } + } #[cfg(feature = "std")] @@ -36,6 +25,10 @@ mod std_impl { fn read(&mut self, buf: &mut [u8]) -> Result { Ok(::read(self, buf)?) } + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Ok(::read_exact(self, buf)?) + } } impl Write for W { @@ -45,6 +38,10 @@ mod std_impl { ::write(self, buf) } + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + ::write_all(self, buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { ::flush(self) } @@ -61,6 +58,10 @@ mod core2_impl { fn read(&mut self, buf: &mut [u8]) -> Result { Ok(::read(self, buf)?) } + + fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Ok(::read_exact(self, buf)?) + } } impl Write for W { @@ -70,12 +71,84 @@ mod core2_impl { Ok(::write(self, buf)?) } + fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + ::write_all(self, buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { Ok(::flush(self)?) } } } +#[cfg(all(not(feature = "use-core2"), not(feature = "std")))] +mod default_impl { + use super::{Read, Write}; + + pub enum DefaultError { + UnexpectedEof, + } + + impl Read for &[u8] { + type Error = DefaultError; + + fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result { + let amt = ::core::cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error> { + if buf.len() > self.len() { + return Err(Self::Error::UnexpectedEof); + } + let (a, b) = self.split_at(buf.len()); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if buf.len() == 1 { + buf[0] = a[0]; + } else { + buf.copy_from_slice(a); + } + + *self = b; + Ok(()) + } + } + + impl Write for ::alloc::vec::Vec { + type Error = DefaultError; + + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error> { + self.extend_from_slice(buf); + Ok(()) + } + + fn flush(&mut self) -> ::core::result::Result<(), Self::Error> { + Ok(()) + } + } +} + + #[cfg(test)] mod tests { From 73c4aadee53288bc1f83640d7f5fd42273f3eb90 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 11 May 2021 14:04:49 +0200 Subject: [PATCH 04/14] upgrade CI --- .github/workflows/rust.yml | 7 ++--- contrib/test.sh | 56 ++++++++++++++++---------------------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 405fb09..0f174ae 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ jobs: rust: - nightly steps: - - name: Checkout Crate + - name: Checkout Crate uses: actions/checkout@v2 - name: Checkout Toolchain uses: actions-rs/toolchain@v1 @@ -29,7 +29,7 @@ jobs: DO_BENCH: true run: ./contrib/test.sh - wasm: + wasm: name: Stable - Docs / WebAssembly Build runs-on: ubuntu-latest strategy: @@ -79,7 +79,7 @@ jobs: - name: Running cargo env: DO_FEATURE_MATRIX: true - DO_SCHEMARS_TESTS: ${{matrix.rust != '1.29.0'}} + ON_1_29_0: ${{matrix.rust == '1.29.0'}} run: ./contrib/test.sh Embedded: @@ -103,4 +103,3 @@ jobs: CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER: "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" run: cd embedded && cargo run --target thumbv7m-none-eabi - diff --git a/contrib/test.sh b/contrib/test.sh index 8267a3b..053b588 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -1,10 +1,11 @@ -#!/bin/sh -ex +#!/bin/bash -ex -FEATURES="serde serde-std" +# Combination of features to test, should be every features combination but std and core2 together +# note std has a comma in the end so that following regex avoid matching serde-std +FEATURES=("" "std," "use-core2" "std,serde-std" "use-core2,serde-std") # Use toolchain if explicitly specified -if [ -n "$TOOLCHAIN" ] -then +if [[ -n "$TOOLCHAIN" ]]; then alias cargo="cargo +$TOOLCHAIN" fi @@ -18,35 +19,27 @@ export CARGO_TERM_VERBOSE=true cargo build --all cargo test --all -if [ "$DO_FEATURE_MATRIX" = true ]; then - cargo build --all --no-default-features - cargo test --all --no-default-features - - # All features - cargo build --all --no-default-features --features="$FEATURES" - cargo test --all --features="$FEATURES" - # Single features - for feature in ${FEATURES} - do - cargo build --all --no-default-features --features="$feature" - cargo test --all --features="$feature" - done - - # Other combos - cargo test --all --features="serde-std" +if [[ "$DO_FEATURE_MATRIX" = true ]]; then + for feature in "${FEATURES[@]}" + do + # On rust 1.29.0 we are only testing with std lib + if [[ "$ON_1_29_0" = false || ${feature} =~ "std," ]]; then + echo "--------------$feature----------------" + cargo build --no-default-features --features="$feature" + if [[ ${feature} =~ "std," ]] ; then + cargo test --no-default-features --features="$feature" + fi + cargo doc --no-default-features --features="$feature" + fi + done fi -if [ "$DO_SCHEMARS_TESTS" = true ]; then +if [[ "$ON_1_29_0" = false ]]; then (cd extended_tests/schemars && cargo test) fi -# Docs -if [ "$DO_DOCS" = true ]; then - cargo doc --all --features="$FEATURES" -fi - # Webassembly stuff -if [ "$DO_WASM" = true ]; then +if [[ "$DO_WASM" = true ]]; then clang --version && CARGO_TARGET_DIR=wasm cargo install --force wasm-pack && printf '\n[lib]\ncrate-type = ["cdylib", "rlib"]\n' >> Cargo.toml && @@ -55,20 +48,19 @@ if [ "$DO_WASM" = true ]; then fi # Address Sanitizer -if [ "$DO_ASAN" = true ]; then +if [[ "$DO_ASAN" = true ]]; then cargo clean CC='clang -fsanitize=address -fno-omit-frame-pointer' \ RUSTFLAGS='-Zsanitizer=address -Clinker=clang -Cforce-frame-pointers=yes' \ ASAN_OPTIONS='detect_leaks=1 detect_invalid_pointer_pairs=1 detect_stack_use_after_return=1' \ - cargo test --lib --all --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu + cargo test --lib --all -Zbuild-std --target x86_64-unknown-linux-gnu cargo clean CC='clang -fsanitize=memory -fno-omit-frame-pointer' \ RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins -Cforce-frame-pointers=yes' \ - cargo test --lib --all --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu + cargo test --lib --all -Zbuild-std --target x86_64-unknown-linux-gnu fi # Bench -if [ "$DO_BENCH" = true ]; then +if [[ "$DO_BENCH" = true ]]; then cargo bench --all --features="unstable" fi - From 62855e686090228779d9593cf60b1900734afb2a Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 19 May 2021 10:51:52 +0200 Subject: [PATCH 05/14] defaults to core2 if both std and core2 features enabled, fixes --- Cargo.toml | 1 + contrib/test.sh | 10 +++++----- src/literacy.rs | 30 ++++++++++++++++++------------ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 47980b1..42a06fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ std = [] serde-std = ["serde/std"] unstable = [] # for benchmarking use-core2 = ["core2"] +use-core2-std = ["use-core2", "core2/std"] [dependencies] serde = { version = "1.0", default-features = false, optional = true } diff --git a/contrib/test.sh b/contrib/test.sh index 053b588..3956d94 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -2,7 +2,7 @@ # Combination of features to test, should be every features combination but std and core2 together # note std has a comma in the end so that following regex avoid matching serde-std -FEATURES=("" "std," "use-core2" "std,serde-std" "use-core2,serde-std") +FEATURES=("" "std," "use-core2" "use-core2-std" "std,use-core2" "std,serde-std" "use-core2,serde-std") # Use toolchain if explicitly specified if [[ -n "$TOOLCHAIN" ]]; then @@ -22,8 +22,8 @@ cargo test --all if [[ "$DO_FEATURE_MATRIX" = true ]]; then for feature in "${FEATURES[@]}" do - # On rust 1.29.0 we are only testing with std lib - if [[ "$ON_1_29_0" = false || ${feature} =~ "std," ]]; then + # On rust 1.29.0 we are only testing with std lib and without use-core2 + if [[ "$ON_1_29_0" = false || (${feature} =~ "std," && ! ${feature} =~ "use-core2") ]]; then echo "--------------$feature----------------" cargo build --no-default-features --features="$feature" if [[ ${feature} =~ "std," ]] ; then @@ -53,11 +53,11 @@ if [[ "$DO_ASAN" = true ]]; then CC='clang -fsanitize=address -fno-omit-frame-pointer' \ RUSTFLAGS='-Zsanitizer=address -Clinker=clang -Cforce-frame-pointers=yes' \ ASAN_OPTIONS='detect_leaks=1 detect_invalid_pointer_pairs=1 detect_stack_use_after_return=1' \ - cargo test --lib --all -Zbuild-std --target x86_64-unknown-linux-gnu + cargo test --lib --all --all-features -Zbuild-std --target x86_64-unknown-linux-gnu cargo clean CC='clang -fsanitize=memory -fno-omit-frame-pointer' \ RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins -Cforce-frame-pointers=yes' \ - cargo test --lib --all -Zbuild-std --target x86_64-unknown-linux-gnu + cargo test --lib --all --all-features -Zbuild-std --target x86_64-unknown-linux-gnu fi # Bench diff --git a/src/literacy.rs b/src/literacy.rs index f4a155c..549dad1 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -1,5 +1,3 @@ -#[cfg(all(feature = "std", feature = "use-core2"))] -compile_error!("feature \"std\" and \"use-core2\" cannot be enabled together."); pub trait Read { type Error; @@ -12,10 +10,9 @@ pub trait Write { fn write(&mut self, buf: &[u8]) -> ::core::result::Result; fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error>; fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; - } -#[cfg(feature = "std")] +#[cfg(all(feature = "std", not(feature = "use-core2")))] mod std_impl { use super::{Read, Write}; @@ -23,11 +20,11 @@ mod std_impl { type Error = ::std::io::Error; fn read(&mut self, buf: &mut [u8]) -> Result { - Ok(::read(self, buf)?) + ::read(self, buf) } fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { - Ok(::read_exact(self, buf)?) + ::read_exact(self, buf) } } @@ -56,11 +53,11 @@ mod core2_impl { type Error = core2::io::Error; fn read(&mut self, buf: &mut [u8]) -> Result { - Ok(::read(self, buf)?) + ::read(self, buf) } fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { - Ok(::read_exact(self, buf)?) + ::read_exact(self, buf) } } @@ -68,7 +65,7 @@ mod core2_impl { type Error = core2::io::Error; fn write(&mut self, buf: &[u8]) -> Result { - Ok(::write(self, buf)?) + ::write(self, buf) } fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { @@ -76,7 +73,7 @@ mod core2_impl { } fn flush(&mut self) -> Result<(), Self::Error> { - Ok(::flush(self)?) + ::flush(self) } } } @@ -152,7 +149,7 @@ mod default_impl { #[cfg(test)] mod tests { - #[cfg(feature = "std")] + #[cfg(all(feature = "std", not(feature = "use-core2")))] mod std_test { use ::literacy::{Read, Write}; @@ -186,11 +183,20 @@ mod tests { } #[test] - fn test_core2_write() { + #[cfg(feature = "use-core2-std")] + fn test_core2_write_cursor() { let mut cursor = core2::io::Cursor::new(vec![]); let mut buf = [10u8; 1]; cursor.write(&mut buf).unwrap(); assert_eq!(cursor.into_inner(), vec![10u8]); } + + #[test] + fn test_core2_write() { + let mut write_buf = [0u8; 1]; + let mut buf = [10u8; 1]; + (&mut write_buf[..]).write(&mut buf).unwrap(); + assert_eq!(write_buf, [10u8; 1]); + } } } From 8bf2f9ed2fd5f6be0693b502e9a5351edef300d4 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 19 May 2021 11:28:48 +0200 Subject: [PATCH 06/14] Docs for literacy module and restore enforcing docs --- src/error.rs | 2 +- src/lib.rs | 2 +- src/literacy.rs | 34 +++++++++++++++++++++++++++++++++- src/sha256.rs | 2 +- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 8c87f62..9e0399a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,7 @@ use core::fmt; -/// [bitcoin_hashes] error. +/// This crate error. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Error { /// Tried to create a fixed-length hash from a slice with the wrong size (expected, got). diff --git a/src/lib.rs b/src/lib.rs index f1e57b0..d6bdd34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ #![deny(non_camel_case_types)] #![deny(non_snake_case)] #![deny(unused_mut)] -//#![deny(missing_docs)] +#![deny(missing_docs)] // In general, rust is absolutely horrid at supporting users doing things like, // for example, compiling Rust code for real environments. Disable useless lints diff --git a/src/literacy.rs b/src/literacy.rs index 549dad1..f9317d7 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -1,14 +1,46 @@ - +// Bitcoin Hashes Library +// Written in 2021 by +// The rust-bitcoin developers. +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! # Literacy traits +//! +//! literacy: "the ability to [Read] and [Write]." +//! +//! * With `std` enabled, traits are automatically implemented for `std::io::{Read, Write}` +//! * Without `std` but using feature `use-core2`, they are implemented for `core2::io::{Read, Write}` +//! * Without neither `std` and nor `core2` default implementation `impl Read for &[u8]` +//! and `impl Write for ::alloc::vec::Vec` are provided +//! + +/// The Read trait allows for reading bytes from a source. pub trait Read { + /// The error type returned in Result type Error; + /// see [std::io::Read::read] fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; + /// see [std::io::Read::read_exact] fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>; } +/// The Write trait allows to write bytes in the object implementing it. pub trait Write { + /// The error type returned in Result type Error; + /// see [std::io::Write::write] fn write(&mut self, buf: &[u8]) -> ::core::result::Result; + /// see [std::io::Write::write_all] fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error>; + /// see [std::io::Write::flush] fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; } diff --git a/src/sha256.rs b/src/sha256.rs index 6c97dbd..0c5b41b 100644 --- a/src/sha256.rs +++ b/src/sha256.rs @@ -237,7 +237,7 @@ macro_rules! round( impl HashEngine { /// Create a new [HashEngine] from a midstate. /// - /// Be aware that this method panics when [length] is + /// Be aware that this method panics when `length` is /// not a multiple of the block size. pub fn from_midstate(midstate: Midstate, length: usize) -> HashEngine { assert!(length % BLOCK_SIZE == 0, "length is no multiple of the block size"); From 12580fdb9d94f482b13bc4f157a16d562d16b84e Mon Sep 17 00:00:00 2001 From: Sebastian Geisler Date: Thu, 20 May 2021 15:59:50 +0200 Subject: [PATCH 07/14] use macros to impl literacy::Write Using a macro makes the code more compact and allows us to easily add an impl for &mut HashEngine which could not be achieved through the type system otherwise. --- src/impls.rs | 120 ++++++++++++++++++--------------------------------- 1 file changed, 41 insertions(+), 79 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index bd7aca4..92a8948 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -19,97 +19,59 @@ use {sha1, sha256, sha512, ripemd160, siphash24}; use ::{HashEngine, literacy}; -#[cfg(any(test, feature = "std"))] -impl ::std::error::Error for ::Error { - fn cause(&self) -> Option<&::std::error::Error> { None } - fn description(&self) -> &str { "`std::error::description` is deprecated" } -} - -#[cfg(any(test, feature = "std"))] -impl ::std::error::Error for ::hex::Error { - fn cause(&self) -> Option<&::std::error::Error> { None } - fn description(&self) -> &str { "`std::error::description` is deprecated" } -} - -impl literacy::Write for sha1::HashEngine { - type Error = (); - - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { - self.input(buf); - Ok(buf.len()) - } +macro_rules! write_impl { + ($HashEngine: ty) => { + impl literacy::Write for $HashEngine { + type Error = (); + + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + self.input(buf); + Ok(buf.len()) + } - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { - self.write(buf)?; - Ok(()) - } + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } -} + fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } + } -impl literacy::Write for sha256::HashEngine { - type Error = (); + impl<'a> literacy::Write for &'a mut $HashEngine { + type Error = (); - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { - self.input(buf); - Ok(buf.len()) - } + fn write<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result { + self.input(buf); + Ok(buf.len()) + } - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { - self.write(buf)?; - Ok(()) - } + fn write_all<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result<(), ()> { + self.write(buf)?; + Ok(()) + } - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } + fn flush<'b>(&'b mut self) -> ::core::result::Result<(), ()> { Ok(()) } + } + }; } -impl literacy::Write for sha512::HashEngine { - type Error = (); - - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { - self.input(buf); - Ok(buf.len()) - } - - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { - self.write(buf)?; - Ok(()) - } - - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } +#[cfg(any(test, feature = "std"))] +impl ::std::error::Error for ::Error { + fn cause(&self) -> Option<&::std::error::Error> { None } + fn description(&self) -> &str { "`std::error::description` is deprecated" } } -impl literacy::Write for ripemd160::HashEngine { - type Error = (); - - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { - self.input(buf); - Ok(buf.len()) - } - - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { - self.write(buf)?; - Ok(()) - } - - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } +#[cfg(any(test, feature = "std"))] +impl ::std::error::Error for ::hex::Error { + fn cause(&self) -> Option<&::std::error::Error> { None } + fn description(&self) -> &str { "`std::error::description` is deprecated" } } -impl literacy::Write for siphash24::HashEngine { - type Error = (); - - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { - self.input(buf); - Ok(buf.len()) - } - - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { - self.write(buf)?; - Ok(()) - } - - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } -} +write_impl!(sha1::HashEngine); +write_impl!(sha256::HashEngine); +write_impl!(sha512::HashEngine); +write_impl!(ripemd160::HashEngine); +write_impl!(siphash24::HashEngine); #[cfg(test)] mod tests { From 9e49b27d8019a642969816ee02664d1e87913326 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Fri, 21 May 2021 09:28:17 +0200 Subject: [PATCH 08/14] test with serde feature --- contrib/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/test.sh b/contrib/test.sh index 3956d94..db976cb 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -1,8 +1,8 @@ #!/bin/bash -ex -# Combination of features to test, should be every features combination but std and core2 together +# Combination of features to test # note std has a comma in the end so that following regex avoid matching serde-std -FEATURES=("" "std," "use-core2" "use-core2-std" "std,use-core2" "std,serde-std" "use-core2,serde-std") +FEATURES=("" "std," "std,serde" "serde" "use-core2,serde" "use-core2" "use-core2-std" "std,use-core2" "std,serde-std" "use-core2,serde-std") # Use toolchain if explicitly specified if [[ -n "$TOOLCHAIN" ]]; then From a37f2bed96697b0f8fac04b217e6911bc1ba8417 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Fri, 21 May 2021 10:40:38 +0200 Subject: [PATCH 09/14] Add take to literacy::Read trait --- src/literacy.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/literacy.rs b/src/literacy.rs index f9317d7..de775ec 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -26,10 +26,14 @@ pub trait Read { /// The error type returned in Result type Error; + /// The type to implement limited reads + type Take; /// see [std::io::Read::read] fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; /// see [std::io::Read::read_exact] fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>; + /// see [std::io::Read::take] + fn take(self, limit: u64) -> Self::Take; } /// The Write trait allows to write bytes in the object implementing it. @@ -50,6 +54,7 @@ mod std_impl { impl Read for R { type Error = ::std::io::Error; + type Take = ::std::io::Take; fn read(&mut self, buf: &mut [u8]) -> Result { ::read(self, buf) @@ -58,6 +63,11 @@ mod std_impl { fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { ::read_exact(self, buf) } + + fn take(self, limit: u64) -> Self::Take { + ::take(self, limit) + } + } impl Write for W { @@ -83,6 +93,7 @@ mod core2_impl { impl Read for R { type Error = core2::io::Error; + type Take = core2::io::Take; fn read(&mut self, buf: &mut [u8]) -> Result { ::read(self, buf) @@ -91,6 +102,10 @@ mod core2_impl { fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { ::read_exact(self, buf) } + + fn take(self, limit: u64) -> Self::Take { + ::take(self, limit) + } } impl Write for W { @@ -114,12 +129,14 @@ mod core2_impl { mod default_impl { use super::{Read, Write}; + #[derive(Debug)] pub enum DefaultError { UnexpectedEof, } - impl Read for &[u8] { + impl<'a> Read for &'a [u8] { type Error = DefaultError; + type Take = &'a [u8]; fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result { let amt = ::core::cmp::min(buf.len(), self.len()); @@ -156,6 +173,10 @@ mod default_impl { *self = b; Ok(()) } + + fn take(self, limit: u64) -> Self::Take { + &self[..limit as usize] + } } impl Write for ::alloc::vec::Vec { From a8eb19c23e1348ae7688054abcc9a4c1182b18d4 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Fri, 21 May 2021 12:22:52 +0200 Subject: [PATCH 10/14] rexport literacy::Error according to the activated feature --- src/lib.rs | 1 + src/literacy.rs | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6bdd34..ab1bf85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ #[cfg(any(test, feature="std"))] extern crate core; #[cfg(feature="serde")] pub extern crate serde; #[cfg(all(test,feature="serde"))] extern crate serde_test; +#[cfg(feature = "use-core2")] pub extern crate core2; #[doc(hidden)] pub mod _export { diff --git a/src/literacy.rs b/src/literacy.rs index de775ec..c937d84 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -48,6 +48,9 @@ pub trait Write { fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; } +#[cfg(all(feature = "std", not(feature = "use-core2")))] +pub use ::std::io::Error; + #[cfg(all(feature = "std", not(feature = "use-core2")))] mod std_impl { use super::{Read, Write}; @@ -87,6 +90,9 @@ mod std_impl { } } +#[cfg(feature = "use-core2")] +pub use core2::io::Error; + #[cfg(feature = "use-core2")] mod core2_impl { use super::{Read, Write}; @@ -126,16 +132,19 @@ mod core2_impl { } #[cfg(all(not(feature = "use-core2"), not(feature = "std")))] -mod default_impl { - use super::{Read, Write}; +#[derive(Debug)] +/// The Error for the default implementation +pub enum Error { + /// Unexpected "end of file" + UnexpectedEof, +} - #[derive(Debug)] - pub enum DefaultError { - UnexpectedEof, - } +#[cfg(all(not(feature = "use-core2"), not(feature = "std")))] +mod default_impl { + use super::{Read, Write, Error}; impl<'a> Read for &'a [u8] { - type Error = DefaultError; + type Error = Error; type Take = &'a [u8]; fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result { @@ -180,7 +189,7 @@ mod default_impl { } impl Write for ::alloc::vec::Vec { - type Error = DefaultError; + type Error = Error; fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.extend_from_slice(buf); @@ -198,7 +207,6 @@ mod default_impl { } } - #[cfg(test)] mod tests { From 1f500f1e41c7d29f914558b3b1c73ab101b61610 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Fri, 21 May 2021 13:35:34 +0200 Subject: [PATCH 11/14] use literacy::Error also for engines --- src/impls.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index 92a8948..7d88a8e 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -15,6 +15,9 @@ //! `std` Impls //! //! impls of traits defined in `std` and not `core` +//! +//! Note engines implementation cannot error, however, we use [crate::literacy::Error] type for +//! more ergonomic use inside other methods returning errors. use {sha1, sha256, sha512, ripemd160, siphash24}; use ::{HashEngine, literacy}; @@ -22,35 +25,35 @@ use ::{HashEngine, literacy}; macro_rules! write_impl { ($HashEngine: ty) => { impl literacy::Write for $HashEngine { - type Error = (); + type Error = literacy::Error; - fn write(&mut self, buf: &[u8]) -> ::core::result::Result { + fn write(&mut self, buf: &[u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } - fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), ()> { + fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error> { self.write(buf)?; Ok(()) } - fn flush(&mut self) -> ::core::result::Result<(), ()> { Ok(()) } + fn flush(&mut self) -> ::core::result::Result<(), Self::Error> { Ok(()) } } impl<'a> literacy::Write for &'a mut $HashEngine { - type Error = (); + type Error = literacy::Error; - fn write<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result { + fn write<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result { self.input(buf); Ok(buf.len()) } - fn write_all<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result<(), ()> { + fn write_all<'b>(&'b mut self, buf: &'b [u8]) -> ::core::result::Result<(), Self::Error> { self.write(buf)?; Ok(()) } - fn flush<'b>(&'b mut self) -> ::core::result::Result<(), ()> { Ok(()) } + fn flush<'b>(&'b mut self) -> ::core::result::Result<(), Self::Error> { Ok(()) } } }; } From 6b7b28fc3536fda034d70d39a5d1bded78f07670 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Fri, 21 May 2021 18:58:21 +0200 Subject: [PATCH 12/14] add Read trait bound to Take --- src/literacy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/literacy.rs b/src/literacy.rs index c937d84..72009df 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -27,7 +27,7 @@ pub trait Read { /// The error type returned in Result type Error; /// The type to implement limited reads - type Take; + type Take: Read; /// see [std::io::Read::read] fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; /// see [std::io::Read::read_exact] From c6d4ca48fd56189a1c20b08c8fd81786b3b65d74 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 25 May 2021 11:59:04 +0200 Subject: [PATCH 13/14] remove Take --- src/literacy.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/literacy.rs b/src/literacy.rs index 72009df..c2941aa 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -26,14 +26,10 @@ pub trait Read { /// The error type returned in Result type Error; - /// The type to implement limited reads - type Take: Read; /// see [std::io::Read::read] fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; /// see [std::io::Read::read_exact] fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>; - /// see [std::io::Read::take] - fn take(self, limit: u64) -> Self::Take; } /// The Write trait allows to write bytes in the object implementing it. @@ -57,7 +53,6 @@ mod std_impl { impl Read for R { type Error = ::std::io::Error; - type Take = ::std::io::Take; fn read(&mut self, buf: &mut [u8]) -> Result { ::read(self, buf) @@ -66,11 +61,6 @@ mod std_impl { fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { ::read_exact(self, buf) } - - fn take(self, limit: u64) -> Self::Take { - ::take(self, limit) - } - } impl Write for W { @@ -99,7 +89,6 @@ mod core2_impl { impl Read for R { type Error = core2::io::Error; - type Take = core2::io::Take; fn read(&mut self, buf: &mut [u8]) -> Result { ::read(self, buf) @@ -108,10 +97,6 @@ mod core2_impl { fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { ::read_exact(self, buf) } - - fn take(self, limit: u64) -> Self::Take { - ::take(self, limit) - } } impl Write for W { @@ -145,7 +130,6 @@ mod default_impl { impl<'a> Read for &'a [u8] { type Error = Error; - type Take = &'a [u8]; fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result { let amt = ::core::cmp::min(buf.len(), self.len()); @@ -182,10 +166,6 @@ mod default_impl { *self = b; Ok(()) } - - fn take(self, limit: u64) -> Self::Take { - &self[..limit as usize] - } } impl Write for ::alloc::vec::Vec { From 38bb903819033be2f9cc62f7b5b7cbcc3a58dfc9 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 25 May 2021 15:16:59 +0200 Subject: [PATCH 14/14] Add literacy:Error with inexpensive ErrorKind and inner error for introspection, adds ErrorTrait to expose kind --- src/lib.rs | 2 +- src/literacy.rs | 130 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ab1bf85..fcbc202 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ pub mod _export { #[cfg(feature = "schemars")] extern crate schemars; -#[cfg(all(not(feature = "use-core2"), not(feature = "std")))] +#[cfg(not(feature = "std"))] extern crate alloc; #[macro_use] mod util; diff --git a/src/literacy.rs b/src/literacy.rs index c2941aa..4a28dea 100644 --- a/src/literacy.rs +++ b/src/literacy.rs @@ -22,12 +22,20 @@ //! and `impl Write for ::alloc::vec::Vec` are provided //! +#[cfg(feature = "std")] +use std::boxed::Box as AllocBox; + +#[cfg(not(feature = "std"))] +use alloc::boxed::Box as AllocBox; + /// The Read trait allows for reading bytes from a source. pub trait Read { /// The error type returned in Result - type Error; + type Error: ErrorTrait; + /// see [std::io::Read::read] fn read(&mut self, buf: &mut [u8]) -> ::core::result::Result; + /// see [std::io::Read::read_exact] fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error>; } @@ -35,17 +43,92 @@ pub trait Read { /// The Write trait allows to write bytes in the object implementing it. pub trait Write { /// The error type returned in Result - type Error; + type Error: ErrorTrait; + /// see [std::io::Write::write] fn write(&mut self, buf: &[u8]) -> ::core::result::Result; + /// see [std::io::Write::write_all] fn write_all(&mut self, buf: &[u8]) -> ::core::result::Result<(), Self::Error>; + /// see [std::io::Write::flush] fn flush(&mut self) -> ::core::result::Result<(), Self::Error>; } +/// The literacy Error trait, custom errors must implement this +pub trait ErrorTrait { + /// The error category + fn kind(&self) -> ErrorKind; +} + +/// Same as [std::io::ErrorKind] that we have to duplicate because ErrorKind is not in `core` +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[allow(missing_docs)] +pub enum ErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + BrokenPipe, + AlreadyExists, + WouldBlock, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + Interrupted, + Other, + UnexpectedEof, +} + +/// The Error type in case we are not using [std::io::Error] or [core2::io::Error] +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, + + error: AllocBox, +} + +trait InnerError: ::core::fmt::Debug + ::core::any::Any {} + +impl InnerError for () {} + +impl ErrorTrait for Error { + fn kind(&self) -> ErrorKind { + self.kind + } +} + #[cfg(all(feature = "std", not(feature = "use-core2")))] -pub use ::std::io::Error; +impl ErrorTrait for ::std::io::Error { + fn kind(&self) -> ErrorKind { + match self.kind() { + ::std::io::ErrorKind::NotFound => ErrorKind::NotFound, + ::std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied, + ::std::io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused, + ::std::io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset, + ::std::io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted, + ::std::io::ErrorKind::NotConnected => ErrorKind::NotConnected, + ::std::io::ErrorKind::AddrInUse => ErrorKind::AddrInUse, + ::std::io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable, + ::std::io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe, + ::std::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists, + ::std::io::ErrorKind::WouldBlock => ErrorKind::WouldBlock, + ::std::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput, + ::std::io::ErrorKind::InvalidData => ErrorKind::InvalidData, + ::std::io::ErrorKind::TimedOut => ErrorKind::TimedOut, + ::std::io::ErrorKind::WriteZero => ErrorKind::WriteZero, + ::std::io::ErrorKind::Interrupted => ErrorKind::Interrupted, + ::std::io::ErrorKind::Other => ErrorKind::Other, + ::std::io::ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof, + _ => ErrorKind::Other, + } + } +} #[cfg(all(feature = "std", not(feature = "use-core2")))] mod std_impl { @@ -81,7 +164,31 @@ mod std_impl { } #[cfg(feature = "use-core2")] -pub use core2::io::Error; +impl ErrorTrait for core2::io::Error { + fn kind(&self) -> ErrorKind { + match self.kind() { + core2::io::ErrorKind::NotFound => ErrorKind::NotFound, + core2::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied, + core2::io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused, + core2::io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset, + core2::io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted, + core2::io::ErrorKind::NotConnected => ErrorKind::NotConnected, + core2::io::ErrorKind::AddrInUse => ErrorKind::AddrInUse, + core2::io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable, + core2::io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe, + core2::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists, + core2::io::ErrorKind::WouldBlock => ErrorKind::WouldBlock, + core2::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput, + core2::io::ErrorKind::InvalidData => ErrorKind::InvalidData, + core2::io::ErrorKind::TimedOut => ErrorKind::TimedOut, + core2::io::ErrorKind::WriteZero => ErrorKind::WriteZero, + core2::io::ErrorKind::Interrupted => ErrorKind::Interrupted, + core2::io::ErrorKind::Other => ErrorKind::Other, + core2::io::ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof, + _ => ErrorKind::Other, + } + } +} #[cfg(feature = "use-core2")] mod core2_impl { @@ -116,17 +223,9 @@ mod core2_impl { } } -#[cfg(all(not(feature = "use-core2"), not(feature = "std")))] -#[derive(Debug)] -/// The Error for the default implementation -pub enum Error { - /// Unexpected "end of file" - UnexpectedEof, -} - #[cfg(all(not(feature = "use-core2"), not(feature = "std")))] mod default_impl { - use super::{Read, Write, Error}; + use super::{Read, Write, Error, ErrorKind}; impl<'a> Read for &'a [u8] { type Error = Error; @@ -150,7 +249,10 @@ mod default_impl { fn read_exact(&mut self, buf: &mut [u8]) -> ::core::result::Result<(), Self::Error> { if buf.len() > self.len() { - return Err(Self::Error::UnexpectedEof); + return Err( Self::Error { + kind: ErrorKind::UnexpectedEof, + error: alloc::boxed::Box::new(()), + }); } let (a, b) = self.split_at(buf.len());