Skip to content

Feature: no_std support, following the pattern of serde #187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ deserializing Rust structs.
This project is developed using TDD and CI, so any found bugs will be fixed without breaking
existing functionality.

- **`no_std` support**

This crate uses the same `no_std` portability pattern as `serde`. That is, there is a `std`
feature which is on by default, and an `alloc` feature which cannot be combined with the `std`
feature and requires the nightly compiler to use.
The crate then depends on the unstable `alloc` crate instead of `std`.

## Requirements

- Rust 1.16
Expand Down
12 changes: 12 additions & 0 deletions nostd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "nostd"
version = "0.1.0"
authors = ["Vadzim Dambrouski <pftbest@gmail.com>"]

[dependencies.byteorder]
version = "1"
default-features = false

[dependencies.arrayvec]
version = "0.4"
default-features = false
10 changes: 10 additions & 0 deletions nostd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
no_std
======

This is a shim that provides alternate impls of some `std` traits so that `rmp`
can be used in a `no_std` target, when `std` trait is omitted.

This is based on work of "Vadzim Dambrouski <pftbest@gmail.com>"
From this repo: https://github.com/pftbest/msgpack-rust at branch `no_std`

with some light refactor / updates.
17 changes: 17 additions & 0 deletions nostd/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use core::fmt::{Debug, Display};
use core::str::Utf8Error;

pub trait Error: Debug + Display {
fn description(&self) -> &str;
fn cause(&self) -> Option<&Error> { None }
}

impl Error for Utf8Error {
fn description(&self) -> &str {
"Utf8Error"
}

fn cause(&self) -> Option<&Error> {
None
}
}
38 changes: 38 additions & 0 deletions nostd/src/io/cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use core::cmp;
use io::{Read, Result};

#[derive(Clone, Debug)]
pub struct Cursor<T> {
inner: T,
pos: u64,
}

impl<T> Cursor<T> {
pub fn new(inner: T) -> Cursor<T> {
Cursor { pos: 0, inner: inner }
}

pub fn position(&self) -> u64 { self.pos }
}

impl<T> Cursor<T> where T: AsRef<[u8]> {
fn fill_buf(&mut self) -> Result<&[u8]> {
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64);
Ok(&self.inner.as_ref()[(amt as usize)..])
}
}

impl<T> Read for Cursor<T> where T: AsRef<[u8]> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let n = Read::read(&mut self.fill_buf()?, buf)?;
self.pos += n as u64;
Ok(n)
}

fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
let n = buf.len();
Read::read_exact(&mut self.fill_buf()?, buf)?;
self.pos += n as u64;
Ok(())
}
}
89 changes: 89 additions & 0 deletions nostd/src/io/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use core::{result, fmt};
use core::fmt::{Display, Formatter};
use error;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub struct Error {
reason: &'static str,
}

impl Error {
pub fn new(reason: &'static str) -> Self {
Error {
reason: reason,
}
}
}

impl error::Error for Error {
fn description(&self) -> &str {
self.reason
}

fn cause(&self) -> Option<&error::Error> {
None
}
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> {
error::Error::description(self).fmt(f)
}
}

#[cfg(feature = "std")]
impl From<::std::io::Error> for Error {
fn from(_err: ::std::io::Error) -> Self {
return ::io::Error { reason: "IO Error" };
}
}

// Note(chbeck): Added this because rmp-serde relies on io::error::ErrorKind
// This is a redux of what existed in rust-sgx-sdk io::error::ErrorKind

/// A list specifying general categories of I/O error.
///
/// This list is intended to grow over time and it is not recommended to
/// exhaustively match against it.
///
/// It is used with the [`io::Error`] type.
///
/// [`io::Error`]: struct.Error.html
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[allow(deprecated)]
pub enum ErrorKind {
/// An error returned when an operation could not be completed because an
/// "end of file" was reached prematurely.
///
/// This typically means that an operation could only succeed if it read a
/// particular number of bytes but only a smaller number of bytes could be
/// read.
UnexpectedEof,

/// A marker variant that tells the compiler that users of this enum cannot
/// match it exhaustively.
#[doc(hidden)]
__Nonexhaustive,
}

impl ErrorKind {
fn as_str(&self) -> &'static str {
match *self {
ErrorKind::UnexpectedEof => "unexpected end of file",
ErrorKind::__Nonexhaustive => unreachable!()
}
}
}

/// Intended for use for errors not exposed to the user, where allocating onto
/// the heap (for normal construction via Error::new) is too costly.
impl From<ErrorKind> for Error {
#[inline]
fn from(kind: ErrorKind) -> Error {
Error {
reason: kind.as_str()
}
}
}
9 changes: 9 additions & 0 deletions nostd/src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod read;
mod write;
mod error;
mod cursor;

pub use self::read::Read;
pub use self::write::Write;
pub use self::error::{Error, ErrorKind, Result};
pub use self::cursor::Cursor;
77 changes: 77 additions & 0 deletions nostd/src/io/read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use core::cmp;
use io::{Error, Result};

pub trait Read {
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
while !buf.is_empty() {
match self.read(buf) {
Ok(0) => break,
Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; }
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(Error::new("failed to fill whole buffer"))
} else {
Ok(())
}
}
}

impl<'a, R: Read + ?Sized> Read for &'a mut R {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
(**self).read(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
(**self).read_exact(buf)
}
}

impl<'a> Read for &'a [u8] {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let amt = 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)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
if buf.len() > self.len() {
return Err(Error::new("failed to fill whole buffer"));
}
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(())
}
}

#[cfg(feature = "std")]
impl<T> Read for ::std::io::Cursor<T> where T: AsRef<[u8]> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
::std::io::Read::read(self, buf).map_err(Into::into)
}
}
49 changes: 49 additions & 0 deletions nostd/src/io/write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use core::{mem, cmp};
use io::{Result, Error};

pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn write_all(&mut self, buf: &[u8]) -> Result<()>;
}

impl<'a, W: Write + ?Sized> Write for &'a mut W {
#[inline]
fn write(&mut self, buf: &[u8]) -> Result<usize> {
(**self).write(buf)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
(**self).write_all(buf)
}
}

impl<'a> Write for &'a mut [u8] {
#[inline]
fn write(&mut self, data: &[u8]) -> Result<usize> {
let amt = cmp::min(data.len(), self.len());
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
a.copy_from_slice(&data[..amt]);
*self = b;
Ok(amt)
}

#[inline]
fn write_all(&mut self, data: &[u8]) -> Result<()> {
if self.write(data)? == data.len() {
Ok(())
} else {
Err(Error::new("failed to write whole buffer"))
}
}
}

impl Write for alloc::vec::Vec<u8> {
fn write(&mut self, data: &[u8]) -> Result<usize> {
self.extend_from_slice(data);
Ok(data.len())
}
fn write_all(&mut self, data: &[u8]) -> Result<()> {
self.extend_from_slice(data);
Ok(())
}
}
Loading