Skip to content

Commit

Permalink
Support no_std in runtime crate
Browse files Browse the repository at this point in the history
- Add the `std` feature to the quick-protobuf runtime crate
- Put uses of Vec and std::io::Write behind the `std` feature
- Change other uses of `std` to `core`
- Add the `quick-protobuf/no-std-example` compile test
  • Loading branch information
mullr committed Aug 30, 2019
1 parent 097a003 commit 24af3a6
Show file tree
Hide file tree
Showing 14 changed files with 239 additions and 27 deletions.
10 changes: 5 additions & 5 deletions quick-protobuf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
11 changes: 11 additions & 0 deletions quick-protobuf/no-std-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "no-std-example"
version = "0.1.0"
authors = ["Russell Mull <[email protected]>"]
edition = "2018"

[dependencies]
quick-protobuf = { path = "..", default-features = false, features = ["with_arrayvec"] }
arrayvec = { version = "0.4.11", default-features = false }

[workspace]
2 changes: 2 additions & 0 deletions quick-protobuf/no-std-example/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This is just a compile-test; the actual functionality is all available in normal
builds.
17 changes: 17 additions & 0 deletions quick-protobuf/no-std-example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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);
}

2 changes: 2 additions & 0 deletions quick-protobuf/no-std-example/src/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Automatically generated mod.rs
pub mod protos;
20 changes: 20 additions & 0 deletions quick-protobuf/no-std-example/src/no_std.proto
Original file line number Diff line number Diff line change
@@ -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];
}
2 changes: 2 additions & 0 deletions quick-protobuf/no-std-example/src/protos/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Automatically generated mod.rs
pub mod no_std;
125 changes: 125 additions & 0 deletions quick-protobuf/no-std-example/src/protos/no_std.rs
Original file line number Diff line number Diff line change
@@ -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<i32> 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<Self> {
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<W: WriterBackend>(&self, w: &mut Writer<W>) -> 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<protos::no_std::EmbeddedMessage>,
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<Self> {
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::<protos::no_std::EmbeddedMessage>(bytes)?),
Ok(34) => msg.messages.push(r.read_message::<protos::no_std::EmbeddedMessage>(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::<usize>()
}

fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> 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(())
}
}

24 changes: 17 additions & 7 deletions quick-protobuf/src/errors.rs
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -39,10 +46,12 @@ pub enum Error {
}

/// A wrapper for `Result<T, Error>`
pub type Result<T> = ::std::result::Result<T, Error>;
pub type Result<T> = ::core::result::Result<T, Error>;

#[cfg(feature = "std")]
impl Into<io::Error> 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),
Expand All @@ -51,14 +60,15 @@ impl Into<io::Error> for Error {
}
}

#[cfg(feature = "std")]
impl From<io::Error> 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)
}
}
8 changes: 7 additions & 1 deletion quick-protobuf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![deny(missing_docs)]
#![allow(dead_code)]
#![cfg_attr(not(feature = "std"), no_std)]

extern crate byteorder;
extern crate failure;
Expand All @@ -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;
6 changes: 5 additions & 1 deletion quick-protobuf/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,6 +26,7 @@ pub trait MessageWrite: Sized {
}

/// Writes self into a file
#[cfg(feature = "std")]
fn write_file<P: AsRef<Path>>(&self, p: P) -> Result<()> {
let file = BufWriter::new(File::create(p)?);
let mut writer = Writer::new(file);
Expand Down
18 changes: 12 additions & 6 deletions quick-protobuf/src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -343,14 +346,15 @@ 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())
})
}

/// Reads packed repeated field (Vec<M>)
///
/// 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<Vec<M>>
where
Expand Down Expand Up @@ -395,9 +399,9 @@ impl BytesReader {
if self.len() < len {
return Err(Error::UnexpectedEndOfBuffer);
}
let n = len / ::std::mem::size_of::<M>();
let n = len / ::core::mem::size_of::<M>();
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,
)
Expand Down Expand Up @@ -440,8 +444,8 @@ impl BytesReader {
where
F: FnMut(&mut BytesReader, &'a [u8]) -> Result<K>,
G: FnMut(&mut BytesReader, &'a [u8]) -> Result<V>,
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();
Expand Down Expand Up @@ -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<u8>,
inner: BytesReader,
}

#[cfg(feature = "std")]
impl Reader {
/// Creates a new `Reader`
pub fn from_reader<R: Read>(mut r: R, capacity: usize) -> Result<Reader> {
Expand Down
Loading

0 comments on commit 24af3a6

Please sign in to comment.