diff --git a/quick-protobuf/Cargo.toml b/quick-protobuf/Cargo.toml index dcfebf12..bf610bfd 100644 --- a/quick-protobuf/Cargo.toml +++ b/quick-protobuf/Cargo.toml @@ -11,15 +11,15 @@ repository = "https://github.com/tafia/quick-protobuf" edition = "2018" [dependencies] -byteorder = "1.2.4" -failure = "0.1.1" +byteorder = { version = "1.2.4", default-features = false } +failure = { version = "0.1.1", default-features = false } failure_derive = "0.1.1" -arrayvec = {version = "0.4.11", optional = true } +arrayvec = { version = "0.4.11", optional = true, default-features = false } [dev-dependencies] lazy_static = "0.2.10" -arrayvec = "0.4.11" [features] -default = ["with_arrayvec"] +default = ["std", "with_arrayvec"] +std = ["byteorder/std", "failure/std"] with_arrayvec = ["arrayvec"] diff --git a/quick-protobuf/no-std-example/Cargo.toml b/quick-protobuf/no-std-example/Cargo.toml new file mode 100644 index 00000000..816904f2 --- /dev/null +++ b/quick-protobuf/no-std-example/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "no-std-example" +version = "0.1.0" +authors = ["Russell Mull "] +edition = "2018" + +[dependencies] +quick-protobuf = { path = "..", default-features = false, features = ["with_arrayvec"] } +arrayvec = { version = "0.4.11", default-features = false } + +[workspace] diff --git a/quick-protobuf/no-std-example/Readme.md b/quick-protobuf/no-std-example/Readme.md new file mode 100644 index 00000000..dfa6c0c8 --- /dev/null +++ b/quick-protobuf/no-std-example/Readme.md @@ -0,0 +1,2 @@ +This is just a compile-test; the actual functionality is all available in normal +builds. diff --git a/quick-protobuf/no-std-example/src/lib.rs b/quick-protobuf/no-std-example/src/lib.rs new file mode 100644 index 00000000..5501698c --- /dev/null +++ b/quick-protobuf/no-std-example/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] + +use quick_protobuf::{serialize_into_slice, deserialize_from_slice}; +use crate::protos::no_std::*; + +mod protos; + +pub fn round_trip() { + let message = NoStdMessage::default(); + + let mut buf = [0u8; 1024]; + serialize_into_slice(&message, &mut buf).unwrap(); + + let read_message = deserialize_from_slice(&buf).unwrap(); + assert_eq!(message, read_message); +} + diff --git a/quick-protobuf/no-std-example/src/mod.rs b/quick-protobuf/no-std-example/src/mod.rs new file mode 100644 index 00000000..cb5b6283 --- /dev/null +++ b/quick-protobuf/no-std-example/src/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub mod protos; diff --git a/quick-protobuf/no-std-example/src/no_std.proto b/quick-protobuf/no-std-example/src/no_std.proto new file mode 100644 index 00000000..2ff021b8 --- /dev/null +++ b/quick-protobuf/no-std-example/src/no_std.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package protos.no_std; + +enum MyEnum { + Val0 = 0; + Val1 = 1; +} + +message EmbeddedMessage { + int32 val = 1; + MyEnum e = 2; +} + +message NoStdMessage { + fixed32 num = 1; + repeated fixed32 nums = 2 [rust_gen_arrayvec = 16]; + EmbeddedMessage message = 3; + repeated EmbeddedMessage messages = 4 [rust_gen_arrayvec = 16]; +} diff --git a/quick-protobuf/no-std-example/src/protos/mod.rs b/quick-protobuf/no-std-example/src/protos/mod.rs new file mode 100644 index 00000000..13ac3b3a --- /dev/null +++ b/quick-protobuf/no-std-example/src/protos/mod.rs @@ -0,0 +1,2 @@ +// Automatically generated mod.rs +pub mod no_std; diff --git a/quick-protobuf/no-std-example/src/protos/no_std.rs b/quick-protobuf/no-std-example/src/protos/no_std.rs new file mode 100644 index 00000000..dc424fc3 --- /dev/null +++ b/quick-protobuf/no-std-example/src/protos/no_std.rs @@ -0,0 +1,125 @@ +// Automatically generated rust module for 'no_std.proto' file + +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(unused_imports)] +#![allow(unknown_lints)] +#![allow(clippy)] +#![cfg_attr(rustfmt, rustfmt_skip)] + + +use quick_protobuf::{MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; +use quick_protobuf::sizeofs::*; +use super::super::*; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum MyEnum { + Val0 = 0, + Val1 = 1, +} + +impl Default for MyEnum { + fn default() -> Self { + MyEnum::Val0 + } +} + +impl From for MyEnum { + fn from(i: i32) -> Self { + match i { + 0 => MyEnum::Val0, + 1 => MyEnum::Val1, + _ => Self::default(), + } + } +} + +impl<'a> From<&'a str> for MyEnum { + fn from(s: &'a str) -> Self { + match s { + "Val0" => MyEnum::Val0, + "Val1" => MyEnum::Val1, + _ => Self::default(), + } + } +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub struct EmbeddedMessage { + pub val: i32, + pub e: protos::no_std::MyEnum, +} + +impl<'a> MessageRead<'a> for EmbeddedMessage { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(8) => msg.val = r.read_int32(bytes)?, + Ok(16) => msg.e = r.read_enum(bytes)?, + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for EmbeddedMessage { + fn get_size(&self) -> usize { + 0 + + if self.val == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.val) as u64) } + + if self.e == protos::no_std::MyEnum::Val0 { 0 } else { 1 + sizeof_varint(*(&self.e) as u64) } + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if self.val != 0i32 { w.write_with_tag(8, |w| w.write_int32(*&self.val))?; } + if self.e != protos::no_std::MyEnum::Val0 { w.write_with_tag(16, |w| w.write_enum(*&self.e as i32))?; } + Ok(()) + } +} + +#[derive(Debug, Default, PartialEq, Clone)] +pub struct NoStdMessage { + pub num: u32, + pub nums: arrayvec::ArrayVec<[u32; 16]>, + pub message: Option, + pub messages: arrayvec::ArrayVec<[protos::no_std::EmbeddedMessage; 16]>, +} + +impl<'a> MessageRead<'a> for NoStdMessage { + fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { + let mut msg = Self::default(); + while !r.is_eof() { + match r.next_tag(bytes) { + Ok(13) => msg.num = r.read_fixed32(bytes)?, + Ok(18) => msg.nums = r.read_packed_arrayvec(bytes, |r, bytes| Ok(r.read_fixed32(bytes)?))?, + Ok(26) => msg.message = Some(r.read_message::(bytes)?), + Ok(34) => msg.messages.push(r.read_message::(bytes)?), + Ok(t) => { r.read_unknown(bytes, t)?; } + Err(e) => return Err(e), + } + } + Ok(msg) + } +} + +impl MessageWrite for NoStdMessage { + fn get_size(&self) -> usize { + 0 + + if self.num == 0u32 { 0 } else { 1 + 4 } + + if self.nums.is_empty() { 0 } else { 1 + sizeof_len(self.nums.len() * 4) } + + self.message.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + + self.messages.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() + } + + fn write_message(&self, w: &mut Writer) -> Result<()> { + if self.num != 0u32 { w.write_with_tag(13, |w| w.write_fixed32(*&self.num))?; } + w.write_packed_fixed_with_tag(18, &self.nums)?; + if let Some(ref s) = self.message { w.write_with_tag(26, |w| w.write_message(s))?; } + for s in &self.messages { w.write_with_tag(34, |w| w.write_message(s))?; } + Ok(()) + } +} + diff --git a/quick-protobuf/src/errors.rs b/quick-protobuf/src/errors.rs index 84fedd2c..4293bdde 100644 --- a/quick-protobuf/src/errors.rs +++ b/quick-protobuf/src/errors.rs @@ -1,30 +1,37 @@ -//! A module to handle all errors via error-chain crate +//! A module to handle all errors via failure crate -use failure::Fail; +#[cfg(feature = "std")] use std::io; /// An error enum which derives `Fail` -#[derive(Debug, Fail)] +#[derive(Debug, failure_derive::Fail)] pub enum Error { /// Io error + #[cfg(feature = "std")] #[fail(display = "{}", _0)] Io(#[cause] io::Error), + /// Utf8 Error #[fail(display = "{}", _0)] - Utf8(#[cause] ::std::str::Utf8Error), + Utf8(::core::str::Utf8Error), /// Deprecated feature (in protocol buffer specification) #[fail(display = "Feature '{}' has been deprecated", _0)] Deprecated(&'static str), + /// Unknown wire type #[fail(display = "Unknown wire type '{}', must be less than 6", _0)] UnknownWireType(u8), + /// Varint decoding error #[fail(display = "Cannot decode varint")] Varint, + /// Error while parsing protocol buffer message + #[cfg(feature = "std")] #[fail(display = "Error while parsing message: {}", _0)] Message(String), + /// Unexpected map tag #[fail(display = "Unexpected map tag: '{}', expecting 1 or 2", _0)] Map(u8), @@ -39,10 +46,12 @@ pub enum Error { } /// A wrapper for `Result` -pub type Result = ::std::result::Result; +pub type Result = ::core::result::Result; +#[cfg(feature = "std")] impl Into for Error { fn into(self) -> ::std::io::Error { + use failure::Fail; match self { Error::Io(x) => x, Error::Utf8(x) => io::Error::new(io::ErrorKind::InvalidData, x), @@ -51,14 +60,15 @@ impl Into for Error { } } +#[cfg(feature = "std")] impl From for Error { fn from(e: io::Error) -> Error { Error::Io(e) } } -impl From<::std::str::Utf8Error> for Error { - fn from(e: ::std::str::Utf8Error) -> Error { +impl From<::core::str::Utf8Error> for Error { + fn from(e: ::core::str::Utf8Error) -> Error { Error::Utf8(e) } } diff --git a/quick-protobuf/src/lib.rs b/quick-protobuf/src/lib.rs index 2a5ca46c..b8c79447 100644 --- a/quick-protobuf/src/lib.rs +++ b/quick-protobuf/src/lib.rs @@ -4,6 +4,7 @@ #![deny(missing_docs)] #![allow(dead_code)] +#![cfg_attr(not(feature = "std"), no_std)] extern crate byteorder; extern crate failure; @@ -18,4 +19,9 @@ pub mod writer; pub use crate::errors::{Error, Result}; pub use crate::message::{MessageRead, MessageWrite}; pub use crate::reader::{deserialize_from_slice, BytesReader}; -pub use crate::writer::{serialize_into_vec, serialize_into_slice, BytesWriter, Writer, WriterBackend}; +pub use crate::writer::{serialize_into_slice, BytesWriter, Writer, WriterBackend}; + +#[cfg(feature = "std")] +pub use crate::reader::Reader; +#[cfg(feature = "std")] +pub use crate::writer::serialize_into_vec; diff --git a/quick-protobuf/src/message.rs b/quick-protobuf/src/message.rs index 6ab0eb29..77e5a9fb 100644 --- a/quick-protobuf/src/message.rs +++ b/quick-protobuf/src/message.rs @@ -2,8 +2,11 @@ //! //! Creates the struct and implements a reader +#[cfg(feature = "std")] use std::fs::File; -use std::io::{BufWriter, Write}; +#[cfg(feature = "std")] +use std::io::BufWriter; +#[cfg(feature = "std")] use std::path::Path; use crate::errors::Result; @@ -23,6 +26,7 @@ pub trait MessageWrite: Sized { } /// Writes self into a file + #[cfg(feature = "std")] fn write_file>(&self, p: P) -> Result<()> { let file = BufWriter::new(File::create(p)?); let mut writer = Writer::new(file); diff --git a/quick-protobuf/src/reader.rs b/quick-protobuf/src/reader.rs index 3395d54a..67e8e01d 100644 --- a/quick-protobuf/src/reader.rs +++ b/quick-protobuf/src/reader.rs @@ -7,8 +7,11 @@ //! //! It is advised, for convenience to directly work with a `Reader`. +#[cfg(feature = "std")] use std::fs::File; -use std::io::{self, Read}; +#[cfg(feature = "std")] +use std::io::Read; +#[cfg(feature = "std")] use std::path::Path; #[cfg(feature = "with_arrayvec")] @@ -343,7 +346,7 @@ impl BytesReader { #[inline] pub fn read_string<'a>(&mut self, bytes: &'a [u8]) -> Result<&'a str> { self.read_len_varint(bytes, |r, b| { - ::std::str::from_utf8(&b[r.start..r.end]).map_err(|e| e.into()) + ::core::str::from_utf8(&b[r.start..r.end]).map_err(|e| e.into()) }) } @@ -351,6 +354,7 @@ impl BytesReader { /// /// Note: packed field are stored as a variable length chunk of data, while regular repeated /// fields behaves like an iterator, yielding their tag everytime + #[cfg(feature = "std")] #[inline] pub fn read_packed<'a, M, F>(&mut self, bytes: &'a [u8], mut read: F) -> Result> where @@ -395,9 +399,9 @@ impl BytesReader { if self.len() < len { return Err(Error::UnexpectedEndOfBuffer); } - let n = len / ::std::mem::size_of::(); + let n = len / ::core::mem::size_of::(); let slice = unsafe { - ::std::slice::from_raw_parts( + ::core::slice::from_raw_parts( bytes.get_unchecked(self.start) as *const u8 as *const M, n, ) @@ -440,8 +444,8 @@ impl BytesReader { where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, G: FnMut(&mut BytesReader, &'a [u8]) -> Result, - K: ::std::fmt::Debug + Default, - V: ::std::fmt::Debug + Default, + K: ::core::fmt::Debug + Default, + V: ::core::fmt::Debug + Default, { self.read_len_varint(bytes, |r, bytes| { let mut k = K::default(); @@ -547,11 +551,13 @@ impl BytesReader { /// println!("Found {} foos and {} bars!", foobar.foos.len(), foobar.bars.len()); /// } /// ``` +#[cfg(feature = "std")] pub struct Reader { buffer: Vec, inner: BytesReader, } +#[cfg(feature = "std")] impl Reader { /// Creates a new `Reader` pub fn from_reader(mut r: R, capacity: usize) -> Result { diff --git a/quick-protobuf/src/writer.rs b/quick-protobuf/src/writer.rs index 176ff9c6..f98d9707 100644 --- a/quick-protobuf/src/writer.rs +++ b/quick-protobuf/src/writer.rs @@ -1,11 +1,11 @@ //! A module to manage protobuf serialization -use std::io::Write; - use crate::errors::{Error, Result}; use crate::message::MessageWrite; use byteorder::{ByteOrder, LittleEndian as LE}; + +#[cfg(feature = "std")] use byteorder::WriteBytesExt; /// A struct to write protobuf messages @@ -204,8 +204,8 @@ impl Writer { /// all data at once #[inline] pub fn write_packed_fixed(&mut self, v: &[M]) -> Result<()> { - let len = v.len() * ::std::mem::size_of::(); - let bytes = unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; + let len = v.len() * ::core::mem::size_of::(); + let bytes = unsafe { ::core::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; self.write_bytes(bytes) } @@ -263,8 +263,8 @@ impl Writer { } self.write_tag(tag)?; - let len = ::std::mem::size_of::() * v.len(); - let bytes = unsafe { ::std::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; + let len = ::core::mem::size_of::() * v.len(); + let bytes = unsafe { ::core::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; self.write_bytes(bytes) } @@ -283,7 +283,7 @@ impl Writer { self.write_tag(tag)?; let len = v.len() * item_size; let bytes = - unsafe { ::std::slice::from_raw_parts(v as *const [M] as *const M as *const u8, len) }; + unsafe { ::core::slice::from_raw_parts(v as *const [M] as *const M as *const u8, len) }; self.write_bytes(bytes) } @@ -309,6 +309,7 @@ impl Writer { } /// Serialize a `MessageWrite` into a `Vec` +#[cfg(feature = "std")] pub fn serialize_into_vec(message: &M) -> Result> { let len = message.get_size(); let mut v = Vec::with_capacity(len + crate::sizeofs::sizeof_len(len)); @@ -463,6 +464,7 @@ impl<'a> WriterBackend for BytesWriter<'a> { } } +#[cfg(feature = "std")] impl WriterBackend for W { #[inline(always)] fn pb_write_u8(&mut self, x: u8) -> Result<()> { diff --git a/run_test.sh b/run_test.sh index 2fc10863..d25bd06c 100755 --- a/run_test.sh +++ b/run_test.sh @@ -8,4 +8,9 @@ cargo run -p quick-protobuf --example pb_rs_example_v3_owned cargo run -p quick-protobuf --example pb_rs_example cargo run -p quick-protobuf --example pb_rs_example_v3 +# test that no_std can build +pushd quick-protobuf/no-std-example + cargo build +popd + cargo test -p pb-rs -p quick-protobuf