Rewrite Bytes / BytesMut core implementation

The previous implementation didn't factor in a single `Bytes` handle
being stored in an `Arc`. This new implementation correctly impelments
both `Bytes` and `BytesMut` such that both are `Sync`.

The rewrite also increases the number of bytes that can be stored
inline.
This commit is contained in:
Carl Lerche
2017-02-20 10:41:20 -08:00
parent 0360f191f8
commit cf5a1bc4f1
9 changed files with 1184 additions and 500 deletions
+100 -34
View File
@@ -5,11 +5,10 @@ use bytes::{Bytes, BytesMut, BufMut};
const LONG: &'static [u8] = b"mary had a little lamb, little lamb, little lamb";
const SHORT: &'static [u8] = b"hello world";
#[cfg(target_pointer_width = "64")]
const INLINE_CAP: usize = 8 * 3;
#[cfg(target_pointer_width = "32")]
const INNER_CAP: usize = 4 * 3;
fn inline_cap() -> usize {
use std::mem;
4 * mem::size_of::<usize>() - 1
}
fn is_sync<T: Sync>() {}
fn is_send<T: Send>() {}
@@ -17,6 +16,7 @@ fn is_send<T: Send>() {}
#[test]
fn test_bounds() {
is_sync::<Bytes>();
is_sync::<BytesMut>();
is_send::<Bytes>();
is_send::<BytesMut>();
}
@@ -90,25 +90,25 @@ fn slice() {
#[should_panic]
fn slice_oob_1() {
let a = Bytes::from(&b"hello world"[..]);
a.slice(5, 25);
a.slice(5, inline_cap() + 1);
}
#[test]
#[should_panic]
fn slice_oob_2() {
let a = Bytes::from(&b"hello world"[..]);
a.slice(25, 30);
a.slice(inline_cap() + 1, inline_cap() + 5);
}
#[test]
fn split_off() {
let hello = Bytes::from(&b"helloworld"[..]);
let mut hello = Bytes::from(&b"helloworld"[..]);
let world = hello.split_off(5);
assert_eq!(hello, &b"hello"[..]);
assert_eq!(world, &b"world"[..]);
let hello = BytesMut::from(&b"helloworld"[..]);
let mut hello = BytesMut::from(&b"helloworld"[..]);
let world = hello.split_off(5);
assert_eq!(hello, &b"hello"[..]);
@@ -118,21 +118,14 @@ fn split_off() {
#[test]
#[should_panic]
fn split_off_oob() {
let hello = Bytes::from(&b"helloworld"[..]);
hello.split_off(25);
}
#[test]
#[should_panic]
fn split_off_oob_mut() {
let hello = BytesMut::from(&b"helloworld"[..]);
hello.split_off(25);
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.split_off(inline_cap() + 1);
}
#[test]
fn split_off_uninitialized() {
let mut bytes = BytesMut::with_capacity(1024);
let other = bytes.split_off_mut(128);
let other = bytes.split_off(128);
assert_eq!(bytes.len(), 0);
assert_eq!(bytes.capacity(), 128);
@@ -144,44 +137,55 @@ fn split_off_uninitialized() {
#[test]
fn drain_to_1() {
// Inline
let a = Bytes::from(SHORT);
let mut a = Bytes::from(SHORT);
let b = a.drain_to(4);
assert_eq!(SHORT[4..], a);
assert_eq!(SHORT[..4], b);
// Allocated
let a = Bytes::from(LONG);
let mut a = Bytes::from(LONG);
let b = a.drain_to(4);
assert_eq!(LONG[4..], a);
assert_eq!(LONG[..4], b);
let a = Bytes::from(LONG);
let mut a = Bytes::from(LONG);
let b = a.drain_to(30);
assert_eq!(LONG[30..], a);
assert_eq!(LONG[..30], b);
}
#[test]
fn drain_to_2() {
let mut a = Bytes::from(LONG);
assert_eq!(LONG, a);
let b = a.drain_to(1);
assert_eq!(LONG[1..], a);
drop(b);
}
#[test]
#[should_panic]
fn drain_to_oob() {
let hello = Bytes::from(&b"helloworld"[..]);
hello.drain_to(30);
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.drain_to(inline_cap() + 1);
}
#[test]
#[should_panic]
fn drain_to_oob_mut() {
let hello = BytesMut::from(&b"helloworld"[..]);
hello.drain_to(30);
let mut hello = BytesMut::from(&b"helloworld"[..]);
hello.drain_to(inline_cap() + 1);
}
#[test]
fn drain_to_uninitialized() {
let mut bytes = BytesMut::with_capacity(1024);
let other = bytes.drain_to_mut(128);
let other = bytes.drain_to(128);
assert_eq!(bytes.len(), 0);
assert_eq!(bytes.capacity(), 896);
@@ -212,12 +216,12 @@ fn reserve() {
assert_eq!(bytes, "hello");
// Inline -> Inline
let mut bytes = BytesMut::with_capacity(INLINE_CAP);
let mut bytes = BytesMut::with_capacity(inline_cap());
bytes.put("abcdefghijkl");
let a = bytes.drain_to(10);
bytes.reserve(INLINE_CAP - 3);
assert_eq!(INLINE_CAP, bytes.capacity());
bytes.reserve(inline_cap() - 3);
assert_eq!(inline_cap(), bytes.capacity());
assert_eq!(bytes, "kl");
assert_eq!(a, "abcdefghij");
@@ -238,19 +242,19 @@ fn reserve() {
}
#[test]
fn try_reclaim() {
fn try_reclaim_1() {
// Inline w/ start at zero
let mut bytes = BytesMut::from(&SHORT[..]);
assert!(bytes.try_reclaim());
assert_eq!(bytes.capacity(), INLINE_CAP);
assert_eq!(bytes.capacity(), inline_cap());
assert_eq!(bytes, SHORT);
// Inline w/ start not at zero
let mut bytes = BytesMut::from(&SHORT[..]);
let _ = bytes.drain_to(2);
assert_eq!(bytes.capacity(), INLINE_CAP - 2);
assert_eq!(bytes.capacity(), inline_cap());
assert!(bytes.try_reclaim());
assert_eq!(bytes.capacity(), INLINE_CAP);
assert_eq!(bytes.capacity(), inline_cap());
assert_eq!(bytes, &SHORT[2..]);
// Arc
@@ -263,3 +267,65 @@ fn try_reclaim() {
assert!(bytes.try_reclaim());
assert_eq!(bytes.capacity(), LONG.len());
}
#[test]
fn try_reclaim_2() {
let mut bytes = BytesMut::from(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
// Create a new handle to the shared memory region
let a = bytes.drain_to(5);
// Attempting to reclaim here will fail due to `a` still being in
// existence.
assert!(!bytes.try_reclaim());
assert_eq!(bytes.capacity(), 51);
// Dropping the handle will allow reclaim to succeed.
drop(a);
assert!(bytes.try_reclaim());
assert_eq!(bytes.capacity(), 56);
}
#[test]
fn inline_storage() {
let mut bytes = BytesMut::with_capacity(inline_cap());
let zero = [0u8; 64];
bytes.put(&zero[0..inline_cap()]);
assert_eq!(*bytes, zero[0..inline_cap()]);
}
#[test]
fn stress() {
// Tests promoting a buffer from a vec -> shared in a concurrent situation
use std::sync::{Arc, Barrier};
use std::thread;
const THREADS: usize = 8;
const ITERS: usize = 1_000;
for i in 0..ITERS {
let data = [i as u8; 256];
let buf = Arc::new(BytesMut::from(&data[..]));
let barrier = Arc::new(Barrier::new(THREADS));
let mut joins = Vec::with_capacity(THREADS);
for _ in 0..THREADS {
let c = barrier.clone();
let buf = buf.clone();
joins.push(thread::spawn(move || {
c.wait();
let _buf = buf.clone();
}));
}
for th in joins {
th.join().unwrap();
}
assert_eq!(*buf, data[..]);
}
}