mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-08 00:00:26 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe6e673864 | ||
|
|
f330ef6c4d | ||
|
|
87b75cce16 | ||
|
|
3853a1fac4 | ||
|
|
f9ebf74091 | ||
|
|
939a5edf3d | ||
|
|
788cb158ce | ||
|
|
ab028eb6a8 | ||
|
|
e0eebde993 | ||
|
|
729bc7c208 | ||
|
|
8695c08bcc | ||
|
|
39b6646e66 | ||
|
|
c7cf716180 | ||
|
|
8ae3bb2104 | ||
|
|
8733f74d59 | ||
|
|
17a8ac91e0 | ||
|
|
a7fc5274ad | ||
|
|
7e80f3b646 | ||
|
|
a4908213a6 | ||
|
|
af606aab9b |
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user