mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-16 00:00:15 +02:00
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:
+100
-34
@@ -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[..]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user