diff --git a/Cargo.toml b/Cargo.toml index b03ad53..e9c5dc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,12 @@ license = "Apache-2.0" edition = "2018" [dependencies] -thiserror = "1.0.37" -serde = { version = "1.0.145", features = ["derive"] } +serde = { version = "1.0.145", default-features = false, features = ["derive"] } +thiserror = { version = "2.0.18", default-features = false } + +[features] +default = ["std"] +std = [] [dev-dependencies] criterion = "0.3.6" diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 5e3a425..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.73.0 diff --git a/src/de.rs b/src/de.rs index 08534a2..03aa952 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::{Error, Result}; -use serde::de::{self, Deserialize, DeserializeOwned, DeserializeSeed, IntoDeserializer, Visitor}; -use std::{convert::TryFrom, io::Read}; +use core::convert::TryFrom; +#[cfg(feature = "std")] +use serde::de::DeserializeOwned; +use serde::de::{self, Deserialize, DeserializeSeed, IntoDeserializer, Visitor}; -/// Deserializes a `&[u8]` into a type. +/// Deserializes into a type. /// /// This function will attempt to interpret `bytes` as the BCS serialized form of `T` and /// deserialize `T` from `bytes`. @@ -85,8 +87,9 @@ where Ok(t) } -/// Deserialize a type from an implementation of [`Read`]. -pub fn from_reader(mut reader: impl Read) -> Result +#[cfg(feature = "std")] +/// Deserialize a type from an implementation of [`std::io::Read`]. +pub fn from_reader(mut reader: impl std::io::Read) -> Result where T: DeserializeOwned, { @@ -96,9 +99,10 @@ where Ok(t) } +#[cfg(feature = "std")] /// Same as `from_reader_seed` but use `limit` as max container depth instead of MAX_CONTAINER_DEPTH` /// Note that `limit` has to be lower than MAX_CONTAINER_DEPTH -pub fn from_reader_with_limit(mut reader: impl Read, limit: usize) -> Result +pub fn from_reader_with_limit(mut reader: impl std::io::Read, limit: usize) -> Result where T: DeserializeOwned, { @@ -111,8 +115,9 @@ where Ok(t) } -/// Deserialize a type from an implementation of [`Read`] using the provided seed -pub fn from_reader_seed(seed: T, mut reader: impl Read) -> Result +#[cfg(feature = "std")] +/// Deserialize a type from an implementation of [`std::io::Read`] using the provided seed +pub fn from_reader_seed(seed: T, mut reader: impl std::io::Read) -> Result where for<'a> T: DeserializeSeed<'a, Value = V>, { @@ -122,9 +127,14 @@ where Ok(t) } +#[cfg(feature = "std")] /// Same as `from_reader_seed` but use `limit` as max container depth instead of MAX_CONTAINER_DEPTH` /// Note that `limit` has to be lower than MAX_CONTAINER_DEPTH -pub fn from_reader_seed_with_limit(seed: T, mut reader: impl Read, limit: usize) -> Result +pub fn from_reader_seed_with_limit( + seed: T, + mut reader: impl std::io::Read, + limit: usize, +) -> Result where for<'a> T: DeserializeSeed<'a, Value = V>, { @@ -143,7 +153,8 @@ struct Deserializer { max_remaining_depth: usize, } -impl<'de, R: Read> Deserializer> { +#[cfg(feature = "std")] +impl<'de, R: std::io::Read> Deserializer> { fn from_reader(input: &'de mut R, max_remaining_depth: usize) -> Self { Deserializer { input: TeeReader::new(input), @@ -163,7 +174,8 @@ impl<'de> Deserializer<&'de [u8]> { } } -/// A reader that can optionally capture all bytes from an underlying [`Read`]er +#[cfg(feature = "std")] +/// A reader that can optionally capture all bytes from an underlying [`std::io::Read`]er struct TeeReader<'de, R> { /// the underlying reader reader: &'de mut R, @@ -171,6 +183,7 @@ struct TeeReader<'de, R> { captured_keys: Vec>, } +#[cfg(feature = "std")] impl<'de, R> TeeReader<'de, R> { /// Wraps the provided reader in a new [`TeeReader`]. pub fn new(reader: &'de mut R) -> Self { @@ -181,7 +194,8 @@ impl<'de, R> TeeReader<'de, R> { } } -impl<'de, R: Read> Read for TeeReader<'de, R> { +#[cfg(feature = "std")] +impl<'de, R: std::io::Read> std::io::Read for TeeReader<'de, R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let bytes_read = self.reader.read(buf)?; if let Some(buffer) = self.captured_keys.last_mut() { @@ -289,7 +303,8 @@ trait BcsDeserializer<'de> { } } -impl<'de, R: Read> Deserializer> { +#[cfg(feature = "std")] +impl<'de, R: std::io::Read> Deserializer> { fn parse_vec(&mut self) -> Result> { let len = self.parse_length()?; let mut output = vec![0; len]; @@ -303,11 +318,12 @@ impl<'de, R: Read> Deserializer> { } } -impl<'de, R: Read> BcsDeserializer<'de> for Deserializer> { +#[cfg(feature = "std")] +impl<'de, R: std::io::Read> BcsDeserializer<'de> for Deserializer> { type MaybeBorrowedBytes = Vec; fn fill_slice(&mut self, slice: &mut [u8]) -> Result<()> { - Ok(self.input.read_exact(slice)?) + Ok(std::io::Read::read_exact(&mut self.input, slice)?) } fn parse_and_visit_str(&mut self, visitor: V) -> Result @@ -339,7 +355,7 @@ impl<'de, R: Read> BcsDeserializer<'de> for Deserializer> { fn end(&mut self) -> Result<()> { let mut byte = [0u8; 1]; - match self.input.read_exact(&mut byte) { + match std::io::Read::read_exact(&mut self.input, &mut byte) { Ok(_) => Err(Error::RemainingInput), Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(()), Err(e) => Err(e.into()), @@ -410,7 +426,7 @@ impl<'de> Deserializer<&'de [u8]> { fn parse_string(&mut self) -> Result<&'de str> { let slice = self.parse_bytes()?; - std::str::from_utf8(slice).map_err(|_| Error::Utf8) + core::str::from_utf8(slice).map_err(|_| Error::Utf8) } } diff --git a/src/error.rs b/src/error.rs index 80d567b..63287fa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,12 @@ // Copyright (c) The Diem Core Contributors // SPDX-License-Identifier: Apache-2.0 +use alloc::string::{String, ToString}; +use core::fmt; use serde::{de, ser}; -use std::{fmt, io::ErrorKind}; use thiserror::Error; -pub type Result = std::result::Result; +pub type Result = core::result::Result; #[derive(Clone, Debug, Error, Eq, PartialEq)] pub enum Error { @@ -43,9 +44,10 @@ pub enum Error { IntegerOverflowDuringUleb128Decoding, } +#[cfg(feature = "std")] impl From for Error { fn from(err: std::io::Error) -> Self { - if err.kind() == ErrorKind::UnexpectedEof { + if err.kind() == std::io::ErrorKind::UnexpectedEof { Error::Eof } else { Error::Io(err.to_string()) diff --git a/src/lib.rs b/src/lib.rs index 4ede21a..4e692a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #![forbid(unsafe_code)] +#![cfg_attr(not(feature = "std"), no_std)] //! # Binary Canonical Serialization (BCS) //! @@ -303,9 +304,13 @@ //! # Ok(())} //! ``` +extern crate alloc; + mod de; mod error; mod ser; + +#[cfg(feature = "std")] pub mod test_helpers; /// Variable length sequences in BCS are limited to max length of 2^31 - 1. @@ -314,12 +319,14 @@ pub const MAX_SEQUENCE_LENGTH: usize = (1 << 31) - 1; /// Maximal allowed depth of BCS data, counting only structs and enums. pub const MAX_CONTAINER_DEPTH: usize = 500; -pub use de::{ - from_bytes, from_bytes_seed, from_bytes_seed_with_limit, from_bytes_with_limit, from_reader, - from_reader_seed, from_reader_seed_with_limit, from_reader_with_limit, -}; +pub use de::{from_bytes, from_bytes_seed, from_bytes_seed_with_limit, from_bytes_with_limit}; +#[cfg(feature = "std")] +pub use de::{from_reader, from_reader_seed, from_reader_seed_with_limit, from_reader_with_limit}; pub use error::{Error, Result}; pub use ser::{ - is_human_readable, serialize_into, serialize_into_with_limit, serialized_size, - serialized_size_with_limit, to_bytes, to_bytes_with_limit, + is_human_readable, serialize_into_with_limit, serialized_size, serialized_size_with_limit, + to_bytes, to_bytes_with_limit, }; + +#[cfg(feature = "std")] +pub use ser::serialize_into; diff --git a/src/ser.rs b/src/ser.rs index 9bd42ce..581591d 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::{Error, Result}; +use alloc::{string::ToString, vec::Vec}; use serde::{ser, Serialize}; /// Serialize the given data structure as a `Vec` of BCS. @@ -69,10 +70,10 @@ where Ok(output) } -/// Same as `to_bytes` but write directly into an `std::io::Write` object. +/// Same as `to_bytes` but write directly into an `Write` object. pub fn serialize_into(write: &mut W, value: &T) -> Result<()> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, T: ?Sized + Serialize, { let serializer = Serializer::new(write, crate::MAX_CONTAINER_DEPTH); @@ -83,7 +84,7 @@ where /// Note that `limit` has to be lower than MAX_CONTAINER_DEPTH pub fn serialize_into_with_limit(write: &mut W, value: &T, limit: usize) -> Result<()> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, T: ?Sized + Serialize, { if limit > crate::MAX_CONTAINER_DEPTH { @@ -95,14 +96,41 @@ where struct WriteCounter(usize); -impl std::io::Write for WriteCounter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { +impl WriteCounter { + const ERROR_MESSAGE: &'static str = "WriteCounter reached max value"; + + fn capture_length(&mut self, buf: &[u8]) -> Result { let len = buf.len(); - self.0 = self.0.checked_add(len).ok_or_else(|| { - std::io::Error::new(std::io::ErrorKind::Other, "WriteCounter reached max value") - })?; + self.0 = self + .0 + .checked_add(len) + .ok_or_else(|| Error::Io(Self::ERROR_MESSAGE.to_string()))?; Ok(len) } +} + +#[cfg(not(feature = "std"))] +impl Write for WriteCounter { + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + let _ = self.capture_length(buf)?; + Ok(()) + } +} + +#[cfg(not(feature = "std"))] +impl Write for Vec { + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + self.extend_from_slice(buf); + Ok(()) + } +} + +#[cfg(feature = "std")] +impl std::io::Write for WriteCounter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.capture_length(buf) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, Self::ERROR_MESSAGE)) + } fn flush(&mut self) -> std::io::Result<()> { Ok(()) @@ -145,9 +173,23 @@ struct Serializer<'a, W: ?Sized> { max_remaining_depth: usize, } -impl<'a, W> Serializer<'a, W> +pub trait Write { + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>; +} + +#[cfg(feature = "std")] +impl<'a, W> Write for W where W: ?Sized + std::io::Write, +{ + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { + Ok(std::io::Write::write_all(self, buf)?) + } +} + +impl<'a, W> Serializer<'a, W> +where + W: ?Sized + Write, { /// Creates a new `Serializer` which will emit BCS. fn new(output: &'a mut W, max_remaining_depth: usize) -> Self { @@ -192,7 +234,7 @@ where impl<'a, W> ser::Serializer for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -270,6 +312,13 @@ where self.serialize_bytes(v.as_bytes()) } + fn collect_str(self, value: &T) -> core::result::Result + where + T: ?Sized + core::fmt::Display, + { + self.serialize_str(&value.to_string()) + } + // Serialize a byte array as an array of bytes. fn serialize_bytes(mut self, v: &[u8]) -> Result<()> { self.output_seq_len(v.len())?; @@ -405,7 +454,7 @@ where impl<'a, W> ser::SerializeSeq for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -424,7 +473,7 @@ where impl<'a, W> ser::SerializeTuple for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -443,7 +492,7 @@ where impl<'a, W> ser::SerializeTupleStruct for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -462,7 +511,7 @@ where impl<'a, W> ser::SerializeTupleVariant for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -498,7 +547,7 @@ impl<'a, W: ?Sized> MapSerializer<'a, W> { impl<'a, W> ser::SerializeMap for MapSerializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -559,7 +608,7 @@ where impl<'a, W> ser::SerializeStruct for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error; @@ -578,7 +627,7 @@ where impl<'a, W> ser::SerializeStructVariant for Serializer<'a, W> where - W: ?Sized + std::io::Write, + W: ?Sized + Write, { type Ok = (); type Error = Error;