Skip to content
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

alloc support (for no_std environments) #256

Closed
wants to merge 2 commits 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions .travis.yml
Expand Up @@ -33,6 +33,14 @@ matrix:
- env: RUST_TEST_THREADS=1 TARGET=powerpc-unknown-linux-gnu
- env: RUST_TEST_THREADS=1 TARGET=powerpc64-unknown-linux-gnu

# Ensure crate compiles without default features (i.e. for no_std)
- env: EXTRA_ARGS="--no-default-features"
script: cargo build $EXTRA_ARGS

# `alloc` crate-based implementation
- env: EXTRA_ARGS="--no-default-features --features=alloc --lib --tests"
rust: nightly # TODO: test 'alloc' on stable rust when it is fully stabilized

# Serde implementation
- env: EXTRA_ARGS="--features serde"

Expand Down
7 changes: 5 additions & 2 deletions Cargo.toml
Expand Up @@ -22,19 +22,22 @@ exclude = [
"bench/**/*",
"test/**/*"
]
categories = ["network-programming", "data-structures"]
categories = ["network-programming", "data-structures", "no-std"]

[package.metadata.docs.rs]
features = ["i128"]

[dependencies]
byteorder = "1.1.0"
iovec = "0.1"
iovec = { version = "0.1", optional = true }
serde = { version = "1.0", optional = true }
either = { version = "1.5", default-features = false, optional = true }

[dev-dependencies]
serde_test = "1.0"

[features]
default = ["std"]
alloc = []
i128 = ["byteorder/i128"]
std = ["alloc", "iovec"]
12 changes: 11 additions & 1 deletion src/buf/buf.rs
@@ -1,8 +1,13 @@
use super::{IntoBuf, Take, Reader, Iter, FromBuf, Chain};
use byteorder::{BigEndian, ByteOrder, LittleEndian};
#[cfg(feature = "iovec")]
use iovec::IoVec;
#[cfg(feature = "alloc")]
use prelude::*;

use std::{cmp, io, ptr};
use core::{cmp, ptr};
#[cfg(feature = "std")]
use std::io;

macro_rules! buf_get_impl {
($this:ident, $size:expr, $conv:path) => ({
Expand Down Expand Up @@ -146,6 +151,7 @@ pub trait Buf {
/// with `dst` being a zero length slice.
///
/// [`writev`]: http://man7.org/linux/man-pages/man2/readv.2.html
#[cfg(feature = "iovec")]
fn bytes_vec<'a>(&'a self, dst: &mut [&'a IoVec]) -> usize {
if dst.is_empty() {
return 0;
Expand Down Expand Up @@ -1061,6 +1067,7 @@ impl<'a, T: Buf + ?Sized> Buf for &'a mut T {
(**self).bytes()
}

#[cfg(feature = "iovec")]
fn bytes_vec<'b>(&'b self, dst: &mut [&'b IoVec]) -> usize {
(**self).bytes_vec(dst)
}
Expand All @@ -1070,6 +1077,7 @@ impl<'a, T: Buf + ?Sized> Buf for &'a mut T {
}
}

#[cfg(feature = "alloc")]
impl<T: Buf + ?Sized> Buf for Box<T> {
fn remaining(&self) -> usize {
(**self).remaining()
Expand All @@ -1079,6 +1087,7 @@ impl<T: Buf + ?Sized> Buf for Box<T> {
(**self).bytes()
}

#[cfg(feature = "iovec")]
fn bytes_vec<'b>(&'b self, dst: &mut [&'b IoVec]) -> usize {
(**self).bytes_vec(dst)
}
Expand All @@ -1088,6 +1097,7 @@ impl<T: Buf + ?Sized> Buf for Box<T> {
}
}

#[cfg(feature = "std")]
impl<T: AsRef<[u8]>> Buf for io::Cursor<T> {
fn remaining(&self) -> usize {
let len = self.get_ref().as_ref().len();
Expand Down
15 changes: 13 additions & 2 deletions src/buf/buf_mut.rs
@@ -1,8 +1,13 @@
use super::{IntoBuf, Writer};
use byteorder::{LittleEndian, ByteOrder, BigEndian};
#[cfg(feature = "iovec")]
use iovec::IoVec;
#[cfg(feature = "alloc")]
use prelude::*;

use std::{cmp, io, ptr, usize};
use core::{cmp, ptr, usize};
#[cfg(feature = "std")]
use std::io;

/// A trait for values that provide sequential write access to bytes.
///
Expand Down Expand Up @@ -189,6 +194,7 @@ pub trait BufMut {
/// with `dst` being a zero length slice.
///
/// [`readv`]: http://man7.org/linux/man-pages/man2/readv.2.html
#[cfg(feature = "iovec")]
unsafe fn bytes_vec_mut<'a>(&'a mut self, dst: &mut [&'a mut IoVec]) -> usize {
if dst.is_empty() {
return 0;
Expand Down Expand Up @@ -1072,6 +1078,7 @@ impl<'a, T: BufMut + ?Sized> BufMut for &'a mut T {
(**self).bytes_mut()
}

#[cfg(feature = "iovec")]
unsafe fn bytes_vec_mut<'b>(&'b mut self, dst: &mut [&'b mut IoVec]) -> usize {
(**self).bytes_vec_mut(dst)
}
Expand All @@ -1081,6 +1088,7 @@ impl<'a, T: BufMut + ?Sized> BufMut for &'a mut T {
}
}

#[cfg(feature = "alloc")]
impl<T: BufMut + ?Sized> BufMut for Box<T> {
fn remaining_mut(&self) -> usize {
(**self).remaining_mut()
Expand All @@ -1090,6 +1098,7 @@ impl<T: BufMut + ?Sized> BufMut for Box<T> {
(**self).bytes_mut()
}

#[cfg(feature = "iovec")]
unsafe fn bytes_vec_mut<'b>(&'b mut self, dst: &mut [&'b mut IoVec]) -> usize {
(**self).bytes_vec_mut(dst)
}
Expand All @@ -1099,6 +1108,7 @@ impl<T: BufMut + ?Sized> BufMut for Box<T> {
}
}

#[cfg(feature = "std")]
impl<T: AsMut<[u8]> + AsRef<[u8]>> BufMut for io::Cursor<T> {
fn remaining_mut(&self) -> usize {
use Buf;
Expand Down Expand Up @@ -1127,6 +1137,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]>> BufMut for io::Cursor<T> {
}
}

#[cfg(feature = "alloc")]
impl BufMut for Vec<u8> {
#[inline]
fn remaining_mut(&self) -> usize {
Expand All @@ -1148,7 +1159,7 @@ impl BufMut for Vec<u8> {

#[inline]
unsafe fn bytes_mut(&mut self) -> &mut [u8] {
use std::slice;
use core::slice;

if self.capacity() == self.len() {
self.reserve(64); // Grow the vec
Expand Down
3 changes: 3 additions & 0 deletions src/buf/chain.rs
@@ -1,4 +1,5 @@
use {Buf, BufMut};
#[cfg(feature = "iovec")]
use iovec::IoVec;

/// A `Chain` sequences two buffers.
Expand Down Expand Up @@ -177,6 +178,7 @@ impl<T, U> Buf for Chain<T, U>
self.b.advance(cnt);
}

#[cfg(feature = "iovec")]
fn bytes_vec<'a>(&'a self, dst: &mut [&'a IoVec]) -> usize {
let mut n = self.a.bytes_vec(dst);
n += self.b.bytes_vec(&mut dst[n..]);
Expand Down Expand Up @@ -218,6 +220,7 @@ impl<T, U> BufMut for Chain<T, U>
self.b.advance_mut(cnt);
}

#[cfg(feature = "iovec")]
unsafe fn bytes_vec_mut<'a>(&'a mut self, dst: &mut [&'a mut IoVec]) -> usize {
let mut n = self.a.bytes_vec_mut(dst);
n += self.b.bytes_vec_mut(&mut dst[n..]);
Expand Down
9 changes: 8 additions & 1 deletion src/buf/from_buf.rs
@@ -1,4 +1,8 @@
use {Buf, BufMut, IntoBuf, Bytes, BytesMut};
use {IntoBuf};
#[cfg(feature = "alloc")]
use prelude::*;
#[cfg(feature = "alloc")]
use {Buf, BufMut, Bytes, BytesMut};

/// Conversion from a [`Buf`]
///
Expand Down Expand Up @@ -86,6 +90,7 @@ pub trait FromBuf {
fn from_buf<T>(buf: T) -> Self where T: IntoBuf;
}

#[cfg(feature = "alloc")]
impl FromBuf for Vec<u8> {
fn from_buf<T>(buf: T) -> Self
where T: IntoBuf
Expand All @@ -97,6 +102,7 @@ impl FromBuf for Vec<u8> {
}
}

#[cfg(feature = "alloc")]
impl FromBuf for Bytes {
fn from_buf<T>(buf: T) -> Self
where T: IntoBuf
Expand All @@ -105,6 +111,7 @@ impl FromBuf for Bytes {
}
}

#[cfg(feature = "alloc")]
impl FromBuf for BytesMut {
fn from_buf<T>(buf: T) -> Self
where T: IntoBuf
Expand Down
12 changes: 12 additions & 0 deletions src/buf/into_buf.rs
@@ -1,5 +1,8 @@
use super::{Buf};
#[cfg(feature = "std")]
use prelude::*;

#[cfg(feature = "std")]
use std::io;

/// Conversion into a `Buf`
Expand Down Expand Up @@ -55,6 +58,7 @@ impl<T: Buf> IntoBuf for T {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a [u8] {
type Buf = io::Cursor<&'a [u8]>;

Expand All @@ -63,6 +67,7 @@ impl<'a> IntoBuf for &'a [u8] {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a mut [u8] {
type Buf = io::Cursor<&'a mut [u8]>;
Copy link
Author

@tarcieri tarcieri Apr 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To really support alloc properly I think these impls (both this one and the one below for &'a str need to change to not use an io::Cursor), however as Buf is an associated type, I imagine it's considered part of the public API and changes to the underlying Buf type would be considered breaking. As a breaking change I think ideally we could fix this by eliminating all usages of io::Cursor from the crate (which, I believe, was @carllerche's plan for v0.5.x anyway).

However I'd like to stress that even without these impls, bytes + alloc is still useful, because downstream no_std + alloc consumers of bytes can define their own buffer type (possibly just vendoring io::Cursor from libstd and using the existing std-dependent impls) and then impl FromBuf and impl IntoBuf for their own type. This would be my planned approach for now if this PR were to be merged.


Expand All @@ -71,6 +76,7 @@ impl<'a> IntoBuf for &'a mut [u8] {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a str {
type Buf = io::Cursor<&'a [u8]>;

Expand All @@ -79,6 +85,7 @@ impl<'a> IntoBuf for &'a str {
}
}

#[cfg(feature = "std")]
impl IntoBuf for Vec<u8> {
type Buf = io::Cursor<Vec<u8>>;

Expand All @@ -87,6 +94,7 @@ impl IntoBuf for Vec<u8> {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a Vec<u8> {
type Buf = io::Cursor<&'a [u8]>;

Expand All @@ -97,6 +105,7 @@ impl<'a> IntoBuf for &'a Vec<u8> {

// Kind of annoying... but this impl is required to allow passing `&'static
// [u8]` where for<'a> &'a T: IntoBuf is required.
#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a &'static [u8] {
type Buf = io::Cursor<&'static [u8]>;

Expand All @@ -105,6 +114,7 @@ impl<'a> IntoBuf for &'a &'static [u8] {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a &'static str {
type Buf = io::Cursor<&'static [u8]>;

Expand All @@ -113,6 +123,7 @@ impl<'a> IntoBuf for &'a &'static str {
}
}

#[cfg(feature = "std")]
impl IntoBuf for String {
type Buf = io::Cursor<Vec<u8>>;

Expand All @@ -121,6 +132,7 @@ impl IntoBuf for String {
}
}

#[cfg(feature = "std")]
impl<'a> IntoBuf for &'a String {
type Buf = io::Cursor<&'a [u8]>;

Expand Down
1 change: 1 addition & 0 deletions src/buf/mod.rs
Expand Up @@ -24,6 +24,7 @@ mod into_buf;
mod iter;
mod reader;
mod take;
#[cfg(feature = "alloc")]
mod vec_deque;
mod writer;

Expand Down
3 changes: 3 additions & 0 deletions src/buf/reader.rs
@@ -1,5 +1,6 @@
use {Buf};

#[cfg(feature = "std")]
use std::{cmp, io};

/// A `Buf` adapter which implements `io::Read` for the inner value.
Expand Down Expand Up @@ -78,6 +79,7 @@ impl<B: Buf> Reader<B> {
}
}

#[cfg(feature = "std")]
impl<B: Buf + Sized> io::Read for Reader<B> {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
let len = cmp::min(self.buf.remaining(), dst.len());
Expand All @@ -87,6 +89,7 @@ impl<B: Buf + Sized> io::Read for Reader<B> {
}
}

#[cfg(feature = "std")]
impl<B: Buf + Sized> io::BufRead for Reader<B> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
Ok(self.buf.bytes())
Expand Down
2 changes: 1 addition & 1 deletion src/buf/take.rs
@@ -1,6 +1,6 @@
use {Buf};

use std::cmp;
use core::cmp;

/// A `Buf` adapter which limits the bytes read from an underlying buffer.
///
Expand Down
4 changes: 4 additions & 0 deletions src/buf/vec_deque.rs
@@ -1,3 +1,6 @@
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::collections::vec_deque::VecDeque;
#[cfg(feature = "std")]
use std::collections::VecDeque;

use super::Buf;
Expand All @@ -24,6 +27,7 @@ impl Buf for VecDeque<u8> {
#[cfg(test)]
mod tests {
use super::*;
use prelude::*;

#[test]
fn hello_world() {
Expand Down
2 changes: 2 additions & 0 deletions src/buf/writer.rs
@@ -1,5 +1,6 @@
use BufMut;

#[cfg(feature = "std")]
use std::{cmp, io};

/// A `BufMut` adapter which implements `io::Write` for the inner value.
Expand Down Expand Up @@ -74,6 +75,7 @@ impl<B: BufMut> Writer<B> {
}
}

#[cfg(feature = "std")]
impl<B: BufMut + Sized> io::Write for Writer<B> {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
let n = cmp::min(self.buf.remaining_mut(), src.len());
Expand Down