mirror of
https://github.com/tokio-rs/bytes.git
synced 2026-08-08 00:00:26 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c8ad3227 | ||
|
|
002df10b8c | ||
|
|
76c0fbb54e | ||
|
|
924c82bf00 | ||
|
|
91402cee60 | ||
|
|
2256e6dc3e | ||
|
|
245adff079 | ||
|
|
00cc5ff2bd | ||
|
|
5b79d316c9 | ||
|
|
417dccdeff | ||
|
|
d0293b0e35 | ||
|
|
804ee6d039 | ||
|
|
fd426ca084 | ||
|
|
b4ed70daee | ||
|
|
94e42915a9 | ||
|
|
acd1e0ffb8 |
@@ -1,3 +1,28 @@
|
||||
# 1.12.1 (July 8th, 2026)
|
||||
|
||||
### Fixed
|
||||
- Properly handle when `Box::new` panics (#837)
|
||||
|
||||
# 1.12.0 (June 18th, 2026)
|
||||
|
||||
### Added
|
||||
- Add `BytesMut::extend_from_within()` (#818)
|
||||
- Add `BytesMut::try_unsplit()` (#746)
|
||||
|
||||
### Fixed
|
||||
- Fix panic in `get_int` if `nbytes` is zero (#806)
|
||||
|
||||
### Changed
|
||||
- Pass vtable data by value (#826)
|
||||
- Exclude development scripts from published package (#810)
|
||||
|
||||
### Documented
|
||||
- Document that `BytesMut::{reserve,try_reserve}` doesn't preserve unused capacity (#808)
|
||||
|
||||
# 1.11.1 (February 3rd, 2026)
|
||||
|
||||
- Fix integer overflow in `BytesMut::reserve`
|
||||
|
||||
# 1.11.0 (November 14th, 2025)
|
||||
|
||||
- Bump MSRV to 1.57 (#788)
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@ name = "bytes"
|
||||
# When releasing to crates.io:
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.x.y" git tag.
|
||||
version = "1.11.0"
|
||||
version = "1.12.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
license = "MIT"
|
||||
@@ -17,6 +17,7 @@ repository = "https://github.com/tokio-rs/bytes"
|
||||
readme = "README.md"
|
||||
keywords = ["buffers", "zero-copy", "io"]
|
||||
categories = ["network-programming", "data-structures"]
|
||||
include = ["CHANGELOG.md", "LICENSE", "README.md", "SECURITY.md", "Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "clippy.toml"]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
||||
@@ -8,3 +8,6 @@ export MIRIFLAGS="-Zmiri-strict-provenance"
|
||||
|
||||
cargo miri test
|
||||
cargo miri test --target mips64-unknown-linux-gnuabi64
|
||||
|
||||
# run with wrapping integer overflow instead of panic
|
||||
cargo miri test --release
|
||||
|
||||
+7
-2
@@ -86,8 +86,13 @@ macro_rules! buf_get_impl {
|
||||
|
||||
// https://en.wikipedia.org/wiki/Sign_extension
|
||||
fn sign_extend(val: u64, nbytes: usize) -> i64 {
|
||||
let shift = (8 - nbytes) * 8;
|
||||
(val << shift) as i64 >> shift
|
||||
if nbytes == 0 {
|
||||
// avoid `val << 64` panic
|
||||
0
|
||||
} else {
|
||||
let shift = (8 - nbytes) * 8;
|
||||
(val << shift) as i64 >> shift
|
||||
}
|
||||
}
|
||||
|
||||
/// Read bytes from a buffer.
|
||||
|
||||
+112
-114
@@ -1,4 +1,4 @@
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
use core::mem::{self, ManuallyDrop, MaybeUninit};
|
||||
use core::ops::{Deref, RangeBounds};
|
||||
use core::ptr::NonNull;
|
||||
use core::{cmp, fmt, hash, ptr, slice};
|
||||
@@ -106,18 +106,22 @@ pub struct Bytes {
|
||||
vtable: &'static Vtable,
|
||||
}
|
||||
|
||||
// `data` is passed by value (`*mut ()` instead of `&mut AtomicPtr<()>`)
|
||||
// when `&mut self` or `self` is consumed.
|
||||
// This allows the optimizer to see that the address of the `Bytes` is not
|
||||
// captured by the indirect call, enabling further optimizations.
|
||||
pub(crate) struct Vtable {
|
||||
/// fn(data, ptr, len)
|
||||
pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Bytes,
|
||||
/// fn(data, ptr, len)
|
||||
///
|
||||
/// `into_*` consumes the `Bytes`, returning the respective value.
|
||||
pub into_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec<u8>,
|
||||
pub into_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> BytesMut,
|
||||
pub into_vec: unsafe fn(*mut (), *const u8, usize) -> Vec<u8>,
|
||||
pub into_mut: unsafe fn(*mut (), *const u8, usize) -> BytesMut,
|
||||
/// fn(data)
|
||||
pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool,
|
||||
/// fn(data, ptr, len)
|
||||
pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize),
|
||||
pub drop: unsafe fn(*mut (), *const u8, usize),
|
||||
}
|
||||
|
||||
impl Bytes {
|
||||
@@ -367,34 +371,7 @@ impl Bytes {
|
||||
/// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing
|
||||
/// will panic.
|
||||
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
|
||||
use core::ops::Bound;
|
||||
|
||||
let len = self.len();
|
||||
|
||||
let begin = match range.start_bound() {
|
||||
Bound::Included(&n) => n,
|
||||
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
|
||||
Bound::Excluded(&n) => n,
|
||||
Bound::Unbounded => len,
|
||||
};
|
||||
|
||||
assert!(
|
||||
begin <= end,
|
||||
"range start must not be greater than end: {:?} <= {:?}",
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
assert!(
|
||||
end <= len,
|
||||
"range end out of bounds: {:?} <= {:?}",
|
||||
end,
|
||||
len,
|
||||
);
|
||||
let (begin, end) = crate::range(range, self.len());
|
||||
|
||||
if end == begin {
|
||||
return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(begin));
|
||||
@@ -512,6 +489,8 @@ impl Bytes {
|
||||
|
||||
self.len = at;
|
||||
|
||||
// SAFETY: `at` has been asserted to be <= `self.len()`, and the
|
||||
// `at == self.len()` and `at == 0` cases were handled above.
|
||||
unsafe { ret.inc_start(at) };
|
||||
|
||||
ret
|
||||
@@ -560,6 +539,8 @@ impl Bytes {
|
||||
|
||||
let mut ret = self.clone();
|
||||
|
||||
// SAFETY: `at` has been asserted to be <= `self.len()`, and the
|
||||
// `at == self.len()` and `at == 0` cases were handled above.
|
||||
unsafe { self.inc_start(at) };
|
||||
|
||||
ret.len = at;
|
||||
@@ -586,7 +567,9 @@ impl Bytes {
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
if len < self.len {
|
||||
if len == 0 {
|
||||
drop(mem::replace(self, Bytes::new_empty_with_ptr(self.ptr)));
|
||||
} else 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.
|
||||
@@ -671,6 +654,11 @@ impl Bytes {
|
||||
self.len -= by;
|
||||
self.ptr = self.ptr.add(by);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn data_mut(&mut self) -> *mut () {
|
||||
self.data.with_mut(|p| *p)
|
||||
}
|
||||
}
|
||||
|
||||
// Vtable must enforce this behavior
|
||||
@@ -680,7 +668,8 @@ unsafe impl Sync for Bytes {}
|
||||
impl Drop for Bytes {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { (self.vtable.drop)(&mut self.data, self.ptr, self.len) }
|
||||
let data = self.data_mut();
|
||||
unsafe { (self.vtable.drop)(data, self.ptr, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -959,24 +948,26 @@ impl From<&'static str> for Bytes {
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(vec: Vec<u8>) -> Bytes {
|
||||
// Avoid an extra allocation if possible.
|
||||
if vec.len() == vec.capacity() {
|
||||
return Bytes::from(vec.into_boxed_slice());
|
||||
}
|
||||
|
||||
let shared = Box::new(MaybeUninit::<Shared>::uninit());
|
||||
let mut vec = ManuallyDrop::new(vec);
|
||||
let ptr = vec.as_mut_ptr();
|
||||
let len = vec.len();
|
||||
let cap = vec.capacity();
|
||||
|
||||
// Avoid an extra allocation if possible.
|
||||
if len == cap {
|
||||
let vec = ManuallyDrop::into_inner(vec);
|
||||
return Bytes::from(vec.into_boxed_slice());
|
||||
}
|
||||
let shared = Shared::init_to_raw(
|
||||
shared,
|
||||
Shared {
|
||||
buf: ptr,
|
||||
cap,
|
||||
ref_cnt: AtomicUsize::new(1),
|
||||
},
|
||||
);
|
||||
|
||||
let shared = Box::new(Shared {
|
||||
buf: ptr,
|
||||
cap,
|
||||
ref_cnt: AtomicUsize::new(1),
|
||||
});
|
||||
|
||||
let shared = Box::into_raw(shared);
|
||||
// The pointer should be aligned, so this assert should
|
||||
// always succeed.
|
||||
debug_assert!(
|
||||
@@ -1040,8 +1031,9 @@ impl From<Bytes> for BytesMut {
|
||||
/// assert_eq!(BytesMut::from(bytes), BytesMut::from(&b"hello"[..]));
|
||||
/// ```
|
||||
fn from(bytes: Bytes) -> Self {
|
||||
let bytes = ManuallyDrop::new(bytes);
|
||||
unsafe { (bytes.vtable.into_mut)(&bytes.data, bytes.ptr, bytes.len) }
|
||||
let mut bytes = ManuallyDrop::new(bytes);
|
||||
let data = bytes.data_mut();
|
||||
unsafe { (bytes.vtable.into_mut)(data, bytes.ptr, bytes.len) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,8 +1045,9 @@ impl From<String> for Bytes {
|
||||
|
||||
impl From<Bytes> for Vec<u8> {
|
||||
fn from(bytes: Bytes) -> Vec<u8> {
|
||||
let bytes = ManuallyDrop::new(bytes);
|
||||
unsafe { (bytes.vtable.into_vec)(&bytes.data, bytes.ptr, bytes.len) }
|
||||
let mut bytes = ManuallyDrop::new(bytes);
|
||||
let data = bytes.data_mut();
|
||||
unsafe { (bytes.vtable.into_vec)(data, bytes.ptr, bytes.len) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1084,12 +1077,12 @@ unsafe fn static_clone(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
|
||||
Bytes::from_static(slice)
|
||||
}
|
||||
|
||||
unsafe fn static_to_vec(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
unsafe fn static_to_vec(_: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
let slice = slice::from_raw_parts(ptr, len);
|
||||
slice.to_vec()
|
||||
}
|
||||
|
||||
unsafe fn static_to_mut(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
unsafe fn static_to_mut(_: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
let slice = slice::from_raw_parts(ptr, len);
|
||||
BytesMut::from(slice)
|
||||
}
|
||||
@@ -1098,7 +1091,7 @@ fn static_is_unique(_: &AtomicPtr<()>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) {
|
||||
unsafe fn static_drop(_: *mut (), _: *const u8, _: usize) {
|
||||
// nothing to drop for &'static [u8]
|
||||
}
|
||||
|
||||
@@ -1135,15 +1128,15 @@ unsafe fn owned_clone<T>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn owned_to_vec<T>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
unsafe fn owned_to_vec<T>(owned: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
let slice = slice::from_raw_parts(ptr, len);
|
||||
let vec = slice.to_vec();
|
||||
owned_drop_impl::<T>(data.load(Ordering::Relaxed));
|
||||
owned_drop_impl::<T>(owned);
|
||||
vec
|
||||
}
|
||||
|
||||
unsafe fn owned_to_mut<T>(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
BytesMut::from_vec(owned_to_vec::<T>(data, ptr, len))
|
||||
unsafe fn owned_to_mut<T>(owned: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
BytesMut::from_vec(owned_to_vec::<T>(owned, ptr, len))
|
||||
}
|
||||
|
||||
unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool {
|
||||
@@ -1168,9 +1161,8 @@ unsafe fn owned_drop_impl<T>(owned: *mut ()) {
|
||||
drop(Box::<Owned<T>>::from_raw(owned.cast()));
|
||||
}
|
||||
|
||||
unsafe fn owned_drop<T>(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
|
||||
let owned = data.load(Ordering::Relaxed);
|
||||
owned_drop_impl::<T>(owned);
|
||||
unsafe fn owned_drop<T>(data: *mut (), _ptr: *const u8, _len: usize) {
|
||||
owned_drop_impl::<T>(data);
|
||||
}
|
||||
|
||||
// ===== impl PromotableVtable =====
|
||||
@@ -1205,12 +1197,11 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize
|
||||
}
|
||||
|
||||
unsafe fn promotable_to_vec(
|
||||
data: &AtomicPtr<()>,
|
||||
shared: *mut (),
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
f: fn(*mut ()) -> *mut u8,
|
||||
) -> Vec<u8> {
|
||||
let shared = data.load(Ordering::Acquire);
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
|
||||
if kind == KIND_ARC {
|
||||
@@ -1231,12 +1222,11 @@ unsafe fn promotable_to_vec(
|
||||
}
|
||||
|
||||
unsafe fn promotable_to_mut(
|
||||
data: &AtomicPtr<()>,
|
||||
shared: *mut (),
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
f: fn(*mut ()) -> *mut u8,
|
||||
) -> BytesMut {
|
||||
let shared = data.load(Ordering::Acquire);
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
|
||||
if kind == KIND_ARC {
|
||||
@@ -1259,31 +1249,28 @@ unsafe fn promotable_to_mut(
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn promotable_even_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
promotable_to_vec(data, ptr, len, |shared| {
|
||||
unsafe fn promotable_even_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
promotable_to_vec(shared, ptr, len, |shared| {
|
||||
ptr_map(shared.cast(), |addr| addr & !KIND_MASK)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn promotable_even_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
promotable_to_mut(data, ptr, len, |shared| {
|
||||
unsafe fn promotable_even_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
promotable_to_mut(shared, ptr, len, |shared| {
|
||||
ptr_map(shared.cast(), |addr| addr & !KIND_MASK)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn promotable_even_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
|
||||
data.with_mut(|shared| {
|
||||
let shared = *shared;
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
unsafe fn promotable_even_drop(shared: *mut (), ptr: *const u8, len: usize) {
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
|
||||
if kind == KIND_ARC {
|
||||
release_shared(shared.cast());
|
||||
} else {
|
||||
debug_assert_eq!(kind, KIND_VEC);
|
||||
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
|
||||
free_boxed_slice(buf, ptr, len);
|
||||
}
|
||||
});
|
||||
if kind == KIND_ARC {
|
||||
release_shared(shared.cast());
|
||||
} else {
|
||||
debug_assert_eq!(kind, KIND_VEC);
|
||||
let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK);
|
||||
free_boxed_slice(buf, ptr, len);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes {
|
||||
@@ -1298,27 +1285,24 @@ unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn promotable_odd_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
promotable_to_vec(data, ptr, len, |shared| shared.cast())
|
||||
unsafe fn promotable_odd_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
promotable_to_vec(shared, ptr, len, |shared| shared.cast())
|
||||
}
|
||||
|
||||
unsafe fn promotable_odd_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
promotable_to_mut(data, ptr, len, |shared| shared.cast())
|
||||
unsafe fn promotable_odd_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
promotable_to_mut(shared, ptr, len, |shared| shared.cast())
|
||||
}
|
||||
|
||||
unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) {
|
||||
data.with_mut(|shared| {
|
||||
let shared = *shared;
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
unsafe fn promotable_odd_drop(shared: *mut (), ptr: *const u8, len: usize) {
|
||||
let kind = shared as usize & KIND_MASK;
|
||||
|
||||
if kind == KIND_ARC {
|
||||
release_shared(shared.cast());
|
||||
} else {
|
||||
debug_assert_eq!(kind, KIND_VEC);
|
||||
if kind == KIND_ARC {
|
||||
release_shared(shared.cast());
|
||||
} else {
|
||||
debug_assert_eq!(kind, KIND_VEC);
|
||||
|
||||
free_boxed_slice(shared.cast(), ptr, len);
|
||||
}
|
||||
});
|
||||
free_boxed_slice(shared.cast(), ptr, len);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool {
|
||||
@@ -1347,6 +1331,15 @@ struct Shared {
|
||||
ref_cnt: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Shared {
|
||||
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
|
||||
let shared = Box::into_raw(b).cast::<Self>();
|
||||
// SAFETY: The Box has the right layout.
|
||||
unsafe { shared.write(v) };
|
||||
shared
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Shared {
|
||||
fn drop(&mut self) {
|
||||
unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
|
||||
@@ -1357,7 +1350,12 @@ impl Drop for Shared {
|
||||
// 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.
|
||||
const _: () = {
|
||||
assert!(
|
||||
mem::align_of::<Shared>() % 2 == 0,
|
||||
"Shared alignment must be divisible by 2 for pointer tagging"
|
||||
);
|
||||
};
|
||||
|
||||
static SHARED_VTABLE: Vtable = Vtable {
|
||||
clone: shared_clone,
|
||||
@@ -1405,8 +1403,8 @@ unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn shared_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
shared_to_vec_impl(data.load(Ordering::Relaxed).cast(), ptr, len)
|
||||
unsafe fn shared_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
shared_to_vec_impl(shared.cast(), ptr, len)
|
||||
}
|
||||
|
||||
unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> BytesMut {
|
||||
@@ -1444,8 +1442,8 @@ unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) ->
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn shared_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
shared_to_mut_impl(data.load(Ordering::Relaxed).cast(), ptr, len)
|
||||
unsafe fn shared_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
shared_to_mut_impl(shared.cast(), ptr, len)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool {
|
||||
@@ -1454,10 +1452,8 @@ pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool {
|
||||
ref_cnt == 1
|
||||
}
|
||||
|
||||
unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
|
||||
data.with_mut(|shared| {
|
||||
release_shared(shared.cast());
|
||||
});
|
||||
unsafe fn shared_drop(shared: *mut (), _ptr: *const u8, _len: usize) {
|
||||
release_shared(shared.cast());
|
||||
}
|
||||
|
||||
unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) -> Bytes {
|
||||
@@ -1494,16 +1490,18 @@ unsafe fn shallow_clone_vec(
|
||||
// updated and since the buffer hasn't been promoted to an
|
||||
// `Arc`, those three fields still are the components of the
|
||||
// vector.
|
||||
let shared = Box::new(Shared {
|
||||
buf,
|
||||
cap: offset.offset_from(buf) as usize + len,
|
||||
// Initialize refcount to 2. One for this reference, and one
|
||||
// for the new clone that will be returned from
|
||||
// `shallow_clone`.
|
||||
ref_cnt: AtomicUsize::new(2),
|
||||
});
|
||||
|
||||
let shared = Box::into_raw(shared);
|
||||
let shared = Box::new(MaybeUninit::<Shared>::uninit());
|
||||
let shared = Shared::init_to_raw(
|
||||
shared,
|
||||
Shared {
|
||||
buf,
|
||||
cap: offset.offset_from(buf) as usize + len,
|
||||
// Initialize refcount to 2. One for this reference, and one
|
||||
// for the new clone that will be returned from
|
||||
// `shallow_clone`.
|
||||
ref_cnt: AtomicUsize::new(2),
|
||||
},
|
||||
);
|
||||
|
||||
// The pointer should be aligned, so this assert should
|
||||
// always succeed.
|
||||
|
||||
+123
-26
@@ -79,11 +79,25 @@ struct Shared {
|
||||
ref_count: AtomicUsize,
|
||||
}
|
||||
|
||||
impl Shared {
|
||||
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
|
||||
let shared = Box::into_raw(b).cast::<Self>();
|
||||
// SAFETY: The Box has the right layout.
|
||||
unsafe { shared.write(v) };
|
||||
shared
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
const _: () = {
|
||||
assert!(
|
||||
mem::align_of::<Shared>() % 2 == 0,
|
||||
"Shared alignment must be divisible by 2 for pointer tagging"
|
||||
);
|
||||
};
|
||||
|
||||
// Buffer storage strategy flags.
|
||||
const KIND_ARC: usize = 0b0;
|
||||
@@ -324,6 +338,10 @@ impl BytesMut {
|
||||
self.capacity(),
|
||||
);
|
||||
unsafe {
|
||||
// SAFETY: `shallow_clone` increments the reference count (or
|
||||
// promotes to shared) and returns a bitwise copy of the handle.
|
||||
// The caller immediately adjusts both handles so they represent
|
||||
// disjoint regions.
|
||||
let mut other = self.shallow_clone();
|
||||
// SAFETY: We've checked that `at` <= `self.capacity()` above.
|
||||
other.advance_unchecked(at);
|
||||
@@ -400,6 +418,10 @@ impl BytesMut {
|
||||
);
|
||||
|
||||
unsafe {
|
||||
// SAFETY: `shallow_clone` increments the reference count (or
|
||||
// promotes to shared) and returns a bitwise copy of the handle.
|
||||
// The caller immediately adjusts both handles so they represent
|
||||
// disjoint regions.
|
||||
let mut other = self.shallow_clone();
|
||||
// SAFETY: We've checked that `at` <= `self.len()` and we know that `self.len()` <=
|
||||
// `self.capacity()`.
|
||||
@@ -551,6 +573,8 @@ impl BytesMut {
|
||||
/// and the original buffer is large enough to fit the requested additional
|
||||
/// capacity, then reallocations will never happen.
|
||||
///
|
||||
/// This method does not preserve data stored in the unused capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// In the following example, a new buffer is allocated.
|
||||
@@ -695,9 +719,15 @@ impl BytesMut {
|
||||
|
||||
let offset = self.ptr.as_ptr().offset_from(ptr) as usize;
|
||||
|
||||
let new_cap_plus_offset = match new_cap.checked_add(offset) {
|
||||
Some(new_cap_plus_offset) => new_cap_plus_offset,
|
||||
None if !allocate => return false,
|
||||
None => panic!("overflow"),
|
||||
};
|
||||
|
||||
// Compare the condition in the `kind == KIND_VEC` case above
|
||||
// for more details.
|
||||
if v_capacity >= new_cap + offset {
|
||||
if v_capacity >= new_cap_plus_offset {
|
||||
self.cap = new_cap;
|
||||
// no copy is necessary
|
||||
} else if v_capacity >= new_cap && offset >= len {
|
||||
@@ -713,14 +743,12 @@ impl BytesMut {
|
||||
if !allocate {
|
||||
return false;
|
||||
}
|
||||
// calculate offset
|
||||
let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize);
|
||||
|
||||
// new_cap is calculated in terms of `BytesMut`, not the underlying
|
||||
// `Vec`, so it does not take the offset into account.
|
||||
//
|
||||
// Thus we have to manually add it here.
|
||||
new_cap = new_cap.checked_add(off).expect("overflow");
|
||||
new_cap = new_cap_plus_offset;
|
||||
|
||||
// The vector capacity is not sufficient. The reserve request is
|
||||
// asking for more than the initial buffer capacity. Allocate more
|
||||
@@ -742,13 +770,13 @@ impl BytesMut {
|
||||
// the unused capacity of the vector is copied over to the new
|
||||
// allocation, so we need to ensure that we don't have any data we
|
||||
// care about in the unused capacity before calling `reserve`.
|
||||
debug_assert!(off + len <= v.capacity());
|
||||
v.set_len(off + len);
|
||||
debug_assert!(offset + len <= v.capacity());
|
||||
v.set_len(offset + len);
|
||||
v.reserve(new_cap - v.len());
|
||||
|
||||
// Update the info
|
||||
self.ptr = vptr(v.as_mut_ptr().add(off));
|
||||
self.cap = v.capacity() - off;
|
||||
self.ptr = vptr(v.as_mut_ptr().add(offset));
|
||||
self.cap = v.capacity() - offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -797,6 +825,8 @@ impl BytesMut {
|
||||
/// references through other `BytesMut`s or `Bytes` which point to the same underlying
|
||||
/// storage.
|
||||
///
|
||||
/// This method does not preserve data stored in the unused capacity.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -876,7 +906,43 @@ impl BytesMut {
|
||||
}
|
||||
}
|
||||
|
||||
/// Absorbs a `BytesMut` that was previously split off.
|
||||
/// Clones the elements in the given `range` within this `BytesMut` and
|
||||
/// appends them to the end.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `range` is out of bounds for this `BytesMut`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::BytesMut;
|
||||
///
|
||||
/// let mut buf = BytesMut::with_capacity(0);
|
||||
/// buf.extend_from_slice(b"aaabbb_");
|
||||
/// buf.extend_from_within(3..6);
|
||||
///
|
||||
/// assert_eq!(b"aaabbb_bbb", &buf[..]);
|
||||
/// ```
|
||||
pub fn extend_from_within(&mut self, range: impl core::ops::RangeBounds<usize>) {
|
||||
let (begin, end) = crate::range(range, self.len());
|
||||
|
||||
let cnt = end - begin;
|
||||
self.reserve(cnt);
|
||||
|
||||
// SAFETY: range is already checked
|
||||
let src = unsafe { self.as_ptr().add(begin) };
|
||||
let dst = self.spare_capacity_mut();
|
||||
|
||||
// SAFETY: range doesn't overlap with spare capacity
|
||||
unsafe { ptr::copy_nonoverlapping(src, dst.as_mut_ptr().cast(), cnt) }
|
||||
|
||||
// SAFETY: capacity is already reserved and filled with data
|
||||
unsafe { self.advance_mut(cnt) }
|
||||
}
|
||||
|
||||
/// Absorbs a `BytesMut` that was previously split off if they are
|
||||
/// contiguous, otherwise appends its bytes to this `BytesMut`.
|
||||
///
|
||||
/// If the two `BytesMut` objects were previously contiguous and not mutated
|
||||
/// in a way that causes re-allocation i.e., if `other` was created by
|
||||
@@ -989,7 +1055,35 @@ impl BytesMut {
|
||||
self.cap -= count;
|
||||
}
|
||||
|
||||
fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> {
|
||||
/// Absorbs a `BytesMut` that was previously split off.
|
||||
///
|
||||
/// 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 returns an error
|
||||
/// containing the original `other`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::BytesMut;
|
||||
///
|
||||
/// let mut buf = BytesMut::with_capacity(64);
|
||||
/// buf.extend_from_slice(b"aaabbbcccddd");
|
||||
///
|
||||
/// let mut split_1 = buf.split_off(3);
|
||||
/// let split_2 = split_1.split_off(3);
|
||||
/// assert_eq!(b"aaa", &buf[..]);
|
||||
/// assert_eq!(b"bbb", &split_1[..]);
|
||||
/// assert_eq!(b"cccddd", &split_2[..]);
|
||||
///
|
||||
/// let split_2 = buf.try_unsplit(split_2).unwrap_err();
|
||||
///
|
||||
/// buf.try_unsplit(split_1).unwrap();
|
||||
/// buf.try_unsplit(split_2).unwrap();
|
||||
/// assert_eq!(b"aaabbbcccddd", &buf[..]);
|
||||
/// ```
|
||||
pub fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> {
|
||||
if other.capacity() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1032,13 +1126,18 @@ impl BytesMut {
|
||||
// updated and since the buffer hasn't been promoted to an
|
||||
// `Arc`, those three fields still are the components of the
|
||||
// vector.
|
||||
let shared = Box::new(Shared {
|
||||
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off),
|
||||
original_capacity_repr,
|
||||
ref_count: AtomicUsize::new(ref_cnt),
|
||||
});
|
||||
|
||||
let shared = Box::into_raw(shared);
|
||||
//
|
||||
// Explicitly allocate before invoking rebuild_vec() so that
|
||||
// the vector is not dropped if Box::new() panics.
|
||||
let shared = Box::new(MaybeUninit::<Shared>::uninit());
|
||||
let shared = Shared::init_to_raw(
|
||||
shared,
|
||||
Shared {
|
||||
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off),
|
||||
original_capacity_repr,
|
||||
ref_count: AtomicUsize::new(ref_cnt),
|
||||
},
|
||||
);
|
||||
|
||||
// The pointer should be aligned, so this assert should
|
||||
// always succeed.
|
||||
@@ -1806,8 +1905,8 @@ unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By
|
||||
Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
let shared: *mut Shared = data.load(Ordering::Relaxed).cast();
|
||||
unsafe fn shared_v_to_vec(shared: *mut (), ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
let shared: *mut Shared = shared.cast();
|
||||
|
||||
if (*shared).is_unique() {
|
||||
let shared = &mut *shared;
|
||||
@@ -1828,8 +1927,8 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn shared_v_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut {
|
||||
let shared: *mut Shared = data.load(Ordering::Relaxed).cast();
|
||||
unsafe fn shared_v_to_mut(shared: *mut (), ptr: *const u8, len: usize) -> BytesMut {
|
||||
let shared: *mut Shared = shared.cast();
|
||||
|
||||
if (*shared).is_unique() {
|
||||
let shared = &mut *shared;
|
||||
@@ -1863,10 +1962,8 @@ unsafe fn shared_v_is_unique(data: &AtomicPtr<()>) -> bool {
|
||||
ref_count == 1
|
||||
}
|
||||
|
||||
unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) {
|
||||
data.with_mut(|shared| {
|
||||
release_shared(*shared as *mut Shared);
|
||||
});
|
||||
unsafe fn shared_v_drop(shared: *mut (), _ptr: *const u8, _len: usize) {
|
||||
release_shared(shared.cast());
|
||||
}
|
||||
|
||||
// compile-fails
|
||||
|
||||
+37
@@ -129,6 +129,43 @@ fn min_u64_usize(a: u64, b: usize) -> usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs bounds checking of a range.
|
||||
///
|
||||
/// This is a spiritual copy of [core::slice::index::range] because that
|
||||
/// function is currently unstable.
|
||||
#[inline(always)]
|
||||
#[track_caller]
|
||||
fn range(range: impl core::ops::RangeBounds<usize>, len: usize) -> (usize, usize) {
|
||||
use core::ops::Bound;
|
||||
|
||||
let begin = match range.start_bound() {
|
||||
Bound::Included(&n) => n,
|
||||
Bound::Excluded(&n) => n.checked_add(1).expect("out of range"),
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(&n) => n.checked_add(1).expect("out of range"),
|
||||
Bound::Excluded(&n) => n,
|
||||
Bound::Unbounded => len,
|
||||
};
|
||||
|
||||
assert!(
|
||||
begin <= end,
|
||||
"range start must not be greater than end: {:?} <= {:?}",
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
assert!(
|
||||
end <= len,
|
||||
"range end out of bounds: {:?} <= {:?}",
|
||||
end,
|
||||
len,
|
||||
);
|
||||
|
||||
(begin, end)
|
||||
}
|
||||
|
||||
/// Error type for the `try_get_` methods of [`Buf`].
|
||||
/// Indicates that there were not enough remaining
|
||||
/// bytes in the buffer while attempting
|
||||
|
||||
+29
-7
@@ -250,12 +250,12 @@ macro_rules! buf_tests {
|
||||
buf_tests!(number $make_input, get_f64_le, get_f64_le_overflow, f64, get_f64_le, f64::from_bits(0x7144726a727146ff));
|
||||
buf_tests!(number $make_input, get_f64_ne, get_f64_ne_overflow, f64, get_f64_ne, f64::from_bits(e!(0xff4671726a724471, 0x7144726a727146ff)));
|
||||
|
||||
buf_tests!(var_number $make_input, get_uint_be, get_uint_be_overflow, u64, get_uint, 3, 0xff4671);
|
||||
buf_tests!(var_number $make_input, get_uint_le, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff);
|
||||
buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff));
|
||||
buf_tests!(var_number $make_input, get_int_be, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64);
|
||||
buf_tests!(var_number $make_input, get_int_le, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff);
|
||||
buf_tests!(var_number $make_input, get_int_ne, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff));
|
||||
buf_tests!(var_number $make_input, get_uint_be, get_uint_be_zero, get_uint_be_overflow, u64, get_uint, 3, 0xff4671);
|
||||
buf_tests!(var_number $make_input, get_uint_le, get_uint_le_zero, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff);
|
||||
buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_zero, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff));
|
||||
buf_tests!(var_number $make_input, get_int_be, get_int_be_zero, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64);
|
||||
buf_tests!(var_number $make_input, get_int_le, get_int_le_zero, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff);
|
||||
buf_tests!(var_number $make_input, get_int_ne, get_int_ne_zero, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff));
|
||||
};
|
||||
(number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $value:expr) => {
|
||||
#[test]
|
||||
@@ -276,7 +276,7 @@ macro_rules! buf_tests {
|
||||
let _ = buf.$method();
|
||||
}
|
||||
};
|
||||
(var_number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => {
|
||||
(var_number $make_input:ident, $ok_name:ident, $ok_zero_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => {
|
||||
#[test]
|
||||
fn $ok_name() {
|
||||
let mut buf = $make_input(INPUT);
|
||||
@@ -287,6 +287,17 @@ macro_rules! buf_tests {
|
||||
assert_eq!(value, $value);
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/tokio-rs/bytes/issues/798
|
||||
#[test]
|
||||
fn $ok_zero_name() {
|
||||
let mut buf = $make_input(INPUT);
|
||||
|
||||
let value = buf.$method(0);
|
||||
assert_eq!(buf.remaining(), 64);
|
||||
assert!(buf.has_remaining());
|
||||
assert_eq!(value, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn $panic_name() {
|
||||
@@ -437,3 +448,14 @@ fn test_deref_buf_forwards() {
|
||||
assert_eq!((Box::new(Special) as Box<dyn Buf>).get_u8(), b'x');
|
||||
assert_eq!(Box::new(Special).get_u8(), b'x');
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_to_bytes_mut() {
|
||||
let mut bytes_mut = BytesMut::from(b"foobar".as_slice());
|
||||
let ptr = bytes_mut.as_ptr();
|
||||
let ret = bytes_mut.copy_to_bytes(bytes_mut.len());
|
||||
assert_eq!(ret.as_ptr(), ptr);
|
||||
drop(bytes_mut);
|
||||
let bytes_mut2 = BytesMut::from(ret);
|
||||
assert_eq!(bytes_mut2.as_ptr(), ptr);
|
||||
}
|
||||
|
||||
@@ -339,6 +339,31 @@ fn truncate() {
|
||||
assert_eq!(hello, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn truncate_to_zero_releases_shared_reference() {
|
||||
let mut bytes = BytesMut::from(&b"hello"[..]);
|
||||
drop(bytes.split_off(bytes.len()));
|
||||
let mut truncated = bytes.freeze();
|
||||
let remaining = truncated.clone();
|
||||
|
||||
truncated.truncate(0);
|
||||
|
||||
assert!(truncated.is_empty());
|
||||
let mut remaining = remaining.try_into_mut().unwrap();
|
||||
remaining[0] = b'H';
|
||||
assert_eq!(remaining, b"Hello"[..]);
|
||||
|
||||
let mut bytes = BytesMut::from(&b"hello"[..]);
|
||||
drop(bytes.split_off(bytes.len()));
|
||||
let mut nonempty = bytes.freeze();
|
||||
let remaining = nonempty.clone();
|
||||
|
||||
nonempty.truncate(1);
|
||||
|
||||
assert_eq!(nonempty, b"h"[..]);
|
||||
assert!(remaining.try_into_mut().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freeze_clone_shared() {
|
||||
let s = &b"abcdefgh"[..];
|
||||
@@ -607,6 +632,23 @@ fn extend_from_slice_mut() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_from_within_normal() {
|
||||
let mut bytes = BytesMut::new();
|
||||
bytes.extend_from_slice(&LONG[..23]);
|
||||
bytes.extend_from_within(10..22);
|
||||
bytes.extend_from_within(22..35);
|
||||
assert_eq!(LONG[..], *bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn extend_from_within_out_of_range() {
|
||||
let mut bytes = BytesMut::new();
|
||||
bytes.extend_from_slice(&LONG[..23]);
|
||||
bytes.extend_from_within(23..=23);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_mut_from_bytes() {
|
||||
let mut bytes = BytesMut::with_capacity(0);
|
||||
@@ -1526,6 +1568,65 @@ fn split_to_empty_addr_mut() {
|
||||
let _ = &buf[..];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bytes_mut_split_boundary_capacities() {
|
||||
// VEC mode
|
||||
for at in [0, 5, 11] {
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
buf.extend_from_slice(b"hello world");
|
||||
|
||||
let other = buf.split_off(at);
|
||||
assert_eq!(
|
||||
buf.capacity() + other.capacity(),
|
||||
64,
|
||||
"split_off at {} should preserve total capacity",
|
||||
at
|
||||
);
|
||||
}
|
||||
|
||||
for at in [0, 5, 11] {
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
buf.extend_from_slice(b"hello world");
|
||||
|
||||
let other = buf.split_to(at);
|
||||
assert_eq!(
|
||||
buf.capacity() + other.capacity(),
|
||||
64,
|
||||
"split_to at {} should preserve total capacity",
|
||||
at
|
||||
);
|
||||
}
|
||||
|
||||
// ARC mode (promote via a no-op split)
|
||||
for at in [0, 5, 11] {
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
buf.extend_from_slice(b"hello world");
|
||||
let _ = buf.split_to(0); // promotes to ARC
|
||||
|
||||
let other = buf.split_off(at);
|
||||
assert_eq!(
|
||||
buf.capacity() + other.capacity(),
|
||||
64,
|
||||
"ARC split_off at {} should preserve total capacity",
|
||||
at
|
||||
);
|
||||
}
|
||||
|
||||
for at in [0, 5, 11] {
|
||||
let mut buf = BytesMut::with_capacity(64);
|
||||
buf.extend_from_slice(b"hello world");
|
||||
let _ = buf.split_to(0); // promotes to ARC
|
||||
|
||||
let other = buf.split_to(at);
|
||||
assert_eq!(
|
||||
buf.capacity() + other.capacity(),
|
||||
64,
|
||||
"ARC split_to at {} should preserve total capacity",
|
||||
at
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SharedAtomicCounter(Arc<AtomicUsize>);
|
||||
|
||||
@@ -1707,3 +1808,16 @@ fn bytes_mut_put_bytes_specialization() {
|
||||
// If allocation is reused, capacity should be equal to original vec capacity.
|
||||
assert_eq!(bytes_mut.capacity(), capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bytes_mut_reserve_overflow() {
|
||||
let mut a = BytesMut::from(&b"hello world"[..]);
|
||||
let mut b = a.split_off(5);
|
||||
// Ensure b becomes the unique owner of the backing storage
|
||||
drop(a);
|
||||
// Trigger overflow in new_cap + offset inside reserve
|
||||
b.reserve(usize::MAX - 6);
|
||||
// This call relies on the corrupted cap and may cause UB & HBO
|
||||
b.put_u8(b'h');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user