Compare commits

..
20 Commits
Author SHA1 Message Date
Sean McArthur fe6e673864 v0.5.4 2020-01-23 10:41:50 -08:00
Stepan KoltsovandSean McArthur f330ef6c4d Do not panic on Bytes::slice_ref on empty slice (#355)
Use case:

```
let bytes: Bytes = ...
let subbytes = bytes.slice(a..b); // where a == b
let slice = &subbytes[..];
let slice_bytes = bytes.slice_ref(slice);
```

Last line should not panic, because `slice` object is derived from
the original `Bytes` object.

Before this commit it panics, because `Bytes::slice` returns a fresh
`Bytes` object when `begin == end`.
2020-01-23 10:29:42 -08:00
Sean McArthurandGitHub 87b75cce16 Disable loom on Windows (#366) 2020-01-23 10:10:15 -08:00
南浦月andSean McArthur 3853a1fac4 merge hex & debug into fmt (#357) 2020-01-23 10:08:40 -08:00
Nikhil BeneschandSean McArthur f9ebf74091 Minor documentation fixes (#351)
* Remove incorrect comment about behavior of put_slice
* Improve grammar and clarity of BytesMut documentation
2020-01-23 10:06:49 -08:00
Andrew Tunnell-JonesandSean McArthur 939a5edf3d Fix reversed arguments in PartialOrd impls (#358) 2020-01-23 10:06:00 -08:00
John-John TedroandSean McArthur 788cb158ce Assert that the alignment of Shared is appropriate (#362) 2020-01-23 10:05:20 -08:00
Stepan KoltsovandSean McArthur ab028eb6a8 rebuild_boxed_slice instead of rebuild_boxed_vec (#364)
"Promotable" `Bytes` object is constructed from disassembling a
boxed slice object, not a vec.

Thus we should reassemble data into a boxed slice, not into a vec.

Although, it does not create any problems in practice (`Box<[u8]>`
is allocated exactly the same way as `Vec<u8>`), technically it is
a violation of `Vec::from_raw_parts` spec which says that a pointer
"needs to have been previously allocated via `String`/`Vec<T>`".
2020-01-23 10:04:13 -08:00
Sean McArthurandCarl Lerche e0eebde993 Fix Bytes::truncate losing the original Vec's capacity (#361) 2020-01-22 16:37:53 -08:00
Stepan KoltsovandSean McArthur 729bc7c208 Make Bytes::new const fn (#356) 2020-01-09 14:27:28 -08:00
Sean McArthurandCarl Lerche 8695c08bcc Improve assertion messages (#349) 2020-01-07 21:29:36 -08:00
Thayne McCombsandSean McArthur 39b6646e66 Re-add impl From<BytesMut> for Bytes (#348) 2019-12-19 11:09:14 -08:00
Sean McArthurandGitHub c7cf716180 Fix Bytes when Vec pointer's LSB is set (#346)
This separates the `SharedVtable` into 3:

- `PromotableEvenVtable`: The original `SharedVtable`, which will
  promote the `Vec` to `Shared` on the first clone, and is selected when
  the `Vec`'s pointer has the LSB unset.
- `PromotableOddVtable`: Similar to the `PromotableEvenVtable`, but
  selected when the `Vec`'s pointer has the LSB set. This vtable differs
  in the masking used when reconstructing the `Vec`.
- `SharedVtable`: This no longer checks if its current kind is `VEC` or
  `ARC`, and is only created by the "promotable" vtables.

This also adds a test using an "odd" global allocator that purposefully
bumps all pointers with alignment of 1.

Closes #343
2019-12-17 13:23:15 -08:00
Sean McArthur 8ae3bb2104 v0.5.3 2019-12-12 12:00:21 -08:00
Sean McArthur 8733f74d59 Assert the LSB is 0 when converting Vec into Bytes 2019-12-12 11:46:51 -08:00
Steven FacklerandSean McArthur 17a8ac91e0 Fix conversion of empty vectors to Bytes
Closes #340
2019-12-12 08:33:07 -08:00
Sean McArthur a7fc5274ad Make Deref impls of Buf and BufMut forward more methods 2019-12-10 13:35:26 -08:00
Sean McArthurandCarl Lerche 7e80f3b646 Optimize BufMut for Vec and BytesMut (#338) 2019-12-10 13:30:49 -08:00
Sean McArthur a4908213a6 Add must_use to split, split_off, and split_to 2019-12-04 12:39:48 -08:00
Mikhail ZabaluevandCarl Lerche af606aab9b Fix regression in Bytes::truncate (#333)
When the length to truncate is greater than the buffer's current
length, do nothing instead of clearing the contents.
2019-12-01 14:00:42 -08:00
16 changed files with 853 additions and 200 deletions
+21
View File
@@ -1,3 +1,24 @@
# 0.5.4 (January 23, 2020)
### Added
- Make `Bytes::new` a `const fn`.
- Add `From<BytesMut>` for `Bytes`.
### Fix
- Fix reversed arguments in `PartialOrd` for `Bytes`.
- Fix `Bytes::truncate` losing original capacity when repr is an unshared `Vec`.
- Fix `Bytes::from(Vec)` when allocator gave `Vec` a pointer with LSB set.
- Fix panic in `Bytes::slice_ref` if argument is an empty slice.
# 0.5.3 (December 12, 2019)
### Added
- `must_use` attributes to `split`, `split_off`, and `split_to` methods (#337).
### Fix
- Potential freeing of a null pointer in `Bytes` when constructed with an empty `Vec<u8>` (#341, #342).
- Calling `Bytes::truncate` with a size large than the length will no longer clear the `Bytes` (#333).
# 0.5.2 (November 27, 2019)
### Added
+10 -3
View File
@@ -6,9 +6,12 @@ name = "bytes"
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create "v0.5.x" git tag.
version = "0.5.2"
version = "0.5.4"
license = "MIT"
authors = ["Carl Lerche <[email protected]>"]
authors = [
"Carl Lerche <[email protected]>",
"Sean McArthur <[email protected]>",
]
description = "Types and traits for working with bytes"
documentation = "https://docs.rs/bytes"
repository = "https://github.com/tokio-rs/bytes"
@@ -25,5 +28,9 @@ std = []
serde = { version = "1.0", optional = true }
[dev-dependencies]
loom = "0.2.10"
serde_test = "1.0"
# loom is currently not compiling on windows.
# See: https://github.com/Xudong-Huang/generator-rs/issues/19
[target.'cfg(not(windows))'.dev-dependencies]
loom = "0.2.13"
+93 -17
View File
@@ -809,7 +809,8 @@ pub trait Buf {
}
}
impl<T: Buf + ?Sized> Buf for &mut T {
macro_rules! deref_forward_buf {
() => (
fn remaining(&self) -> usize {
(**self).remaining()
}
@@ -826,25 +827,100 @@ impl<T: Buf + ?Sized> Buf for &mut T {
fn advance(&mut self, cnt: usize) {
(**self).advance(cnt)
}
fn has_remaining(&self) -> bool {
(**self).has_remaining()
}
fn copy_to_slice(&mut self, dst: &mut [u8]) {
(**self).copy_to_slice(dst)
}
fn get_u8(&mut self) -> u8 {
(**self).get_u8()
}
fn get_i8(&mut self) -> i8 {
(**self).get_i8()
}
fn get_u16(&mut self) -> u16 {
(**self).get_u16()
}
fn get_u16_le(&mut self) -> u16 {
(**self).get_u16_le()
}
fn get_i16(&mut self) -> i16 {
(**self).get_i16()
}
fn get_i16_le(&mut self) -> i16 {
(**self).get_i16_le()
}
fn get_u32(&mut self) -> u32 {
(**self).get_u32()
}
fn get_u32_le(&mut self) -> u32 {
(**self).get_u32_le()
}
fn get_i32(&mut self) -> i32 {
(**self).get_i32()
}
fn get_i32_le(&mut self) -> i32 {
(**self).get_i32_le()
}
fn get_u64(&mut self) -> u64 {
(**self).get_u64()
}
fn get_u64_le(&mut self) -> u64 {
(**self).get_u64_le()
}
fn get_i64(&mut self) -> i64 {
(**self).get_i64()
}
fn get_i64_le(&mut self) -> i64 {
(**self).get_i64_le()
}
fn get_uint(&mut self, nbytes: usize) -> u64 {
(**self).get_uint(nbytes)
}
fn get_uint_le(&mut self, nbytes: usize) -> u64 {
(**self).get_uint_le(nbytes)
}
fn get_int(&mut self, nbytes: usize) -> i64 {
(**self).get_int(nbytes)
}
fn get_int_le(&mut self, nbytes: usize) -> i64 {
(**self).get_int_le(nbytes)
}
fn to_bytes(&mut self) -> crate::Bytes {
(**self).to_bytes()
}
)
}
impl<T: Buf + ?Sized> Buf for &mut T {
deref_forward_buf!();
}
impl<T: Buf + ?Sized> Buf for Box<T> {
fn remaining(&self) -> usize {
(**self).remaining()
}
fn bytes(&self) -> &[u8] {
(**self).bytes()
}
#[cfg(feature = "std")]
fn bytes_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize {
(**self).bytes_vectored(dst)
}
fn advance(&mut self, cnt: usize) {
(**self).advance(cnt)
}
deref_forward_buf!();
}
impl Buf for &[u8] {
+93 -17
View File
@@ -871,7 +871,8 @@ pub trait BufMut {
}
}
impl<T: BufMut + ?Sized> BufMut for &mut T {
macro_rules! deref_forward_bufmut {
() => (
fn remaining_mut(&self) -> usize {
(**self).remaining_mut()
}
@@ -888,25 +889,75 @@ impl<T: BufMut + ?Sized> BufMut for &mut T {
unsafe fn advance_mut(&mut self, cnt: usize) {
(**self).advance_mut(cnt)
}
fn put_slice(&mut self, src: &[u8]) {
(**self).put_slice(src)
}
fn put_u8(&mut self, n: u8) {
(**self).put_u8(n)
}
fn put_i8(&mut self, n: i8) {
(**self).put_i8(n)
}
fn put_u16(&mut self, n: u16) {
(**self).put_u16(n)
}
fn put_u16_le(&mut self, n: u16) {
(**self).put_u16_le(n)
}
fn put_i16(&mut self, n: i16) {
(**self).put_i16(n)
}
fn put_i16_le(&mut self, n: i16) {
(**self).put_i16_le(n)
}
fn put_u32(&mut self, n: u32) {
(**self).put_u32(n)
}
fn put_u32_le(&mut self, n: u32) {
(**self).put_u32_le(n)
}
fn put_i32(&mut self, n: i32) {
(**self).put_i32(n)
}
fn put_i32_le(&mut self, n: i32) {
(**self).put_i32_le(n)
}
fn put_u64(&mut self, n: u64) {
(**self).put_u64(n)
}
fn put_u64_le(&mut self, n: u64) {
(**self).put_u64_le(n)
}
fn put_i64(&mut self, n: i64) {
(**self).put_i64(n)
}
fn put_i64_le(&mut self, n: i64) {
(**self).put_i64_le(n)
}
)
}
impl<T: BufMut + ?Sized> BufMut for &mut T {
deref_forward_bufmut!();
}
impl<T: BufMut + ?Sized> BufMut for Box<T> {
fn remaining_mut(&self) -> usize {
(**self).remaining_mut()
}
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
(**self).bytes_mut()
}
#[cfg(feature = "std")]
fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize {
(**self).bytes_vectored_mut(dst)
}
unsafe fn advance_mut(&mut self, cnt: usize) {
(**self).advance_mut(cnt)
}
deref_forward_bufmut!();
}
impl BufMut for &mut [u8] {
@@ -964,6 +1015,31 @@ impl BufMut for Vec<u8> {
&mut slice::from_raw_parts_mut(ptr, cap)[len..]
}
}
// Specialize these methods so they can skip checking `remaining_mut`
// and `advance_mut`.
fn put<T: super::Buf>(&mut self, mut src: T) where Self: Sized {
// In case the src isn't contiguous, reserve upfront
self.reserve(src.remaining());
while src.has_remaining() {
let l;
// a block to contain the src.bytes() borrow
{
let s = src.bytes();
l = s.len();
self.extend_from_slice(s);
}
src.advance(l);
}
}
fn put_slice(&mut self, src: &[u8]) {
self.extend_from_slice(src);
}
}
// The existence of this function makes the compiler catch if the BufMut
+208 -64
View File
@@ -6,7 +6,6 @@ use alloc::{vec::Vec, string::String, boxed::Box, borrow::Borrow};
use crate::Buf;
use crate::buf::IntoIter;
use crate::debug;
use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
/// A reference counted contiguous slice of memory.
@@ -96,8 +95,18 @@ impl Bytes {
/// assert_eq!(&b[..], b"");
/// ```
#[inline]
#[cfg(not(all(loom, test)))]
pub const fn new() -> Bytes {
// Make it a named const to work around
// "unsizing casts are not allowed in const fn"
const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
#[cfg(all(loom, test))]
pub fn new() -> Bytes {
Bytes::from_static(b"")
const EMPTY: &[u8] = &[];
Bytes::from_static(EMPTY)
}
/// Creates a new `Bytes` from a static slice.
@@ -209,8 +218,18 @@ impl Bytes {
Bound::Unbounded => len,
};
assert!(begin <= end);
assert!(end <= len);
assert!(
begin <= end,
"range start must not be greater than end: {:?} <= {:?}",
begin,
end,
);
assert!(
end <= len,
"range end out of bounds: {:?} <= {:?}",
end,
len,
);
if end == begin {
return Bytes::new();
@@ -251,14 +270,32 @@ impl Bytes {
/// Requires that the given `sub` slice is in fact contained within the
/// `Bytes` buffer; otherwise this function will panic.
pub fn slice_ref(&self, subset: &[u8]) -> Bytes {
// Empty slice and empty Bytes may have their pointers reset
// so explicitly allow empty slice to be a subslice of any slice.
if subset.is_empty() {
return Bytes::new();
}
let bytes_p = self.as_ptr() as usize;
let bytes_len = self.len();
let sub_p = subset.as_ptr() as usize;
let sub_len = subset.len();
assert!(sub_p >= bytes_p);
assert!(sub_p + sub_len <= bytes_p + bytes_len);
assert!(
sub_p >= bytes_p,
"subset pointer ({:p}) is smaller than self pointer ({:p})",
sub_p as *const u8,
bytes_p as *const u8,
);
assert!(
sub_p + sub_len <= bytes_p + bytes_len,
"subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})",
bytes_p as *const u8,
bytes_len,
sub_p as *const u8,
sub_len,
);
let sub_offset = sub_p - bytes_p;
@@ -288,8 +325,14 @@ impl Bytes {
/// # Panics
///
/// Panics if `at > len`.
#[must_use = "consider Bytes::truncate if you don't need the other half"]
pub fn split_off(&mut self, at: usize) -> Bytes {
assert!(at <= self.len());
assert!(
at <= self.len(),
"split_off out of bounds: {:?} <= {:?}",
at,
self.len(),
);
if at == self.len() {
return Bytes::new();
@@ -331,8 +374,14 @@ impl Bytes {
/// # Panics
///
/// Panics if `at > len`.
#[must_use = "consider Bytes::advance if you don't need the other half"]
pub fn split_to(&mut self, at: usize) -> Bytes {
assert!(at <= self.len());
assert!(
at <= self.len(),
"split_to out of bounds: {:?} <= {:?}",
at,
self.len(),
);
if at == self.len() {
return mem::replace(self, Bytes::new());
@@ -373,10 +422,16 @@ impl Bytes {
/// [`split_off`]: #method.split_off
#[inline]
pub fn truncate(&mut self, len: usize) {
if len >= self.len {
self.len = 0;
} else {
self.len = len;
if len < self.len {
// The Vec "promotable" vtables do not store the capacity,
// so we cannot truncate while using this repr. We *have* to
// promote using `split_off` so the capacity can be stored.
if self.vtable as *const Vtable == &PROMOTABLE_EVEN_VTABLE ||
self.vtable as *const Vtable == &PROMOTABLE_ODD_VTABLE {
drop(self.split_off(len));
} else {
self.len = len;
}
}
}
@@ -418,7 +473,7 @@ impl Bytes {
#[inline]
unsafe fn inc_start(&mut self, by: usize) {
// should already be asserted, but debug assert for tests
debug_assert!(self.len >= by);
debug_assert!(self.len >= by, "internal: inc_start out of bounds");
self.len -= by;
self.ptr = self.ptr.offset(by as isize);
}
@@ -446,12 +501,6 @@ impl Clone for Bytes {
}
}
impl fmt::Debug for Bytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&debug::BsDebug(&self.as_slice()), f)
}
}
impl Buf for Bytes {
#[inline]
fn remaining(&self) -> usize {
@@ -465,7 +514,13 @@ impl Buf for Bytes {
#[inline]
fn advance(&mut self, cnt: usize) {
assert!(cnt <= self.len(), "cannot advance past `remaining`");
assert!(
cnt <= self.len(),
"cannot advance past `remaining`: {:?} <= {:?}",
cnt,
self.len(),
);
unsafe {
self.inc_start(cnt);
}
@@ -570,7 +625,7 @@ impl PartialEq<Bytes> for [u8] {
impl PartialOrd<Bytes> for [u8] {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self, other)
}
}
@@ -594,7 +649,7 @@ impl PartialEq<Bytes> for str {
impl PartialOrd<Bytes> for str {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self.as_bytes(), other)
}
}
@@ -618,7 +673,7 @@ impl PartialEq<Bytes> for Vec<u8> {
impl PartialOrd<Bytes> for Vec<u8> {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self, other)
}
}
@@ -642,7 +697,7 @@ impl PartialEq<Bytes> for String {
impl PartialOrd<Bytes> for String {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self.as_bytes(), other)
}
}
@@ -654,7 +709,7 @@ impl PartialEq<Bytes> for &[u8] {
impl PartialOrd<Bytes> for &[u8] {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self, other)
}
}
@@ -666,7 +721,7 @@ impl PartialEq<Bytes> for &str {
impl PartialOrd<Bytes> for &str {
fn partial_cmp(&self, other: &Bytes) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self.as_bytes(), other)
}
}
@@ -709,17 +764,33 @@ impl From<&'static str> for Bytes {
impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Bytes {
// into_boxed_slice doesn't return a heap allocation for empty vectors,
// so the pointer isn't aligned enough for the KIND_VEC stashing to
// work.
if vec.is_empty() {
return Bytes::new();
}
let slice = vec.into_boxed_slice();
let len = slice.len();
let ptr = slice.as_ptr();
drop(Box::into_raw(slice));
let data = ptr as usize | KIND_VEC;
Bytes {
ptr,
len,
data: AtomicPtr::new(data as *mut _),
vtable: &SHARED_VTABLE,
if ptr as usize & 0x1 == 0 {
let data = ptr as usize | KIND_VEC;
Bytes {
ptr,
len,
data: AtomicPtr::new(data as *mut _),
vtable: &PROMOTABLE_EVEN_VTABLE,
}
} else {
Bytes {
ptr,
len,
data: AtomicPtr::new(ptr as *mut _),
vtable: &PROMOTABLE_ODD_VTABLE,
}
}
}
}
@@ -757,6 +828,74 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
// nothing to drop for &'static [u8]
}
// ===== impl PromotableVtable =====
static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable {
clone: promotable_even_clone,
drop: promotable_even_drop,
};
static PROMOTABLE_ODD_VTABLE: Vtable = Vtable {
clone: promotable_odd_clone,
drop: promotable_odd_drop,
};
unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shallow_clone_arc(shared as _, ptr, len)
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = (shared as usize & !KIND_MASK) as *mut u8;
shallow_clone_vec(data, shared, buf, ptr, len)
}
}
unsafe fn promotable_even_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
let shared = *data.get_mut();
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared as *mut Shared);
} else {
debug_assert_eq!(kind, KIND_VEC);
let buf = (shared as usize & !KIND_MASK) as *mut u8;
drop(rebuild_boxed_slice(buf, ptr, len));
}
}
unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shallow_clone_arc(shared as _, ptr, len)
} else {
debug_assert_eq!(kind, KIND_VEC);
shallow_clone_vec(data, shared, shared as *mut u8, ptr, len)
}
}
unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
let shared = *data.get_mut();
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared as *mut Shared);
} else {
debug_assert_eq!(kind, KIND_VEC);
drop(rebuild_boxed_slice(shared as *mut u8, ptr, len));
}
}
unsafe fn rebuild_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) -> Box<[u8]> {
let cap = (offset as usize - buf as usize) + len;
Box::from_raw(slice::from_raw_parts_mut(buf, cap))
}
// ===== impl SharedVtable =====
struct Shared {
@@ -765,6 +904,12 @@ struct Shared {
ref_cnt: AtomicUsize,
}
// Assert that the alignment of `Shared` is divisible by 2.
// This is a necessary invariant since we depend on allocating `Shared` a
// shared object to implicitly carry the `KIND_ARC` flag in its pointer.
// This flag is set when the LSB is 0.
const _: [(); 0 - mem::align_of::<Shared>() % 2] = []; // Assert that the alignment of `Shared` is divisible by 2.
static SHARED_VTABLE: Vtable = Vtable {
clone: shared_clone,
drop: shared_drop,
@@ -776,36 +921,12 @@ const KIND_MASK: usize = 0b1;
unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
let shared = data.load(Ordering::Acquire);
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
shallow_clone_arc(shared as _, ptr, len)
} else {
debug_assert_eq!(kind, KIND_VEC);
shallow_clone_vec(data, shared, ptr, len)
}
shallow_clone_arc(shared as _, ptr, len)
}
unsafe fn shared_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
let shared = *data.get_mut();
let kind = shared as usize & KIND_MASK;
if kind == KIND_ARC {
release_shared(shared as *mut Shared);
} else {
debug_assert_eq!(kind, KIND_VEC);
drop(rebuild_vec(shared, ptr, len));
}
}
unsafe fn rebuild_vec(shared: *const (), offset: *const u8, len: usize) -> Vec<u8> {
debug_assert_eq!(shared as usize & KIND_MASK, KIND_VEC);
let buf = (shared as usize & !KIND_MASK) as *mut u8;
let cap = (offset as usize - buf as usize) + len;
Vec::from_raw_parts(buf, cap, cap)
release_shared(shared as *mut Shared);
}
unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) -> Bytes {
@@ -824,13 +945,11 @@ unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) ->
}
#[cold]
unsafe fn shallow_clone_vec(atom: &AtomicPtr<()>, ptr: *const (), offset: *const u8, len: usize) -> Bytes {
unsafe fn shallow_clone_vec(atom: &AtomicPtr<()>, ptr: *const (), buf: *mut u8, offset: *const u8, len: usize) -> Bytes {
// If the buffer is still tracked in a `Vec<u8>`. It is time to
// promote the vec to an `Arc`. This could potentially be called
// concurrently, so some care must be taken.
debug_assert_eq!(ptr as usize & KIND_MASK, KIND_VEC);
// First, allocate a new `Shared` instance containing the
// `Vec` fields. It's important to note that `ptr`, `len`,
// and `cap` cannot be mutated without having `&mut self`.
@@ -838,7 +957,7 @@ unsafe fn shallow_clone_vec(atom: &AtomicPtr<()>, ptr: *const (), offset: *const
// updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the
// vector.
let vec = rebuild_vec(ptr as *const (), offset, len);
let vec = rebuild_boxed_slice(buf, offset, len).into_vec();
let shared = Box::new(Shared {
_vec: vec,
// Initialize refcount to 2. One for this reference, and one
@@ -851,7 +970,10 @@ unsafe fn shallow_clone_vec(atom: &AtomicPtr<()>, ptr: *const (), offset: *const
// The pointer should be aligned, so this assert should
// always succeed.
debug_assert!(0 == (shared as usize & KIND_MASK));
debug_assert!(
0 == (shared as usize & KIND_MASK),
"internal: Box<Shared> should have an aligned pointer",
);
// Try compare & swapping the pointer into the `arc` field.
// `Release` is used synchronize with other threads that
@@ -915,6 +1037,28 @@ unsafe fn release_shared(ptr: *mut Shared) {
Box::from_raw(ptr);
}
// compile-fails
/// ```compile_fail
/// use bytes::Bytes;
/// #[deny(unused_must_use)]
/// {
/// let mut b1 = Bytes::from("hello world");
/// b1.split_to(6);
/// }
/// ```
fn _split_to_must_use() {}
/// ```compile_fail
/// use bytes::Bytes;
/// #[deny(unused_must_use)]
/// {
/// let mut b1 = Bytes::from("hello world");
/// b1.split_off(6);
/// }
/// ```
fn _split_off_must_use() {}
// fuzz tests
#[cfg(all(test, loom))]
mod fuzz {
+119 -31
View File
@@ -9,7 +9,6 @@ use alloc::{vec::Vec, string::String, boxed::Box, borrow::{Borrow, BorrowMut}};
use crate::{Bytes, Buf, BufMut};
use crate::bytes::Vtable;
use crate::buf::IntoIter;
use crate::debug;
use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
/// A unique reference to a contiguous slice of memory.
@@ -275,8 +274,14 @@ impl BytesMut {
/// # Panics
///
/// Panics if `at > capacity`.
#[must_use = "consider BytesMut::truncate if you don't need the other half"]
pub fn split_off(&mut self, at: usize) -> BytesMut {
assert!(at <= self.capacity());
assert!(
at <= self.capacity(),
"split_off out of bounds: {:?} <= {:?}",
at,
self.capacity(),
);
unsafe {
let mut other = self.shallow_clone();
other.set_start(at);
@@ -310,6 +315,7 @@ impl BytesMut {
///
/// assert_eq!(other, b"hello world"[..]);
/// ```
#[must_use = "consider BytesMut::advance(len()) if you don't need the other half"]
pub fn split(&mut self) -> BytesMut {
let len = self.len();
self.split_to(len)
@@ -341,8 +347,14 @@ impl BytesMut {
/// # Panics
///
/// Panics if `at > len`.
#[must_use = "consider BytesMut::advance if you don't need the other half"]
pub fn split_to(&mut self, at: usize) -> BytesMut {
assert!(at <= self.len());
assert!(
at <= self.len(),
"split_to out of bounds: {:?} <= {:?}",
at,
self.len(),
);
unsafe {
let mut other = self.shallow_clone();
@@ -456,7 +468,7 @@ impl BytesMut {
/// assert_eq!(&b[..], b"hello world");
/// ```
pub unsafe fn set_len(&mut self, len: usize) {
debug_assert!(len <= self.cap);
debug_assert!(len <= self.cap, "set_len out of bounds");
self.len = len;
}
@@ -640,10 +652,11 @@ impl BytesMut {
self.len = v.len();
self.cap = v.capacity();
}
/// Appends given bytes to this object.
/// Appends given bytes to this `BytesMut`.
///
/// If this `BytesMut` object has not enough capacity, it is resized first.
/// So unlike `put_slice` operation, `extend_from_slice` does not panic.
/// If this `BytesMut` object does not have enough capacity, it is resized
/// first.
///
/// # Examples
///
@@ -657,13 +670,31 @@ impl BytesMut {
/// assert_eq!(b"aaabbbcccddd", &buf[..]);
/// ```
pub fn extend_from_slice(&mut self, extend: &[u8]) {
self.reserve(extend.len());
self.put_slice(extend);
let cnt = extend.len();
self.reserve(cnt);
unsafe {
let dst = self.maybe_uninit_bytes();
// Reserved above
debug_assert!(dst.len() >= cnt);
ptr::copy_nonoverlapping(
extend.as_ptr(),
dst.as_mut_ptr() as *mut u8,
cnt);
}
unsafe { self.advance_mut(cnt); }
}
/// Combine splitted BytesMut objects back as contiguous.
/// Absorbs a `BytesMut` that was previously split off.
///
/// If `BytesMut` objects were not contiguous originally, they will be extended.
/// If the two `BytesMut` objects were previously contiguous, i.e., if
/// `other` was created by calling `split_off` on this `BytesMut`, then
/// this is an `O(1)` operation that just decreases a reference
/// count and sets a few indices. Otherwise this method degenerates to
/// `self.extend_from_slice(other.as_ref())`.
///
/// # Examples
///
@@ -673,11 +704,11 @@ impl BytesMut {
/// let mut buf = BytesMut::with_capacity(64);
/// buf.extend_from_slice(b"aaabbbcccddd");
///
/// let splitted = buf.split_off(6);
/// let split = buf.split_off(6);
/// assert_eq!(b"aaabbb", &buf[..]);
/// assert_eq!(b"cccddd", &splitted[..]);
/// assert_eq!(b"cccddd", &split[..]);
///
/// buf.unsplit(splitted);
/// buf.unsplit(split);
/// assert_eq!(b"aaabbbcccddd", &buf[..]);
/// ```
pub fn unsplit(&mut self, other: BytesMut) {
@@ -738,7 +769,7 @@ impl BytesMut {
return;
}
debug_assert!(start <= self.cap);
debug_assert!(start <= self.cap, "internal: set_start out of bounds");
let kind = self.kind();
@@ -777,7 +808,7 @@ impl BytesMut {
unsafe fn set_end(&mut self, end: usize) {
debug_assert_eq!(self.kind(), KIND_ARC);
assert!(end <= self.cap);
assert!(end <= self.cap, "set_end out of bounds");
self.cap = end;
self.len = cmp::min(self.len, end);
@@ -873,6 +904,16 @@ impl BytesMut {
self.data = ((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)) as *mut _;
}
#[inline]
fn maybe_uninit_bytes(&mut self) -> &mut [mem::MaybeUninit<u8>] {
unsafe {
let ptr = self.ptr.as_ptr().offset(self.len as isize);
let len = self.cap - self.len;
slice::from_raw_parts_mut(ptr as *mut mem::MaybeUninit<u8>, len)
}
}
}
impl Drop for BytesMut {
@@ -905,7 +946,12 @@ impl Buf for BytesMut {
#[inline]
fn advance(&mut self, cnt: usize) {
assert!(cnt <= self.remaining(), "cannot advance past `remaining`");
assert!(
cnt <= self.remaining(),
"cannot advance past `remaining`: {:?} <= {:?}",
cnt,
self.remaining(),
);
unsafe { self.set_start(cnt); }
}
@@ -932,14 +978,24 @@ impl BufMut for BytesMut {
if self.capacity() == self.len() {
self.reserve(64);
}
self.maybe_uninit_bytes()
}
unsafe {
let ptr = self.ptr.as_ptr().offset(self.len as isize);
let len = self.cap - self.len;
// Specialize these methods so they can skip checking `remaining_mut`
// and `advance_mut`.
slice::from_raw_parts_mut(ptr as *mut mem::MaybeUninit<u8>, len)
fn put<T: crate::Buf>(&mut self, mut src: T) where Self: Sized {
while src.has_remaining() {
let s = src.bytes();
let l = s.len();
self.extend_from_slice(s);
src.advance(l);
}
}
fn put_slice(&mut self, src: &[u8]) {
self.extend_from_slice(src);
}
}
impl AsRef<[u8]> for BytesMut {
@@ -983,6 +1039,12 @@ impl<'a> From<&'a str> for BytesMut {
}
}
impl From<BytesMut> for Bytes {
fn from(src: BytesMut) -> Bytes {
src.freeze()
}
}
impl PartialEq for BytesMut {
fn eq(&self, other: &BytesMut) -> bool {
self.as_slice() == other.as_slice()
@@ -1011,12 +1073,6 @@ impl Default for BytesMut {
}
}
impl fmt::Debug for BytesMut {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&debug::BsDebug(&self.as_slice()), fmt)
}
}
impl hash::Hash for BytesMut {
fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
let s: &[u8] = self.as_ref();
@@ -1264,7 +1320,7 @@ impl PartialEq<BytesMut> for [u8] {
impl PartialOrd<BytesMut> for [u8] {
fn partial_cmp(&self, other: &BytesMut) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self, other)
}
}
@@ -1288,7 +1344,7 @@ impl PartialEq<BytesMut> for str {
impl PartialOrd<BytesMut> for str {
fn partial_cmp(&self, other: &BytesMut) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self.as_bytes(), other)
}
}
@@ -1336,7 +1392,7 @@ impl PartialEq<BytesMut> for String {
impl PartialOrd<BytesMut> for String {
fn partial_cmp(&self, other: &BytesMut) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self.as_bytes(), other)
}
}
@@ -1364,7 +1420,7 @@ impl PartialEq<BytesMut> for &[u8] {
impl PartialOrd<BytesMut> for &[u8] {
fn partial_cmp(&self, other: &BytesMut) -> Option<cmp::Ordering> {
other.partial_cmp(self)
<[u8] as PartialOrd<[u8]>>::partial_cmp(self, other)
}
}
@@ -1428,6 +1484,38 @@ unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize)
release_shared(shared as *mut Shared);
}
// compile-fails
/// ```compile_fail
/// use bytes::BytesMut;
/// #[deny(unused_must_use)]
/// {
/// let mut b1 = BytesMut::from("hello world");
/// b1.split_to(6);
/// }
/// ```
fn _split_to_must_use() {}
/// ```compile_fail
/// use bytes::BytesMut;
/// #[deny(unused_must_use)]
/// {
/// let mut b1 = BytesMut::from("hello world");
/// b1.split_off(6);
/// }
/// ```
fn _split_off_must_use() {}
/// ```compile_fail
/// use bytes::BytesMut;
/// #[deny(unused_must_use)]
/// {
/// let mut b1 = BytesMut::from("hello world");
/// b1.split();
/// }
/// ```
fn _split_must_use() {}
// fuzz tests
#[cfg(all(test, loom))]
mod fuzz {
-40
View File
@@ -1,40 +0,0 @@
use core::fmt;
/// Alternative implementation of `fmt::Debug` for byte slice.
///
/// Standard `Debug` implementation for `[u8]` is comma separated
/// list of numbers. Since large amount of byte strings are in fact
/// ASCII strings or contain a lot of ASCII strings (e. g. HTTP),
/// it is convenient to print strings as ASCII when possible.
///
/// This struct wraps `&[u8]` just to override `fmt::Debug`.
///
/// `BsDebug` is not a part of public API of bytes crate.
pub struct BsDebug<'a>(pub &'a [u8]);
impl fmt::Debug for BsDebug<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "b\"")?;
for &c in self.0 {
// https://doc.rust-lang.org/reference.html#byte-escapes
if c == b'\n' {
write!(fmt, "\\n")?;
} else if c == b'\r' {
write!(fmt, "\\r")?;
} else if c == b'\t' {
write!(fmt, "\\t")?;
} else if c == b'\\' || c == b'"' {
write!(fmt, "\\{}", c as char)?;
} else if c == b'\0' {
write!(fmt, "\\0")?;
// ASCII printable
} else if c >= 0x20 && c < 0x7f {
write!(fmt, "{}", c as char)?;
} else {
write!(fmt, "\\x{:02x}", c)?;
}
}
write!(fmt, "\"")?;
Ok(())
}
}
+49
View File
@@ -0,0 +1,49 @@
use core::fmt::{Debug, Formatter, Result};
use crate::{Bytes, BytesMut};
use super::BytesRef;
/// Alternative implementation of `std::fmt::Debug` for byte slice.
///
/// Standard `Debug` implementation for `[u8]` is comma separated
/// list of numbers. Since large amount of byte strings are in fact
/// ASCII strings or contain a lot of ASCII strings (e. g. HTTP),
/// it is convenient to print strings as ASCII when possible.
impl Debug for BytesRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "b\"")?;
for &b in self.0 {
// https://doc.rust-lang.org/reference/tokens.html#byte-escapes
if b == b'\n' {
write!(f, "\\n")?;
} else if b == b'\r' {
write!(f, "\\r")?;
} else if b == b'\t' {
write!(f, "\\t")?;
} else if b == b'\\' || b == b'"' {
write!(f, "\\{}", b as char)?;
} else if b == b'\0' {
write!(f, "\\0")?;
// ASCII printable
} else if b >= 0x20 && b < 0x7f {
write!(f, "{}", b as char)?;
} else {
write!(f, "\\x{:02x}", b)?;
}
}
write!(f, "\"")?;
Ok(())
}
}
impl Debug for Bytes {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
Debug::fmt(&BytesRef(&self.as_ref()), f)
}
}
impl Debug for BytesMut {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
Debug::fmt(&BytesRef(&self.as_ref()), f)
}
}
+6 -6
View File
@@ -1,20 +1,20 @@
use crate::{Bytes, BytesMut};
use core::fmt::{Formatter, LowerHex, Result, UpperHex};
struct BytesRef<'a>(&'a [u8]);
use crate::{Bytes, BytesMut};
use super::BytesRef;
impl<'a> LowerHex for BytesRef<'a> {
impl LowerHex for BytesRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for b in self.0 {
for &b in self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl<'a> UpperHex for BytesRef<'a> {
impl UpperHex for BytesRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for b in self.0 {
for &b in self.0 {
write!(f, "{:02X}", b)?;
}
Ok(())
+5
View File
@@ -0,0 +1,5 @@
mod debug;
mod hex;
/// `BytesRef` is not a part of public API of bytes crate.
struct BytesRef<'a>(&'a [u8]);
+2 -3
View File
@@ -1,5 +1,5 @@
#![deny(warnings, missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![doc(html_root_url = "https://docs.rs/bytes/0.5.2")]
#![doc(html_root_url = "https://docs.rs/bytes/0.5.4")]
#![no_std]
//! Provides abstractions for working with bytes.
@@ -86,8 +86,7 @@ pub use crate::buf::{
mod bytes_mut;
mod bytes;
mod debug;
mod hex;
mod fmt;
mod loom;
pub use crate::bytes_mut::BytesMut;
pub use crate::bytes::Bytes;
+30
View File
@@ -69,3 +69,33 @@ fn test_vec_deque() {
buffer.copy_to_slice(&mut out);
assert_eq!(b"world piece", &out[..]);
}
#[test]
fn test_deref_buf_forwards() {
struct Special;
impl Buf for Special {
fn remaining(&self) -> usize {
unreachable!("remaining");
}
fn bytes(&self) -> &[u8] {
unreachable!("bytes");
}
fn advance(&mut self, _: usize) {
unreachable!("advance");
}
fn get_u8(&mut self) -> u8 {
// specialized!
b'x'
}
}
// these should all use the specialized method
assert_eq!(Special.get_u8(), b'x');
assert_eq!((&mut Special as &mut dyn Buf).get_u8(), b'x');
assert_eq!((Box::new(Special) as Box<dyn Buf>).get_u8(), b'x');
assert_eq!(Box::new(Special).get_u8(), b'x');
}
+29
View File
@@ -87,3 +87,32 @@ fn test_mut_slice() {
let mut s = &mut v[..];
s.put_u32(42);
}
#[test]
fn test_deref_bufmut_forwards() {
struct Special;
impl BufMut for Special {
fn remaining_mut(&self) -> usize {
unreachable!("remaining_mut");
}
fn bytes_mut(&mut self) -> &mut [std::mem::MaybeUninit<u8>] {
unreachable!("bytes_mut");
}
unsafe fn advance_mut(&mut self, _: usize) {
unreachable!("advance");
}
fn put_u8(&mut self, _: u8) {
// specialized!
}
}
// these should all use the specialized method
Special.put_u8(b'x');
(&mut Special as &mut dyn BufMut).put_u8(b'x');
(Box::new(Special) as Box<dyn BufMut>).put_u8(b'x');
Box::new(Special).put_u8(b'x');
}
+46 -19
View File
@@ -176,7 +176,7 @@ fn split_off() {
#[should_panic]
fn split_off_oob() {
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.split_off(44);
let _ = hello.split_off(44);
}
#[test]
@@ -273,14 +273,14 @@ fn split_to_2() {
#[should_panic]
fn split_to_oob() {
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.split_to(33);
let _ = hello.split_to(33);
}
#[test]
#[should_panic]
fn split_to_oob_mut() {
let mut hello = BytesMut::from(&b"helloworld"[..]);
hello.split_to(33);
let _ = hello.split_to(33);
}
#[test]
@@ -300,18 +300,30 @@ fn split_off_to_at_gt_len() {
use std::panic;
make_bytes().split_to(4);
make_bytes().split_off(4);
let _ = make_bytes().split_to(4);
let _ = make_bytes().split_off(4);
assert!(panic::catch_unwind(move || {
make_bytes().split_to(5);
let _ = make_bytes().split_to(5);
}).is_err());
assert!(panic::catch_unwind(move || {
make_bytes().split_off(5);
let _ = make_bytes().split_off(5);
}).is_err());
}
#[test]
fn truncate() {
let s = &b"helloworld"[..];
let mut hello = Bytes::from(s);
hello.truncate(15);
assert_eq!(hello, s);
hello.truncate(10);
assert_eq!(hello, s);
hello.truncate(5);
assert_eq!(hello, "hello");
}
#[test]
fn freeze_clone_shared() {
let s = &b"abcdefgh"[..];
@@ -416,7 +428,7 @@ fn reserve_vec_recycling() {
#[test]
fn reserve_in_arc_unique_does_not_overallocate() {
let mut bytes = BytesMut::with_capacity(1000);
bytes.split();
let _ = bytes.split();
// now bytes is Arc and refcount == 1
@@ -428,7 +440,7 @@ fn reserve_in_arc_unique_does_not_overallocate() {
#[test]
fn reserve_in_arc_unique_doubles() {
let mut bytes = BytesMut::with_capacity(1000);
bytes.split();
let _ = bytes.split();
// now bytes is Arc and refcount == 1
@@ -711,12 +723,12 @@ fn bytes_mut_unsplit_arc_different() {
let mut buf = BytesMut::with_capacity(64);
buf.extend_from_slice(b"aaaabbbbeeee");
buf.split_off(8); //arc
let _ = buf.split_off(8); //arc
let mut buf2 = BytesMut::with_capacity(64);
buf2.extend_from_slice(b"ccccddddeeee");
buf2.split_off(8); //arc
let _ = buf2.split_off(8); //arc
buf.unsplit(buf2);
assert_eq!(b"aaaabbbbccccdddd", &buf[..]);
@@ -796,6 +808,16 @@ fn slice_ref_empty() {
assert_eq!(&sub[..], b"");
}
#[test]
fn slice_ref_empty_subslice() {
let bytes = Bytes::from(&b"abcde"[..]);
let subbytes = bytes.slice(0..0);
let slice = &subbytes[..];
// The `slice` object is derived from the original `bytes` object
// so `slice_ref` should work.
assert_eq!(Bytes::new(), bytes.slice_ref(slice));
}
#[test]
#[should_panic]
fn slice_ref_catches_not_a_subset() {
@@ -806,21 +828,19 @@ fn slice_ref_catches_not_a_subset() {
}
#[test]
#[should_panic]
fn slice_ref_catches_not_an_empty_subset() {
fn slice_ref_not_an_empty_subset() {
let bytes = Bytes::from(&b"012345678"[..]);
let slice = &b""[0..0];
bytes.slice_ref(slice);
assert_eq!(Bytes::new(), bytes.slice_ref(slice));
}
#[test]
#[should_panic]
fn empty_slice_ref_catches_not_an_empty_subset() {
let bytes = Bytes::copy_from_slice(&b""[..]);
let slice = &b""[0..0];
fn empty_slice_ref_not_an_empty_subset() {
let bytes = Bytes::new();
let slice = &b"some other slice"[0..0];
bytes.slice_ref(slice);
assert_eq!(Bytes::new(), bytes.slice_ref(slice));
}
#[test]
@@ -853,3 +873,10 @@ fn bytes_reserve_overflow() {
bytes.reserve(usize::MAX);
}
#[test]
fn bytes_with_capacity_but_empty() {
// See https://github.com/tokio-rs/bytes/issues/340
let vec = Vec::with_capacity(1);
let _ = Bytes::from(vec);
}
+67
View File
@@ -0,0 +1,67 @@
//! Test using `Bytes` with an allocator that hands out "odd" pointers for
//! vectors (pointers where the LSB is set).
use std::alloc::{GlobalAlloc, Layout, System};
use std::ptr;
use bytes::Bytes;
#[global_allocator]
static ODD: Odd = Odd;
struct Odd;
unsafe impl GlobalAlloc for Odd {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() == 1 && layout.size() > 0 {
// Allocate slightly bigger so that we can offset the pointer by 1
let size = layout.size() + 1;
let new_layout = match Layout::from_size_align(size, 1) {
Ok(layout) => layout,
Err(_err) => return ptr::null_mut(),
};
let ptr = System.alloc(new_layout);
if !ptr.is_null() {
let ptr = ptr.offset(1);
ptr
} else {
ptr
}
} else {
System.alloc(layout)
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.align() == 1 && layout.size() > 0 {
let size = layout.size() + 1;
let new_layout = match Layout::from_size_align(size, 1) {
Ok(layout) => layout,
Err(_err) => std::process::abort(),
};
System.dealloc(ptr.offset(-1), new_layout);
} else {
System.dealloc(ptr, layout);
}
}
}
#[test]
fn sanity_check_odd_allocator() {
let vec = vec![33u8; 1024];
let p = vec.as_ptr() as usize;
assert!(p & 0x1 == 0x1, "{:#b}", p);
}
#[test]
fn test_bytes_from_vec_drop() {
let vec = vec![33u8; 1024];
let _b = Bytes::from(vec);
}
#[test]
fn test_bytes_clone_drop() {
let vec = vec![33u8; 1024];
let b1 = Bytes::from(vec);
let _b2 = b1.clone();
}
+75
View File
@@ -0,0 +1,75 @@
use std::alloc::{GlobalAlloc, Layout, System};
use std::{mem, ptr};
use bytes::{Buf, Bytes};
#[global_allocator]
static LEDGER: Ledger = Ledger;
struct Ledger;
const USIZE_SIZE: usize = mem::size_of::<usize>();
unsafe impl GlobalAlloc for Ledger {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() == 1 && layout.size() > 0 {
// Allocate extra space to stash a record of
// how much space there was.
let orig_size = layout.size();
let size = orig_size + USIZE_SIZE;
let new_layout = match Layout::from_size_align(size, 1) {
Ok(layout) => layout,
Err(_err) => return ptr::null_mut(),
};
let ptr = System.alloc(new_layout);
if !ptr.is_null() {
(ptr as *mut usize).write(orig_size);
let ptr = ptr.offset(USIZE_SIZE as isize);
ptr
} else {
ptr
}
} else {
System.alloc(layout)
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.align() == 1 && layout.size() > 0 {
let off_ptr = (ptr as *mut usize).offset(-1);
let orig_size = off_ptr.read();
if orig_size != layout.size() {
panic!("bad dealloc: alloc size was {}, dealloc size is {}", orig_size, layout.size());
}
let new_layout = match Layout::from_size_align(layout.size() + USIZE_SIZE, 1) {
Ok(layout) => layout,
Err(_err) => std::process::abort(),
};
System.dealloc(off_ptr as *mut u8, new_layout);
} else {
System.dealloc(ptr, layout);
}
}
}
#[test]
fn test_bytes_advance() {
let mut bytes = Bytes::from(vec![10, 20, 30]);
bytes.advance(1);
drop(bytes);
}
#[test]
fn test_bytes_truncate() {
let mut bytes = Bytes::from(vec![10, 20, 30]);
bytes.truncate(2);
drop(bytes);
}
#[test]
fn test_bytes_truncate_and_advance() {
let mut bytes = Bytes::from(vec![10, 20, 30]);
bytes.truncate(2);
bytes.advance(1);
drop(bytes);
}