From fa9cbf1258cea7812d4009639ed8700b987d8d4e Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sun, 8 Aug 2021 18:43:09 +0100 Subject: [PATCH 01/48] Clarify BufPut::put_int behavior (#486) * writes low bytes, discards high bytes * panics if `nbytes` is greater than 8 --- src/buf/buf_mut.rs | 12 ++++++------ tests/test_buf_mut.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index bf33fe635..b94665864 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -703,7 +703,7 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()[0..nbytes]); } - /// Writes a signed n-byte integer to `self` in big-endian byte order. + /// Writes low `nbytes` of a signed integer to `self` in big-endian byte order. /// /// The current position is advanced by `nbytes`. /// @@ -713,19 +713,19 @@ pub unsafe trait BufMut { /// use bytes::BufMut; /// /// let mut buf = vec![]; - /// buf.put_int(0x010203, 3); + /// buf.put_int(0x0504010203, 3); /// assert_eq!(buf, b"\x01\x02\x03"); /// ``` /// /// # Panics /// /// This function panics if there is not enough remaining capacity in - /// `self`. + /// `self` or if `nbytes` is greater than 8. fn put_int(&mut self, n: i64, nbytes: usize) { self.put_slice(&n.to_be_bytes()[mem::size_of_val(&n) - nbytes..]); } - /// Writes a signed n-byte integer to `self` in little-endian byte order. + /// Writes low `nbytes` of a signed integer to `self` in little-endian byte order. /// /// The current position is advanced by `nbytes`. /// @@ -735,14 +735,14 @@ pub unsafe trait BufMut { /// use bytes::BufMut; /// /// let mut buf = vec![]; - /// buf.put_int_le(0x010203, 3); + /// buf.put_int_le(0x0504010203, 3); /// assert_eq!(buf, b"\x03\x02\x01"); /// ``` /// /// # Panics /// /// This function panics if there is not enough remaining capacity in - /// `self`. + /// `self` or if `nbytes` is greater than 8. fn put_int_le(&mut self, n: i64, nbytes: usize) { self.put_slice(&n.to_le_bytes()[0..nbytes]); } diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index f63198226..78a76bcb2 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -45,6 +45,34 @@ fn test_put_u16() { assert_eq!(b"\x54\x21", &buf[..]); } +#[test] +fn test_put_int() { + let mut buf = Vec::with_capacity(8); + buf.put_int(0x1020304050607080, 3); + assert_eq!(b"\x60\x70\x80", &buf[..]); +} + +#[test] +#[should_panic] +fn test_put_int_nbytes_overflow() { + let mut buf = Vec::with_capacity(8); + buf.put_int(0x1020304050607080, 9); +} + +#[test] +fn test_put_int_le() { + let mut buf = Vec::with_capacity(8); + buf.put_int_le(0x1020304050607080, 3); + assert_eq!(b"\x80\x70\x60", &buf[..]); +} + +#[test] +#[should_panic] +fn test_put_int_le_nbytes_overflow() { + let mut buf = Vec::with_capacity(8); + buf.put_int_le(0x1020304050607080, 9); +} + #[test] #[should_panic(expected = "cannot advance")] fn test_vec_advance_mut() { From 2697fa7a9dd871d8d64d1959b7ac46227b7b23b4 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sun, 8 Aug 2021 18:43:53 +0100 Subject: [PATCH 02/48] BufMut::put_bytes(self, val, cnt) (#487) Equivalent to ``` for _ in 0..cnt { self.put_u8(val); } ``` but may work faster. Name and signature is chosen to be consistent with `ptr::write_bytes`. Include three specializations: * `Vec` * `&mut [u8]` * `BytesMut` `BytesMut` and `&mut [u8]` specializations use `ptr::write`, `Vec` specialization uses `Vec::resize`. --- src/buf/buf_mut.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ src/bytes_mut.rs | 13 +++++++++++++ tests/test_buf_mut.rs | 18 ++++++++++++++++++ tests/test_bytes.rs | 8 ++++++++ 4 files changed, 83 insertions(+) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index b94665864..4c2bd2cce 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -261,6 +261,37 @@ pub unsafe trait BufMut { } } + /// Put `cnt` bytes `val` into `self`. + /// + /// Logically equivalent to calling `self.put_u8(val)` `cnt` times, but may work faster. + /// + /// `self` must have at least `cnt` remaining capacity. + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut dst = [0; 6]; + /// + /// { + /// let mut buf = &mut dst[..]; + /// buf.put_bytes(b'a', 4); + /// + /// assert_eq!(2, buf.remaining_mut()); + /// } + /// + /// assert_eq!(b"aaaa\0\0", &dst); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_bytes(&mut self, val: u8, cnt: usize) { + for _ in 0..cnt { + self.put_u8(val); + } + } + /// Writes an unsigned 8 bit integer to `self`. /// /// The current position is advanced by 1. @@ -1027,6 +1058,14 @@ unsafe impl BufMut for &mut [u8] { self.advance_mut(src.len()); } } + + fn put_bytes(&mut self, val: u8, cnt: usize) { + assert!(self.remaining_mut() >= cnt); + unsafe { + ptr::write_bytes(self.as_mut_ptr(), val, cnt); + self.advance_mut(cnt); + } + } } unsafe impl BufMut for Vec { @@ -1091,6 +1130,11 @@ unsafe impl BufMut for Vec { fn put_slice(&mut self, src: &[u8]) { self.extend_from_slice(src); } + + fn put_bytes(&mut self, val: u8, cnt: usize) { + let new_len = self.len().checked_add(cnt).unwrap(); + self.resize(new_len, val); + } } // The existence of this function makes the compiler catch if the BufMut diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 8e42079e6..b754ed1fc 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1010,6 +1010,19 @@ unsafe impl BufMut for BytesMut { fn put_slice(&mut self, src: &[u8]) { self.extend_from_slice(src); } + + fn put_bytes(&mut self, val: u8, cnt: usize) { + self.reserve(cnt); + unsafe { + let dst = self.uninit_slice(); + // Reserved above + debug_assert!(dst.len() >= cnt); + + ptr::write_bytes(dst.as_mut_ptr(), val, cnt); + + self.advance_mut(cnt); + } + } } impl AsRef<[u8]> for BytesMut { diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 78a76bcb2..53f4e8611 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -27,6 +27,14 @@ fn test_vec_as_mut_buf() { assert_eq!(buf.len(), 68); } +#[test] +fn test_vec_put_bytes() { + let mut buf = Vec::new(); + buf.push(17); + buf.put_bytes(19, 2); + assert_eq!([17, 19, 19], &buf[..]); +} + #[test] fn test_put_u8() { let mut buf = Vec::with_capacity(8); @@ -103,6 +111,16 @@ fn test_mut_slice() { assert_eq!(&v, &[0, 0, 0, 42]); } +#[test] +fn test_slice_put_bytes() { + let mut v = [0, 0, 0, 0]; + let mut s = &mut v[..]; + s.put_u8(17); + s.put_bytes(19, 2); + assert_eq!(1, s.remaining_mut()); + assert_eq!(&[17, 19, 19, 0], &v[..]); +} + #[test] fn test_deref_bufmut_forwards() { struct Special; diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 2d22fdddb..0914155b9 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -986,3 +986,11 @@ fn bytes_with_capacity_but_empty() { let vec = Vec::with_capacity(1); let _ = Bytes::from(vec); } + +#[test] +fn bytes_put_bytes() { + let mut bytes = BytesMut::new(); + bytes.put_u8(17); + bytes.put_bytes(19, 2); + assert_eq!([17, 19, 19], bytes.as_ref()); +} From ee24be7fa0561169c39308678c35e1ac0f172d5a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Aug 2021 12:33:16 +0200 Subject: [PATCH 03/48] ci: fetch cargo hack from github release (#507) Co-authored-by: Taiki Endo --- ci/test-stable.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/test-stable.sh b/ci/test-stable.sh index 01a32f5a6..4421f3a97 100644 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -5,7 +5,8 @@ set -ex cmd="${1:-test}" # Install cargo-hack for feature flag test -cargo install cargo-hack +host=$(rustc -Vv | grep host | sed 's/host: //') +curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-$host.tar.gz | tar xzf - -C ~/.cargo/bin # Run with each feature # * --each-feature includes both default/no-default features From 0e9fa0b602eb0fb977e2e900cf43532cd869d6b3 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 24 Aug 2021 11:42:22 +0100 Subject: [PATCH 04/48] impl From> for Bytes (#504) --- src/bytes.rs | 12 +++++++++--- tests/test_bytes.rs | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index b1b35ea83..d0be0d276 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -797,14 +797,20 @@ impl From<&'static str> for Bytes { impl From> for Bytes { fn from(vec: Vec) -> Bytes { - // into_boxed_slice doesn't return a heap allocation for empty vectors, + let slice = vec.into_boxed_slice(); + slice.into() + } +} + +impl From> for Bytes { + fn from(slice: Box<[u8]>) -> Bytes { + // Box<[u8]> doesn't contain a heap allocation for empty slices, // so the pointer isn't aligned enough for the KIND_VEC stashing to // work. - if vec.is_empty() { + if slice.is_empty() { return Bytes::new(); } - let slice = vec.into_boxed_slice(); let len = slice.len(); let ptr = Box::into_raw(slice) as *mut u8; diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 0914155b9..f0cae9990 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -994,3 +994,11 @@ fn bytes_put_bytes() { bytes.put_bytes(19, 2); assert_eq!([17, 19, 19], bytes.as_ref()); } + +#[test] +fn box_slice_empty() { + // See https://github.com/tokio-rs/bytes/issues/340 + let empty: Box<[u8]> = Default::default(); + let b = Bytes::from(empty); + assert!(b.is_empty()); +} From 55e296850d3de4563ef5b260b076e6c697391604 Mon Sep 17 00:00:00 2001 From: Christopher Hotchkiss Date: Tue, 24 Aug 2021 10:12:20 -0400 Subject: [PATCH 05/48] Clarifying actions of clear and truncate. (#508) --- src/bytes_mut.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index b754ed1fc..147484d53 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -380,6 +380,8 @@ impl BytesMut { /// If `len` is greater than the buffer's current length, this has no /// effect. /// + /// Existing underlying capacity is preserved. + /// /// The [`split_off`] method can emulate `truncate`, but this causes the /// excess bytes to be returned instead of dropped. /// @@ -402,7 +404,7 @@ impl BytesMut { } } - /// Clears the buffer, removing all data. + /// Clears the buffer, removing all data. Existing capacity is preserved. /// /// # Examples /// From ebc61e5af14cd9b436ba880cf19e849b05a04c29 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 25 Aug 2021 17:48:41 +0200 Subject: [PATCH 06/48] chore: prepare bytes v1.1.0 (#509) --- CHANGELOG.md | 21 +++++++++++++++++++++ Cargo.toml | 4 ++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99975bf89..636d36bbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# 1.1.0 (August 25, 2021) + +### Added + +- `BufMut::put_bytes(self, val, cnt)` (#487) +- Implement `From>` for `Bytes` (#504) + +### Changed + +- Override `put_slice` for `&mut [u8]` (#483) +- Panic on integer overflow in `Chain::remaining` (#482) +- Add inline tags to `UninitSlice` methods (#443) +- Override `copy_to_bytes` for Chain and Take (#481) +- Keep capacity when unsplit on empty other buf (#502) + +### Documented + +- Clarify `BufMut` allocation guarantees (#501) +- Clarify `BufMut::put_int` behavior (#486) +- Clarify actions of `clear` and `truncate`. (#508) + # 1.0.1 (January 11, 2021) ### Changed diff --git a/Cargo.toml b/Cargo.toml index 34d70f8f7..2b7e32b06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ name = "bytes" # When releasing to crates.io: # - Update CHANGELOG.md. -# - Create "v1.0.x" git tag. -version = "1.0.1" +# - Create "v1.x.y" git tag. +version = "1.1.0" license = "MIT" authors = [ "Carl Lerche ", From ba5c5c93affe9386dd052c5236cad83057f9a81d Mon Sep 17 00:00:00 2001 From: Noah Kennedy Date: Sun, 7 Nov 2021 10:23:12 -0600 Subject: [PATCH 07/48] Appease miri (#515) Rewrote the ledger in test_bytes_vec_alloc.rs to not piss off miri. The ledger is now a table within the allocator, which seems to satisfy miri. The old solution was to bundle an extra usize into the beginning of each allocation and then index past the start when deallocating data to get the size. --- .github/workflows/ci.yml | 2 +- tests/test_bytes_vec_alloc.rs | 104 +++++++++++++++++++++------------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00a4414eb..d41dd0dc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 - nightly: nightly-2021-04-13 + nightly: nightly-2021-11-05 defaults: run: diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index 418a9cd64..9d9823284 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -1,61 +1,87 @@ use std::alloc::{GlobalAlloc, Layout, System}; -use std::{mem, ptr}; +use std::ptr::null_mut; +use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use bytes::{Buf, Bytes}; #[global_allocator] -static LEDGER: Ledger = Ledger; +static LEDGER: Ledger = Ledger::new(); -struct Ledger; +const LEDGER_LENGTH: usize = 2048; -const USIZE_SIZE: usize = mem::size_of::(); +struct Ledger { + alloc_table: [(AtomicPtr, AtomicUsize); LEDGER_LENGTH], +} -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 +impl Ledger { + const fn new() -> Self { + const ELEM: (AtomicPtr, AtomicUsize) = + (AtomicPtr::new(null_mut()), AtomicUsize::new(0)); + let alloc_table = [ELEM; LEDGER_LENGTH]; + + Self { alloc_table } + } + + /// Iterate over our table until we find an open entry, then insert into said entry + fn insert(&self, ptr: *mut u8, size: usize) { + for (entry_ptr, entry_size) in self.alloc_table.iter() { + // SeqCst is good enough here, we don't care about perf, i just want to be correct! + if entry_ptr + .compare_exchange(null_mut(), ptr, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + entry_size.store(size, Ordering::SeqCst); + break; } - } 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() - ); + fn remove(&self, ptr: *mut u8) -> usize { + for (entry_ptr, entry_size) in self.alloc_table.iter() { + // set the value to be something that will never try and be deallocated, so that we + // don't have any chance of a race condition + // + // dont worry, LEDGER_LENGTH is really long to compensate for us not reclaiming space + if entry_ptr + .compare_exchange( + ptr, + usize::MAX as *mut u8, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { + return entry_size.load(Ordering::SeqCst); } + } - 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); + panic!("Couldn't find a matching entry for {:x?}", ptr); + } +} + +unsafe impl GlobalAlloc for Ledger { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let size = layout.size(); + let ptr = System.alloc(layout); + self.insert(ptr, size); + ptr + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let orig_size = self.remove(ptr); + + if orig_size != layout.size() { + panic!( + "bad dealloc: alloc size was {}, dealloc size is {}", + orig_size, + layout.size() + ); } else { System.dealloc(ptr, layout); } } } + #[test] fn test_bytes_advance() { let mut bytes = Bytes::from(vec![10, 20, 30]); From d946ef2e91651a4d73a04b48715ecf0cde6db75d Mon Sep 17 00:00:00 2001 From: Cyborus04 <87248184+Cyborus04@users.noreply.github.com> Date: Tue, 9 Nov 2021 05:41:40 -0500 Subject: [PATCH 08/48] `const`-ify `Bytes::len` and `Bytes::is_empty` (#514) --- src/bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index d0be0d276..c4fbd4a89 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -179,7 +179,7 @@ impl Bytes { /// assert_eq!(b.len(), 5); /// ``` #[inline] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.len } @@ -194,7 +194,7 @@ impl Bytes { /// assert!(b.is_empty()); /// ``` #[inline] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.len == 0 } From 68afb40df9ea2e61a93d0e26f6f8a9f231979452 Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Wed, 24 Nov 2021 17:20:21 +0800 Subject: [PATCH 09/48] Add `BytesMut::zeroed` (#517) --- src/bytes_mut.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 147484d53..f39b73ef8 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -8,6 +8,7 @@ use alloc::{ borrow::{Borrow, BorrowMut}, boxed::Box, string::String, + vec, vec::Vec, }; @@ -258,6 +259,22 @@ impl BytesMut { } } + /// Creates a new `BytesMut`, which is initialized with zero. + /// + /// # Examples + /// + /// ``` + /// use bytes::BytesMut; + /// + /// let zeros = BytesMut::zeroed(42); + /// + /// assert_eq!(zeros.len(), 42); + /// zeros.into_iter().for_each(|x| assert_eq!(x, 0)); + /// ``` + pub fn zeroed(len: usize) -> BytesMut { + BytesMut::from_vec(vec![0; len]) + } + /// Splits the bytes into two at the given index. /// /// Afterwards `self` contains elements `[0, at)`, and the returned From 0e3b2466f199375031006857164fbf70e3ea479f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 24 Jan 2022 08:58:05 +0000 Subject: [PATCH 10/48] Address various clippy warnings (#528) --- benches/buf.rs | 2 +- benches/bytes.rs | 1 + clippy.toml | 1 + src/bytes.rs | 10 +++++----- src/bytes_mut.rs | 22 +++++++++++----------- src/fmt/debug.rs | 6 +++--- tests/test_bytes.rs | 8 ++++---- tests/test_bytes_odd_alloc.rs | 3 +-- 8 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 clippy.toml diff --git a/benches/buf.rs b/benches/buf.rs index 6dc8516dd..616d18748 100644 --- a/benches/buf.rs +++ b/benches/buf.rs @@ -46,7 +46,7 @@ impl TestBuf { } impl Buf for TestBuf { fn remaining(&self) -> usize { - return self.buf.len() - self.pos; + self.buf.len() - self.pos } fn advance(&mut self, cnt: usize) { self.pos += cnt; diff --git a/benches/bytes.rs b/benches/bytes.rs index c5b84124f..61d1e832a 100644 --- a/benches/bytes.rs +++ b/benches/bytes.rs @@ -88,6 +88,7 @@ fn from_long_slice(b: &mut Bencher) { #[bench] fn slice_empty(b: &mut Bencher) { b.iter(|| { + // `clone` is to convert to ARC let b = Bytes::from(vec![17; 1024]).clone(); for i in 0..1000 { test::black_box(b.slice(i % 100..i % 100)); diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..53095b15d --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +msrv = "1.39" diff --git a/src/bytes.rs b/src/bytes.rs index c4fbd4a89..f3e4c5cba 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -262,7 +262,7 @@ impl Bytes { let mut ret = self.clone(); ret.len = end - begin; - ret.ptr = unsafe { ret.ptr.offset(begin as isize) }; + ret.ptr = unsafe { ret.ptr.add(begin) }; ret } @@ -501,7 +501,7 @@ impl Bytes { // should already be asserted, but debug assert for tests debug_assert!(self.len >= by, "internal: inc_start out of bounds"); self.len -= by; - self.ptr = self.ptr.offset(by as isize); + self.ptr = self.ptr.add(by); } } @@ -604,7 +604,7 @@ impl<'a> IntoIterator for &'a Bytes { type IntoIter = core::slice::Iter<'a, u8>; fn into_iter(self) -> Self::IntoIter { - self.as_slice().into_iter() + self.as_slice().iter() } } @@ -686,7 +686,7 @@ impl PartialOrd for str { impl PartialEq> for Bytes { fn eq(&self, other: &Vec) -> bool { - *self == &other[..] + *self == other[..] } } @@ -710,7 +710,7 @@ impl PartialOrd for Vec { impl PartialEq for Bytes { fn eq(&self, other: &String) -> bool { - *self == &other[..] + *self == other[..] } } diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index f39b73ef8..970ed9596 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -603,7 +603,7 @@ impl BytesMut { v.reserve(additional); // Update the info - self.ptr = vptr(v.as_mut_ptr().offset(off as isize)); + self.ptr = vptr(v.as_mut_ptr().add(off)); self.len = v.len() - off; self.cap = v.capacity() - off; } @@ -818,7 +818,7 @@ impl BytesMut { // Updating the start of the view is setting `ptr` to point to the // new start and updating the `len` field to reflect the new length // of the view. - self.ptr = vptr(self.ptr.as_ptr().offset(start as isize)); + self.ptr = vptr(self.ptr.as_ptr().add(start)); if self.len >= start { self.len -= start; @@ -842,7 +842,7 @@ impl BytesMut { return Ok(()); } - let ptr = unsafe { self.ptr.as_ptr().offset(self.len as isize) }; + let ptr = unsafe { self.ptr.as_ptr().add(self.len) }; if ptr == other.ptr.as_ptr() && self.kind() == KIND_ARC && other.kind() == KIND_ARC @@ -931,7 +931,7 @@ impl BytesMut { #[inline] fn uninit_slice(&mut self) -> &mut UninitSlice { unsafe { - let ptr = self.ptr.as_ptr().offset(self.len as isize); + let ptr = self.ptr.as_ptr().add(self.len); let len = self.cap - self.len; UninitSlice::from_raw_parts_mut(ptr, len) @@ -1178,7 +1178,7 @@ impl<'a> IntoIterator for &'a BytesMut { type IntoIter = core::slice::Iter<'a, u8>; fn into_iter(self) -> Self::IntoIter { - self.as_ref().into_iter() + self.as_ref().iter() } } @@ -1207,7 +1207,7 @@ impl<'a> Extend<&'a u8> for BytesMut { where T: IntoIterator, { - self.extend(iter.into_iter().map(|b| *b)) + self.extend(iter.into_iter().copied()) } } @@ -1219,7 +1219,7 @@ impl FromIterator for BytesMut { impl<'a> FromIterator<&'a u8> for BytesMut { fn from_iter>(into_iter: T) -> Self { - BytesMut::from_iter(into_iter.into_iter().map(|b| *b)) + BytesMut::from_iter(into_iter.into_iter().copied()) } } @@ -1409,7 +1409,7 @@ impl PartialOrd for str { impl PartialEq> for BytesMut { fn eq(&self, other: &Vec) -> bool { - *self == &other[..] + *self == other[..] } } @@ -1433,7 +1433,7 @@ impl PartialOrd for Vec { impl PartialEq for BytesMut { fn eq(&self, other: &String) -> bool { - *self == &other[..] + *self == other[..] } } @@ -1499,13 +1499,13 @@ impl PartialOrd for &str { impl PartialEq for Bytes { fn eq(&self, other: &BytesMut) -> bool { - &other[..] == &self[..] + other[..] == self[..] } } impl PartialEq for BytesMut { fn eq(&self, other: &Bytes) -> bool { - &other[..] == &self[..] + other[..] == self[..] } } diff --git a/src/fmt/debug.rs b/src/fmt/debug.rs index a8545514e..83de695dd 100644 --- a/src/fmt/debug.rs +++ b/src/fmt/debug.rs @@ -25,7 +25,7 @@ impl Debug for BytesRef<'_> { } else if b == b'\0' { write!(f, "\\0")?; // ASCII printable - } else if b >= 0x20 && b < 0x7f { + } else if (0x20..0x7f).contains(&b) { write!(f, "{}", b as char)?; } else { write!(f, "\\x{:02x}", b)?; @@ -38,12 +38,12 @@ impl Debug for BytesRef<'_> { impl Debug for Bytes { fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&BytesRef(&self.as_ref()), f) + 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) + Debug::fmt(&BytesRef(self.as_ref()), f) } } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index f0cae9990..3819b9f6f 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -4,8 +4,8 @@ use bytes::{Buf, BufMut, Bytes, BytesMut}; use std::usize; -const LONG: &'static [u8] = b"mary had a little lamb, little lamb, little lamb"; -const SHORT: &'static [u8] = b"hello world"; +const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb"; +const SHORT: &[u8] = b"hello world"; fn is_sync() {} fn is_send() {} @@ -874,7 +874,7 @@ fn from_iter_no_size_hint() { fn test_slice_ref(bytes: &Bytes, start: usize, end: usize, expected: &[u8]) { let slice = &(bytes.as_ref()[start..end]); - let sub = bytes.slice_ref(&slice); + let sub = bytes.slice_ref(slice); assert_eq!(&sub[..], expected); } @@ -894,7 +894,7 @@ fn slice_ref_empty() { let bytes = Bytes::from(&b""[..]); let slice = &(bytes.as_ref()[0..0]); - let sub = bytes.slice_ref(&slice); + let sub = bytes.slice_ref(slice); assert_eq!(&sub[..], b""); } diff --git a/tests/test_bytes_odd_alloc.rs b/tests/test_bytes_odd_alloc.rs index 04ba7c2f1..1c243cb81 100644 --- a/tests/test_bytes_odd_alloc.rs +++ b/tests/test_bytes_odd_alloc.rs @@ -24,8 +24,7 @@ unsafe impl GlobalAlloc for Odd { }; let ptr = System.alloc(new_layout); if !ptr.is_null() { - let ptr = ptr.offset(1); - ptr + ptr.offset(1) } else { ptr } From 131dae161fff368e8b6dbc8da0abf053dac5e523 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 24 Jan 2022 08:58:18 +0000 Subject: [PATCH 11/48] Implement `Extend` for `BytesMut` (#527) --- src/bytes_mut.rs | 11 +++++++++++ tests/test_bytes.rs | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 970ed9596..d43e0ef4f 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1211,6 +1211,17 @@ impl<'a> Extend<&'a u8> for BytesMut { } } +impl Extend for BytesMut { + fn extend(&mut self, iter: T) + where + T: IntoIterator, + { + for bytes in iter { + self.extend_from_slice(&bytes) + } + } +} + impl FromIterator for BytesMut { fn from_iter>(into_iter: T) -> Self { BytesMut::from_vec(Vec::from_iter(into_iter)) diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 3819b9f6f..860474a7a 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -544,6 +544,13 @@ fn extend_from_slice_mut() { } } +#[test] +fn extend_mut_from_bytes() { + let mut bytes = BytesMut::with_capacity(0); + bytes.extend([Bytes::from(LONG)]); + assert_eq!(*bytes, LONG[..]); +} + #[test] fn extend_mut_without_size_hint() { let mut bytes = BytesMut::with_capacity(0); From 88f5e12350db414c5beac65d488f634fe296223a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 9 Mar 2022 04:29:04 -0500 Subject: [PATCH 12/48] update Miri CI config (#534) --- ci/miri.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ci/miri.sh b/ci/miri.sh index 88d2b6a8c..b84d9665d 100755 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -1,11 +1,9 @@ #!/bin/bash set -e -MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) -echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" -rustup set profile minimal -rustup default "$MIRI_NIGHTLY" -rustup component add miri +rustup toolchain install nightly --component miri +rustup override set nightly +cargo miri setup cargo miri test cargo miri test --target mips64-unknown-linux-gnuabi64 From d4f5023383d233819e727d2b223493ba1c730211 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 17 Mar 2022 01:11:42 +1100 Subject: [PATCH 13/48] Optimize BytesMut::reserve: Reuse vec if possible (#529) * Optimize `BytesMut::reserve`: Reuse vec if possible If the `BytesMut` holds a unqiue reference to `KIND_ARC` while the capacity of the `Vec` is not big enough , reuse the existing `Vec` instead of allocating a new one. Signed-off-by: Jiahao XU --- src/bytes_mut.rs | 40 ++++++++++++++++++++++++++++------------ tests/test_bytes.rs | 2 +- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d43e0ef4f..868b955e2 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -644,21 +644,37 @@ impl BytesMut { self.ptr = vptr(ptr); self.cap = v.capacity(); + } else { + // calculate offset + let off = v.capacity() - self.cap; + + // 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"); + + // The vector capacity is not sufficient. The reserve request is + // asking for more than the initial buffer capacity. Allocate more + // than requested if `new_cap` is not much bigger than the current + // capacity. + // + // There are some situations, using `reserve_exact` that the + // buffer capacity could be below `original_capacity`, so do a + // check. + let double = v.capacity().checked_shl(1).unwrap_or(new_cap); + + new_cap = cmp::max(double, new_cap); - return; + // No space - allocate more + v.reserve(new_cap - v.len()); + + // Update the info + self.ptr = vptr(v.as_mut_ptr().add(off)); + self.cap = v.capacity() - off; } - // The vector capacity is not sufficient. The reserve request is - // asking for more than the initial buffer capacity. Allocate more - // than requested if `new_cap` is not much bigger than the current - // capacity. - // - // There are some situations, using `reserve_exact` that the - // buffer capacity could be below `original_capacity`, so do a - // check. - let double = v.capacity().checked_shl(1).unwrap_or(new_cap); - - new_cap = cmp::max(cmp::max(double, new_cap), original_capacity); + return; } else { new_cap = cmp::max(new_cap, original_capacity); } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 860474a7a..76f65132f 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -443,7 +443,7 @@ fn reserve_growth() { let _ = bytes.split(); bytes.reserve(65); - assert_eq!(bytes.capacity(), 128); + assert_eq!(bytes.capacity(), 117); } #[test] From e4c723697d49b5b6fa1ae92db18e9affe6c6744f Mon Sep 17 00:00:00 2001 From: Anthony Deschamps Date: Fri, 25 Mar 2022 05:55:13 -0400 Subject: [PATCH 14/48] docs: redraw layout diagram with box drawing characters. (#539) I find this diagram very helpful, but a little hard to distinguish between the boxes and the lines that connect them. This commit redraws the boxes with line drawing characters so that the boxes appear a little more solid, and stand out from the other lines. --- src/bytes.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index f3e4c5cba..753bb5c44 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -55,7 +55,7 @@ use crate::Buf; /// # Sharing /// /// `Bytes` contains a vtable, which allows implementations of `Bytes` to define -/// how sharing/cloneing is implemented in detail. +/// how sharing/cloning is implemented in detail. /// When `Bytes::clone()` is called, `Bytes` will call the vtable function for /// cloning the backing storage in order to share it behind between multiple /// `Bytes` instances. @@ -78,18 +78,18 @@ use crate::Buf; /// /// ```text /// -/// Arc ptrs +---------+ -/// ________________________ / | Bytes 2 | -/// / +---------+ -/// / +-----------+ | | -/// |_________/ | Bytes 1 | | | -/// | +-----------+ | | +/// Arc ptrs ┌─────────┐ +/// ________________________ / │ Bytes 2 │ +/// / └─────────┘ +/// / ┌───────────┐ | | +/// |_________/ │ Bytes 1 │ | | +/// | └───────────┘ | | /// | | | ___/ data | tail /// | data | tail |/ | /// v v v v -/// +-----+---------------------------------+-----+ -/// | Arc | | | | | -/// +-----+---------------------------------+-----+ +/// ┌─────┬─────┬───────────┬───────────────┬─────┐ +/// │ Arc │ │ │ │ │ +/// └─────┴─────┴───────────┴───────────────┴─────┘ /// ``` pub struct Bytes { ptr: *const u8, From 9e6edd18d297ec1b1bf9e01b1fce7a52eacdd8cc Mon Sep 17 00:00:00 2001 From: Evan Cameron Date: Wed, 6 Apr 2022 09:19:00 -0400 Subject: [PATCH 15/48] Clarify `BytesMut::unsplit` docs (#535) --- src/bytes_mut.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 868b955e2..d13cf05a9 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -733,10 +733,11 @@ impl 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 degenerates to + /// 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 + /// 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 From 724476982b35f094b59d160ecc02c042082ac604 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 6 Apr 2022 10:59:20 -0400 Subject: [PATCH 16/48] Fix aliasing in Clone by using a raw pointer (#523) Previously, this code produced a &mut[u8] and a Box<[u8]> to the shared allocation upon cloning it. If the underlying allocation were actually shared, such as through a &[u8] from the Deref impl, creating either of these types incorrectly asserted uniqueness of the allocation. This fixes the example in #522, but Miri still does not pass on this test suite with -Zmiri-tag-raw-pointers because Miri does not currently understand int to pointer casts. --- src/bytes.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 753bb5c44..24c213787 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -2,7 +2,13 @@ use core::iter::FromIterator; use core::ops::{Deref, RangeBounds}; use core::{cmp, fmt, hash, mem, ptr, slice, usize}; -use alloc::{borrow::Borrow, boxed::Box, string::String, vec::Vec}; +use alloc::{ + alloc::{dealloc, Layout}, + borrow::Borrow, + boxed::Box, + string::String, + vec::Vec, +}; use crate::buf::IntoIter; #[allow(unused)] @@ -941,11 +947,18 @@ unsafe fn rebuild_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) -> Bo // ===== impl SharedVtable ===== struct Shared { - // holds vec for drop, but otherwise doesnt access it - _vec: Vec, + // Holds arguments to dealloc upon Drop, but otherwise doesn't use them + buf: *mut u8, + cap: usize, ref_cnt: AtomicUsize, } +impl Drop for Shared { + fn drop(&mut self) { + unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) } + } +} + // 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. @@ -1006,9 +1019,9 @@ 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 vec = rebuild_boxed_slice(buf, offset, len).into_vec(); let shared = Box::new(Shared { - _vec: vec, + buf, + cap: (offset as usize - 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`. From 547a32033ec60edc143da33c6c0fbde9d845e91a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 15 Apr 2022 22:46:40 +0200 Subject: [PATCH 17/48] Add TSAN support (#541) --- src/bytes.rs | 7 +++++-- src/bytes_mut.rs | 7 +++++-- src/loom.rs | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 24c213787..1afab3355 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -13,7 +13,7 @@ use alloc::{ use crate::buf::IntoIter; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; -use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use crate::Buf; /// A cheaply cloneable and sliceable chunk of contiguous memory. @@ -1095,7 +1095,10 @@ unsafe fn release_shared(ptr: *mut Shared) { // > "acquire" operation before deleting the object. // // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - atomic::fence(Ordering::Acquire); + // + // Thread sanitizer does not support atomic fences. Use an atomic load + // instead. + (*ptr).ref_cnt.load(Ordering::Acquire); // Drop the data Box::from_raw(ptr); diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d13cf05a9..88d7f007c 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -16,7 +16,7 @@ use crate::buf::{IntoIter, UninitSlice}; use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; -use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use crate::{Buf, BufMut, Bytes}; /// A unique reference to a contiguous slice of memory. @@ -1288,7 +1288,10 @@ unsafe fn release_shared(ptr: *mut Shared) { // > "acquire" operation before deleting the object. // // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - atomic::fence(Ordering::Acquire); + // + // Thread sanitizer does not support atomic fences. Use an atomic load + // instead. + (*ptr).ref_count.load(Ordering::Acquire); // Drop the data Box::from_raw(ptr); diff --git a/src/loom.rs b/src/loom.rs index 1cae8812e..9e6b2d5e2 100644 --- a/src/loom.rs +++ b/src/loom.rs @@ -1,7 +1,7 @@ #[cfg(not(all(test, loom)))] pub(crate) mod sync { pub(crate) mod atomic { - pub(crate) use core::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering}; + pub(crate) use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; pub(crate) trait AtomicMut { fn with_mut(&mut self, f: F) -> R @@ -23,7 +23,7 @@ pub(crate) mod sync { #[cfg(all(test, loom))] pub(crate) mod sync { pub(crate) mod atomic { - pub(crate) use loom::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering}; + pub(crate) use loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; pub(crate) trait AtomicMut {} } From 8198f9e28ebbf59760af25748d18d6a43260edf3 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 16 Apr 2022 00:26:58 +0200 Subject: [PATCH 18/48] Make strict provenance compatible (#542) --- ci/miri.sh | 2 ++ src/bytes.rs | 46 ++++++++++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/ci/miri.sh b/ci/miri.sh index b84d9665d..0158756cd 100755 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -5,5 +5,7 @@ rustup toolchain install nightly --component miri rustup override set nightly cargo miri setup +export MIRIFLAGS="-Zmiri-strict-provenance" + cargo miri test cargo miri test --target mips64-unknown-linux-gnuabi64 diff --git a/src/bytes.rs b/src/bytes.rs index 1afab3355..0f2ee3a88 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -314,15 +314,15 @@ impl Bytes { assert!( sub_p >= bytes_p, "subset pointer ({:p}) is smaller than self pointer ({:p})", - sub_p as *const u8, - bytes_p as *const u8, + subset.as_ptr(), + self.as_ptr(), ); assert!( sub_p + sub_len <= bytes_p + bytes_len, "subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})", - bytes_p as *const u8, + self.as_ptr(), bytes_len, - sub_p as *const u8, + subset.as_ptr(), sub_len, ); @@ -821,18 +821,18 @@ impl From> for Bytes { let ptr = Box::into_raw(slice) as *mut u8; if ptr as usize & 0x1 == 0 { - let data = ptr as usize | KIND_VEC; + let data = ptr_map(ptr, |addr| addr | KIND_VEC); Bytes { ptr, len, - data: AtomicPtr::new(data as *mut _), + data: AtomicPtr::new(data.cast()), vtable: &PROMOTABLE_EVEN_VTABLE, } } else { Bytes { ptr, len, - data: AtomicPtr::new(ptr as *mut _), + data: AtomicPtr::new(ptr.cast()), vtable: &PROMOTABLE_ODD_VTABLE, } } @@ -889,10 +889,10 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize let kind = shared as usize & KIND_MASK; if kind == KIND_ARC { - shallow_clone_arc(shared as _, ptr, len) + shallow_clone_arc(shared.cast(), ptr, len) } else { debug_assert_eq!(kind, KIND_VEC); - let buf = (shared as usize & !KIND_MASK) as *mut u8; + let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK); shallow_clone_vec(data, shared, buf, ptr, len) } } @@ -903,11 +903,11 @@ unsafe fn promotable_even_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: us let kind = shared as usize & KIND_MASK; if kind == KIND_ARC { - release_shared(shared as *mut Shared); + release_shared(shared.cast()); } else { debug_assert_eq!(kind, KIND_VEC); - let buf = (shared as usize & !KIND_MASK) as *mut u8; - drop(rebuild_boxed_slice(buf, ptr, len)); + let buf = ptr_map(shared.cast(), |addr| addr & !KIND_MASK); + free_boxed_slice(buf, ptr, len); } }); } @@ -920,7 +920,7 @@ unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) 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) + shallow_clone_vec(data, shared, shared.cast(), ptr, len) } } @@ -930,18 +930,18 @@ unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usi let kind = shared as usize & KIND_MASK; if kind == KIND_ARC { - release_shared(shared as *mut Shared); + release_shared(shared.cast()); } else { debug_assert_eq!(kind, KIND_VEC); - drop(rebuild_boxed_slice(shared as *mut u8, ptr, len)); + free_boxed_slice(shared.cast(), ptr, len); } }); } -unsafe fn rebuild_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) -> Box<[u8]> { +unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) { let cap = (offset as usize - buf as usize) + len; - Box::from_raw(slice::from_raw_parts_mut(buf, cap)) + dealloc(buf, Layout::from_size_align(cap, 1).unwrap()) } // ===== impl SharedVtable ===== @@ -981,7 +981,7 @@ unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Byte unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { data.with_mut(|shared| { - release_shared(*shared as *mut Shared); + release_shared(shared.cast()); }); } @@ -1104,6 +1104,16 @@ unsafe fn release_shared(ptr: *mut Shared) { Box::from_raw(ptr); } +fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 +where + F: FnOnce(usize) -> usize, +{ + let old_addr = ptr as usize; + let new_addr = f(old_addr); + // this optimizes better than `ptr.wrapping_add(new_addr.wrapping_sub(old_addr))` + ptr.wrapping_sub(old_addr).wrapping_add(new_addr) +} + // compile-fails /// ```compile_fail From 0a2c43af8811fecf6fd08379f16571594bcbb738 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 28 Apr 2022 19:37:33 +1000 Subject: [PATCH 19/48] Fix bugs in `BytesMut::reserve_inner` (#544) --- src/bytes_mut.rs | 2 +- tests/test_bytes.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 88d7f007c..cc1a3ba0d 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -646,7 +646,7 @@ impl BytesMut { self.cap = v.capacity(); } else { // calculate offset - let off = v.capacity() - self.cap; + 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. diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 76f65132f..a1f0af80e 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -527,6 +527,25 @@ fn reserve_in_arc_nonunique_does_not_overallocate() { assert_eq!(2001, bytes.capacity()); } +/// This function tests `BytesMut::reserve_inner`, where `BytesMut` holds +/// a unique reference to the shared vector and decide to reuse it +/// by reallocating the `Vec`. +#[test] +fn reserve_shared_reuse() { + let mut bytes = BytesMut::with_capacity(1000); + bytes.put_slice(b"Hello, World!"); + drop(bytes.split()); + + bytes.put_slice(b"!123ex123,sadchELLO,_wORLD!"); + // Use split_off so that v.capacity() - self.cap != off + drop(bytes.split_off(9)); + assert_eq!(&*bytes, b"!123ex123"); + + bytes.reserve(2000); + assert_eq!(&*bytes, b"!123ex123"); + assert_eq!(bytes.capacity(), 2009); +} + #[test] fn extend_mut() { let mut bytes = BytesMut::with_capacity(0); From 89061c323861c4e9555881fef940836f1cd132cc Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 29 Apr 2022 19:47:34 +0200 Subject: [PATCH 20/48] Only avoid pointer casts when using miri --- src/bytes.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 0f2ee3a88..4e7a3e5bb 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1104,14 +1104,25 @@ unsafe fn release_shared(ptr: *mut Shared) { Box::from_raw(ptr); } +#[cfg(miri)] fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 where F: FnOnce(usize) -> usize, { let old_addr = ptr as usize; let new_addr = f(old_addr); - // this optimizes better than `ptr.wrapping_add(new_addr.wrapping_sub(old_addr))` - ptr.wrapping_sub(old_addr).wrapping_add(new_addr) + let diff = new_addr.wrapping_sub(old_addr); + ptr.wrapping_add(diff) +} + +#[cfg(not(miri))] +fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 +where + F: FnOnce(usize) -> usize, +{ + let old_addr = ptr as usize; + let new_addr = f(old_addr); + new_addr as *mut u8 } // compile-fails From b4b2c18c27cc0a33d68e7f0db29e0b6626fef6e8 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 29 Apr 2022 19:49:28 +0200 Subject: [PATCH 21/48] Revert accidental push directly to master This reverts commit 89061c323861c4e9555881fef940836f1cd132cc. Why am I even able to push to master? --- src/bytes.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 4e7a3e5bb..0f2ee3a88 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1104,25 +1104,14 @@ unsafe fn release_shared(ptr: *mut Shared) { Box::from_raw(ptr); } -#[cfg(miri)] fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 where F: FnOnce(usize) -> usize, { let old_addr = ptr as usize; let new_addr = f(old_addr); - let diff = new_addr.wrapping_sub(old_addr); - ptr.wrapping_add(diff) -} - -#[cfg(not(miri))] -fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 -where - F: FnOnce(usize) -> usize, -{ - let old_addr = ptr as usize; - let new_addr = f(old_addr); - new_addr as *mut u8 + // this optimizes better than `ptr.wrapping_add(new_addr.wrapping_sub(old_addr))` + ptr.wrapping_sub(old_addr).wrapping_add(new_addr) } // compile-fails From 716a0b189e3508dbab808e067a8bfb857401a744 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 29 Apr 2022 22:01:26 +0200 Subject: [PATCH 22/48] Only avoid pointer casts when using miri (#545) --- src/bytes.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 0f2ee3a88..576e90523 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1104,14 +1104,31 @@ unsafe fn release_shared(ptr: *mut Shared) { Box::from_raw(ptr); } +// Ideally we would always use this version of `ptr_map` since it is strict +// provenance compatible, but it results in worse codegen. We will however still +// use it on miri because it gives better diagnostics for people who test bytes +// code with miri. +// +// See https://github.com/tokio-rs/bytes/pull/545 for more info. +#[cfg(miri)] fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 where F: FnOnce(usize) -> usize, { let old_addr = ptr as usize; let new_addr = f(old_addr); - // this optimizes better than `ptr.wrapping_add(new_addr.wrapping_sub(old_addr))` - ptr.wrapping_sub(old_addr).wrapping_add(new_addr) + let diff = new_addr.wrapping_sub(old_addr); + ptr.wrapping_add(diff) +} + +#[cfg(not(miri))] +fn ptr_map(ptr: *mut u8, f: F) -> *mut u8 +where + F: FnOnce(usize) -> usize, +{ + let old_addr = ptr as usize; + let new_addr = f(old_addr); + new_addr as *mut u8 } // compile-fails From 0ce4fe3c91f0d008674869348ffd804b78846eca Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 1 May 2022 21:50:55 +0900 Subject: [PATCH 23/48] Update actions/checkout action to v3 (#546) --- .github/workflows/ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d41dd0dc6..e3c706f40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: name: rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable && rustup default stable - name: Check formatting @@ -35,7 +35,7 @@ jobs: # name: clippy # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v2 + # - uses: actions/checkout@v3 # - name: Apply clippy lints # run: cargo clippy --all-features @@ -48,7 +48,7 @@ jobs: name: minrust runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update 1.39.0 && rustup default 1.39.0 - name: Check @@ -65,7 +65,7 @@ jobs: - windows-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update stable --no-self-update && rustup default stable @@ -77,7 +77,7 @@ jobs: name: nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Test @@ -96,7 +96,7 @@ jobs: - wasm32-unknown-unknown runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable && rustup default stable - name: cross build --target ${{ matrix.target }} @@ -116,7 +116,7 @@ jobs: name: tsan runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Install rust-src @@ -127,7 +127,7 @@ jobs: name: miri runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Miri run: ci/miri.sh @@ -136,7 +136,7 @@ jobs: name: loom runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Loom tests @@ -155,7 +155,7 @@ jobs: - loom runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Rust run: rustup update stable && rustup default stable - name: Build documentation From b8d27c016f53f0c1fea920223bc5a92f329d47df Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Tue, 10 May 2022 05:22:19 -0400 Subject: [PATCH 24/48] Add `UninitSlice::as_uninit_slice_mut()` (#548) This adds an unsafe method to convert a `&mut UninitSlice` into a `&mut [MaybeUninit]`. This method is unsafe because some of the bytes in the slice may be initialized, and the caller should not overwrite them with uninitialized bytes. This came about when auditing [tokio-util's udp frame], where they want to pass the unitialized portion of a `BytesMut` to [ReadBuf::uninit]. They need to do this unsafe pointer casting in a few places, which complicates audits. This method lets us document the safety invariants the caller needs to maintain when doing this conversion. [tokio-util's udp frame]: https://github.com/tokio-rs/tokio/blob/master/tokio-util/src/udp/frame.rs#L87 [ReadBuf::uninit]: https://docs.rs/tokio/latest/tokio/io/struct.ReadBuf.html#method.uninit --- src/buf/uninit_slice.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index fb67c0afd..a6c9ead64 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -124,6 +124,32 @@ impl UninitSlice { self.0.as_mut_ptr() as *mut _ } + /// Return a `&mut [MaybeUninit]` to this slice's buffer. + /// + /// # Safety + /// + /// The caller **must not** read from the referenced memory and **must not** write + /// **uninitialized** bytes to the slice either. This is because `BufMut` implementation + /// that created the `UninitSlice` knows which parts are initialized. Writing uninitalized + /// bytes to the slice may cause the `BufMut` to read those bytes and trigger undefined + /// behavior. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut data = [0, 1, 2]; + /// let mut slice = &mut data[..]; + /// unsafe { + /// let uninit_slice = BufMut::chunk_mut(&mut slice).as_uninit_slice_mut(); + /// }; + /// ``` + #[inline] + pub unsafe fn as_uninit_slice_mut<'a>(&'a mut self) -> &'a mut [MaybeUninit] { + &mut *(self as *mut _ as *mut [MaybeUninit]) + } + /// Returns the number of bytes in the slice. /// /// # Examples From 3536017ccfc5cf2d837a5cee8262b1de6c3eafc7 Mon Sep 17 00:00:00 2001 From: Zettroke Date: Sat, 11 Jun 2022 07:12:53 +0300 Subject: [PATCH 25/48] Fix chain remaining_mut(), allowing to chain growing buffer (#488) --- src/buf/buf_mut.rs | 4 ++++ src/buf/chain.rs | 3 +-- tests/test_chain.rs | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 4c2bd2cce..7f67f7b00 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -56,6 +56,10 @@ pub unsafe trait BufMut { /// Implementations of `remaining_mut` should ensure that the return value /// does not change unless a call is made to `advance_mut` or any other /// function that is documented to change the `BufMut`'s current position. + /// + /// # Note + /// + /// `remaining_mut` may return value smaller than actual available space. fn remaining_mut(&self) -> usize; /// Advance the internal cursor of the BufMut diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 9ce5f23aa..78979a123 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -198,8 +198,7 @@ where fn remaining_mut(&self) -> usize { self.a .remaining_mut() - .checked_add(self.b.remaining_mut()) - .unwrap() + .saturating_add(self.b.remaining_mut()) } fn chunk_mut(&mut self) -> &mut UninitSlice { diff --git a/tests/test_chain.rs b/tests/test_chain.rs index affaf7a9e..cfda6b8dc 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -133,6 +133,28 @@ fn vectored_read() { } } +#[test] +fn chain_growing_buffer() { + let mut buff = [' ' as u8; 10]; + let mut vec = b"wassup".to_vec(); + + let mut chained = (&mut buff[..]).chain_mut(&mut vec).chain_mut(Vec::new()); // Required for potential overflow because remaining_mut for Vec is isize::MAX - vec.len(), but for chain_mut is usize::MAX + + chained.put_slice(b"hey there123123"); + + assert_eq!(&buff, b"hey there1"); + assert_eq!(&vec, b"wassup23123"); +} + +#[test] +fn chain_overflow_remaining_mut() { + let mut chained = Vec::::new().chain_mut(Vec::new()).chain_mut(Vec::new()); + + assert_eq!(chained.remaining_mut(), usize::MAX); + chained.put_slice(&[0; 256]); + assert_eq!(chained.remaining_mut(), usize::MAX); +} + #[test] fn chain_get_bytes() { let mut ab = Bytes::copy_from_slice(b"ab"); From 28a1eab1e0531a0e7b9624e474a8c06dfd5c8072 Mon Sep 17 00:00:00 2001 From: Lucio Franco Date: Thu, 23 Jun 2022 12:16:03 -0400 Subject: [PATCH 26/48] chore: Fix unused warnings (#551) --- tests/test_bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index a1f0af80e..68536770d 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -411,8 +411,8 @@ fn freeze_after_split_off() { fn fns_defined_for_bytes_mut() { let mut bytes = BytesMut::from(&b"hello world"[..]); - bytes.as_ptr(); - bytes.as_mut_ptr(); + let _ = bytes.as_ptr(); + let _ = bytes.as_mut_ptr(); // Iterator let v: Vec = bytes.as_ref().iter().cloned().collect(); From f514bd38dac85695e9053d990b251643e9e4ef92 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 9 Jul 2022 21:54:34 +0200 Subject: [PATCH 27/48] miri: don't use int2ptr casts for invalid pointers (#553) --- src/bytes_mut.rs | 30 +++++++++++++++++++++--------- tests/test_bytes_vec_alloc.rs | 11 ++++++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index cc1a3ba0d..4f9a8851c 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -253,7 +253,7 @@ impl BytesMut { let ptr = self.ptr.as_ptr(); let len = self.len; - let data = AtomicPtr::new(self.data as _); + let data = AtomicPtr::new(self.data.cast()); mem::forget(self); unsafe { Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) } } @@ -613,7 +613,7 @@ impl BytesMut { } debug_assert_eq!(kind, KIND_ARC); - let shared: *mut Shared = self.data as _; + let shared: *mut Shared = self.data; // Reserving involves abandoning the currently shared buffer and // allocating a new vector with the requested capacity. @@ -692,7 +692,7 @@ impl BytesMut { // Update self let data = (original_capacity_repr << ORIGINAL_CAPACITY_OFFSET) | KIND_VEC; - self.data = data as _; + self.data = invalid_ptr(data); self.ptr = vptr(v.as_mut_ptr()); self.len = v.len(); self.cap = v.capacity(); @@ -723,7 +723,7 @@ impl BytesMut { // Reserved above debug_assert!(dst.len() >= cnt); - ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr() as *mut u8, cnt); + ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr(), cnt); } unsafe { @@ -788,7 +788,7 @@ impl BytesMut { ptr, len, cap, - data: data as *mut _, + data: invalid_ptr(data), } } @@ -909,7 +909,7 @@ impl BytesMut { // always succeed. debug_assert_eq!(shared as usize & KIND_MASK, KIND_ARC); - self.data = shared as _; + self.data = shared; } /// Makes an exact shallow clone of `self`. @@ -942,7 +942,7 @@ impl BytesMut { debug_assert_eq!(self.kind(), KIND_VEC); debug_assert!(pos <= MAX_VEC_POS); - self.data = ((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)) as *mut _; + self.data = invalid_ptr((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)); } #[inline] @@ -968,7 +968,7 @@ impl Drop for BytesMut { let _ = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off); } } else if kind == KIND_ARC { - unsafe { release_shared(self.data as _) }; + unsafe { release_shared(self.data) }; } } } @@ -1549,6 +1549,18 @@ fn vptr(ptr: *mut u8) -> NonNull { } } +/// Returns a dangling pointer with the given address. This is used to store +/// integer data in pointer fields. +/// +/// It is equivalent to `addr as *mut T`, but this fails on miri when strict +/// provenance checking is enabled. +#[inline] +fn invalid_ptr(addr: usize) -> *mut T { + let ptr = core::ptr::null_mut::().wrapping_add(addr); + debug_assert_eq!(ptr as usize, addr); + ptr.cast::() +} + unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize) -> Vec { let ptr = ptr.offset(-(off as isize)); len += off; @@ -1568,7 +1580,7 @@ unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By let shared = data.load(Ordering::Relaxed) as *mut Shared; increment_shared(shared); - let data = AtomicPtr::new(shared as _); + let data = AtomicPtr::new(shared as *mut ()); Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) } diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index 9d9823284..752009f38 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -45,7 +45,7 @@ impl Ledger { if entry_ptr .compare_exchange( ptr, - usize::MAX as *mut u8, + invalid_ptr(usize::MAX), Ordering::SeqCst, Ordering::SeqCst, ) @@ -103,3 +103,12 @@ fn test_bytes_truncate_and_advance() { bytes.advance(1); drop(bytes); } + +/// Returns a dangling pointer with the given address. This is used to store +/// integer data in pointer fields. +#[inline] +fn invalid_ptr(addr: usize) -> *mut T { + let ptr = std::ptr::null_mut::().wrapping_add(addr); + debug_assert_eq!(ptr as usize, addr); + ptr.cast::() +} From 068ed41bc02c21fe0a0a4d8e95af8a4668276f5d Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Sun, 10 Jul 2022 20:44:29 +1000 Subject: [PATCH 28/48] Add conversion from BytesMut to Vec (#543) --- src/bytes_mut.rs | 37 +++++++++++++++++++++++++++++++++++++ tests/test_bytes.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 4f9a8851c..65f97b46e 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1540,6 +1540,43 @@ impl PartialEq for BytesMut { } } +impl From for Vec { + fn from(mut bytes: BytesMut) -> Self { + let kind = bytes.kind(); + + let mut vec = if kind == KIND_VEC { + unsafe { + let (off, _) = bytes.get_vec_pos(); + rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off) + } + } else if kind == KIND_ARC { + let shared = unsafe { &mut *(bytes.data as *mut Shared) }; + if shared.is_unique() { + let vec = mem::replace(&mut shared.vec, Vec::new()); + + unsafe { release_shared(shared) }; + + vec + } else { + return bytes.deref().into(); + } + } else { + return bytes.deref().into(); + }; + + let len = bytes.len; + + unsafe { + ptr::copy(bytes.ptr.as_ptr(), vec.as_mut_ptr(), len); + vec.set_len(len); + } + + mem::forget(bytes); + + vec + } +} + #[inline] fn vptr(ptr: *mut u8) -> NonNull { if cfg!(debug_assertions) { diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 68536770d..ef7512b77 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1028,3 +1028,40 @@ fn box_slice_empty() { let b = Bytes::from(empty); assert!(b.is_empty()); } + +#[test] +fn bytes_into_vec() { + // Test kind == KIND_VEC + let content = b"helloworld"; + + let mut bytes = BytesMut::new(); + bytes.put_slice(content); + + let vec: Vec = bytes.into(); + assert_eq!(&vec, content); + + // Test kind == KIND_ARC, shared.is_unique() == True + let mut bytes = BytesMut::new(); + bytes.put_slice(b"abcdewe23"); + bytes.put_slice(content); + + // Overwrite the bytes to make sure only one reference to the underlying + // Vec exists. + bytes = bytes.split_off(9); + + let vec: Vec = bytes.into(); + assert_eq!(&vec, content); + + // Test kind == KIND_ARC, shared.is_unique() == False + let prefix = b"abcdewe23"; + + let mut bytes = BytesMut::new(); + bytes.put_slice(prefix); + bytes.put_slice(content); + + let vec: Vec = bytes.split_off(prefix.len()).into(); + assert_eq!(&vec, content); + + let vec: Vec = bytes.into(); + assert_eq!(&vec, prefix); +} From 10d1f6ec5c4e878aad9d13140d3479b3ea78ffcf Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Wed, 13 Jul 2022 16:59:54 +1000 Subject: [PATCH 29/48] Fix: `From fo Vec` implementation (#554) Signed-off-by: Jiahao XU --- src/bytes_mut.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 65f97b46e..25f0eccc2 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1550,18 +1550,19 @@ impl From for Vec { rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off) } } else if kind == KIND_ARC { - let shared = unsafe { &mut *(bytes.data as *mut Shared) }; - if shared.is_unique() { - let vec = mem::replace(&mut shared.vec, Vec::new()); + let shared = bytes.data as *mut Shared; + + if unsafe { (*shared).is_unique() } { + let vec = mem::replace(unsafe { &mut (*shared).vec }, Vec::new()); unsafe { release_shared(shared) }; vec } else { - return bytes.deref().into(); + return bytes.deref().to_vec(); } } else { - return bytes.deref().into(); + return bytes.deref().to_vec(); }; let len = bytes.len; From cd188cbd67c073128fdc339be8d5d67436d76f36 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Wed, 13 Jul 2022 17:04:23 +1000 Subject: [PATCH 30/48] Add conversion from Bytes to Vec (#547) Signed-off-by: Jiahao XU Co-authored-by: Alice Ryhl --- src/bytes.rs | 89 +++++++++++++++++++++++++++++++++++ src/bytes_mut.rs | 23 +++++++++ tests/test_bytes.rs | 70 +++++++++++++++++++++++++++ tests/test_bytes_odd_alloc.rs | 29 ++++++++++++ tests/test_bytes_vec_alloc.rs | 29 ++++++++++++ 5 files changed, 240 insertions(+) diff --git a/src/bytes.rs b/src/bytes.rs index 576e90523..948b10e57 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -109,6 +109,10 @@ pub(crate) struct Vtable { /// fn(data, ptr, len) pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Bytes, /// fn(data, ptr, len) + /// + /// takes `Bytes` to value + pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec, + /// fn(data, ptr, len) pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize), } @@ -845,6 +849,13 @@ impl From for Bytes { } } +impl From for Vec { + fn from(bytes: Bytes) -> Vec { + let bytes = mem::ManuallyDrop::new(bytes); + unsafe { (bytes.vtable.to_vec)(&bytes.data, bytes.ptr, bytes.len) } + } +} + // ===== impl Vtable ===== impl fmt::Debug for Vtable { @@ -860,6 +871,7 @@ impl fmt::Debug for Vtable { const STATIC_VTABLE: Vtable = Vtable { clone: static_clone, + to_vec: static_to_vec, drop: static_drop, }; @@ -868,6 +880,11 @@ 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 { + let slice = slice::from_raw_parts(ptr, len); + slice.to_vec() +} + unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { // nothing to drop for &'static [u8] } @@ -876,11 +893,13 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable { clone: promotable_even_clone, + to_vec: promotable_even_to_vec, drop: promotable_even_drop, }; static PROMOTABLE_ODD_VTABLE: Vtable = Vtable { clone: promotable_odd_clone, + to_vec: promotable_odd_to_vec, drop: promotable_odd_drop, }; @@ -897,6 +916,38 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize } } +unsafe fn promotable_to_vec( + data: &AtomicPtr<()>, + ptr: *const u8, + len: usize, + f: fn(*mut ()) -> *mut u8, +) -> Vec { + let shared = data.load(Ordering::Acquire); + let kind = shared as usize & KIND_MASK; + + if kind == KIND_ARC { + shared_to_vec_impl(shared.cast(), ptr, len) + } else { + // If Bytes holds a Vec, then the offset must be 0. + debug_assert_eq!(kind, KIND_VEC); + + let buf = f(shared); + + let cap = (ptr as usize - buf as usize) + len; + + // Copy back buffer + ptr::copy(ptr, buf, len); + + Vec::from_raw_parts(buf, len, cap) + } +} + +unsafe fn promotable_even_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { + promotable_to_vec(data, 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; @@ -924,6 +975,10 @@ 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 { + promotable_to_vec(data, 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; @@ -967,6 +1022,7 @@ const _: [(); 0 - mem::align_of::() % 2] = []; // Assert that the alignm static SHARED_VTABLE: Vtable = Vtable { clone: shared_clone, + to_vec: shared_to_vec, drop: shared_drop, }; @@ -979,6 +1035,39 @@ unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Byte shallow_clone_arc(shared as _, ptr, len) } +unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> Vec { + // Check that the ref_cnt is 1 (unique). + // + // If it is unique, then it is set to 0 with AcqRel fence for the same + // reason in release_shared. + // + // Otherwise, we take the other branch and call release_shared. + if (*shared) + .ref_cnt + .compare_exchange(1, 0, Ordering::AcqRel, Ordering::Relaxed) + .is_ok() + { + let buf = (*shared).buf; + let cap = (*shared).cap; + + // Deallocate Shared + drop(Box::from_raw(shared as *mut mem::ManuallyDrop)); + + // Copy back buffer + ptr::copy(ptr, buf, len); + + Vec::from_raw_parts(buf, len, cap) + } else { + let v = slice::from_raw_parts(ptr, len).to_vec(); + release_shared(shared); + v + } +} + +unsafe fn shared_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { + shared_to_vec_impl(data.load(Ordering::Relaxed).cast(), ptr, len) +} + unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { data.with_mut(|shared| { release_shared(shared.cast()); diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 25f0eccc2..3026f0982 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1611,6 +1611,7 @@ unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize) static SHARED_VTABLE: Vtable = Vtable { clone: shared_v_clone, + to_vec: shared_v_to_vec, drop: shared_v_drop, }; @@ -1622,6 +1623,28 @@ 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 { + let shared: *mut Shared = data.load(Ordering::Relaxed).cast(); + + if (*shared).is_unique() { + let shared = &mut *shared; + + // Drop shared + let mut vec = mem::replace(&mut shared.vec, Vec::new()); + release_shared(shared); + + // Copy back buffer + ptr::copy(ptr, vec.as_mut_ptr(), len); + vec.set_len(len); + + vec + } else { + let v = slice::from_raw_parts(ptr, len).to_vec(); + release_shared(shared); + v + } +} + unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { data.with_mut(|shared| { release_shared(*shared as *mut Shared); diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index ef7512b77..6bf01d67d 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1065,3 +1065,73 @@ fn bytes_into_vec() { let vec: Vec = bytes.into(); assert_eq!(&vec, prefix); } + +#[test] +fn test_bytes_into_vec() { + // Test STATIC_VTABLE.to_vec + let bs = b"1b23exfcz3r"; + let vec: Vec = Bytes::from_static(bs).into(); + assert_eq!(&*vec, bs); + + // Test bytes_mut.SHARED_VTABLE.to_vec impl + eprintln!("1"); + let mut bytes_mut: BytesMut = bs[..].into(); + + // Set kind to KIND_ARC so that after freeze, Bytes will use bytes_mut.SHARED_VTABLE + eprintln!("2"); + drop(bytes_mut.split_off(bs.len())); + + eprintln!("3"); + let b1 = bytes_mut.freeze(); + eprintln!("4"); + let b2 = b1.clone(); + + eprintln!("{:#?}", (&*b1).as_ptr()); + + // shared.is_unique() = False + eprintln!("5"); + assert_eq!(&*Vec::from(b2), bs); + + // shared.is_unique() = True + eprintln!("6"); + assert_eq!(&*Vec::from(b1), bs); + + // Test bytes_mut.SHARED_VTABLE.to_vec impl where offset != 0 + let mut bytes_mut1: BytesMut = bs[..].into(); + let bytes_mut2 = bytes_mut1.split_off(9); + + let b1 = bytes_mut1.freeze(); + let b2 = bytes_mut2.freeze(); + + assert_eq!(Vec::from(b2), bs[9..]); + assert_eq!(Vec::from(b1), bs[..9]); +} + +#[test] +fn test_bytes_into_vec_promotable_even() { + let vec = vec![33u8; 1024]; + + // Test cases where kind == KIND_VEC + let b1 = Bytes::from(vec.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 1 + let b1 = Bytes::from(vec.clone()); + drop(b1.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 2 + let b1 = Bytes::from(vec.clone()); + let b2 = b1.clone(); + assert_eq!(Vec::from(b1), vec); + + // Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 + assert_eq!(Vec::from(b2), vec); + + // Test cases where offset != 0 + let mut b1 = Bytes::from(vec.clone()); + let b2 = b1.split_off(20); + + assert_eq!(Vec::from(b2), vec[20..]); + assert_eq!(Vec::from(b1), vec[..20]); +} diff --git a/tests/test_bytes_odd_alloc.rs b/tests/test_bytes_odd_alloc.rs index 1c243cb81..27ed87736 100644 --- a/tests/test_bytes_odd_alloc.rs +++ b/tests/test_bytes_odd_alloc.rs @@ -66,3 +66,32 @@ fn test_bytes_clone_drop() { let b1 = Bytes::from(vec); let _b2 = b1.clone(); } + +#[test] +fn test_bytes_into_vec() { + let vec = vec![33u8; 1024]; + + // Test cases where kind == KIND_VEC + let b1 = Bytes::from(vec.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 1 + let b1 = Bytes::from(vec.clone()); + drop(b1.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 2 + let b1 = Bytes::from(vec.clone()); + let b2 = b1.clone(); + assert_eq!(Vec::from(b1), vec); + + // Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 + assert_eq!(Vec::from(b2), vec); + + // Test cases where offset != 0 + let mut b1 = Bytes::from(vec.clone()); + let b2 = b1.split_off(20); + + assert_eq!(Vec::from(b2), vec[20..]); + assert_eq!(Vec::from(b1), vec[..20]); +} diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index 752009f38..107e56e58 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -112,3 +112,32 @@ fn invalid_ptr(addr: usize) -> *mut T { debug_assert_eq!(ptr as usize, addr); ptr.cast::() } + +#[test] +fn test_bytes_into_vec() { + let vec = vec![33u8; 1024]; + + // Test cases where kind == KIND_VEC + let b1 = Bytes::from(vec.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 1 + let b1 = Bytes::from(vec.clone()); + drop(b1.clone()); + assert_eq!(Vec::from(b1), vec); + + // Test cases where kind == KIND_ARC, ref_cnt == 2 + let b1 = Bytes::from(vec.clone()); + let b2 = b1.clone(); + assert_eq!(Vec::from(b1), vec); + + // Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 + assert_eq!(Vec::from(b2), vec); + + // Test cases where offset != 0 + let mut b1 = Bytes::from(vec.clone()); + let b2 = b1.split_off(20); + + assert_eq!(Vec::from(b2), vec[20..]); + assert_eq!(Vec::from(b1), vec[..20]); +} From 7553a67be2b9d7ef2ac42dbcd38fd8365ee64cb5 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Tue, 19 Jul 2022 21:17:53 +1000 Subject: [PATCH 31/48] Fix amortized asymptotics of `BytesMut` (#555) Signed-off-by: Jiahao XU Co-authored-by: Frank Steffahn --- src/bytes.rs | 2 +- src/bytes_mut.rs | 80 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 948b10e57..f8d3ce319 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1190,7 +1190,7 @@ unsafe fn release_shared(ptr: *mut Shared) { (*ptr).ref_cnt.load(Ordering::Acquire); // Drop the data - Box::from_raw(ptr); + drop(Box::from_raw(ptr)); } // Ideally we would always use this version of `ptr_map` since it is strict diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 3026f0982..99e849716 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -511,11 +511,20 @@ impl BytesMut { /// reallocations. A call to `reserve` may result in an allocation. /// /// Before allocating new buffer space, the function will attempt to reclaim - /// space in the existing buffer. If the current handle references a small - /// view in the original buffer and all other handles have been dropped, - /// and the requested capacity is less than or equal to the existing - /// buffer's capacity, then the current view will be copied to the front of - /// the buffer and the handle will take ownership of the full buffer. + /// space in the existing buffer. If the current handle references a view + /// into a larger original buffer, and all other handles referencing part + /// of the same original buffer have been dropped, then the current view + /// can be copied/shifted to the front of the buffer and the handle can take + /// ownership of the full buffer, provided that the full buffer is large + /// enough to fit the requested additional capacity. + /// + /// This optimization will only happen if shifting the data from the current + /// view to the front of the buffer is not too expensive in terms of the + /// (amortized) time required. The precise condition is subject to change; + /// as of now, the length of the data being shifted needs to be at least as + /// large as the distance that it's shifted by. If the current view is empty + /// and the original buffer is large enough to fit the requested additional + /// capacity, then reallocations will never happen. /// /// # Examples /// @@ -579,17 +588,34 @@ impl BytesMut { // space. // // Otherwise, since backed by a vector, use `Vec::reserve` + // + // We need to make sure that this optimization does not kill the + // amortized runtimes of BytesMut's operations. unsafe { let (off, prev) = self.get_vec_pos(); // Only reuse space if we can satisfy the requested additional space. - if self.capacity() - self.len() + off >= additional { - // There's space - reuse it + // + // Also check if the value of `off` suggests that enough bytes + // have been read to account for the overhead of shifting all + // the data (in an amortized analysis). + // Hence the condition `off >= self.len()`. + // + // This condition also already implies that the buffer is going + // to be (at least) half-empty in the end; so we do not break + // the (amortized) runtime with future resizes of the underlying + // `Vec`. + // + // [For more details check issue #524, and PR #525.] + if self.capacity() - self.len() + off >= additional && off >= self.len() { + // There's enough space, and it's not too much overhead: + // reuse the space! // // Just move the pointer back to the start after copying // data back. let base_ptr = self.ptr.as_ptr().offset(-(off as isize)); - ptr::copy(self.ptr.as_ptr(), base_ptr, self.len); + // Since `off >= self.len()`, the two regions don't overlap. + ptr::copy_nonoverlapping(self.ptr.as_ptr(), base_ptr, self.len); self.ptr = vptr(base_ptr); self.set_vec_pos(0, prev); @@ -597,7 +623,8 @@ impl BytesMut { // can gain capacity back. self.cap += off; } else { - // No space - allocate more + // Not enough space, or reusing might be too much overhead: + // allocate more space! let mut v = ManuallyDrop::new(rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off)); v.reserve(additional); @@ -636,11 +663,19 @@ impl BytesMut { // sure that the vector has enough capacity. let v = &mut (*shared).vec; - if v.capacity() >= new_cap { - // The capacity is sufficient, reclaim the buffer - let ptr = v.as_mut_ptr(); + let v_capacity = v.capacity(); + let ptr = v.as_mut_ptr(); + + let offset = offset_from(self.ptr.as_ptr(), ptr); - ptr::copy(self.ptr.as_ptr(), ptr, len); + // Compare the condition in the `kind == KIND_VEC` case above + // for more details. + if v_capacity >= new_cap && offset >= len { + // The capacity is sufficient, and copying is not too much + // overhead: reclaim the buffer! + + // `offset >= len` means: no overlap + ptr::copy_nonoverlapping(self.ptr.as_ptr(), ptr, len); self.ptr = vptr(ptr); self.cap = v.capacity(); @@ -1294,7 +1329,7 @@ unsafe fn release_shared(ptr: *mut Shared) { (*ptr).ref_count.load(Ordering::Acquire); // Drop the data - Box::from_raw(ptr); + drop(Box::from_raw(ptr)); } impl Shared { @@ -1599,6 +1634,23 @@ fn invalid_ptr(addr: usize) -> *mut T { ptr.cast::() } +/// Precondition: dst >= original +/// +/// The following line is equivalent to: +/// +/// ```rust,ignore +/// self.ptr.as_ptr().offset_from(ptr) as usize; +/// ``` +/// +/// But due to min rust is 1.39 and it is only stablised +/// in 1.47, we cannot use it. +#[inline] +fn offset_from(dst: *mut u8, original: *mut u8) -> usize { + debug_assert!(dst >= original); + + dst as usize - original as usize +} + unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize) -> Vec { let ptr = ptr.offset(-(off as isize)); len += off; From 38fd42acbaced11ff19f0a4ca2af44a308af5063 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 19 Jul 2022 13:39:58 +0200 Subject: [PATCH 32/48] chore: prepare bytes v1.2.0 (#556) --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 636d36bbd..5ea40979e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# 1.2.0 (July 19, 2022) + +### Added + +- Add `BytesMut::zeroed` (#517) +- Implement `Extend` for `BytesMut` (#527) +- Add conversion from `BytesMut` to `Vec` (#543, #554) +- Add conversion from `Bytes` to `Vec` (#547) +- Add `UninitSlice::as_uninit_slice_mut()` (#548) +- Add const to `Bytes::{len,is_empty}` (#514) + +### Changed + +- Reuse vector in `BytesMut::reserve` (#539, #544) + +### Fixed + +- Make miri happy (#515, #523, #542, #545, #553) +- Make tsan happy (#541) +- Fix `remaining_mut()` on chain (#488) +- Fix amortized asymptotics of `BytesMut` (#555) + +### Documented + +- Redraw layout diagram with box drawing characters (#539) +- Clarify `BytesMut::unsplit` docs (#535) + # 1.1.0 (August 25, 2021) ### Added diff --git a/Cargo.toml b/Cargo.toml index 2b7e32b06..c3396e99a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "bytes" # When releasing to crates.io: # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. -version = "1.1.0" +version = "1.2.0" license = "MIT" authors = [ "Carl Lerche ", From d6e1999d978a688625441348a81504ccab669aed Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Sat, 30 Jul 2022 02:16:32 -0700 Subject: [PATCH 33/48] Fix reserve over allocating underlying buffer (#560) Fixes calls to `reserve` when the underlying shared buffer was already big enough to fit the requested capacity. Previously a new even larger buffer was created anyways. This could eventually lead to an OOM condition. --- src/bytes_mut.rs | 5 ++++- tests/test_bytes.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 99e849716..2282bdc48 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -670,7 +670,10 @@ impl BytesMut { // Compare the condition in the `kind == KIND_VEC` case above // for more details. - if v_capacity >= new_cap && offset >= len { + if v_capacity >= new_cap + offset { + self.cap = new_cap; + // no copy is necessary + } else if v_capacity >= new_cap && offset >= len { // The capacity is sufficient, and copying is not too much // overhead: reclaim the buffer! diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 6bf01d67d..4ddb24de5 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -515,6 +515,34 @@ fn reserve_in_arc_unique_doubles() { assert_eq!(2000, bytes.capacity()); } +#[test] +fn reserve_in_arc_unique_does_not_overallocate_after_split() { + let mut bytes = BytesMut::from(LONG); + let orig_capacity = bytes.capacity(); + drop(bytes.split_off(LONG.len() / 2)); + + // now bytes is Arc and refcount == 1 + + let new_capacity = bytes.capacity(); + bytes.reserve(orig_capacity - new_capacity); + assert_eq!(bytes.capacity(), orig_capacity); +} + +#[test] +fn reserve_in_arc_unique_does_not_overallocate_after_multiple_splits() { + let mut bytes = BytesMut::from(LONG); + let orig_capacity = bytes.capacity(); + for _ in 0..10 { + drop(bytes.split_off(LONG.len() / 2)); + + // now bytes is Arc and refcount == 1 + + let new_capacity = bytes.capacity(); + bytes.reserve(orig_capacity - new_capacity); + } + assert_eq!(bytes.capacity(), orig_capacity); +} + #[test] fn reserve_in_arc_nonunique_does_not_overallocate() { let mut bytes = BytesMut::with_capacity(1000); From b7249149a926f0d5f9f89897853eb7dc7506d27b Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 30 Jul 2022 11:35:26 +0200 Subject: [PATCH 34/48] chore: prepare bytes v1.2.1 (#561) --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ea40979e..401014d60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.2.1 (July 30, 2022) + +### Fixed + +- Fix unbounded memory growth when using `reserve` (#560) + # 1.2.0 (July 19, 2022) ### Added diff --git a/Cargo.toml b/Cargo.toml index c3396e99a..bd6ebfd7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "bytes" # When releasing to crates.io: # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. -version = "1.2.0" +version = "1.2.1" license = "MIT" authors = [ "Carl Lerche ", From d1b5d4ceb1645d0843729479a04be6c119a04369 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 12 Aug 2022 12:22:43 +0200 Subject: [PATCH 35/48] Don't have important data in unused capacity when calling reserve (#563) --- src/bytes_mut.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 2282bdc48..a292ca7bd 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -705,6 +705,15 @@ impl BytesMut { new_cap = cmp::max(double, new_cap); // No space - allocate more + // + // The length field of `Shared::vec` is not used by the `BytesMut`; + // instead we use the `len` field in the `BytesMut` itself. However, + // when calling `reserve`, it doesn't guarantee that data stored in + // 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); v.reserve(new_cap - v.len()); // Update the info From a36f661354a99f8dd14b15acfe69bc16d5505fbe Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Wed, 24 Aug 2022 06:30:21 -0500 Subject: [PATCH 36/48] docs: Bytes::new etc should return Self not Bytes (#568) --- src/bytes.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index f8d3ce319..fa43d3a2e 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -131,7 +131,7 @@ impl Bytes { /// ``` #[inline] #[cfg(not(all(loom, test)))] - pub const fn new() -> Bytes { + pub const fn new() -> Self { // Make it a named const to work around // "unsizing casts are not allowed in const fn" const EMPTY: &[u8] = &[]; @@ -139,7 +139,7 @@ impl Bytes { } #[cfg(all(loom, test))] - pub fn new() -> Bytes { + pub fn new() -> Self { const EMPTY: &[u8] = &[]; Bytes::from_static(EMPTY) } @@ -159,7 +159,7 @@ impl Bytes { /// ``` #[inline] #[cfg(not(all(loom, test)))] - pub const fn from_static(bytes: &'static [u8]) -> Bytes { + pub const fn from_static(bytes: &'static [u8]) -> Self { Bytes { ptr: bytes.as_ptr(), len: bytes.len(), @@ -169,7 +169,7 @@ impl Bytes { } #[cfg(all(loom, test))] - pub fn from_static(bytes: &'static [u8]) -> Bytes { + pub fn from_static(bytes: &'static [u8]) -> Self { Bytes { ptr: bytes.as_ptr(), len: bytes.len(), @@ -235,7 +235,7 @@ impl Bytes { /// /// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing /// will panic. - pub fn slice(&self, range: impl RangeBounds) -> Bytes { + pub fn slice(&self, range: impl RangeBounds) -> Self { use core::ops::Bound; let len = self.len(); @@ -302,7 +302,7 @@ 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 { + pub fn slice_ref(&self, subset: &[u8]) -> Self { // 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() { @@ -359,7 +359,7 @@ impl Bytes { /// /// 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 { + pub fn split_off(&mut self, at: usize) -> Self { assert!( at <= self.len(), "split_off out of bounds: {:?} <= {:?}", @@ -408,7 +408,7 @@ impl Bytes { /// /// 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 { + pub fn split_to(&mut self, at: usize) -> Self { assert!( at <= self.len(), "split_to out of bounds: {:?} <= {:?}", From 6e4b1f244b15dc486578103ae2db927bedee2d3e Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 4 Oct 2022 13:23:07 +0300 Subject: [PATCH 37/48] Rename and expose `BytesMut::spare_capacity_mut` (#572) --- src/buf/uninit_slice.rs | 6 +++++- src/bytes_mut.rs | 43 ++++++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index a6c9ead64..3161a147e 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -22,6 +22,10 @@ use core::ops::{ pub struct UninitSlice([MaybeUninit]); impl UninitSlice { + pub(crate) fn from_slice(slice: &mut [MaybeUninit]) -> &mut UninitSlice { + unsafe { &mut *(slice as *mut [MaybeUninit] as *mut UninitSlice) } + } + /// Create a `&mut UninitSlice` from a pointer and a length. /// /// # Safety @@ -44,7 +48,7 @@ impl UninitSlice { pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut UninitSlice { let maybe_init: &mut [MaybeUninit] = core::slice::from_raw_parts_mut(ptr as *mut _, len); - &mut *(maybe_init as *mut [MaybeUninit] as *mut UninitSlice) + Self::from_slice(maybe_init) } /// Write a single byte at the specified offset. diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index a292ca7bd..70613b224 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1,5 +1,5 @@ use core::iter::{FromIterator, Iterator}; -use core::mem::{self, ManuallyDrop}; +use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; use core::{cmp, fmt, hash, isize, slice, usize}; @@ -766,11 +766,11 @@ impl BytesMut { self.reserve(cnt); unsafe { - let dst = self.uninit_slice(); + let dst = self.spare_capacity_mut(); // Reserved above debug_assert!(dst.len() >= cnt); - ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr(), cnt); + ptr::copy_nonoverlapping(extend.as_ptr(), dst.as_mut_ptr().cast(), cnt); } unsafe { @@ -992,13 +992,42 @@ impl BytesMut { self.data = invalid_ptr((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)); } + /// Returns the remaining spare capacity of the buffer as a slice of `MaybeUninit`. + /// + /// The returned slice can be used to fill the buffer with data (e.g. by + /// reading from a file) before marking the data as initialized using the + /// [`set_len`] method. + /// + /// [`set_len`]: BytesMut::set_len + /// + /// # Examples + /// + /// ``` + /// use bytes::BytesMut; + /// + /// // Allocate buffer big enough for 10 bytes. + /// let mut buf = BytesMut::with_capacity(10); + /// + /// // Fill in the first 3 elements. + /// let uninit = buf.spare_capacity_mut(); + /// uninit[0].write(0); + /// uninit[1].write(1); + /// uninit[2].write(2); + /// + /// // Mark the first 3 bytes of the buffer as being initialized. + /// unsafe { + /// buf.set_len(3); + /// } + /// + /// assert_eq!(&buf[..], &[0, 1, 2]); + /// ``` #[inline] - fn uninit_slice(&mut self) -> &mut UninitSlice { + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { unsafe { let ptr = self.ptr.as_ptr().add(self.len); let len = self.cap - self.len; - UninitSlice::from_raw_parts_mut(ptr, len) + slice::from_raw_parts_mut(ptr.cast(), len) } } } @@ -1072,7 +1101,7 @@ unsafe impl BufMut for BytesMut { if self.capacity() == self.len() { self.reserve(64); } - self.uninit_slice() + UninitSlice::from_slice(self.spare_capacity_mut()) } // Specialize these methods so they can skip checking `remaining_mut` @@ -1097,7 +1126,7 @@ unsafe impl BufMut for BytesMut { fn put_bytes(&mut self, val: u8, cnt: usize) { self.reserve(cnt); unsafe { - let dst = self.uninit_slice(); + let dst = self.spare_capacity_mut(); // Reserved above debug_assert!(dst.len() >= cnt); From 5c7b4317e019f9b46dcc11e5a6a18e514ce85ebe Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sun, 13 Nov 2022 17:56:33 -0800 Subject: [PATCH 38/48] Update nightly snapshot to the current one (#577) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e3c706f40..f6c685112 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 - nightly: nightly-2021-11-05 + nightly: nightly-2022-11-12 defaults: run: From 8a8c41f5c215e03d94ab9b38a88f5991b4cf24ab Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 14 Nov 2022 01:33:36 -0800 Subject: [PATCH 39/48] Implement native-endian get and put functions for Buf and BufMut (#576) Fixes #549 --- src/buf/buf_impl.rs | 318 ++++++++++++++++++++++++++++++++++++++++ src/buf/buf_mut.rs | 346 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 664 insertions(+) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index a33c8a42d..d903d5c9f 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -354,6 +354,29 @@ pub trait Buf { buf_get_impl!(self, u16::from_le_bytes); } + /// Gets an unsigned 16 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x08\x09 hello", + /// false => b"\x09\x08 hello", + /// }; + /// assert_eq!(0x0809, buf.get_u16_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_u16_ne(&mut self) -> u16 { + buf_get_impl!(self, u16::from_ne_bytes); + } + /// Gets a signed 16 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 2. @@ -394,6 +417,29 @@ pub trait Buf { buf_get_impl!(self, i16::from_le_bytes); } + /// Gets a signed 16 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x08\x09 hello", + /// false => b"\x09\x08 hello", + /// }; + /// assert_eq!(0x0809, buf.get_i16_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_i16_ne(&mut self) -> i16 { + buf_get_impl!(self, i16::from_ne_bytes); + } + /// Gets an unsigned 32 bit integer from `self` in the big-endian byte order. /// /// The current position is advanced by 4. @@ -434,6 +480,29 @@ pub trait Buf { buf_get_impl!(self, u32::from_le_bytes); } + /// Gets an unsigned 32 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x08\x09\xA0\xA1 hello", + /// false => b"\xA1\xA0\x09\x08 hello", + /// }; + /// assert_eq!(0x0809A0A1, buf.get_u32_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_u32_ne(&mut self) -> u32 { + buf_get_impl!(self, u32::from_ne_bytes); + } + /// Gets a signed 32 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 4. @@ -474,6 +543,29 @@ pub trait Buf { buf_get_impl!(self, i32::from_le_bytes); } + /// Gets a signed 32 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x08\x09\xA0\xA1 hello", + /// false => b"\xA1\xA0\x09\x08 hello", + /// }; + /// assert_eq!(0x0809A0A1, buf.get_i32_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_i32_ne(&mut self) -> i32 { + buf_get_impl!(self, i32::from_ne_bytes); + } + /// Gets an unsigned 64 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 8. @@ -514,6 +606,29 @@ pub trait Buf { buf_get_impl!(self, u64::from_le_bytes); } + /// Gets an unsigned 64 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03\x04\x05\x06\x07\x08 hello", + /// false => b"\x08\x07\x06\x05\x04\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x0102030405060708, buf.get_u64_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_u64_ne(&mut self) -> u64 { + buf_get_impl!(self, u64::from_ne_bytes); + } + /// Gets a signed 64 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 8. @@ -554,6 +669,29 @@ pub trait Buf { buf_get_impl!(self, i64::from_le_bytes); } + /// Gets a signed 64 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03\x04\x05\x06\x07\x08 hello", + /// false => b"\x08\x07\x06\x05\x04\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x0102030405060708, buf.get_i64_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_i64_ne(&mut self) -> i64 { + buf_get_impl!(self, i64::from_ne_bytes); + } + /// Gets an unsigned 128 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 16. @@ -594,6 +732,29 @@ pub trait Buf { buf_get_impl!(self, u128::from_le_bytes); } + /// Gets an unsigned 128 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16 hello", + /// false => b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x01020304050607080910111213141516, buf.get_u128_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_u128_ne(&mut self) -> u128 { + buf_get_impl!(self, u128::from_ne_bytes); + } + /// Gets a signed 128 bit integer from `self` in big-endian byte order. /// /// The current position is advanced by 16. @@ -634,6 +795,29 @@ pub trait Buf { buf_get_impl!(self, i128::from_le_bytes); } + /// Gets a signed 128 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16 hello", + /// false => b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x01020304050607080910111213141516, buf.get_i128_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_i128_ne(&mut self) -> i128 { + buf_get_impl!(self, i128::from_ne_bytes); + } + /// Gets an unsigned n-byte integer from `self` in big-endian byte order. /// /// The current position is advanced by `nbytes`. @@ -674,6 +858,33 @@ pub trait Buf { buf_get_impl!(le => self, u64, nbytes); } + /// Gets an unsigned n-byte integer from `self` in native-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03 hello", + /// false => b"\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x010203, buf.get_uint_ne(3)); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_uint_ne(&mut self, nbytes: usize) -> u64 { + if cfg!(target_endian = "big") { + self.get_uint(nbytes) + } else { + self.get_uint_le(nbytes) + } + } + /// Gets a signed n-byte integer from `self` in big-endian byte order. /// /// The current position is advanced by `nbytes`. @@ -714,6 +925,33 @@ pub trait Buf { buf_get_impl!(le => self, i64, nbytes); } + /// Gets a signed n-byte integer from `self` in native-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03 hello", + /// false => b"\x03\x02\x01 hello", + /// }; + /// assert_eq!(0x010203, buf.get_int_ne(3)); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_int_ne(&mut self, nbytes: usize) -> i64 { + if cfg!(target_endian = "big") { + self.get_int(nbytes) + } else { + self.get_int_le(nbytes) + } + } + /// Gets an IEEE754 single-precision (4 bytes) floating point number from /// `self` in big-endian byte order. /// @@ -756,6 +994,30 @@ pub trait Buf { f32::from_bits(Self::get_u32_le(self)) } + /// Gets an IEEE754 single-precision (4 bytes) floating point number from + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x3F\x99\x99\x9A hello", + /// false => b"\x9A\x99\x99\x3F hello", + /// }; + /// assert_eq!(1.2f32, buf.get_f32_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_f32_ne(&mut self) -> f32 { + f32::from_bits(Self::get_u32_ne(self)) + } + /// Gets an IEEE754 double-precision (8 bytes) floating point number from /// `self` in big-endian byte order. /// @@ -798,6 +1060,30 @@ pub trait Buf { f64::from_bits(Self::get_u64_le(self)) } + /// Gets an IEEE754 double-precision (8 bytes) floating point number from + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x3F\xF3\x33\x33\x33\x33\x33\x33 hello", + /// false => b"\x33\x33\x33\x33\x33\x33\xF3\x3F hello", + /// }; + /// assert_eq!(1.2f64, buf.get_f64_ne()); + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining data in `self`. + fn get_f64_ne(&mut self) -> f64 { + f64::from_bits(Self::get_u64_ne(self)) + } + /// Consumes `len` bytes inside self and returns new instance of `Bytes` /// with this data. /// @@ -948,6 +1234,10 @@ macro_rules! deref_forward_buf { (**self).get_u16_le() } + fn get_u16_ne(&mut self) -> u16 { + (**self).get_u16_ne() + } + fn get_i16(&mut self) -> i16 { (**self).get_i16() } @@ -956,6 +1246,10 @@ macro_rules! deref_forward_buf { (**self).get_i16_le() } + fn get_i16_ne(&mut self) -> i16 { + (**self).get_i16_ne() + } + fn get_u32(&mut self) -> u32 { (**self).get_u32() } @@ -964,6 +1258,10 @@ macro_rules! deref_forward_buf { (**self).get_u32_le() } + fn get_u32_ne(&mut self) -> u32 { + (**self).get_u32_ne() + } + fn get_i32(&mut self) -> i32 { (**self).get_i32() } @@ -972,6 +1270,10 @@ macro_rules! deref_forward_buf { (**self).get_i32_le() } + fn get_i32_ne(&mut self) -> i32 { + (**self).get_i32_ne() + } + fn get_u64(&mut self) -> u64 { (**self).get_u64() } @@ -980,6 +1282,10 @@ macro_rules! deref_forward_buf { (**self).get_u64_le() } + fn get_u64_ne(&mut self) -> u64 { + (**self).get_u64_ne() + } + fn get_i64(&mut self) -> i64 { (**self).get_i64() } @@ -988,6 +1294,10 @@ macro_rules! deref_forward_buf { (**self).get_i64_le() } + fn get_i64_ne(&mut self) -> i64 { + (**self).get_i64_ne() + } + fn get_uint(&mut self, nbytes: usize) -> u64 { (**self).get_uint(nbytes) } @@ -996,6 +1306,10 @@ macro_rules! deref_forward_buf { (**self).get_uint_le(nbytes) } + fn get_uint_ne(&mut self, nbytes: usize) -> u64 { + (**self).get_uint_ne(nbytes) + } + fn get_int(&mut self, nbytes: usize) -> i64 { (**self).get_int(nbytes) } @@ -1004,6 +1318,10 @@ macro_rules! deref_forward_buf { (**self).get_int_le(nbytes) } + fn get_int_ne(&mut self, nbytes: usize) -> i64 { + (**self).get_int_ne(nbytes) + } + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { (**self).copy_to_bytes(len) } diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 7f67f7b00..ada07a37e 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -386,6 +386,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes an unsigned 16 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_u16_ne(0x0809); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x08\x09"); + /// } else { + /// assert_eq!(buf, b"\x09\x08"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_u16_ne(&mut self, n: u16) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes a signed 16 bit integer to `self` in big-endian byte order. /// /// The current position is advanced by 2. @@ -430,6 +456,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes a signed 16 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_i16_ne(0x0809); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x08\x09"); + /// } else { + /// assert_eq!(buf, b"\x09\x08"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_i16_ne(&mut self, n: i16) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes an unsigned 32 bit integer to `self` in big-endian byte order. /// /// The current position is advanced by 4. @@ -474,6 +526,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes an unsigned 32 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_u32_ne(0x0809A0A1); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x08\x09\xA0\xA1"); + /// } else { + /// assert_eq!(buf, b"\xA1\xA0\x09\x08"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_u32_ne(&mut self, n: u32) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes a signed 32 bit integer to `self` in big-endian byte order. /// /// The current position is advanced by 4. @@ -518,6 +596,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes a signed 32 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_i32_ne(0x0809A0A1); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x08\x09\xA0\xA1"); + /// } else { + /// assert_eq!(buf, b"\xA1\xA0\x09\x08"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_i32_ne(&mut self, n: i32) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes an unsigned 64 bit integer to `self` in the big-endian byte order. /// /// The current position is advanced by 8. @@ -562,6 +666,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes an unsigned 64 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_u64_ne(0x0102030405060708); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03\x04\x05\x06\x07\x08"); + /// } else { + /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_u64_ne(&mut self, n: u64) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes a signed 64 bit integer to `self` in the big-endian byte order. /// /// The current position is advanced by 8. @@ -606,6 +736,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes a signed 64 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_i64_ne(0x0102030405060708); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03\x04\x05\x06\x07\x08"); + /// } else { + /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_i64_ne(&mut self, n: i64) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes an unsigned 128 bit integer to `self` in the big-endian byte order. /// /// The current position is advanced by 16. @@ -650,6 +806,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes an unsigned 128 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_u128_ne(0x01020304050607080910111213141516); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16"); + /// } else { + /// assert_eq!(buf, b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_u128_ne(&mut self, n: u128) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes a signed 128 bit integer to `self` in the big-endian byte order. /// /// The current position is advanced by 16. @@ -694,6 +876,32 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()) } + /// Writes a signed 128 bit integer to `self` in native-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_i128_ne(0x01020304050607080910111213141516); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16"); + /// } else { + /// assert_eq!(buf, b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_i128_ne(&mut self, n: i128) { + self.put_slice(&n.to_ne_bytes()) + } + /// Writes an unsigned n-byte integer to `self` in big-endian byte order. /// /// The current position is advanced by `nbytes`. @@ -738,6 +946,36 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()[0..nbytes]); } + /// Writes an unsigned n-byte integer to `self` in the native-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_uint_ne(0x010203, 3); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03"); + /// } else { + /// assert_eq!(buf, b"\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_uint_ne(&mut self, n: u64, nbytes: usize) { + if cfg!(target_endian = "big") { + self.put_uint(n, nbytes) + } else { + self.put_uint_le(n, nbytes) + } + } + /// Writes low `nbytes` of a signed integer to `self` in big-endian byte order. /// /// The current position is advanced by `nbytes`. @@ -782,6 +1020,36 @@ pub unsafe trait BufMut { self.put_slice(&n.to_le_bytes()[0..nbytes]); } + /// Writes low `nbytes` of a signed integer to `self` in native-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_int_ne(0x010203, 3); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x01\x02\x03"); + /// } else { + /// assert_eq!(buf, b"\x03\x02\x01"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self` or if `nbytes` is greater than 8. + fn put_int_ne(&mut self, n: i64, nbytes: usize) { + if cfg!(target_endian = "big") { + self.put_int(n, nbytes) + } else { + self.put_int_le(n, nbytes) + } + } + /// Writes an IEEE754 single-precision (4 bytes) floating point number to /// `self` in big-endian byte order. /// @@ -828,6 +1096,33 @@ pub unsafe trait BufMut { self.put_u32_le(n.to_bits()); } + /// Writes an IEEE754 single-precision (4 bytes) floating point number to + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_f32_ne(1.2f32); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x3F\x99\x99\x9A"); + /// } else { + /// assert_eq!(buf, b"\x9A\x99\x99\x3F"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_f32_ne(&mut self, n: f32) { + self.put_u32_ne(n.to_bits()); + } + /// Writes an IEEE754 double-precision (8 bytes) floating point number to /// `self` in big-endian byte order. /// @@ -874,6 +1169,33 @@ pub unsafe trait BufMut { self.put_u64_le(n.to_bits()); } + /// Writes an IEEE754 double-precision (8 bytes) floating point number to + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut buf = vec![]; + /// buf.put_f64_ne(1.2f64); + /// if cfg!(target_endian = "big") { + /// assert_eq!(buf, b"\x3F\xF3\x33\x33\x33\x33\x33\x33"); + /// } else { + /// assert_eq!(buf, b"\x33\x33\x33\x33\x33\x33\xF3\x3F"); + /// } + /// ``` + /// + /// # Panics + /// + /// This function panics if there is not enough remaining capacity in + /// `self`. + fn put_f64_ne(&mut self, n: f64) { + self.put_u64_ne(n.to_bits()); + } + /// Creates an adaptor which can write at most `limit` bytes to `self`. /// /// # Examples @@ -986,6 +1308,10 @@ macro_rules! deref_forward_bufmut { (**self).put_u16_le(n) } + fn put_u16_ne(&mut self, n: u16) { + (**self).put_u16_ne(n) + } + fn put_i16(&mut self, n: i16) { (**self).put_i16(n) } @@ -994,6 +1320,10 @@ macro_rules! deref_forward_bufmut { (**self).put_i16_le(n) } + fn put_i16_ne(&mut self, n: i16) { + (**self).put_i16_ne(n) + } + fn put_u32(&mut self, n: u32) { (**self).put_u32(n) } @@ -1002,6 +1332,10 @@ macro_rules! deref_forward_bufmut { (**self).put_u32_le(n) } + fn put_u32_ne(&mut self, n: u32) { + (**self).put_u32_ne(n) + } + fn put_i32(&mut self, n: i32) { (**self).put_i32(n) } @@ -1010,6 +1344,10 @@ macro_rules! deref_forward_bufmut { (**self).put_i32_le(n) } + fn put_i32_ne(&mut self, n: i32) { + (**self).put_i32_ne(n) + } + fn put_u64(&mut self, n: u64) { (**self).put_u64(n) } @@ -1018,6 +1356,10 @@ macro_rules! deref_forward_bufmut { (**self).put_u64_le(n) } + fn put_u64_ne(&mut self, n: u64) { + (**self).put_u64_ne(n) + } + fn put_i64(&mut self, n: i64) { (**self).put_i64(n) } @@ -1025,6 +1367,10 @@ macro_rules! deref_forward_bufmut { fn put_i64_le(&mut self, n: i64) { (**self).put_i64_le(n) } + + fn put_i64_ne(&mut self, n: i64) { + (**self).put_i64_ne(n) + } }; } From 9b227220dfeb5d176b807c9bf27e1ee438935f78 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Sun, 20 Nov 2022 23:06:49 -0800 Subject: [PATCH 40/48] chore: prepare bytes v1.3.0 (#579) --- CHANGELOG.md | 15 +++++++++++++++ Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401014d60..760c21017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# 1.3.0 (November 20, 2022) + +### Added + +- Rename and expose `BytesMut::spare_capacity_mut` (#572) +- Implement native-endian get and put functions for `Buf` and `BufMut` (#576) + +### Fixed + +- Don't have important data in unused capacity when calling reserve (#563) + +### Documented + +- `Bytes::new` etc should return `Self` not `Bytes` (#568) + # 1.2.1 (July 30, 2022) ### Fixed diff --git a/Cargo.toml b/Cargo.toml index bd6ebfd7f..9efd8a119 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "bytes" # When releasing to crates.io: # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. -version = "1.2.1" +version = "1.3.0" license = "MIT" authors = [ "Carl Lerche ", From 050d65b2cee8b2272687d798dc209dc03fe92719 Mon Sep 17 00:00:00 2001 From: Matthijs van Otterdijk Date: Fri, 25 Nov 2022 22:48:20 +0100 Subject: [PATCH 41/48] make IntoIter constructor public (#581) --- src/buf/iter.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/buf/iter.rs b/src/buf/iter.rs index 8914a40e8..c694e3d41 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -2,8 +2,6 @@ use crate::Buf; /// Iterator over the bytes contained by the buffer. /// -/// This struct is created by the [`iter`] method on [`Buf`]. -/// /// # Examples /// /// Basic usage: @@ -43,7 +41,7 @@ impl IntoIter { /// assert_eq!(iter.next(), Some(b'c')); /// assert_eq!(iter.next(), None); /// ``` - pub(crate) fn new(inner: T) -> IntoIter { + pub fn new(inner: T) -> IntoIter { IntoIter { inner } } From c93a94b974d8c7c926c4e49ed4ce4645fb0e3801 Mon Sep 17 00:00:00 2001 From: Nicolae Mihalache Date: Tue, 20 Dec 2022 11:49:55 +0100 Subject: [PATCH 42/48] Fix duplicate "the the" typos (#585) Co-authored-by: Nicolae Mihalache --- src/bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index fa43d3a2e..b4745a98f 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -32,7 +32,7 @@ use crate::Buf; /// All `Bytes` implementations must fulfill the following requirements: /// - They are cheaply cloneable and thereby shareable between an unlimited amount /// of components, for example by modifying a reference count. -/// - Instances can be sliced to refer to a subset of the the original buffer. +/// - Instances can be sliced to refer to a subset of the original buffer. /// /// ``` /// use bytes::Bytes; @@ -71,7 +71,7 @@ use crate::Buf; /// /// For `Bytes` implementations which point to a reference counted shared storage /// (e.g. an `Arc<[u8]>`), sharing will be implemented by increasing the -/// the reference count. +/// reference count. /// /// Due to this mechanism, multiple `Bytes` instances may point to the same /// shared memory region. From f15bba3375f7e75f412b085f75b3112cceaa6ef1 Mon Sep 17 00:00:00 2001 From: 0xc0001a2040 Date: Tue, 31 Jan 2023 11:42:28 +0100 Subject: [PATCH 43/48] Document which functions require `std` (#591) --- .github/workflows/ci.yml | 2 +- README.md | 9 +++++++++ src/buf/buf_impl.rs | 2 ++ src/buf/buf_mut.rs | 1 + src/lib.rs | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6c685112..a4f7b1d93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,7 +157,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install Rust - run: rustup update stable && rustup default stable + run: rustup update $nightly && rustup default $nightly - name: Build documentation run: cargo doc --no-deps --all-features env: diff --git a/README.md b/README.md index 468485d12..be46642a4 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,15 @@ Serde support is optional and disabled by default. To enable use the feature `se bytes = { version = "1", features = ["serde"] } ``` +## Building documentation + +When building the `bytes` documentation the `docsrs` option should be used, otherwise +feature gates will not be shown. This requires a nightly toolchain: + +``` +RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc +``` + ## License This project is licensed under the [MIT license](LICENSE). diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index d903d5c9f..366cfc989 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -160,6 +160,7 @@ pub trait Buf { /// /// [`writev`]: http://man7.org/linux/man-pages/man2/readv.2.html #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { if dst.is_empty() { return 0; @@ -1183,6 +1184,7 @@ pub trait Buf { /// assert_eq!(&dst[..11], &b"hello world"[..]); /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn reader(self) -> Reader where Self: Sized, diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index ada07a37e..685fcc76b 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1239,6 +1239,7 @@ pub unsafe trait BufMut { /// assert_eq!(*buf, b"hello world"[..]); /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn writer(self) -> Writer where Self: Sized, diff --git a/src/lib.rs b/src/lib.rs index 706735e3d..af436b316 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] #![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] //! Provides abstractions for working with bytes. //! From 05e9d5cab95e5ce1d57dd498a9567639de50f841 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 31 Jan 2023 19:04:22 +0000 Subject: [PATCH 44/48] Avoid large reallocations when freezing BytesMut (#592) When we freeze a BytesMut, we turn it into a Vec, and then convert that to a Bytes. Currently, this happen using Vec::into_boxed_slice, which reallocates to a slice of the same length as the Vev if the length and the capacity are not equal. This can pose a performance problem if the Vec is large or if this happens many times in a loop. Instead, let's compare the length and capacity, and if they're the same, continue to handle this using into_boxed_slice. Otherwise, since we have a type of vtable which can handle a separate capacity, the shared vtable, let's turn our Vec into that kind of Bytes. While this does not avoid allocation altogether, it performs a fixed size allocation and avoids any need to memcpy. --- src/bytes.rs | 32 ++++++++++++++++++++++++++++++-- tests/test_bytes.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index b4745a98f..0404a72db 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -807,8 +807,36 @@ impl From<&'static str> for Bytes { impl From> for Bytes { fn from(vec: Vec) -> Bytes { - let slice = vec.into_boxed_slice(); - slice.into() + let mut vec = vec; + let ptr = vec.as_mut_ptr(); + let len = vec.len(); + let cap = vec.capacity(); + + // Avoid an extra allocation if possible. + if len == cap { + return Bytes::from(vec.into_boxed_slice()); + } + + let shared = Box::new(Shared { + buf: ptr, + cap, + ref_cnt: AtomicUsize::new(1), + }); + mem::forget(vec); + + let shared = Box::into_raw(shared); + // The pointer should be aligned, so this assert should + // always succeed. + debug_assert!( + 0 == (shared as usize & KIND_MASK), + "internal: Box should have an aligned pointer", + ); + Bytes { + ptr, + len, + data: AtomicPtr::new(shared as _), + vtable: &SHARED_VTABLE, + } } } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 4ddb24de5..5ec60a5b0 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1163,3 +1163,48 @@ fn test_bytes_into_vec_promotable_even() { assert_eq!(Vec::from(b2), vec[20..]); assert_eq!(Vec::from(b1), vec[..20]); } + +#[test] +fn test_bytes_vec_conversion() { + let mut vec = Vec::with_capacity(10); + vec.extend(b"abcdefg"); + let b = Bytes::from(vec); + let v = Vec::from(b); + assert_eq!(v.len(), 7); + assert_eq!(v.capacity(), 10); + + let mut b = Bytes::from(v); + b.advance(1); + let v = Vec::from(b); + assert_eq!(v.len(), 6); + assert_eq!(v.capacity(), 10); + assert_eq!(v.as_slice(), b"bcdefg"); +} + +#[test] +fn test_bytes_mut_conversion() { + let mut b1 = BytesMut::with_capacity(10); + b1.extend(b"abcdefg"); + let b2 = Bytes::from(b1); + let v = Vec::from(b2); + assert_eq!(v.len(), 7); + assert_eq!(v.capacity(), 10); + + let mut b = Bytes::from(v); + b.advance(1); + let v = Vec::from(b); + assert_eq!(v.len(), 6); + assert_eq!(v.capacity(), 10); + assert_eq!(v.as_slice(), b"bcdefg"); +} + +#[test] +fn test_bytes_capacity_len() { + for cap in 0..100 { + for len in 0..=cap { + let mut v = Vec::with_capacity(cap); + v.resize(len, 0); + let _ = Bytes::from(v); + } + } +} From 21ed3328364716fa30a4bf7502c913bbf0a90f45 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 31 Jan 2023 20:38:32 +0100 Subject: [PATCH 45/48] chore: prepare bytes v1.4.0 (#593) --- CHANGELOG.md | 15 +++++++++++++++ Cargo.toml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 760c21017..a1bad4a40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# 1.4.0 (January 31, 2023) + +### Added + +- Make `IntoIter` constructor public (#581) + +### Fixed + +- Avoid large reallocations when freezing `BytesMut` (#592) + +### Documented + +- Document which functions require `std` (#591) +- Fix duplicate "the the" typos (#585) + # 1.3.0 (November 20, 2022) ### Added diff --git a/Cargo.toml b/Cargo.toml index 9efd8a119..4a96ec1ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ name = "bytes" # When releasing to crates.io: # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. -version = "1.3.0" +version = "1.4.0" license = "MIT" authors = [ "Carl Lerche ", From 74b04c7aae5fd7e73f4283774eab0ef72a26a8a7 Mon Sep 17 00:00:00 2001 From: Paa Kojo Samanpa Date: Sat, 4 Feb 2023 14:16:41 -0500 Subject: [PATCH 46/48] Mark BytesMut::extend_from_slice as inline (#595) This function can be hot in applications that do a lot of encoding. Ideally would do the same for `::put_slice` and ` Date: Thu, 9 Feb 2023 17:24:23 +0100 Subject: [PATCH 47/48] Add a safe way to create UninitSlice from slices (#598) Introduce UninitSlice::from_slice and UninitSlice::from_uninit_slice methods which safely create Uninit slice from provided slice of maybe uninitialised or initialised memory. In addition, add `From<&mut [T]>` implementations (for `T=u8` and `T=MaybeUninit`) which do conversion from slice to UninitSlice. Closes: #552 --- src/buf/uninit_slice.rs | 56 ++++++++++++++++++++++++++++++++++++----- src/bytes_mut.rs | 2 +- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 3161a147e..84b1d8820 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -22,10 +22,44 @@ use core::ops::{ pub struct UninitSlice([MaybeUninit]); impl UninitSlice { - pub(crate) fn from_slice(slice: &mut [MaybeUninit]) -> &mut UninitSlice { + /// Creates a `&mut UninitSlice` wrapping slice of uninitialised memory. + /// + /// # Examples + /// + /// ``` + /// use bytes::buf::UninitSlice; + /// use core::mem::MaybeUninit; + /// + /// let mut buffer = [MaybeUninit::uninit(); 64]; + /// let slice = UninitSlice::from_uninit_slice(&mut buffer[..]); + /// + /// let mut vec = Vec::with_capacity(1024); + /// let spare: &mut UninitSlice = vec.spare_capacity_mut().into(); + /// ``` + #[inline] + pub fn from_uninit_slice(slice: &mut [MaybeUninit]) -> &mut UninitSlice { unsafe { &mut *(slice as *mut [MaybeUninit] as *mut UninitSlice) } } + fn from_uninit_slice_ref(slice: &[MaybeUninit]) -> &UninitSlice { + unsafe { &*(slice as *const [MaybeUninit] as *const UninitSlice) } + } + + /// Creates a `&mut UninitSlice` wrapping slice of initialised memory. + /// + /// # Examples + /// + /// ``` + /// use bytes::buf::UninitSlice; + /// + /// let mut buffer = [0u8; 64]; + /// let slice = UninitSlice::from_slice(&mut buffer[..]); + /// ``` + #[inline] + pub fn from_slice(slice: &mut [u8]) -> &mut UninitSlice { + unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit] as *mut UninitSlice) } + } + /// Create a `&mut UninitSlice` from a pointer and a length. /// /// # Safety @@ -48,7 +82,7 @@ impl UninitSlice { pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut UninitSlice { let maybe_init: &mut [MaybeUninit] = core::slice::from_raw_parts_mut(ptr as *mut _, len); - Self::from_slice(maybe_init) + Self::from_uninit_slice(maybe_init) } /// Write a single byte at the specified offset. @@ -179,6 +213,18 @@ impl fmt::Debug for UninitSlice { } } +impl<'a> From<&'a mut [u8]> for &'a mut UninitSlice { + fn from(slice: &'a mut [u8]) -> Self { + UninitSlice::from_slice(slice) + } +} + +impl<'a> From<&'a mut [MaybeUninit]> for &'a mut UninitSlice { + fn from(slice: &'a mut [MaybeUninit]) -> Self { + UninitSlice::from_uninit_slice(slice) + } +} + macro_rules! impl_index { ($($t:ty),*) => { $( @@ -187,16 +233,14 @@ macro_rules! impl_index { #[inline] fn index(&self, index: $t) -> &UninitSlice { - let maybe_uninit: &[MaybeUninit] = &self.0[index]; - unsafe { &*(maybe_uninit as *const [MaybeUninit] as *const UninitSlice) } + UninitSlice::from_uninit_slice_ref(&self.0[index]) } } impl IndexMut<$t> for UninitSlice { #[inline] fn index_mut(&mut self, index: $t) -> &mut UninitSlice { - let maybe_uninit: &mut [MaybeUninit] = &mut self.0[index]; - unsafe { &mut *(maybe_uninit as *mut [MaybeUninit] as *mut UninitSlice) } + UninitSlice::from_uninit_slice(&mut self.0[index]) } } )* diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 450b93279..c5c2e52fc 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1102,7 +1102,7 @@ unsafe impl BufMut for BytesMut { if self.capacity() == self.len() { self.reserve(64); } - UninitSlice::from_slice(self.spare_capacity_mut()) + self.spare_capacity_mut().into() } // Specialize these methods so they can skip checking `remaining_mut` From b29112ce4484424a0137173310ec8b9f84db27ae Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 10 Feb 2023 10:28:24 +0100 Subject: [PATCH 48/48] Implement BufMut for `&mut [MaybeUninit]` (#597) --- src/buf/buf_mut.rs | 35 ++++++++++++ tests/test_buf_mut.rs | 123 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 145 insertions(+), 13 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 685fcc76b..2a3c24325 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1419,6 +1419,41 @@ unsafe impl BufMut for &mut [u8] { } } +unsafe impl BufMut for &mut [core::mem::MaybeUninit] { + #[inline] + fn remaining_mut(&self) -> usize { + self.len() + } + + #[inline] + fn chunk_mut(&mut self) -> &mut UninitSlice { + UninitSlice::from_uninit_slice(self) + } + + #[inline] + unsafe fn advance_mut(&mut self, cnt: usize) { + // Lifetime dance taken from `impl Write for &mut [u8]`. + let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt); + *self = b; + } + + #[inline] + fn put_slice(&mut self, src: &[u8]) { + self.chunk_mut()[..src.len()].copy_from_slice(src); + unsafe { + self.advance_mut(src.len()); + } + } + + fn put_bytes(&mut self, val: u8, cnt: usize) { + assert!(self.remaining_mut() >= cnt); + unsafe { + ptr::write_bytes(self.as_mut_ptr() as *mut u8, val, cnt); + self.advance_mut(cnt); + } + } +} + unsafe impl BufMut for Vec { #[inline] fn remaining_mut(&self) -> usize { diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 53f4e8611..865cccc33 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -3,6 +3,7 @@ use bytes::buf::UninitSlice; use bytes::{BufMut, BytesMut}; use core::fmt::Write; +use core::mem::MaybeUninit; use core::usize; #[test] @@ -101,24 +102,120 @@ fn test_clone() { assert!(buf != buf2); } +fn do_test_slice_small(make: impl Fn(&mut [u8]) -> &mut T) +where + for<'r> &'r mut T: BufMut, +{ + let mut buf = [b'X'; 8]; + + let mut slice = make(&mut buf[..]); + slice.put_bytes(b'A', 2); + slice.put_u8(b'B'); + slice.put_slice(b"BCC"); + assert_eq!(2, slice.remaining_mut()); + assert_eq!(b"AABBCCXX", &buf[..]); + + let mut slice = make(&mut buf[..]); + slice.put_u32(0x61626364); + assert_eq!(4, slice.remaining_mut()); + assert_eq!(b"abcdCCXX", &buf[..]); + + let mut slice = make(&mut buf[..]); + slice.put_u32_le(0x30313233); + assert_eq!(4, slice.remaining_mut()); + assert_eq!(b"3210CCXX", &buf[..]); +} + +fn do_test_slice_large(make: impl Fn(&mut [u8]) -> &mut T) +where + for<'r> &'r mut T: BufMut, +{ + const LEN: usize = 100; + const FILL: [u8; LEN] = [b'Y'; LEN]; + + let test = |fill: &dyn Fn(&mut &mut T, usize)| { + for buf_len in 0..LEN { + let mut buf = [b'X'; LEN]; + for fill_len in 0..=buf_len { + let mut slice = make(&mut buf[..buf_len]); + fill(&mut slice, fill_len); + assert_eq!(buf_len - fill_len, slice.remaining_mut()); + let (head, tail) = buf.split_at(fill_len); + assert_eq!(&FILL[..fill_len], head); + assert!(tail.iter().all(|b| *b == b'X')); + } + } + }; + + test(&|slice, fill_len| slice.put_slice(&FILL[..fill_len])); + test(&|slice, fill_len| slice.put_bytes(FILL[0], fill_len)); +} + +fn do_test_slice_put_slice_panics(make: impl Fn(&mut [u8]) -> &mut T) +where + for<'r> &'r mut T: BufMut, +{ + let mut buf = [b'X'; 4]; + let mut slice = make(&mut buf[..]); + slice.put_slice(b"12345"); +} + +fn do_test_slice_put_bytes_panics(make: impl Fn(&mut [u8]) -> &mut T) +where + for<'r> &'r mut T: BufMut, +{ + let mut buf = [b'X'; 4]; + let mut slice = make(&mut buf[..]); + slice.put_bytes(b'1', 5); +} + +#[test] +fn test_slice_buf_mut_small() { + do_test_slice_small(|x| x); +} + #[test] -fn test_mut_slice() { - let mut v = vec![0, 0, 0, 0]; - let mut s = &mut v[..]; - s.put_u32(42); +fn test_slice_buf_mut_large() { + do_test_slice_large(|x| x); +} - assert_eq!(s.len(), 0); - assert_eq!(&v, &[0, 0, 0, 42]); +#[test] +#[should_panic] +fn test_slice_buf_mut_put_slice_overflow() { + do_test_slice_put_slice_panics(|x| x); } #[test] -fn test_slice_put_bytes() { - let mut v = [0, 0, 0, 0]; - let mut s = &mut v[..]; - s.put_u8(17); - s.put_bytes(19, 2); - assert_eq!(1, s.remaining_mut()); - assert_eq!(&[17, 19, 19, 0], &v[..]); +#[should_panic] +fn test_slice_buf_mut_put_bytes_overflow() { + do_test_slice_put_bytes_panics(|x| x); +} + +fn make_maybe_uninit_slice(slice: &mut [u8]) -> &mut [MaybeUninit] { + // SAFETY: [u8] has the same layout as [MaybeUninit]. + unsafe { core::mem::transmute(slice) } +} + +#[test] +fn test_maybe_uninit_buf_mut_small() { + do_test_slice_small(make_maybe_uninit_slice); +} + +#[test] +fn test_maybe_uninit_buf_mut_large() { + do_test_slice_large(make_maybe_uninit_slice); +} + +#[test] +#[should_panic] +fn test_maybe_uninit_buf_mut_put_slice_overflow() { + do_test_slice_put_slice_panics(make_maybe_uninit_slice); +} + +#[test] +#[should_panic] +fn test_maybe_uninit_buf_mut_put_bytes_overflow() { + do_test_slice_put_bytes_panics(make_maybe_uninit_slice); } #[test]