From 8bbe9dd87bdf11b1536e56415a39827eeb6d0f3e Mon Sep 17 00:00:00 2001 From: Tim Hambourger Date: Fri, 13 Mar 2020 07:55:54 -0500 Subject: [PATCH 001/203] Fix #352 -- Make freeze respect the start offset for BytesMuts in Vec mode --- src/bytes_mut.rs | 4 ++- tests/test_bytes.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index dc4e4b179..dda5c471e 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -233,7 +233,9 @@ impl BytesMut { let (off, _) = self.get_vec_pos(); let vec = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off); mem::forget(self); - vec.into() + let mut b: Bytes = vec.into(); + b.advance(off); + b } } else { debug_assert_eq!(self.kind(), KIND_ARC); diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 40fcae431..47757733c 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -342,6 +342,72 @@ fn freeze_clone_unique() { assert_eq!(c, s); } +#[test] +fn freeze_after_advance() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + b.advance(1); + assert_eq!(b, s[1..]); + let b = b.freeze(); + // Verify fix for #352. Previously, freeze would ignore the start offset + // for BytesMuts in Vec mode. + assert_eq!(b, s[1..]); +} + +#[test] +fn freeze_after_advance_arc() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + // Make b Arc + let _ = b.split_to(0); + b.advance(1); + assert_eq!(b, s[1..]); + let b = b.freeze(); + assert_eq!(b, s[1..]); +} + +#[test] +fn freeze_after_split_to() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + let _ = b.split_to(1); + assert_eq!(b, s[1..]); + let b = b.freeze(); + assert_eq!(b, s[1..]); +} + +#[test] +fn freeze_after_truncate() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + b.truncate(7); + assert_eq!(b, s[..7]); + let b = b.freeze(); + assert_eq!(b, s[..7]); +} + +#[test] +fn freeze_after_truncate_arc() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + // Make b Arc + let _ = b.split_to(0); + b.truncate(7); + assert_eq!(b, s[..7]); + let b = b.freeze(); + assert_eq!(b, s[..7]); +} + +#[test] +fn freeze_after_split_off() { + let s = &b"abcdefgh"[..]; + let mut b = BytesMut::from(s); + let _ = b.split_off(7); + assert_eq!(b, s[..7]); + let b = b.freeze(); + assert_eq!(b, s[..7]); +} + #[test] fn fns_defined_for_bytes_mut() { let mut bytes = BytesMut::from(&b"hello world"[..]); From b4ebe8432ec7e90cc4a4b5ccfb2c9e131bda3a36 Mon Sep 17 00:00:00 2001 From: Tim Hambourger Date: Fri, 13 Mar 2020 16:50:04 -0500 Subject: [PATCH 002/203] Fix #354 -- Make advance_mut impl of BufMut for Vec panic if cnt > remaining --- src/buf/buf_mut.rs | 12 +++++++----- tests/test_buf_mut.rs | 5 ++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index f5ed2a771..ab9ad1844 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -990,11 +990,13 @@ impl BufMut for Vec { unsafe fn advance_mut(&mut self, cnt: usize) { let len = self.len(); let remaining = self.capacity() - len; - if cnt > remaining { - // Reserve additional capacity, and ensure that the total length - // will not overflow usize. - self.reserve(cnt); - } + + assert!( + cnt <= remaining, + "cannot advance past `remaining_mut`: {:?} <= {:?}", + cnt, + remaining + ); self.set_len(len + cnt); } diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index f002f7d5f..2e74fb976 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -45,13 +45,12 @@ fn test_put_u16() { } #[test] +#[should_panic(expected = "cannot advance")] fn test_vec_advance_mut() { - // Regression test for carllerche/bytes#108. + // Verify fix for #354 let mut buf = Vec::with_capacity(8); unsafe { buf.advance_mut(12); - assert_eq!(buf.len(), 12); - assert!(buf.capacity() >= 12, "capacity: {}", buf.capacity()); } } From fa464ca483c65464d05f6c59e57cefe7a2dd125d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 May 2020 10:54:52 +0900 Subject: [PATCH 003/203] Fix CI failure on MacOS (#388) macOS-10.13 has been removed. --- ci/azure-test-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure-test-stable.yml b/ci/azure-test-stable.yml index e543eeeb4..b3e614a7b 100644 --- a/ci/azure-test-stable.yml +++ b/ci/azure-test-stable.yml @@ -13,7 +13,7 @@ jobs: ${{ if parameters.cross }}: MacOS: - vmImage: macOS-10.13 + vmImage: macOS-10.14 Windows: vmImage: vs2017-win2016 pool: From e8fa2511ffda2412244275e4287b5c59ce4dfc26 Mon Sep 17 00:00:00 2001 From: Cheng XU <3105373+xu-cheng@users.noreply.github.com> Date: Thu, 21 May 2020 19:18:20 -0700 Subject: [PATCH 004/203] Fix tests for no_std build (#384) This fixes `cargo test --no-default-features`. --- tests/test_buf.rs | 2 ++ tests/test_buf_mut.rs | 9 ++++++--- tests/test_chain.rs | 2 ++ tests/test_reader.rs | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 12b75a4ad..26b95aee8 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -1,6 +1,7 @@ #![deny(warnings, rust_2018_idioms)] use bytes::Buf; +#[cfg(feature = "std")] use std::io::IoSlice; #[test] @@ -42,6 +43,7 @@ fn test_get_u16_buffer_underflow() { buf.get_u16(); } +#[cfg(feature = "std")] #[test] fn test_bufs_vec() { let buf = &b"hello world"[..]; diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 2e74fb976..d2f9b8e4c 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -1,8 +1,10 @@ #![deny(warnings, rust_2018_idioms)] -use bytes::{buf::IoSliceMut, BufMut, BytesMut}; -use std::usize; -use std::fmt::Write; +use bytes::{BufMut, BytesMut}; +#[cfg(feature = "std")] +use bytes::buf::IoSliceMut; +use core::usize; +use core::fmt::Write; #[test] fn test_vec_as_mut_buf() { @@ -64,6 +66,7 @@ fn test_clone() { assert!(buf != buf2); } +#[cfg(feature = "std")] #[test] fn test_bufs_vec_mut() { let b1: &mut [u8] = &mut []; diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 332571d8b..df354bf0f 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -2,6 +2,7 @@ use bytes::{Buf, BufMut, Bytes}; use bytes::buf::{BufExt, BufMutExt}; +#[cfg(feature = "std")] use std::io::IoSlice; #[test] @@ -42,6 +43,7 @@ fn iterating_two_bufs() { assert_eq!(res, &b"helloworld"[..]); } +#[cfg(feature = "std")] #[test] fn vectored_read() { let a = Bytes::from(&b"hello"[..]); diff --git a/tests/test_reader.rs b/tests/test_reader.rs index 9c5972a96..608d97410 100644 --- a/tests/test_reader.rs +++ b/tests/test_reader.rs @@ -1,4 +1,5 @@ #![deny(warnings, rust_2018_idioms)] +#![cfg(feature = "std")] use std::io::{BufRead, Read}; From 9f40abc12a72aed5cb1ac37d769eb39b2a9d9a85 Mon Sep 17 00:00:00 2001 From: Cheng XU <3105373+xu-cheng@users.noreply.github.com> Date: Thu, 21 May 2020 19:19:46 -0700 Subject: [PATCH 005/203] Allow using serde feature in no_std environment (#385) When `serde` is used, it would enable its default feature `std`. This of course breaks no_std build. Fix this by disabling serde's default features. This should work for both serde + std and serde + no_std case. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 257e78858..79b6773b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ default = ["std"] std = [] [dependencies] -serde = { version = "1.0", optional = true } +serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] serde_test = "1.0" From 5e93fa4c6b3d6209329a3a9c90c6823886affea1 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 21 May 2020 19:23:22 -0700 Subject: [PATCH 006/203] Update to loom v0.3 (#381) --- Cargo.toml | 2 +- src/bytes.rs | 48 ++++++++++++++++++++++++++++-------------------- src/bytes_mut.rs | 10 +++++++--- src/loom.rs | 23 ++++++++++++++++++++++- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79b6773b9..c3ba22860 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,4 @@ serde_test = "1.0" # loom is currently not compiling on windows. # See: https://github.com/Xudong-Huang/generator-rs/issues/19 [target.'cfg(not(windows))'.dev-dependencies] -loom = "0.2.13" +loom = "0.3" diff --git a/src/bytes.rs b/src/bytes.rs index 380b1c681..eb75e1b5c 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -7,6 +7,9 @@ use alloc::{vec::Vec, string::String, boxed::Box, borrow::Borrow}; use crate::Buf; use crate::buf::IntoIter; use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +#[allow(unused)] +use crate::loom::sync::atomic::AtomicMut; + /// A reference counted contiguous slice of memory. /// @@ -854,16 +857,18 @@ unsafe fn promotable_even_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize } unsafe fn promotable_even_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) { - let shared = *data.get_mut(); - let kind = shared as usize & KIND_MASK; + data.with_mut(|shared| { + let shared = *shared; + let kind = shared as usize & KIND_MASK; - if kind == KIND_ARC { - release_shared(shared as *mut Shared); - } else { - debug_assert_eq!(kind, KIND_VEC); - let buf = (shared as usize & !KIND_MASK) as *mut u8; - drop(rebuild_boxed_slice(buf, ptr, len)); - } + if kind == KIND_ARC { + release_shared(shared as *mut Shared); + } else { + debug_assert_eq!(kind, KIND_VEC); + let buf = (shared as usize & !KIND_MASK) as *mut u8; + drop(rebuild_boxed_slice(buf, ptr, len)); + } + }); } unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { @@ -879,16 +884,18 @@ unsafe fn promotable_odd_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) } unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) { - let shared = *data.get_mut(); - let kind = shared as usize & KIND_MASK; + data.with_mut(|shared| { + let shared = *shared; + let kind = shared as usize & KIND_MASK; - if kind == KIND_ARC { - release_shared(shared as *mut Shared); - } else { - debug_assert_eq!(kind, KIND_VEC); + if kind == KIND_ARC { + release_shared(shared as *mut Shared); + } else { + debug_assert_eq!(kind, KIND_VEC); - drop(rebuild_boxed_slice(shared as *mut u8, ptr, len)); - } + drop(rebuild_boxed_slice(shared as *mut u8, ptr, len)); + } + }); } unsafe fn rebuild_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) -> Box<[u8]> { @@ -925,8 +932,9 @@ unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Byte } unsafe fn shared_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { - let shared = *data.get_mut(); - release_shared(shared as *mut Shared); + data.with_mut(|shared| { + release_shared(*shared as *mut Shared); + }); } unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) -> Bytes { @@ -1062,7 +1070,7 @@ fn _split_off_must_use() {} // fuzz tests #[cfg(all(test, loom))] mod fuzz { - use std::sync::Arc; + use loom::sync::Arc; use loom::thread; use super::Bytes; diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index dda5c471e..e0630cb9a 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -10,6 +10,9 @@ use crate::{Bytes, Buf, BufMut}; use crate::bytes::Vtable; use crate::buf::IntoIter; use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +#[allow(unused)] +use crate::loom::sync::atomic::AtomicMut; + /// A unique reference to a contiguous slice of memory. /// @@ -1482,8 +1485,9 @@ unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> By } unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { - let shared = (*data.get_mut()) as *mut Shared; - release_shared(shared as *mut Shared); + data.with_mut(|shared| { + release_shared(*shared as *mut Shared); + }); } // compile-fails @@ -1521,7 +1525,7 @@ fn _split_must_use() {} // fuzz tests #[cfg(all(test, loom))] mod fuzz { - use std::sync::Arc; + use loom::sync::Arc; use loom::thread; use crate::Bytes; diff --git a/src/loom.rs b/src/loom.rs index 80947acec..1cae8812e 100644 --- a/src/loom.rs +++ b/src/loom.rs @@ -2,8 +2,29 @@ pub(crate) mod sync { pub(crate) mod atomic { pub(crate) use core::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering}; + + pub(crate) trait AtomicMut { + fn with_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut *mut T) -> R; + } + + impl AtomicMut for AtomicPtr { + fn with_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut *mut T) -> R, + { + f(self.get_mut()) + } + } } } #[cfg(all(test, loom))] -pub(crate) use ::loom::sync; +pub(crate) mod sync { + pub(crate) mod atomic { + pub(crate) use loom::sync::atomic::{fence, AtomicPtr, AtomicUsize, Ordering}; + + pub(crate) trait AtomicMut {} + } +} From 1fbf83816b60fbaa2f9e85585338138c3efa949f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 22 May 2020 13:17:30 +0900 Subject: [PATCH 007/203] Format with rustfmt (#389) * Format with rustfmt * Add rustfmt check to CI --- azure-pipelines.yml | 9 +- benches/buf.rs | 2 +- benches/bytes.rs | 5 +- benches/bytes_mut.rs | 39 +++++-- ci/azure-rustfmt.yml | 13 +++ src/buf/buf_impl.rs | 195 +++++++++++++++++----------------- src/buf/buf_mut.rs | 168 +++++++++++++++-------------- src/buf/ext/chain.rs | 21 ++-- src/buf/ext/limit.rs | 5 +- src/buf/ext/mod.rs | 24 +++-- src/buf/ext/reader.rs | 2 +- src/buf/ext/take.rs | 5 +- src/buf/iter.rs | 3 +- src/buf/mod.rs | 3 +- src/bytes.rs | 57 +++++----- src/bytes_mut.rs | 111 +++++++++++-------- src/fmt/debug.rs | 2 +- src/fmt/hex.rs | 2 +- src/lib.rs | 17 +-- src/serde.rs | 29 +++-- tests/test_buf_mut.rs | 4 +- tests/test_bytes.rs | 12 +-- tests/test_bytes_vec_alloc.rs | 6 +- tests/test_chain.rs | 2 +- tests/test_iter.rs | 1 - tests/test_reader.rs | 2 +- tests/test_serde.rs | 2 +- 27 files changed, 410 insertions(+), 331 deletions(-) create mode 100644 ci/azure-rustfmt.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 18b59745d..a92c3c4bb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,9 +3,10 @@ pr: ["master"] jobs: # Check formatting -# - template: ci/azure-rustfmt.yml -# parameters: -# name: rustfmt +- template: ci/azure-rustfmt.yml + parameters: + name: rustfmt + rust_version: stable # Apply clippy lints # - template: ci/azure-clippy.yml @@ -60,7 +61,7 @@ jobs: - template: ci/azure-deploy-docs.yml parameters: dependsOn: - # - rustfmt + - rustfmt # - clippy - stable - nightly diff --git a/benches/buf.rs b/benches/buf.rs index 0c9a1d955..8f14aec20 100644 --- a/benches/buf.rs +++ b/benches/buf.rs @@ -3,8 +3,8 @@ extern crate test; -use test::Bencher; use bytes::Buf; +use test::Bencher; /// Dummy Buf implementation struct TestBuf { diff --git a/benches/bytes.rs b/benches/bytes.rs index 9c36e6081..1741ba0a1 100644 --- a/benches/bytes.rs +++ b/benches/bytes.rs @@ -3,8 +3,8 @@ extern crate test; -use test::Bencher; use bytes::Bytes; +use test::Bencher; #[bench] fn deref_unique(b: &mut Bencher) { @@ -42,7 +42,8 @@ fn deref_static(b: &mut Bencher) { #[bench] fn clone_static(b: &mut Bencher) { - let bytes = Bytes::from_static("hello world 1234567890 and have a good byte 0987654321".as_bytes()); + let bytes = + Bytes::from_static("hello world 1234567890 and have a good byte 0987654321".as_bytes()); b.iter(|| { for _ in 0..1024 { diff --git a/benches/bytes_mut.rs b/benches/bytes_mut.rs index ded1d1486..8e0226c76 100644 --- a/benches/bytes_mut.rs +++ b/benches/bytes_mut.rs @@ -3,8 +3,8 @@ extern crate test; -use test::Bencher; use bytes::{BufMut, BytesMut}; +use test::Bencher; #[bench] fn alloc_small(b: &mut Bencher) { @@ -29,7 +29,6 @@ fn alloc_big(b: &mut Bencher) { }) } - #[bench] fn deref_unique(b: &mut Bencher) { let mut buf = BytesMut::with_capacity(4096); @@ -92,7 +91,9 @@ fn deref_two(b: &mut Bencher) { #[bench] fn clone_frozen(b: &mut Bencher) { - let bytes = BytesMut::from(&b"hello world 1234567890 and have a good byte 0987654321"[..]).split().freeze(); + let bytes = BytesMut::from(&b"hello world 1234567890 and have a good byte 0987654321"[..]) + .split() + .freeze(); b.iter(|| { for _ in 0..1024 { @@ -137,7 +138,9 @@ fn fmt_write(b: &mut Bencher) { b.iter(|| { let _ = write!(buf, "{}", s); test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }) } @@ -152,7 +155,9 @@ fn bytes_mut_extend(b: &mut Bencher) { buf.extend(&data); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -169,7 +174,9 @@ fn put_slice_bytes_mut(b: &mut Bencher) { buf.put_slice(&data); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -184,7 +191,9 @@ fn put_u8_bytes_mut(b: &mut Bencher) { buf.put_u8(b'x'); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -199,7 +208,9 @@ fn put_slice_vec(b: &mut Bencher) { buf.put_slice(&data); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -214,7 +225,9 @@ fn put_u8_vec(b: &mut Bencher) { buf.put_u8(b'x'); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -229,7 +242,9 @@ fn put_slice_vec_extend(b: &mut Bencher) { buf.extend_from_slice(&data); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } @@ -244,6 +259,8 @@ fn put_u8_vec_push(b: &mut Bencher) { buf.push(b'x'); } test::black_box(&buf); - unsafe { buf.set_len(0); } + unsafe { + buf.set_len(0); + } }); } diff --git a/ci/azure-rustfmt.yml b/ci/azure-rustfmt.yml new file mode 100644 index 000000000..a0a133890 --- /dev/null +++ b/ci/azure-rustfmt.yml @@ -0,0 +1,13 @@ +jobs: +# Check formatting +- job: ${{ parameters.name }} + displayName: Check rustfmt + pool: + vmImage: ubuntu-16.04 + steps: + - template: azure-install-rust.yml + parameters: + rust_version: ${{ parameters.rust_version }} + - script: | + cargo fmt --all -- --check + displayName: Check formatting diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 843db718f..5cd7c686e 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -1,22 +1,23 @@ -use core::{cmp, ptr, mem}; +use core::{cmp, mem, ptr}; #[cfg(feature = "std")] use std::io::IoSlice; -use alloc::{boxed::Box}; +use alloc::boxed::Box; macro_rules! buf_get_impl { - ($this:ident, $typ:tt::$conv:tt) => ({ + ($this:ident, $typ:tt::$conv:tt) => {{ const SIZE: usize = mem::size_of::<$typ>(); - // try to convert directly from the bytes - // this Option trick is to avoid keeping a borrow on self - // when advance() is called (mut borrow) and to call bytes() only once - let ret = $this.bytes().get(..SIZE).map(|src| unsafe { - $typ::$conv(*(src as *const _ as *const [_; SIZE])) - }); + // try to convert directly from the bytes + // this Option trick is to avoid keeping a borrow on self + // when advance() is called (mut borrow) and to call bytes() only once + let ret = $this + .bytes() + .get(..SIZE) + .map(|src| unsafe { $typ::$conv(*(src as *const _ as *const [_; SIZE])) }); if let Some(ret) = ret { - // if the direct conversion was possible, advance and return + // if the direct conversion was possible, advance and return $this.advance(SIZE); return ret; } else { @@ -25,8 +26,8 @@ macro_rules! buf_get_impl { $this.copy_to_slice(&mut buf); // (do the advance) return $typ::$conv(buf); } - }); - (le => $this:ident, $typ:tt, $len_to_read:expr) => ({ + }}; + (le => $this:ident, $typ:tt, $len_to_read:expr) => {{ debug_assert!(mem::size_of::<$typ>() >= $len_to_read); // The same trick as above does not improve the best case speed. @@ -34,12 +35,12 @@ macro_rules! buf_get_impl { let mut buf = [0; (mem::size_of::<$typ>())]; $this.copy_to_slice(&mut buf[..($len_to_read)]); return $typ::from_le_bytes(buf); - }); + }}; (be => $this:ident, $typ:tt, $len_to_read:expr) => {{ debug_assert!(mem::size_of::<$typ>() >= $len_to_read); let mut buf = [0; (mem::size_of::<$typ>())]; - $this.copy_to_slice(&mut buf[mem::size_of::<$typ>()-($len_to_read)..]); + $this.copy_to_slice(&mut buf[mem::size_of::<$typ>() - ($len_to_read)..]); return $typ::from_be_bytes(buf); }}; } @@ -251,8 +252,7 @@ pub trait Buf { let src = self.bytes(); cnt = cmp::min(src.len(), dst.len() - off); - ptr::copy_nonoverlapping( - src.as_ptr(), dst[off..].as_mut_ptr(), cnt); + ptr::copy_nonoverlapping(src.as_ptr(), dst[off..].as_mut_ptr(), cnt); off += cnt; } @@ -810,109 +810,108 @@ pub trait Buf { } macro_rules! deref_forward_buf { - () => ( - fn remaining(&self) -> usize { - (**self).remaining() - } - - fn bytes(&self) -> &[u8] { - (**self).bytes() - } + () => { + fn remaining(&self) -> usize { + (**self).remaining() + } - #[cfg(feature = "std")] - fn bytes_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { - (**self).bytes_vectored(dst) - } + fn bytes(&self) -> &[u8] { + (**self).bytes() + } - fn advance(&mut self, cnt: usize) { - (**self).advance(cnt) - } + #[cfg(feature = "std")] + fn bytes_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { + (**self).bytes_vectored(dst) + } - fn has_remaining(&self) -> bool { - (**self).has_remaining() - } + fn advance(&mut self, cnt: usize) { + (**self).advance(cnt) + } - fn copy_to_slice(&mut self, dst: &mut [u8]) { - (**self).copy_to_slice(dst) - } + fn has_remaining(&self) -> bool { + (**self).has_remaining() + } - fn get_u8(&mut self) -> u8 { - (**self).get_u8() - } + fn copy_to_slice(&mut self, dst: &mut [u8]) { + (**self).copy_to_slice(dst) + } - fn get_i8(&mut self) -> i8 { - (**self).get_i8() - } + fn get_u8(&mut self) -> u8 { + (**self).get_u8() + } - fn get_u16(&mut self) -> u16 { - (**self).get_u16() - } + fn get_i8(&mut self) -> i8 { + (**self).get_i8() + } - fn get_u16_le(&mut self) -> u16 { - (**self).get_u16_le() - } + fn get_u16(&mut self) -> u16 { + (**self).get_u16() + } - fn get_i16(&mut self) -> i16 { - (**self).get_i16() - } + fn get_u16_le(&mut self) -> u16 { + (**self).get_u16_le() + } - fn get_i16_le(&mut self) -> i16 { - (**self).get_i16_le() - } + fn get_i16(&mut self) -> i16 { + (**self).get_i16() + } - fn get_u32(&mut self) -> u32 { - (**self).get_u32() - } + fn get_i16_le(&mut self) -> i16 { + (**self).get_i16_le() + } - fn get_u32_le(&mut self) -> u32 { - (**self).get_u32_le() - } + fn get_u32(&mut self) -> u32 { + (**self).get_u32() + } - fn get_i32(&mut self) -> i32 { - (**self).get_i32() - } + fn get_u32_le(&mut self) -> u32 { + (**self).get_u32_le() + } - fn get_i32_le(&mut self) -> i32 { - (**self).get_i32_le() - } + fn get_i32(&mut self) -> i32 { + (**self).get_i32() + } - fn get_u64(&mut self) -> u64 { - (**self).get_u64() - } + fn get_i32_le(&mut self) -> i32 { + (**self).get_i32_le() + } - fn get_u64_le(&mut self) -> u64 { - (**self).get_u64_le() - } + fn get_u64(&mut self) -> u64 { + (**self).get_u64() + } - fn get_i64(&mut self) -> i64 { - (**self).get_i64() - } + fn get_u64_le(&mut self) -> u64 { + (**self).get_u64_le() + } - fn get_i64_le(&mut self) -> i64 { - (**self).get_i64_le() - } + fn get_i64(&mut self) -> i64 { + (**self).get_i64() + } - fn get_uint(&mut self, nbytes: usize) -> u64 { - (**self).get_uint(nbytes) - } + fn get_i64_le(&mut self) -> i64 { + (**self).get_i64_le() + } - fn get_uint_le(&mut self, nbytes: usize) -> u64 { - (**self).get_uint_le(nbytes) - } + fn get_uint(&mut self, nbytes: usize) -> u64 { + (**self).get_uint(nbytes) + } - fn get_int(&mut self, nbytes: usize) -> i64 { - (**self).get_int(nbytes) - } + fn get_uint_le(&mut self, nbytes: usize) -> u64 { + (**self).get_uint_le(nbytes) + } - fn get_int_le(&mut self, nbytes: usize) -> i64 { - (**self).get_int_le(nbytes) - } + fn get_int(&mut self, nbytes: usize) -> i64 { + (**self).get_int(nbytes) + } - fn to_bytes(&mut self) -> crate::Bytes { - (**self).to_bytes() - } + fn get_int_le(&mut self, nbytes: usize) -> i64 { + (**self).get_int_le(nbytes) + } - ) + fn to_bytes(&mut self) -> crate::Bytes { + (**self).to_bytes() + } + }; } impl Buf for &mut T { @@ -950,7 +949,8 @@ impl Buf for Option<[u8; 1]> { } fn bytes(&self) -> &[u8] { - self.as_ref().map(AsRef::as_ref) + self.as_ref() + .map(AsRef::as_ref) .unwrap_or(Default::default()) } @@ -994,7 +994,8 @@ impl> Buf for std::io::Cursor { fn advance(&mut self, cnt: usize) { let pos = (self.position() as usize) - .checked_add(cnt).expect("overflow"); + .checked_add(cnt) + .expect("overflow"); assert!(pos <= self.get_ref().as_ref().len()); self.set_position(pos as u64); diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index ab9ad1844..628b240a3 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1,9 +1,13 @@ -use core::{cmp, mem::{self, MaybeUninit}, ptr, usize}; +use core::{ + cmp, + mem::{self, MaybeUninit}, + ptr, usize, +}; #[cfg(feature = "std")] use std::fmt; -use alloc::{vec::Vec, boxed::Box}; +use alloc::{boxed::Box, vec::Vec}; /// A trait for values that provide sequential write access to bytes. /// @@ -226,7 +230,10 @@ pub trait BufMut { /// # Panics /// /// Panics if `self` does not have enough capacity to contain `src`. - fn put(&mut self, mut src: T) where Self: Sized { + fn put(&mut self, mut src: T) + where + Self: Sized, + { assert!(self.remaining_mut() >= src.remaining()); while src.has_remaining() { @@ -237,14 +244,13 @@ pub trait BufMut { let d = self.bytes_mut(); l = cmp::min(s.len(), d.len()); - ptr::copy_nonoverlapping( - s.as_ptr(), - d.as_mut_ptr() as *mut u8, - l); + ptr::copy_nonoverlapping(s.as_ptr(), d.as_mut_ptr() as *mut u8, l); } src.advance(l); - unsafe { self.advance_mut(l); } + unsafe { + self.advance_mut(l); + } } } @@ -270,7 +276,12 @@ pub trait BufMut { fn put_slice(&mut self, src: &[u8]) { let mut off = 0; - assert!(self.remaining_mut() >= src.len(), "buffer overflow; remaining = {}; src = {}", self.remaining_mut(), src.len()); + assert!( + self.remaining_mut() >= src.len(), + "buffer overflow; remaining = {}; src = {}", + self.remaining_mut(), + src.len() + ); while off < src.len() { let cnt; @@ -279,16 +290,14 @@ pub trait BufMut { let dst = self.bytes_mut(); cnt = cmp::min(dst.len(), src.len() - off); - ptr::copy_nonoverlapping( - src[off..].as_ptr(), - dst.as_mut_ptr() as *mut u8, - cnt); + ptr::copy_nonoverlapping(src[off..].as_ptr(), dst.as_mut_ptr() as *mut u8, cnt); off += cnt; - } - unsafe { self.advance_mut(cnt); } + unsafe { + self.advance_mut(cnt); + } } } @@ -872,84 +881,84 @@ pub trait BufMut { } macro_rules! deref_forward_bufmut { - () => ( - fn remaining_mut(&self) -> usize { - (**self).remaining_mut() - } + () => { + fn remaining_mut(&self) -> usize { + (**self).remaining_mut() + } - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { - (**self).bytes_mut() - } + fn bytes_mut(&mut self) -> &mut [MaybeUninit] { + (**self).bytes_mut() + } - #[cfg(feature = "std")] - fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize { - (**self).bytes_vectored_mut(dst) - } + #[cfg(feature = "std")] + fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize { + (**self).bytes_vectored_mut(dst) + } - unsafe fn advance_mut(&mut self, cnt: usize) { - (**self).advance_mut(cnt) - } + unsafe fn advance_mut(&mut self, cnt: usize) { + (**self).advance_mut(cnt) + } - fn put_slice(&mut self, src: &[u8]) { - (**self).put_slice(src) - } + fn put_slice(&mut self, src: &[u8]) { + (**self).put_slice(src) + } - fn put_u8(&mut self, n: u8) { - (**self).put_u8(n) - } + fn put_u8(&mut self, n: u8) { + (**self).put_u8(n) + } - fn put_i8(&mut self, n: i8) { - (**self).put_i8(n) - } + fn put_i8(&mut self, n: i8) { + (**self).put_i8(n) + } - fn put_u16(&mut self, n: u16) { - (**self).put_u16(n) - } + fn put_u16(&mut self, n: u16) { + (**self).put_u16(n) + } - fn put_u16_le(&mut self, n: u16) { - (**self).put_u16_le(n) - } + fn put_u16_le(&mut self, n: u16) { + (**self).put_u16_le(n) + } - fn put_i16(&mut self, n: i16) { - (**self).put_i16(n) - } + fn put_i16(&mut self, n: i16) { + (**self).put_i16(n) + } - fn put_i16_le(&mut self, n: i16) { - (**self).put_i16_le(n) - } + fn put_i16_le(&mut self, n: i16) { + (**self).put_i16_le(n) + } - fn put_u32(&mut self, n: u32) { - (**self).put_u32(n) - } + fn put_u32(&mut self, n: u32) { + (**self).put_u32(n) + } - fn put_u32_le(&mut self, n: u32) { - (**self).put_u32_le(n) - } + fn put_u32_le(&mut self, n: u32) { + (**self).put_u32_le(n) + } - fn put_i32(&mut self, n: i32) { - (**self).put_i32(n) - } + fn put_i32(&mut self, n: i32) { + (**self).put_i32(n) + } - fn put_i32_le(&mut self, n: i32) { - (**self).put_i32_le(n) - } + fn put_i32_le(&mut self, n: i32) { + (**self).put_i32_le(n) + } - fn put_u64(&mut self, n: u64) { - (**self).put_u64(n) - } + fn put_u64(&mut self, n: u64) { + (**self).put_u64(n) + } - fn put_u64_le(&mut self, n: u64) { - (**self).put_u64_le(n) - } + fn put_u64_le(&mut self, n: u64) { + (**self).put_u64_le(n) + } - fn put_i64(&mut self, n: i64) { - (**self).put_i64(n) - } + fn put_i64(&mut self, n: i64) { + (**self).put_i64(n) + } - fn put_i64_le(&mut self, n: i64) { - (**self).put_i64_le(n) - } - ) + fn put_i64_le(&mut self, n: i64) { + (**self).put_i64_le(n) + } + }; } impl BufMut for &mut T { @@ -1013,15 +1022,16 @@ impl BufMut for Vec { let len = self.len(); let ptr = self.as_mut_ptr() as *mut MaybeUninit; - unsafe { - &mut slice::from_raw_parts_mut(ptr, cap)[len..] - } + unsafe { &mut slice::from_raw_parts_mut(ptr, cap)[len..] } } // Specialize these methods so they can skip checking `remaining_mut` // and `advance_mut`. - fn put(&mut self, mut src: T) where Self: Sized { + fn put(&mut self, mut src: T) + where + Self: Sized, + { // In case the src isn't contiguous, reserve upfront self.reserve(src.remaining()); diff --git a/src/buf/ext/chain.rs b/src/buf/ext/chain.rs index a1ec597df..e62e2f1b9 100644 --- a/src/buf/ext/chain.rs +++ b/src/buf/ext/chain.rs @@ -1,12 +1,12 @@ -use crate::{Buf, BufMut}; use crate::buf::IntoIter; +use crate::{Buf, BufMut}; use core::mem::MaybeUninit; -#[cfg(feature = "std")] -use std::io::{IoSlice}; #[cfg(feature = "std")] use crate::buf::IoSliceMut; +#[cfg(feature = "std")] +use std::io::IoSlice; /// A `Chain` sequences two buffers. /// @@ -41,10 +41,7 @@ pub struct Chain { impl Chain { /// Creates a new `Chain` sequencing the provided values. pub fn new(a: T, b: U) -> Chain { - Chain { - a, - b, - } + Chain { a, b } } /// Gets a reference to the first underlying `Buf`. @@ -137,8 +134,9 @@ impl Chain { } impl Buf for Chain - where T: Buf, - U: Buf, +where + T: Buf, + U: Buf, { fn remaining(&self) -> usize { self.a.remaining() + self.b.remaining() @@ -179,8 +177,9 @@ impl Buf for Chain } impl BufMut for Chain - where T: BufMut, - U: BufMut, +where + T: BufMut, + U: BufMut, { fn remaining_mut(&self) -> usize { self.a.remaining_mut() + self.b.remaining_mut() diff --git a/src/buf/ext/limit.rs b/src/buf/ext/limit.rs index f86e01151..a36eceeef 100644 --- a/src/buf/ext/limit.rs +++ b/src/buf/ext/limit.rs @@ -11,10 +11,7 @@ pub struct Limit { } pub(super) fn new(inner: T, limit: usize) -> Limit { - Limit { - inner, - limit, - } + Limit { inner, limit } } impl Limit { diff --git a/src/buf/ext/mod.rs b/src/buf/ext/mod.rs index 7b0bdab20..7d6181438 100644 --- a/src/buf/ext/mod.rs +++ b/src/buf/ext/mod.rs @@ -10,9 +10,9 @@ mod take; #[cfg(feature = "std")] mod writer; +pub use self::chain::Chain; pub use self::limit::Limit; pub use self::take::Take; -pub use self::chain::Chain; #[cfg(feature = "std")] pub use self::{reader::Reader, writer::Writer}; @@ -41,7 +41,8 @@ pub trait BufExt: Buf { /// assert_eq!(dst, b" world"); /// ``` fn take(self, limit: usize) -> Take - where Self: Sized + where + Self: Sized, { take::new(self, limit) } @@ -62,7 +63,8 @@ pub trait BufExt: Buf { /// assert_eq!(full.bytes(), b"hello world"); /// ``` fn chain(self, next: U) -> Chain - where Self: Sized + where + Self: Sized, { Chain::new(self, next) } @@ -91,7 +93,10 @@ pub trait BufExt: Buf { /// assert_eq!(&dst[..11], &b"hello world"[..]); /// ``` #[cfg(feature = "std")] - fn reader(self) -> Reader where Self: Sized { + fn reader(self) -> Reader + where + Self: Sized, + { reader::new(self) } } @@ -114,7 +119,8 @@ pub trait BufMutExt: BufMut { /// assert_eq!(dst.remaining_mut(), 10); /// ``` fn limit(self, limit: usize) -> Limit - where Self: Sized + where + Self: Sized, { limit::new(self, limit) } @@ -142,7 +148,10 @@ pub trait BufMutExt: BufMut { /// assert_eq!(*buf, b"hello world"[..]); /// ``` #[cfg(feature = "std")] - fn writer(self) -> Writer where Self: Sized { + fn writer(self) -> Writer + where + Self: Sized, + { writer::new(self) } @@ -167,7 +176,8 @@ pub trait BufMutExt: BufMut { /// assert_eq!(&b[..], b" world"); /// ``` fn chain_mut(self, next: U) -> Chain - where Self: Sized + where + Self: Sized, { Chain::new(self, next) } diff --git a/src/buf/ext/reader.rs b/src/buf/ext/reader.rs index e38103b1d..bc171add1 100644 --- a/src/buf/ext/reader.rs +++ b/src/buf/ext/reader.rs @@ -1,4 +1,4 @@ -use crate::{Buf}; +use crate::Buf; use std::{cmp, io}; diff --git a/src/buf/ext/take.rs b/src/buf/ext/take.rs index 6fc4ffc72..6247165d7 100644 --- a/src/buf/ext/take.rs +++ b/src/buf/ext/take.rs @@ -13,10 +13,7 @@ pub struct Take { } pub fn new(inner: T, limit: usize) -> Take { - Take { - inner, - limit, - } + Take { inner, limit } } impl Take { diff --git a/src/buf/iter.rs b/src/buf/iter.rs index 1af421a8d..5a3f26a2e 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -109,7 +109,6 @@ impl IntoIter { } } - impl Iterator for IntoIter { type Item = u8; @@ -130,4 +129,4 @@ impl Iterator for IntoIter { } } -impl ExactSizeIterator for IntoIter { } +impl ExactSizeIterator for IntoIter {} diff --git a/src/buf/mod.rs b/src/buf/mod.rs index d4538f21e..1d7292c9e 100644 --- a/src/buf/mod.rs +++ b/src/buf/mod.rs @@ -24,8 +24,7 @@ mod vec_deque; pub use self::buf_impl::Buf; pub use self::buf_mut::BufMut; -pub use self::ext::{BufExt, BufMutExt}; #[cfg(feature = "std")] pub use self::buf_mut::IoSliceMut; +pub use self::ext::{BufExt, BufMutExt}; pub use self::iter::IntoIter; - diff --git a/src/bytes.rs b/src/bytes.rs index eb75e1b5c..08bc9b3f3 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,15 +1,14 @@ -use core::{cmp, fmt, hash, mem, ptr, slice, usize}; -use core::iter::{FromIterator}; +use core::iter::FromIterator; use core::ops::{Deref, RangeBounds}; +use core::{cmp, fmt, hash, mem, ptr, slice, usize}; -use alloc::{vec::Vec, string::String, boxed::Box, borrow::Borrow}; +use alloc::{borrow::Borrow, boxed::Box, string::String, vec::Vec}; -use crate::Buf; use crate::buf::IntoIter; -use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; - +use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::Buf; /// A reference counted contiguous slice of memory. /// @@ -176,7 +175,6 @@ impl Bytes { self.len == 0 } - ///Creates `Bytes` instance from slice, by copying it. pub fn copy_from_slice(data: &[u8]) -> Self { data.to_vec().into() @@ -238,7 +236,6 @@ impl Bytes { return Bytes::new(); } - let mut ret = self.clone(); ret.len = end - begin; @@ -394,7 +391,6 @@ impl Bytes { return Bytes::new(); } - let mut ret = self.clone(); unsafe { self.inc_start(at) }; @@ -429,8 +425,9 @@ impl Bytes { // The Vec "promotable" vtables do not store the capacity, // so we cannot truncate while using this repr. We *have* to // promote using `split_off` so the capacity can be stored. - if self.vtable as *const Vtable == &PROMOTABLE_EVEN_VTABLE || - self.vtable as *const Vtable == &PROMOTABLE_ODD_VTABLE { + if self.vtable as *const Vtable == &PROMOTABLE_EVEN_VTABLE + || self.vtable as *const Vtable == &PROMOTABLE_ODD_VTABLE + { drop(self.split_off(len)); } else { self.len = len; @@ -455,7 +452,12 @@ impl Bytes { } #[inline] - pub(crate) unsafe fn with_vtable(ptr: *const u8, len: usize, data: AtomicPtr<()>, vtable: &'static Vtable) -> Bytes { + pub(crate) unsafe fn with_vtable( + ptr: *const u8, + len: usize, + data: AtomicPtr<()>, + vtable: &'static Vtable, + ) -> Bytes { Bytes { ptr, len, @@ -468,9 +470,7 @@ impl Bytes { #[inline] fn as_slice(&self) -> &[u8] { - unsafe { - slice::from_raw_parts(self.ptr, self.len) - } + unsafe { slice::from_raw_parts(self.ptr, self.len) } } #[inline] @@ -489,18 +489,14 @@ unsafe impl Sync for Bytes {} impl Drop for Bytes { #[inline] fn drop(&mut self) { - unsafe { - (self.vtable.drop)(&mut self.data, self.ptr, self.len) - } + unsafe { (self.vtable.drop)(&mut self.data, self.ptr, self.len) } } } impl Clone for Bytes { #[inline] fn clone(&self) -> Bytes { - unsafe { - (self.vtable.clone)(&self.data, self.ptr, self.len) - } + unsafe { (self.vtable.clone)(&self.data, self.ptr, self.len) } } } @@ -551,7 +547,10 @@ impl AsRef<[u8]> for Bytes { } impl hash::Hash for Bytes { - fn hash(&self, state: &mut H) where H: hash::Hasher { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { self.as_slice().hash(state); } } @@ -729,7 +728,8 @@ impl PartialOrd for &str { } impl<'a, T: ?Sized> PartialEq<&'a T> for Bytes - where Bytes: PartialEq +where + Bytes: PartialEq, { fn eq(&self, other: &&'a T) -> bool { *self == **other @@ -737,7 +737,8 @@ impl<'a, T: ?Sized> PartialEq<&'a T> for Bytes } impl<'a, T: ?Sized> PartialOrd<&'a T> for Bytes - where Bytes: PartialOrd +where + Bytes: PartialOrd, { fn partial_cmp(&self, other: &&'a T) -> Option { self.partial_cmp(&**other) @@ -953,7 +954,13 @@ unsafe fn shallow_clone_arc(shared: *mut Shared, ptr: *const u8, len: usize) -> } #[cold] -unsafe fn shallow_clone_vec(atom: &AtomicPtr<()>, ptr: *const (), buf: *mut u8, offset: *const u8, len: usize) -> Bytes { +unsafe fn shallow_clone_vec( + atom: &AtomicPtr<()>, + ptr: *const (), + buf: *mut u8, + offset: *const u8, + len: usize, +) -> Bytes { // If the buffer is still tracked in a `Vec`. It is time to // promote the vec to an `Arc`. This could potentially be called // concurrently, so some care must be taken. diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index e0630cb9a..5178198f0 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1,18 +1,22 @@ -use core::{cmp, fmt, hash, isize, slice, usize}; +use core::iter::{FromIterator, Iterator}; use core::mem::{self, ManuallyDrop}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; -use core::iter::{FromIterator, Iterator}; +use core::{cmp, fmt, hash, isize, slice, usize}; -use alloc::{vec::Vec, string::String, boxed::Box, borrow::{Borrow, BorrowMut}}; +use alloc::{ + borrow::{Borrow, BorrowMut}, + boxed::Box, + string::String, + vec::Vec, +}; -use crate::{Bytes, Buf, BufMut}; -use crate::bytes::Vtable; use crate::buf::IntoIter; -use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; - +use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::{Buf, BufMut, Bytes}; /// A unique reference to a contiguous slice of memory. /// @@ -247,9 +251,7 @@ impl BytesMut { let len = self.len; let data = AtomicPtr::new(self.data as _); mem::forget(self); - unsafe { - Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) - } + unsafe { Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) } } } @@ -391,7 +393,9 @@ impl BytesMut { /// [`split_off`]: #method.split_off pub fn truncate(&mut self, len: usize) { if len <= self.len() { - unsafe { self.set_len(len); } + unsafe { + self.set_len(len); + } } } @@ -572,7 +576,8 @@ impl BytesMut { self.cap += off; } else { // No space - allocate more - let mut v = ManuallyDrop::new(rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off)); + let mut v = + ManuallyDrop::new(rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off)); v.reserve(additional); // Update the info @@ -588,7 +593,6 @@ impl BytesMut { debug_assert_eq!(kind, KIND_ARC); let shared: *mut Shared = self.data as _; - // Reserving involves abandoning the currently shared buffer and // allocating a new vector with the requested capacity. // @@ -632,9 +636,7 @@ impl BytesMut { // check. let double = v.capacity().checked_shl(1).unwrap_or(new_cap); - new_cap = cmp::max( - cmp::max(double, new_cap), - original_capacity); + new_cap = cmp::max(cmp::max(double, new_cap), original_capacity); } else { new_cap = cmp::max(new_cap, original_capacity); } @@ -683,14 +685,12 @@ 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() as *mut u8, cnt); } - unsafe { self.advance_mut(cnt); } + unsafe { + self.advance_mut(cnt); + } } /// Absorbs a `BytesMut` that was previously split off. @@ -755,16 +755,12 @@ impl BytesMut { #[inline] fn as_slice(&self) -> &[u8] { - unsafe { - slice::from_raw_parts(self.ptr.as_ptr(), self.len) - } + unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) } } #[inline] fn as_slice_mut(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) - } + unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } } unsafe fn set_start(&mut self, start: usize) { @@ -793,7 +789,7 @@ impl BytesMut { // on 64 bit systems and will only happen on 32 bit systems // when shifting past 134,217,727 bytes. As such, we don't // worry too much about performance here. - self.promote_to_shared(/*ref_count = */1); + self.promote_to_shared(/*ref_count = */ 1); } } @@ -825,10 +821,10 @@ impl BytesMut { } let ptr = unsafe { self.ptr.as_ptr().offset(self.len as isize) }; - if ptr == other.ptr.as_ptr() && - self.kind() == KIND_ARC && - other.kind() == KIND_ARC && - self.data == other.data + if ptr == other.ptr.as_ptr() + && self.kind() == KIND_ARC + && other.kind() == KIND_ARC + && self.data == other.data { // Contiguous blocks, just combine directly self.len += other.len; @@ -889,7 +885,7 @@ impl BytesMut { increment_shared(self.data); ptr::read(self) } else { - self.promote_to_shared(/*ref_count = */2); + self.promote_to_shared(/*ref_count = */ 2); ptr::read(self) } } @@ -957,7 +953,9 @@ impl Buf for BytesMut { cnt, self.remaining(), ); - unsafe { self.set_start(cnt); } + unsafe { + self.set_start(cnt); + } } fn to_bytes(&mut self) -> crate::Bytes { @@ -974,7 +972,12 @@ impl BufMut for BytesMut { #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { let new_len = self.len() + cnt; - assert!(new_len <= self.cap, "new_len = {}; capacity = {}", new_len, self.cap); + assert!( + new_len <= self.cap, + "new_len = {}; capacity = {}", + new_len, + self.cap + ); self.len = new_len; } @@ -989,7 +992,10 @@ impl BufMut for BytesMut { // Specialize these methods so they can skip checking `remaining_mut` // and `advance_mut`. - fn put(&mut self, mut src: T) where Self: Sized { + fn put(&mut self, mut src: T) + where + Self: Sized, + { while src.has_remaining() { let s = src.bytes(); let l = s.len(); @@ -1068,8 +1074,7 @@ impl Ord for BytesMut { } } -impl Eq for BytesMut { -} +impl Eq for BytesMut {} impl Default for BytesMut { #[inline] @@ -1079,7 +1084,10 @@ impl Default for BytesMut { } impl hash::Hash for BytesMut { - fn hash(&self, state: &mut H) where H: hash::Hasher { + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { let s: &[u8] = self.as_ref(); s.hash(state); } @@ -1139,7 +1147,10 @@ impl<'a> IntoIterator for &'a BytesMut { } impl Extend for BytesMut { - fn extend(&mut self, iter: T) where T: IntoIterator { + fn extend(&mut self, iter: T) + where + T: IntoIterator, + { let iter = iter.into_iter(); let (lower, _) = iter.size_hint(); @@ -1156,7 +1167,10 @@ impl Extend for BytesMut { } impl<'a> Extend<&'a u8> for BytesMut { - fn extend(&mut self, iter: T) where T: IntoIterator { + fn extend(&mut self, iter: T) + where + T: IntoIterator, + { self.extend(iter.into_iter().map(|b| *b)) } } @@ -1234,7 +1248,10 @@ impl Shared { fn original_capacity_to_repr(cap: usize) -> usize { let width = PTR_WIDTH - ((cap >> MIN_ORIGINAL_CAPACITY_WIDTH).leading_zeros() as usize); - cmp::min(width, MAX_ORIGINAL_CAPACITY_WIDTH - MIN_ORIGINAL_CAPACITY_WIDTH) + cmp::min( + width, + MAX_ORIGINAL_CAPACITY_WIDTH - MIN_ORIGINAL_CAPACITY_WIDTH, + ) } fn original_capacity_from_repr(repr: usize) -> usize { @@ -1402,7 +1419,8 @@ impl PartialOrd for String { } impl<'a, T: ?Sized> PartialEq<&'a T> for BytesMut - where BytesMut: PartialEq +where + BytesMut: PartialEq, { fn eq(&self, other: &&'a T) -> bool { *self == **other @@ -1410,7 +1428,8 @@ impl<'a, T: ?Sized> PartialEq<&'a T> for BytesMut } impl<'a, T: ?Sized> PartialOrd<&'a T> for BytesMut - where BytesMut: PartialOrd +where + BytesMut: PartialOrd, { fn partial_cmp(&self, other: &&'a T) -> Option { self.partial_cmp(*other) @@ -1528,8 +1547,8 @@ mod fuzz { use loom::sync::Arc; use loom::thread; - use crate::Bytes; use super::BytesMut; + use crate::Bytes; #[test] fn bytes_mut_cloning_frozen() { diff --git a/src/fmt/debug.rs b/src/fmt/debug.rs index f6a08b863..a8545514e 100644 --- a/src/fmt/debug.rs +++ b/src/fmt/debug.rs @@ -1,7 +1,7 @@ use core::fmt::{Debug, Formatter, Result}; -use crate::{Bytes, BytesMut}; use super::BytesRef; +use crate::{Bytes, BytesMut}; /// Alternative implementation of `std::fmt::Debug` for byte slice. /// diff --git a/src/fmt/hex.rs b/src/fmt/hex.rs index 09170ae1a..97a749a33 100644 --- a/src/fmt/hex.rs +++ b/src/fmt/hex.rs @@ -1,7 +1,7 @@ use core::fmt::{Formatter, LowerHex, Result, UpperHex}; -use crate::{Bytes, BytesMut}; use super::BytesRef; +use crate::{Bytes, BytesMut}; impl LowerHex for BytesRef<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { diff --git a/src/lib.rs b/src/lib.rs index a61e3476a..3cc1fd711 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,9 @@ -#![deny(warnings, missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![deny( + warnings, + missing_docs, + missing_debug_implementations, + rust_2018_idioms +)] #![doc(html_root_url = "https://docs.rs/bytes/0.5.4")] #![no_std] @@ -72,24 +77,20 @@ //! perform a syscall, which has the potential of failing. Operations on `Buf` //! and `BufMut` are infallible. - extern crate alloc; #[cfg(feature = "std")] extern crate std; pub mod buf; -pub use crate::buf::{ - Buf, - BufMut, -}; +pub use crate::buf::{Buf, BufMut}; -mod bytes_mut; mod bytes; +mod bytes_mut; mod fmt; mod loom; -pub use crate::bytes_mut::BytesMut; pub use crate::bytes::Bytes; +pub use crate::bytes_mut::BytesMut; // Optional Serde support #[cfg(feature = "serde")] diff --git a/src/serde.rs b/src/serde.rs index 11020ae7f..0a5bd144a 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,15 +1,16 @@ +use super::{Bytes, BytesMut}; use alloc::string::String; use alloc::vec::Vec; use core::{cmp, fmt}; -use serde::{Serialize, Serializer, Deserialize, Deserializer, de}; -use super::{Bytes, BytesMut}; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; macro_rules! serde_impl { - ($ty:ident, $visitor_ty:ident, $from_slice:ident, $from_vec:ident) => ( + ($ty:ident, $visitor_ty:ident, $from_slice:ident, $from_vec:ident) => { impl Serialize for $ty { #[inline] fn serialize(&self, serializer: S) -> Result - where S: Serializer + where + S: Serializer, { serializer.serialize_bytes(&self) } @@ -26,7 +27,8 @@ macro_rules! serde_impl { #[inline] fn visit_seq(self, mut seq: V) -> Result - where V: de::SeqAccess<'de> + where + V: de::SeqAccess<'de>, { let len = cmp::min(seq.size_hint().unwrap_or(0), 4096); let mut values: Vec = Vec::with_capacity(len); @@ -40,28 +42,32 @@ macro_rules! serde_impl { #[inline] fn visit_bytes(self, v: &[u8]) -> Result - where E: de::Error + where + E: de::Error, { Ok($ty::$from_slice(v)) } #[inline] fn visit_byte_buf(self, v: Vec) -> Result - where E: de::Error + where + E: de::Error, { Ok($ty::$from_vec(v)) } #[inline] fn visit_str(self, v: &str) -> Result - where E: de::Error + where + E: de::Error, { Ok($ty::$from_slice(v.as_bytes())) } #[inline] fn visit_string(self, v: String) -> Result - where E: de::Error + where + E: de::Error, { Ok($ty::$from_vec(v.into_bytes())) } @@ -70,12 +76,13 @@ macro_rules! serde_impl { impl<'de> Deserialize<'de> for $ty { #[inline] fn deserialize(deserializer: D) -> Result<$ty, D::Error> - where D: Deserializer<'de> + where + D: Deserializer<'de>, { deserializer.deserialize_byte_buf($visitor_ty) } } - ); + }; } serde_impl!(Bytes, BytesVisitor, copy_from_slice, from); diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index d2f9b8e4c..c70e20928 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -1,10 +1,10 @@ #![deny(warnings, rust_2018_idioms)] -use bytes::{BufMut, BytesMut}; #[cfg(feature = "std")] use bytes::buf::IoSliceMut; -use core::usize; +use bytes::{BufMut, BytesMut}; use core::fmt::Write; +use core::usize; #[test] fn test_vec_as_mut_buf() { diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 47757733c..106fa6f4f 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1,6 +1,6 @@ #![deny(warnings, rust_2018_idioms)] -use bytes::{Bytes, BytesMut, Buf, BufMut}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; use std::usize; @@ -44,7 +44,6 @@ fn test_layout() { mem::size_of::>(), "BytesMut should be same size as Option", ); - } #[test] @@ -87,13 +86,11 @@ fn fmt_write() { write!(a, "{}", &s[..64]).unwrap(); assert_eq!(a, s[..64].as_bytes()); - let mut b = BytesMut::with_capacity(64); write!(b, "{}", &s[..32]).unwrap(); write!(b, "{}", &s[32..64]).unwrap(); assert_eq!(b, s[..64].as_bytes()); - let mut c = BytesMut::with_capacity(64); write!(c, "{}", s).unwrap(); assert_eq!(c, s[..].as_bytes()); @@ -305,11 +302,13 @@ fn split_off_to_at_gt_len() { assert!(panic::catch_unwind(move || { let _ = make_bytes().split_to(5); - }).is_err()); + }) + .is_err()); assert!(panic::catch_unwind(move || { let _ = make_bytes().split_off(5); - }).is_err()); + }) + .is_err()); } #[test] @@ -864,7 +863,6 @@ fn slice_ref_works() { test_slice_ref(&bytes, 9, 9, b""); } - #[test] fn slice_ref_empty() { let bytes = Bytes::from(&b""[..]); diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index dc007cfd4..418a9cd64 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -39,7 +39,11 @@ unsafe impl GlobalAlloc for Ledger { 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()); + panic!( + "bad dealloc: alloc size was {}, dealloc size is {}", + orig_size, + layout.size() + ); } let new_layout = match Layout::from_size_align(layout.size() + USIZE_SIZE, 1) { diff --git a/tests/test_chain.rs b/tests/test_chain.rs index df354bf0f..82de7fcec 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,7 +1,7 @@ #![deny(warnings, rust_2018_idioms)] -use bytes::{Buf, BufMut, Bytes}; use bytes::buf::{BufExt, BufMutExt}; +use bytes::{Buf, BufMut, Bytes}; #[cfg(feature = "std")] use std::io::IoSlice; diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 13b86cdad..2302a69d6 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -11,7 +11,6 @@ fn iter_len() { assert_eq!(iter.len(), 11); } - #[test] fn empty_iter_len() { let buf = Bytes::from_static(b""); diff --git a/tests/test_reader.rs b/tests/test_reader.rs index 608d97410..b5da2c963 100644 --- a/tests/test_reader.rs +++ b/tests/test_reader.rs @@ -3,7 +3,7 @@ use std::io::{BufRead, Read}; -use bytes::buf::{BufExt}; +use bytes::buf::BufExt; #[test] fn read() { diff --git a/tests/test_serde.rs b/tests/test_serde.rs index 18b135692..36b87f28e 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -1,7 +1,7 @@ #![cfg(feature = "serde")] #![deny(warnings, rust_2018_idioms)] -use serde_test::{Token, assert_tokens}; +use serde_test::{assert_tokens, Token}; #[test] fn test_ser_de_empty() { From 08ec01d1e77ab45532255487a2e544c6ea59c1b8 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Fri, 22 May 2020 08:33:06 +0300 Subject: [PATCH 008/203] Fix reference in Take docs (#383) --- src/buf/ext/take.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buf/ext/take.rs b/src/buf/ext/take.rs index 6247165d7..5a98f6ba6 100644 --- a/src/buf/ext/take.rs +++ b/src/buf/ext/take.rs @@ -5,7 +5,7 @@ use core::cmp; /// A `Buf` adapter which limits the bytes read from an underlying buffer. /// /// This struct is generally created by calling `take()` on `Buf`. See -/// documentation of [`take()`](trait.Buf.html#method.take) for more details. +/// documentation of [`take()`](trait.BufExt.html#method.take) for more details. #[derive(Debug)] pub struct Take { inner: T, From e9877e7a3dfe8d96f3f437e8b48ff46c31149c8e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 24 May 2020 06:30:50 +0900 Subject: [PATCH 009/203] Deny warnings for doc tests (#391) --- src/buf/ext/mod.rs | 6 +++--- src/buf/ext/reader.rs | 2 +- src/buf/ext/take.rs | 6 +++--- src/buf/ext/writer.rs | 2 +- src/buf/iter.rs | 5 +++-- src/lib.rs | 4 ++++ 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/buf/ext/mod.rs b/src/buf/ext/mod.rs index 7d6181438..4a292676a 100644 --- a/src/buf/ext/mod.rs +++ b/src/buf/ext/mod.rs @@ -27,7 +27,7 @@ pub trait BufExt: Buf { /// # Examples /// /// ``` - /// use bytes::{Buf, BufMut, buf::BufExt}; + /// use bytes::{BufMut, buf::BufExt}; /// /// let mut buf = b"hello world"[..].take(5); /// let mut dst = vec![]; @@ -79,7 +79,7 @@ pub trait BufExt: Buf { /// # Examples /// /// ``` - /// use bytes::{Buf, Bytes, buf::BufExt}; + /// use bytes::{Bytes, buf::BufExt}; /// use std::io::Read; /// /// let buf = Bytes::from("hello world"); @@ -135,7 +135,7 @@ pub trait BufMutExt: BufMut { /// # Examples /// /// ``` - /// use bytes::{BufMut, buf::BufMutExt}; + /// use bytes::buf::BufMutExt; /// use std::io::Write; /// /// let mut buf = vec![].writer(); diff --git a/src/buf/ext/reader.rs b/src/buf/ext/reader.rs index bc171add1..dde3548bf 100644 --- a/src/buf/ext/reader.rs +++ b/src/buf/ext/reader.rs @@ -26,7 +26,7 @@ impl Reader { /// ```rust /// use bytes::buf::BufExt; /// - /// let mut buf = b"hello world".reader(); + /// let buf = b"hello world".reader(); /// /// assert_eq!(b"hello world", buf.get_ref()); /// ``` diff --git a/src/buf/ext/take.rs b/src/buf/ext/take.rs index 5a98f6ba6..1d84868bf 100644 --- a/src/buf/ext/take.rs +++ b/src/buf/ext/take.rs @@ -22,7 +22,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::buf::{Buf, BufMut, BufExt}; + /// use bytes::buf::{BufMut, BufExt}; /// /// let mut buf = b"hello world".take(2); /// let mut dst = vec![]; @@ -49,7 +49,7 @@ impl Take { /// ```rust /// use bytes::{Buf, buf::BufExt}; /// - /// let mut buf = b"hello world".take(2); + /// let buf = b"hello world".take(2); /// /// assert_eq!(11, buf.get_ref().remaining()); /// ``` @@ -110,7 +110,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::{Buf, BufMut, buf::BufExt}; + /// use bytes::{BufMut, buf::BufExt}; /// /// let mut buf = b"hello world".take(2); /// let mut dst = vec![]; diff --git a/src/buf/ext/writer.rs b/src/buf/ext/writer.rs index 1418418e8..a14197c81 100644 --- a/src/buf/ext/writer.rs +++ b/src/buf/ext/writer.rs @@ -26,7 +26,7 @@ impl Writer { /// ```rust /// use bytes::buf::BufMutExt; /// - /// let mut buf = Vec::with_capacity(1024).writer(); + /// let buf = Vec::with_capacity(1024).writer(); /// /// assert_eq!(1024, buf.get_ref().capacity()); /// ``` diff --git a/src/buf/iter.rs b/src/buf/iter.rs index 5a3f26a2e..0f9bdc04f 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -9,7 +9,7 @@ use crate::Buf; /// Basic usage: /// /// ``` -/// use bytes::{Buf, Bytes}; +/// use bytes::Bytes; /// /// let buf = Bytes::from(&b"abc"[..]); /// let mut iter = buf.into_iter(); @@ -33,7 +33,7 @@ impl IntoIter { /// # Examples /// /// ``` - /// use bytes::{Buf, Bytes}; + /// use bytes::Bytes; /// use bytes::buf::IntoIter; /// /// let buf = Bytes::from_static(b"abc"); @@ -47,6 +47,7 @@ impl IntoIter { pub fn new(inner: T) -> IntoIter { IntoIter { inner } } + /// Consumes this `IntoIter`, returning the underlying value. /// /// # Examples diff --git a/src/lib.rs b/src/lib.rs index 3cc1fd711..aad09d42d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,10 @@ missing_debug_implementations, rust_2018_idioms )] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] #![doc(html_root_url = "https://docs.rs/bytes/0.5.4")] #![no_std] From 64fe7e10ab849e84f8dd49bb478bc85757713a76 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 25 May 2020 01:41:45 +0900 Subject: [PATCH 010/203] Migrate CI to GitHub Actions (#392) --- .github/workflows/ci.yml | 163 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 +- README.md | 7 +- azure-pipelines.yml | 69 ---------------- ci/azure-cross-compile.yml | 46 ----------- ci/azure-deploy-docs.yml | 39 --------- ci/azure-install-rust.yml | 33 -------- ci/azure-loom.yml | 15 ---- ci/azure-rustfmt.yml | 13 --- ci/azure-test-stable.yml | 50 ------------ ci/azure-tsan.yml | 26 ------ ci/test-stable.sh | 27 ++++++ ci/tsan.sh | 15 ++++ 13 files changed, 210 insertions(+), 299 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 azure-pipelines.yml delete mode 100644 ci/azure-cross-compile.yml delete mode 100644 ci/azure-deploy-docs.yml delete mode 100644 ci/azure-install-rust.yml delete mode 100644 ci/azure-loom.yml delete mode 100644 ci/azure-rustfmt.yml delete mode 100644 ci/azure-test-stable.yml delete mode 100644 ci/azure-tsan.yml create mode 100644 ci/test-stable.sh create mode 100644 ci/tsan.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..164ec1344 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,163 @@ +name: CI + +on: + pull_request: + branches: + - master + push: + branches: + - master + +env: + RUSTFLAGS: -Dwarnings + RUST_BACKTRACE: 1 + +defaults: + run: + shell: bash + +jobs: + # Check formatting + rustfmt: + name: rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable && rustup default stable + - name: Check formatting + run: cargo fmt --all -- --check + + # TODO + # # Apply clippy lints + # clippy: + # name: clippy + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v2 + # - name: Apply clippy lints + # run: cargo clippy --all-features + + # This represents the minimum Rust version supported by + # Bytes. Updating this should be done in a dedicated PR. + # + # Tests are not run as tests may require newer versions of + # rust. + minrust: + name: minrust + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update 1.39.0 && rustup default 1.39.0 + - name: Check + run: . ci/test-stable.sh check + + # Stable + stable: + name: stable + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable && rustup default stable + - name: Test + run: . ci/test-stable.sh test + + # Nightly + nightly: + name: nightly + env: + # Pin nightly to avoid being impacted by breakage + RUST_VERSION: nightly-2019-09-25 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update $RUST_VERSION && rustup default $RUST_VERSION + - name: Test + run: . ci/test-stable.sh test + + # Run tests on some extra platforms + cross: + name: cross + strategy: + matrix: + target: + - i686-unknown-linux-gnu + - armv7-unknown-linux-gnueabihf + - powerpc-unknown-linux-gnu + - powerpc64-unknown-linux-gnu + - wasm32-unknown-unknown + runs-on: ubuntu-16.04 + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable && rustup default stable + - name: cross build --target ${{ matrix.target }} + run: | + cargo install cross + cross build --target ${{ matrix.target }} + if: matrix.target != 'wasm32-unknown-unknown' + # WASM support + - name: cargo build --target ${{ matrix.target }} + run: | + rustup target add ${{ matrix.target }} + cargo build --target ${{ matrix.target }} + if: matrix.target == 'wasm32-unknown-unknown' + + # Sanitizers + tsan: + name: tsan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update nightly && rustup default nightly + - name: TSAN / MSAN + run: . ci/tsan.sh + + # Loom + loom: + name: loom + env: + # Pin nightly to avoid being impacted by breakage + RUST_VERSION: nightly-2020-05-19 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update $RUST_VERSION && rustup default $RUST_VERSION + - name: Loom tests + run: RUSTFLAGS="--cfg loom -Dwarnings" cargo test --lib + + publish_docs: + name: Publish Documentation + needs: + - rustfmt + # - clippy + - stable + - nightly + - minrust + - cross + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable && rustup default stable + - name: Build documentation + run: cargo doc --no-deps --all-features + - name: Publish documentation + run: | + cd target/doc + git init + git add . + git -c user.name='ci' -c user.email='ci' commit -m 'Deploy Bytes API documentation' + git push -f -q https://git:${{ secrets.github_token }}@github.com/${{ github.repository }} HEAD:gh-pages + if: github.event_name == 'push' && github.event.ref == 'refs/heads/master' && github.repository == 'tokio-rs/bytes' diff --git a/Cargo.toml b/Cargo.toml index c3ba22860..2ccb39f15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,10 @@ default = ["std"] std = [] [dependencies] -serde = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } +serde = { version = "1.0.60", optional = true, default-features = false, features = ["alloc"] } [dev-dependencies] serde_test = "1.0" -# loom is currently not compiling on windows. -# See: https://github.com/Xudong-Huang/generator-rs/issues/19 -[target.'cfg(not(windows))'.dev-dependencies] +[target.'cfg(loom)'.dependencies] loom = "0.3" diff --git a/README.md b/README.md index afc2ed21c..73c43abc8 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ A utility library for working with bytes. [![Crates.io][crates-badge]][crates-url] -[![Build Status][azure-badge]][azure-url] +[![Build Status][ci-badge]][ci-url] [crates-badge]: https://img.shields.io/crates/v/bytes.svg [crates-url]: https://crates.io/crates/bytes -[azure-badge]: https://dev.azure.com/tokio-rs/bytes/_apis/build/status/tokio-rs.bytes?branchName=master -[azure-url]: https://dev.azure.com/tokio-rs/bytes/_build/latest?definitionId=3&branchName=master +[ci-badge]: https://github.com/tokio-rs/bytes/workflows/CI/badge.svg +[ci-url]: https://github.com/tokio-rs/bytes/actions [Documentation](https://docs.rs/bytes) @@ -45,4 +45,3 @@ This project is licensed under the [MIT license](LICENSE). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in `bytes` by you, shall be licensed as MIT, without any additional terms or conditions. - diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index a92c3c4bb..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,69 +0,0 @@ -trigger: ["master"] -pr: ["master"] - -jobs: -# Check formatting -- template: ci/azure-rustfmt.yml - parameters: - name: rustfmt - rust_version: stable - -# Apply clippy lints -# - template: ci/azure-clippy.yml -# parameters: -# name: clippy - -# This represents the minimum Rust version supported by -# Bytes. Updating this should be done in a dedicated PR. -# -# Tests are not run as tests may require newer versions of -# rust. -- template: ci/azure-test-stable.yml - parameters: - name: minrust - rust_version: 1.39.0 - cmd: check - -# Stable -- template: ci/azure-test-stable.yml - parameters: - name: stable - cross: true - features: - - serde - -# Nightly -- template: ci/azure-test-stable.yml - parameters: - name: nightly - # Pin nightly to avoid being impacted by breakage - rust_version: nightly-2019-09-25 - benches: true - -# Run tests on some extra platforms -- template: ci/azure-cross-compile.yml - parameters: - name: cross - -# Sanitizers -- template: ci/azure-tsan.yml - parameters: - name: tsan - rust_version: nightly - -# Loom -- template: ci/azure-loom.yml - parameters: - name: loom - rust_version: stable - - -- template: ci/azure-deploy-docs.yml - parameters: - dependsOn: - - rustfmt - # - clippy - - stable - - nightly - - minrust - - cross diff --git a/ci/azure-cross-compile.yml b/ci/azure-cross-compile.yml deleted file mode 100644 index be46ca346..000000000 --- a/ci/azure-cross-compile.yml +++ /dev/null @@ -1,46 +0,0 @@ -parameters: - cmd: build - rust_version: stable - -jobs: -- job: ${{ parameters.name }} - displayName: Cross - strategy: - matrix: - i686: - vmImage: ubuntu-16.04 - target: i686-unknown-linux-gnu - armv7: - vmImage: ubuntu-16.04 - target: armv7-unknown-linux-gnueabihf - powerpc: - vmImage: ubuntu-16.04 - target: powerpc-unknown-linux-gnu - powerpc64: - vmImage: ubuntu-16.04 - target: powerpc64-unknown-linux-gnu - wasm: - vmImage: ubuntu-16.04 - target: wasm32-unknown-unknown - pool: - vmImage: $(vmImage) - - steps: - - template: azure-install-rust.yml - parameters: - rust_version: ${{parameters.rust_version}} - - - script: cargo install cross - displayName: Install cross - condition: not(eq(variables['target'], 'wasm32-unknown-unknown')) - - - script: cross ${{ parameters.cmd }} --target $(target) - displayName: cross ${{ parameters.cmd }} --target $(target) - condition: not(eq(variables['target'], 'wasm32-unknown-unknown')) - - # WASM support - - script: | - rustup target add $(target) - cargo build --target $(target) - displayName: cargo build --target $(target) - condition: eq(variables['target'], 'wasm32-unknown-unknown') diff --git a/ci/azure-deploy-docs.yml b/ci/azure-deploy-docs.yml deleted file mode 100644 index 52ac48fcd..000000000 --- a/ci/azure-deploy-docs.yml +++ /dev/null @@ -1,39 +0,0 @@ -parameters: - dependsOn: [] - -jobs: -- job: documentation - displayName: 'Deploy API Documentation' - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - pool: - vmImage: 'Ubuntu 16.04' - dependsOn: - - ${{ parameters.dependsOn }} - steps: - - template: azure-install-rust.yml - parameters: - rust_version: stable - - script: | - cargo doc --no-deps - cp -R target/doc '$(Build.BinariesDirectory)' - displayName: 'Generate Documentation' - - script: | - set -e - - git --version - ls -la - git init - git config user.name 'Deployment Bot (from Azure Pipelines)' - git config user.email 'deploy@tokio-rs.com' - git config --global credential.helper 'store --file ~/.my-credentials' - printf "protocol=https\nhost=github.com\nusername=carllerche\npassword=%s\n\n" "$GITHUB_TOKEN" | git credential-store --file ~/.my-credentials store - git remote add origin https://github.com/tokio-rs/bytes - git checkout -b gh-pages - git add . - git commit -m 'Deploy Bytes API documentation' - git push -f origin gh-pages - env: - GITHUB_TOKEN: $(githubPersonalToken) - workingDirectory: '$(Build.BinariesDirectory)' - displayName: 'Deploy Documentation' - diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml deleted file mode 100644 index 02176592a..000000000 --- a/ci/azure-install-rust.yml +++ /dev/null @@ -1,33 +0,0 @@ -steps: - # Linux and macOS. - - script: | - set -e - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none - export PATH=$PATH:$HOME/.cargo/bin - rustup toolchain install $RUSTUP_TOOLCHAIN - rustup default $RUSTUP_TOOLCHAIN - echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" - env: - RUSTUP_TOOLCHAIN: ${{parameters.rust_version}} - displayName: "Install rust (*nix)" - condition: not(eq(variables['Agent.OS'], 'Windows_NT')) - - # Windows. - - script: | - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe -y --default-toolchain none - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustup toolchain install %RUSTUP_TOOLCHAIN% - rustup default %RUSTUP_TOOLCHAIN% - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - env: - RUSTUP_TOOLCHAIN: ${{parameters.rust_version}} - displayName: "Install rust (windows)" - condition: eq(variables['Agent.OS'], 'Windows_NT') - - # All platforms. - - script: | - rustup toolchain list - rustc -Vv - cargo -V - displayName: Query rust and cargo versions diff --git a/ci/azure-loom.yml b/ci/azure-loom.yml deleted file mode 100644 index 1db9c3afe..000000000 --- a/ci/azure-loom.yml +++ /dev/null @@ -1,15 +0,0 @@ -jobs: -- job: ${{parameters.name}} - displayName: Loom tests - pool: - vmImage: ubuntu-16.04 - - steps: - - template: azure-install-rust.yml - parameters: - rust_version: ${{parameters.rust_version}} - - - script: RUSTFLAGS="--cfg loom" cargo test --lib - displayName: RUSTFLAGS="--cfg loom" cargo test --lib - - diff --git a/ci/azure-rustfmt.yml b/ci/azure-rustfmt.yml deleted file mode 100644 index a0a133890..000000000 --- a/ci/azure-rustfmt.yml +++ /dev/null @@ -1,13 +0,0 @@ -jobs: -# Check formatting -- job: ${{ parameters.name }} - displayName: Check rustfmt - pool: - vmImage: ubuntu-16.04 - steps: - - template: azure-install-rust.yml - parameters: - rust_version: ${{ parameters.rust_version }} - - script: | - cargo fmt --all -- --check - displayName: Check formatting diff --git a/ci/azure-test-stable.yml b/ci/azure-test-stable.yml deleted file mode 100644 index b3e614a7b..000000000 --- a/ci/azure-test-stable.yml +++ /dev/null @@ -1,50 +0,0 @@ -parameters: - cmd: test - rust_version: stable - features: [] - -jobs: -- job: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - strategy: - matrix: - Linux: - vmImage: ubuntu-16.04 - - ${{ if parameters.cross }}: - MacOS: - vmImage: macOS-10.14 - Windows: - vmImage: vs2017-win2016 - pool: - vmImage: $(vmImage) - - steps: - - template: azure-install-rust.yml - parameters: - rust_version: ${{parameters.rust_version}} - - # Run with default crate features - - script: cargo ${{ parameters.cmd }} - displayName: cargo ${{ parameters.cmd }} - - # Run with each specified feature - - ${{ each feature in parameters.features }}: - - script: cargo ${{ parameters.cmd }} --features ${{ feature }} - displayName: cargo ${{ parameters.cmd }} --features ${{ feature }} - - - ${{ if eq(parameters.cmd, 'test') }}: - - script: cargo doc --no-deps - displayName: cargo doc --no-deps - - - ${{ if parameters.benches }}: - - script: cargo check --benches - displayName: Check benchmarks - - # Run with all features - - script: cargo ${{ parameters.cmd }} --all-features - displayName: cargo ${{ parameters.cmd }} --all-features - - # Run with no default features - - script: cargo check --no-default-features - displayName: cargo check --no-default-features diff --git a/ci/azure-tsan.yml b/ci/azure-tsan.yml deleted file mode 100644 index 198b187ab..000000000 --- a/ci/azure-tsan.yml +++ /dev/null @@ -1,26 +0,0 @@ -jobs: -- job: ${{ parameters.name }} - displayName: TSAN - pool: - vmImage: ubuntu-16.04 - - steps: - - template: azure-install-rust.yml - parameters: - rust_version: ${{ parameters.rust_version }} - - - script: | - set -e - - export RUST_TEST_THREADS=1 - export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" - export TSAN_OPTIONS="suppressions=`pwd`/ci/tsan" - - # Run address sanitizer - RUSTFLAGS="-Z sanitizer=address" \ - cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut - - # Run thread sanitizer - RUSTFLAGS="-Z sanitizer=thread" \ - cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut - displayName: TSAN / MSAN diff --git a/ci/test-stable.sh b/ci/test-stable.sh new file mode 100644 index 000000000..01a32f5a6 --- /dev/null +++ b/ci/test-stable.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +set -ex + +cmd="${1:-test}" + +# Install cargo-hack for feature flag test +cargo install cargo-hack + +# Run with each feature +# * --each-feature includes both default/no-default features +# * --optional-deps is needed for serde feature +cargo hack "${cmd}" --each-feature --optional-deps +# Run with all features +cargo "${cmd}" --all-features + +cargo doc --no-deps --all-features + +if [[ "${RUST_VERSION}" == "nightly"* ]]; then + # Check benchmarks + cargo check --benches + + # Check minimal versions + cargo clean + cargo update -Zminimal-versions + cargo check --all-features +fi diff --git a/ci/tsan.sh b/ci/tsan.sh new file mode 100644 index 000000000..7e5b3be6d --- /dev/null +++ b/ci/tsan.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -ex + +export RUST_TEST_THREADS=1 +export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" +export TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" + +# Run address sanitizer +RUSTFLAGS="-Z sanitizer=address" \ +cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut + +# Run thread sanitizer +RUSTFLAGS="-Z sanitizer=thread" \ +cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut From a3304e8b8b74e2d5260d8419b6d8075e004e4c39 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 28 May 2020 08:02:45 +0300 Subject: [PATCH 011/203] Remove outdated info on BytesMut::with_capacity (#394) Remove a remark from the doc that pertained to the inline layout and is no longer true. --- src/bytes_mut.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 5178198f0..4d0585e80 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -114,8 +114,7 @@ impl BytesMut { /// Creates a new `BytesMut` with the specified capacity. /// /// The returned `BytesMut` will be able to hold at least `capacity` bytes - /// without reallocating. If `capacity` is under `4 * size_of::() - 1`, - /// then `BytesMut` will not allocate. + /// without reallocating. /// /// It is important to note that this function does not specify the length /// of the returned `BytesMut`, but only the capacity. From 008d3e508e2247dbfbf5a73918e2fe999a4e9158 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 18 Jun 2020 21:22:25 +0000 Subject: [PATCH 012/203] v0.5.5 --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a51ab0c..5e6a0325e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.5.5 (June 18, 2020) + +### Added +- Allow using the `serde` feature in `no_std` environments (#385). + +### Fix +- Fix `BufMut::advance_mut` to panic if advanced passed the capacity (#354).. +- Fix `BytesMut::freeze` ignoring amount previously `advance`d (#352). + # 0.5.4 (January 23, 2020) ### Added diff --git a/Cargo.toml b/Cargo.toml index 2ccb39f15..69b73619c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ name = "bytes" # - Update CHANGELOG.md. # - Update doc URL. # - Create "v0.5.x" git tag. -version = "0.5.4" +version = "0.5.5" license = "MIT" authors = [ "Carl Lerche ", diff --git a/src/lib.rs b/src/lib.rs index aad09d42d..accbf71ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/0.5.4")] +#![doc(html_root_url = "https://docs.rs/bytes/0.5.5")] #![no_std] //! Provides abstractions for working with bytes. From 5cde647c2982d0b59edaea8a74aa689b5dd03c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 3 Jul 2020 03:55:16 +0200 Subject: [PATCH 013/203] Remove unnecessary synchronization when cloning shared representation (#404) --- src/bytes.rs | 2 +- src/bytes_mut.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 08bc9b3f3..79a09f398 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -928,7 +928,7 @@ const KIND_VEC: usize = 0b1; const KIND_MASK: usize = 0b1; unsafe fn shared_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { - let shared = data.load(Ordering::Acquire); + let shared = data.load(Ordering::Relaxed); shallow_clone_arc(shared as _, ptr, len) } diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 4d0585e80..802745efa 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1495,7 +1495,7 @@ static SHARED_VTABLE: Vtable = Vtable { }; unsafe fn shared_v_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { - let shared = data.load(Ordering::Acquire) as *mut Shared; + let shared = data.load(Ordering::Relaxed) as *mut Shared; increment_shared(shared); let data = AtomicPtr::new(shared as _); From 3603cec7c238a3414058e76d08ce9daa4a09d61c Mon Sep 17 00:00:00 2001 From: Juan Aguilar Date: Fri, 3 Jul 2020 23:47:32 +0000 Subject: [PATCH 014/203] Add inline attribute to BytesMut::set_len (#408) --- src/bytes_mut.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 802745efa..a49ae76ff 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -475,6 +475,7 @@ impl BytesMut { /// /// assert_eq!(&b[..], b"hello world"); /// ``` + #[inline] pub unsafe fn set_len(&mut self, len: usize) { debug_assert!(len <= self.cap, "set_len out of bounds"); self.len = len; From bc4a6d56f4e1d848c8d50aea97d63d2ac9864116 Mon Sep 17 00:00:00 2001 From: Juan Aguilar Date: Mon, 6 Jul 2020 00:43:26 +0000 Subject: [PATCH 015/203] Add inline attribute to BytesMut::as_mut (#410) --- src/bytes_mut.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index a49ae76ff..4bfe0ffbb 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1026,6 +1026,7 @@ impl Deref for BytesMut { } impl AsMut<[u8]> for BytesMut { + #[inline] fn as_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } From 90e7e650c99b6d231017d9b831a01e95b8c06756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Mon, 6 Jul 2020 18:28:47 +0200 Subject: [PATCH 016/203] Use ThreadSanitizer CI setup that avoids false positives (#406) --- .github/workflows/ci.yml | 4 +++- ci/tsan | 24 ------------------------ ci/tsan.sh | 4 +--- 3 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 ci/tsan diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 164ec1344..47952d94d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,9 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - name: TSAN / MSAN + - name: Install rust-src + run: rustup component add rust-src + - name: ASAN / TSAN run: . ci/tsan.sh # Loom diff --git a/ci/tsan b/ci/tsan deleted file mode 100644 index e53f9b893..000000000 --- a/ci/tsan +++ /dev/null @@ -1,24 +0,0 @@ -# TSAN suppressions file for `bytes` - -# TSAN does not understand fences and `Arc::drop` is implemented using a fence. -# This causes many false positives. -race:Arc*drop -race:arc*Weak*drop - -# `std` mpsc is not used in any Bytes code base. This race is triggered by some -# rust runtime logic. -race:std*mpsc_queue - -# Some test runtime races. Allocation should be race free -race:alloc::alloc - -# Not sure why this is warning, but it is in the test harness and not the library. -race:TestEvent*clone -race:test::run_tests_console::*closure - -# Probably more fences in std. -race:__call_tls_dtors - -# This ignores a false positive caused by `thread::park()`/`thread::unpark()`. -# See: https://github.com/rust-lang/rust/pull/54806#issuecomment-436193353 -race:pthread_cond_destroy diff --git a/ci/tsan.sh b/ci/tsan.sh index 7e5b3be6d..ca520bd7f 100644 --- a/ci/tsan.sh +++ b/ci/tsan.sh @@ -2,9 +2,7 @@ set -ex -export RUST_TEST_THREADS=1 export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" -export TSAN_OPTIONS="suppressions=$(pwd)/ci/tsan" # Run address sanitizer RUSTFLAGS="-Z sanitizer=address" \ @@ -12,4 +10,4 @@ cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf - # Run thread sanitizer RUSTFLAGS="-Z sanitizer=thread" \ -cargo test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut +cargo -Zbuild-std test --target x86_64-unknown-linux-gnu --test test_bytes --test test_buf --test test_buf_mut From 81550da474bcd243b9e8765c6555831310a954e8 Mon Sep 17 00:00:00 2001 From: Bryan Donlan Date: Wed, 8 Jul 2020 20:38:54 +0000 Subject: [PATCH 017/203] BytesMut: Reuse buffer when data fully consumed via Buf Closes #412 --- src/bytes_mut.rs | 5 ++--- tests/test_bytes.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 4bfe0ffbb..e3fa48f16 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -559,9 +559,8 @@ impl BytesMut { unsafe { let (off, prev) = self.get_vec_pos(); - // Only reuse space if we stand to gain at least capacity/2 - // bytes of space back - if off >= additional && off >= (self.cap / 2) { + // Only reuse space if we can satisfy the requested additional space. + if self.capacity() - self.len() + off >= additional { // There's space - reuse it // // Just move the pointer back to the start after copying diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 106fa6f4f..bbe51f582 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -929,6 +929,22 @@ fn bytes_buf_mut_advance() { } } +#[test] +fn bytes_buf_mut_reuse_when_fully_consumed() { + use bytes::{Buf, BytesMut}; + let mut buf = BytesMut::new(); + buf.reserve(8192); + buf.extend_from_slice(&[0u8; 100][..]); + + let p = &buf[0] as *const u8; + buf.advance(100); + + buf.reserve(8192); + buf.extend_from_slice(b" "); + + assert_eq!(&buf[0] as *const u8, p); +} + #[test] #[should_panic] fn bytes_reserve_overflow() { From 972f538b7ed5d01928f00deae14327a004225f8e Mon Sep 17 00:00:00 2001 From: Sean Leather Date: Thu, 9 Jul 2020 18:11:45 +0200 Subject: [PATCH 018/203] docs: Clarify what BytesMut is (#375) --- src/bytes_mut.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index e3fa48f16..a7a8e5798 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -22,8 +22,12 @@ use crate::{Buf, BufMut, Bytes}; /// /// `BytesMut` represents a unique view into a potentially shared memory region. /// Given the uniqueness guarantee, owners of `BytesMut` handles are able to -/// mutate the memory. It is similar to a `Vec` but with less copies and -/// allocations. +/// mutate the memory. +/// +/// `BytesMut` can be thought of as containing a `buf: Arc>`, an offset +/// into `buf`, a slice length, and a guarantee that no other `BytesMut` for the +/// same `buf` overlaps with its slice. That guarantee means that a write lock +/// is not required. /// /// # Growth /// From 7daa7fe05364f347539a35d182dc964b9e8e2ecc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 10 Jul 2020 01:12:39 +0900 Subject: [PATCH 019/203] Change default lint level to warning and deny warnings in CI (#397) --- benches/buf.rs | 2 +- benches/bytes.rs | 2 +- benches/bytes_mut.rs | 2 +- src/lib.rs | 7 +------ tests/test_buf.rs | 2 +- tests/test_buf_mut.rs | 2 +- tests/test_bytes.rs | 2 +- tests/test_chain.rs | 2 +- tests/test_debug.rs | 2 +- tests/test_iter.rs | 2 +- tests/test_reader.rs | 2 +- tests/test_serde.rs | 2 +- tests/test_take.rs | 2 +- 13 files changed, 13 insertions(+), 18 deletions(-) diff --git a/benches/buf.rs b/benches/buf.rs index 8f14aec20..77b0633ee 100644 --- a/benches/buf.rs +++ b/benches/buf.rs @@ -1,5 +1,5 @@ #![feature(test)] -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] extern crate test; diff --git a/benches/bytes.rs b/benches/bytes.rs index 1741ba0a1..c5b84124f 100644 --- a/benches/bytes.rs +++ b/benches/bytes.rs @@ -1,5 +1,5 @@ #![feature(test)] -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] extern crate test; diff --git a/benches/bytes_mut.rs b/benches/bytes_mut.rs index 8e0226c76..b06943621 100644 --- a/benches/bytes_mut.rs +++ b/benches/bytes_mut.rs @@ -1,5 +1,5 @@ #![feature(test)] -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] extern crate test; diff --git a/src/lib.rs b/src/lib.rs index accbf71ce..014720410 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,4 @@ -#![deny( - warnings, - missing_docs, - missing_debug_implementations, - rust_2018_idioms -)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 26b95aee8..17bdd54e8 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::Buf; #[cfg(feature = "std")] diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index c70e20928..b91e2e511 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] #[cfg(feature = "std")] use bytes::buf::IoSliceMut; diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index bbe51f582..6b106a6bc 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::{Buf, BufMut, Bytes, BytesMut}; diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 82de7fcec..6dbc45d04 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::buf::{BufExt, BufMutExt}; use bytes::{Buf, BufMut, Bytes}; diff --git a/tests/test_debug.rs b/tests/test_debug.rs index 7528bac87..08d2f254e 100644 --- a/tests/test_debug.rs +++ b/tests/test_debug.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::Bytes; diff --git a/tests/test_iter.rs b/tests/test_iter.rs index 2302a69d6..a5bfddddf 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::Bytes; diff --git a/tests/test_reader.rs b/tests/test_reader.rs index b5da2c963..10b480fcc 100644 --- a/tests/test_reader.rs +++ b/tests/test_reader.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] #![cfg(feature = "std")] use std::io::{BufRead, Read}; diff --git a/tests/test_serde.rs b/tests/test_serde.rs index 36b87f28e..cf4aeffa7 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -1,5 +1,5 @@ #![cfg(feature = "serde")] -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use serde_test::{assert_tokens, Token}; diff --git a/tests/test_take.rs b/tests/test_take.rs index b9b525b1f..0afb28bb4 100644 --- a/tests/test_take.rs +++ b/tests/test_take.rs @@ -1,4 +1,4 @@ -#![deny(warnings, rust_2018_idioms)] +#![warn(rust_2018_idioms)] use bytes::buf::{Buf, BufExt}; From 2d51809bd7f8f7936bb9c029e06fc58f8c81d67d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 13 Jul 2020 12:33:30 +0900 Subject: [PATCH 020/203] Fix CI failure on Windows (#415) --no-self-update is necessary because the windows environment cannot self-update rustup.exe. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47952d94d..8b99832a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,8 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Rust - run: rustup update stable && rustup default stable + # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. + run: rustup update stable --no-self-update && rustup default stable - name: Test run: . ci/test-stable.sh test From 025bec2954f04e5161fb1bd36396260491303366 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Mon, 13 Jul 2020 16:55:47 -0700 Subject: [PATCH 021/203] Move loom to dev-dependencies (#416) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 69b73619c..1b0a5c957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,5 +30,5 @@ serde = { version = "1.0.60", optional = true, default-features = false, feature [dev-dependencies] serde_test = "1.0" -[target.'cfg(loom)'.dependencies] +[target.'cfg(loom)'.dev-dependencies] loom = "0.3" From 6fdb7391ce83dc71ccaeda6a54211ce723e5d9a5 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Mon, 13 Jul 2020 17:00:00 -0700 Subject: [PATCH 022/203] v0.5.6 --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6a0325e..1b821da42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 0.5.6 (July 13, 2020) + +- Improve `BytesMut` to reuse buffer when fully `advance`d. +- Mark `BytesMut::{as_mut, set_len}` with `#[inline]`. +- Relax synchronization when cloning in shared vtable of `Bytes`. +- Move `loom` to `dev-dependencies`. + # 0.5.5 (June 18, 2020) ### Added diff --git a/Cargo.toml b/Cargo.toml index 1b0a5c957..bcb8f1799 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ name = "bytes" # - Update CHANGELOG.md. # - Update doc URL. # - Create "v0.5.x" git tag. -version = "0.5.5" +version = "0.5.6" license = "MIT" authors = [ "Carl Lerche ", diff --git a/src/lib.rs b/src/lib.rs index 014720410..e375c01b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/0.5.5")] +#![doc(html_root_url = "https://docs.rs/bytes/0.5.6")] #![no_std] //! Provides abstractions for working with bytes. From 422048eb24b4f5bbe5460dfb5d9b05ec7584cee6 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 16 Oct 2020 11:49:27 -0700 Subject: [PATCH 023/203] prepare for 0.6 work (#428) --- CHANGELOG.md | 4 ++++ Cargo.toml | 7 ++++--- src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b821da42..6a9fa202e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.6.0 (unreleased) + + + # 0.5.6 (July 13, 2020) - Improve `BytesMut` to reuse buffer when fully `advance`d. diff --git a/Cargo.toml b/Cargo.toml index bcb8f1799..bb928de14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,20 +5,21 @@ name = "bytes" # - Update html_root_url. # - Update CHANGELOG.md. # - Update doc URL. -# - Create "v0.5.x" git tag. -version = "0.5.6" +# - Create "v0.6.x" git tag. +version = "0.6.0" license = "MIT" authors = [ "Carl Lerche ", "Sean McArthur ", ] description = "Types and traits for working with bytes" -documentation = "https://docs.rs/bytes" +documentation = "https://docs.rs/bytes/0.6.0/bytes/" repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] edition = "2018" +publish = false [features] default = ["std"] diff --git a/src/lib.rs b/src/lib.rs index e375c01b1..cfa626a81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/0.5.6")] +#![doc(html_root_url = "https://docs.rs/bytes/0.6.0")] #![no_std] //! Provides abstractions for working with bytes. From 447530b8a6f97fc6864b39d29b24efb4ac9202d3 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 16 Oct 2020 11:56:03 -0700 Subject: [PATCH 024/203] Remove BufMut::bytes_vectored_mut() (#430) There are issues with regard to uninitialized memory. We are avoiding stabilizing this function for now. --- src/buf/buf_mut.rs | 91 ------------------------------------------- src/buf/ext/chain.rs | 9 ----- src/buf/mod.rs | 2 - tests/test_buf_mut.rs | 19 --------- 4 files changed, 121 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 628b240a3..ff25cb6db 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -4,9 +4,6 @@ use core::{ ptr, usize, }; -#[cfg(feature = "std")] -use std::fmt; - use alloc::{boxed::Box, vec::Vec}; /// A trait for values that provide sequential write access to bytes. @@ -168,48 +165,6 @@ pub trait BufMut { /// return an empty slice. fn bytes_mut(&mut self) -> &mut [MaybeUninit]; - /// Fills `dst` with potentially multiple mutable slices starting at `self`'s - /// current position. - /// - /// If the `BufMut` is backed by disjoint slices of bytes, `bytes_vectored_mut` - /// enables fetching more than one slice at once. `dst` is a slice of - /// mutable `IoSliceMut` references, enabling the slice to be directly used with - /// [`readv`] without any further conversion. The sum of the lengths of all - /// the buffers in `dst` will be less than or equal to - /// `Buf::remaining_mut()`. - /// - /// The entries in `dst` will be overwritten, but the data **contained** by - /// the slices **will not** be modified. If `bytes_vectored_mut` does not fill every - /// entry in `dst`, then `dst` is guaranteed to contain all remaining slices - /// in `self. - /// - /// This is a lower level function. Most operations are done with other - /// functions. - /// - /// # Implementer notes - /// - /// This function should never panic. Once the end of the buffer is reached, - /// i.e., `BufMut::remaining_mut` returns 0, calls to `bytes_vectored_mut` must - /// return 0 without mutating `dst`. - /// - /// Implementations should also take care to properly handle being called - /// with `dst` being a zero length slice. - /// - /// [`readv`]: http://man7.org/linux/man-pages/man2/readv.2.html - #[cfg(feature = "std")] - fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize { - if dst.is_empty() { - return 0; - } - - if self.has_remaining_mut() { - dst[0] = IoSliceMut::from(self.bytes_mut()); - 1 - } else { - 0 - } - } - /// Transfer bytes into `self` from `src` and advance the cursor by the /// number of bytes written. /// @@ -890,11 +845,6 @@ macro_rules! deref_forward_bufmut { (**self).bytes_mut() } - #[cfg(feature = "std")] - fn bytes_vectored_mut<'b>(&'b mut self, dst: &mut [IoSliceMut<'b>]) -> usize { - (**self).bytes_vectored_mut(dst) - } - unsafe fn advance_mut(&mut self, cnt: usize) { (**self).advance_mut(cnt) } @@ -1057,44 +1007,3 @@ impl BufMut for Vec { // The existence of this function makes the compiler catch if the BufMut // trait is "object-safe" or not. fn _assert_trait_object(_b: &dyn BufMut) {} - -// ===== impl IoSliceMut ===== - -/// A buffer type used for `readv`. -/// -/// This is a wrapper around an `std::io::IoSliceMut`, but does not expose -/// the inner bytes in a safe API, as they may point at uninitialized memory. -/// -/// This is `repr(transparent)` of the `std::io::IoSliceMut`, so it is valid to -/// transmute them. However, as the memory might be uninitialized, care must be -/// taken to not *read* the internal bytes, only *write* to them. -#[repr(transparent)] -#[cfg(feature = "std")] -pub struct IoSliceMut<'a>(std::io::IoSliceMut<'a>); - -#[cfg(feature = "std")] -impl fmt::Debug for IoSliceMut<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("IoSliceMut") - .field("len", &self.0.len()) - .finish() - } -} - -#[cfg(feature = "std")] -impl<'a> From<&'a mut [u8]> for IoSliceMut<'a> { - fn from(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(std::io::IoSliceMut::new(buf)) - } -} - -#[cfg(feature = "std")] -impl<'a> From<&'a mut [MaybeUninit]> for IoSliceMut<'a> { - fn from(buf: &'a mut [MaybeUninit]) -> IoSliceMut<'a> { - IoSliceMut(std::io::IoSliceMut::new(unsafe { - // We don't look at the contents, and `std::io::IoSliceMut` - // doesn't either. - mem::transmute::<&'a mut [MaybeUninit], &'a mut [u8]>(buf) - })) - } -} diff --git a/src/buf/ext/chain.rs b/src/buf/ext/chain.rs index e62e2f1b9..598dde70f 100644 --- a/src/buf/ext/chain.rs +++ b/src/buf/ext/chain.rs @@ -3,8 +3,6 @@ use crate::{Buf, BufMut}; use core::mem::MaybeUninit; -#[cfg(feature = "std")] -use crate::buf::IoSliceMut; #[cfg(feature = "std")] use std::io::IoSlice; @@ -210,13 +208,6 @@ where self.b.advance_mut(cnt); } - - #[cfg(feature = "std")] - fn bytes_vectored_mut<'a>(&'a mut self, dst: &mut [IoSliceMut<'a>]) -> usize { - let mut n = self.a.bytes_vectored_mut(dst); - n += self.b.bytes_vectored_mut(&mut dst[n..]); - n - } } impl IntoIterator for Chain diff --git a/src/buf/mod.rs b/src/buf/mod.rs index 1d7292c9e..ac2868127 100644 --- a/src/buf/mod.rs +++ b/src/buf/mod.rs @@ -24,7 +24,5 @@ mod vec_deque; pub use self::buf_impl::Buf; pub use self::buf_mut::BufMut; -#[cfg(feature = "std")] -pub use self::buf_mut::IoSliceMut; pub use self::ext::{BufExt, BufMutExt}; pub use self::iter::IntoIter; diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index b91e2e511..10c526d11 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -1,7 +1,5 @@ #![warn(rust_2018_idioms)] -#[cfg(feature = "std")] -use bytes::buf::IoSliceMut; use bytes::{BufMut, BytesMut}; use core::fmt::Write; use core::usize; @@ -66,23 +64,6 @@ fn test_clone() { assert!(buf != buf2); } -#[cfg(feature = "std")] -#[test] -fn test_bufs_vec_mut() { - let b1: &mut [u8] = &mut []; - let b2: &mut [u8] = &mut []; - let mut dst = [IoSliceMut::from(b1), IoSliceMut::from(b2)]; - - // with no capacity - let mut buf = BytesMut::new(); - assert_eq!(buf.capacity(), 0); - assert_eq!(1, buf.bytes_vectored_mut(&mut dst[..])); - - // with capacity - let mut buf = BytesMut::with_capacity(64); - assert_eq!(1, buf.bytes_vectored_mut(&mut dst[..])); -} - #[test] fn test_mut_slice() { let mut v = vec![0, 0, 0, 0]; From 94c543f74b111e894d16faa43e4ad361b97ee87d Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 16 Oct 2020 15:16:23 -0700 Subject: [PATCH 025/203] remove ext traits (#431) --- src/buf/buf_impl.rs | 85 ++++++++++++++++ src/buf/buf_mut.rs | 81 ++++++++++++++++ src/buf/{ext => }/chain.rs | 12 +-- src/buf/ext/mod.rs | 186 ------------------------------------ src/buf/{ext => }/limit.rs | 0 src/buf/mod.rs | 15 ++- src/buf/{ext => }/reader.rs | 4 +- src/buf/{ext => }/take.rs | 10 +- src/buf/{ext => }/writer.rs | 6 +- tests/test_chain.rs | 1 - tests/test_reader.rs | 6 +- tests/test_take.rs | 2 +- 12 files changed, 199 insertions(+), 209 deletions(-) rename src/buf/{ext => }/chain.rs (95%) delete mode 100644 src/buf/ext/mod.rs rename src/buf/{ext => }/limit.rs (100%) rename src/buf/{ext => }/reader.rs (96%) rename src/buf/{ext => }/take.rs (94%) rename src/buf/{ext => }/writer.rs (94%) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 5cd7c686e..c26681fb0 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -1,3 +1,7 @@ +#[cfg(feature = "std")] +use crate::buf::{reader, Reader}; +use crate::buf::{take, Chain, Take}; + use core::{cmp, mem, ptr}; #[cfg(feature = "std")] @@ -807,6 +811,87 @@ pub trait Buf { ret.put(self); ret.freeze() } + + /// Creates an adaptor which will read at most `limit` bytes from `self`. + /// + /// This function returns a new instance of `Buf` which will read at most + /// `limit` bytes. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Buf, BufMut}; + /// + /// let mut buf = b"hello world"[..].take(5); + /// let mut dst = vec![]; + /// + /// dst.put(&mut buf); + /// assert_eq!(dst, b"hello"); + /// + /// let mut buf = buf.into_inner(); + /// dst.clear(); + /// dst.put(&mut buf); + /// assert_eq!(dst, b" world"); + /// ``` + fn take(self, limit: usize) -> Take + where + Self: Sized, + { + take::new(self, limit) + } + + /// Creates an adaptor which will chain this buffer with another. + /// + /// The returned `Buf` instance will first consume all bytes from `self`. + /// Afterwards the output is equivalent to the output of next. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut chain = b"hello "[..].chain(&b"world"[..]); + /// + /// let full = chain.to_bytes(); + /// assert_eq!(full.bytes(), b"hello world"); + /// ``` + fn chain(self, next: U) -> Chain + where + Self: Sized, + { + Chain::new(self, next) + } + + /// Creates an adaptor which implements the `Read` trait for `self`. + /// + /// This function returns a new value which implements `Read` by adapting + /// the `Read` trait functions to the `Buf` trait functions. Given that + /// `Buf` operations are infallible, none of the `Read` functions will + /// return with `Err`. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Bytes, Buf}; + /// use std::io::Read; + /// + /// let buf = Bytes::from("hello world"); + /// + /// let mut reader = buf.reader(); + /// let mut dst = [0; 1024]; + /// + /// let num = reader.read(&mut dst).unwrap(); + /// + /// assert_eq!(11, num); + /// assert_eq!(&dst[..11], &b"hello world"[..]); + /// ``` + #[cfg(feature = "std")] + fn reader(self) -> Reader + where + Self: Sized, + { + reader::new(self) + } } macro_rules! deref_forward_buf { diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index ff25cb6db..4f6e47d32 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1,3 +1,7 @@ +use crate::buf::{limit, Chain, Limit}; +#[cfg(feature = "std")] +use crate::buf::{writer, Writer}; + use core::{ cmp, mem::{self, MaybeUninit}, @@ -833,6 +837,83 @@ pub trait BufMut { fn put_f64_le(&mut self, n: f64) { self.put_u64_le(n.to_bits()); } + + /// Creates an adaptor which can write at most `limit` bytes to `self`. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let arr = &mut [0u8; 128][..]; + /// assert_eq!(arr.remaining_mut(), 128); + /// + /// let dst = arr.limit(10); + /// assert_eq!(dst.remaining_mut(), 10); + /// ``` + fn limit(self, limit: usize) -> Limit + where + Self: Sized, + { + limit::new(self, limit) + } + + /// Creates an adaptor which implements the `Write` trait for `self`. + /// + /// This function returns a new value which implements `Write` by adapting + /// the `Write` trait functions to the `BufMut` trait functions. Given that + /// `BufMut` operations are infallible, none of the `Write` functions will + /// return with `Err`. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// use std::io::Write; + /// + /// let mut buf = vec![].writer(); + /// + /// let num = buf.write(&b"hello world"[..]).unwrap(); + /// assert_eq!(11, num); + /// + /// let buf = buf.into_inner(); + /// + /// assert_eq!(*buf, b"hello world"[..]); + /// ``` + #[cfg(feature = "std")] + fn writer(self) -> Writer + where + Self: Sized, + { + writer::new(self) + } + + /// Creates an adapter which will chain this buffer with another. + /// + /// The returned `BufMut` instance will first write to all bytes from + /// `self`. Afterwards, it will write to `next`. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut a = [0u8; 5]; + /// let mut b = [0u8; 6]; + /// + /// let mut chain = (&mut a[..]).chain_mut(&mut b[..]); + /// + /// chain.put_slice(b"hello world"); + /// + /// assert_eq!(&a[..], b"hello"); + /// assert_eq!(&b[..], b" world"); + /// ``` + fn chain_mut(self, next: U) -> Chain + where + Self: Sized, + { + Chain::new(self, next) + } } macro_rules! deref_forward_bufmut { diff --git a/src/buf/ext/chain.rs b/src/buf/chain.rs similarity index 95% rename from src/buf/ext/chain.rs rename to src/buf/chain.rs index 598dde70f..020bf085e 100644 --- a/src/buf/ext/chain.rs +++ b/src/buf/chain.rs @@ -18,7 +18,7 @@ use std::io::IoSlice; /// # Examples /// /// ``` -/// use bytes::{Bytes, Buf, buf::BufExt}; +/// use bytes::{Bytes, Buf}; /// /// let mut buf = (&b"hello "[..]) /// .chain(&b"world"[..]); @@ -47,7 +47,7 @@ impl Chain { /// # Examples /// /// ``` - /// use bytes::buf::BufExt; + /// use bytes::Buf; /// /// let buf = (&b"hello"[..]) /// .chain(&b"world"[..]); @@ -63,7 +63,7 @@ impl Chain { /// # Examples /// /// ``` - /// use bytes::{Buf, buf::BufExt}; + /// use bytes::Buf; /// /// let mut buf = (&b"hello"[..]) /// .chain(&b"world"[..]); @@ -82,7 +82,7 @@ impl Chain { /// # Examples /// /// ``` - /// use bytes::buf::BufExt; + /// use bytes::Buf; /// /// let buf = (&b"hello"[..]) /// .chain(&b"world"[..]); @@ -98,7 +98,7 @@ impl Chain { /// # Examples /// /// ``` - /// use bytes::{Buf, buf::BufExt}; + /// use bytes::Buf; /// /// let mut buf = (&b"hello "[..]) /// .chain(&b"world"[..]); @@ -117,7 +117,7 @@ impl Chain { /// # Examples /// /// ``` - /// use bytes::buf::BufExt; + /// use bytes::Buf; /// /// let chain = (&b"hello"[..]) /// .chain(&b"world"[..]); diff --git a/src/buf/ext/mod.rs b/src/buf/ext/mod.rs deleted file mode 100644 index 4a292676a..000000000 --- a/src/buf/ext/mod.rs +++ /dev/null @@ -1,186 +0,0 @@ -//! Extra utilities for `Buf` and `BufMut` types. - -use super::{Buf, BufMut}; - -mod chain; -mod limit; -#[cfg(feature = "std")] -mod reader; -mod take; -#[cfg(feature = "std")] -mod writer; - -pub use self::chain::Chain; -pub use self::limit::Limit; -pub use self::take::Take; - -#[cfg(feature = "std")] -pub use self::{reader::Reader, writer::Writer}; - -/// Extra methods for implementations of `Buf`. -pub trait BufExt: Buf { - /// Creates an adaptor which will read at most `limit` bytes from `self`. - /// - /// This function returns a new instance of `Buf` which will read at most - /// `limit` bytes. - /// - /// # Examples - /// - /// ``` - /// use bytes::{BufMut, buf::BufExt}; - /// - /// let mut buf = b"hello world"[..].take(5); - /// let mut dst = vec![]; - /// - /// dst.put(&mut buf); - /// assert_eq!(dst, b"hello"); - /// - /// let mut buf = buf.into_inner(); - /// dst.clear(); - /// dst.put(&mut buf); - /// assert_eq!(dst, b" world"); - /// ``` - fn take(self, limit: usize) -> Take - where - Self: Sized, - { - take::new(self, limit) - } - - /// Creates an adaptor which will chain this buffer with another. - /// - /// The returned `Buf` instance will first consume all bytes from `self`. - /// Afterwards the output is equivalent to the output of next. - /// - /// # Examples - /// - /// ``` - /// use bytes::{Buf, buf::BufExt}; - /// - /// let mut chain = b"hello "[..].chain(&b"world"[..]); - /// - /// let full = chain.to_bytes(); - /// assert_eq!(full.bytes(), b"hello world"); - /// ``` - fn chain(self, next: U) -> Chain - where - Self: Sized, - { - Chain::new(self, next) - } - - /// Creates an adaptor which implements the `Read` trait for `self`. - /// - /// This function returns a new value which implements `Read` by adapting - /// the `Read` trait functions to the `Buf` trait functions. Given that - /// `Buf` operations are infallible, none of the `Read` functions will - /// return with `Err`. - /// - /// # Examples - /// - /// ``` - /// use bytes::{Bytes, buf::BufExt}; - /// use std::io::Read; - /// - /// let buf = Bytes::from("hello world"); - /// - /// let mut reader = buf.reader(); - /// let mut dst = [0; 1024]; - /// - /// let num = reader.read(&mut dst).unwrap(); - /// - /// assert_eq!(11, num); - /// assert_eq!(&dst[..11], &b"hello world"[..]); - /// ``` - #[cfg(feature = "std")] - fn reader(self) -> Reader - where - Self: Sized, - { - reader::new(self) - } -} - -impl BufExt for B {} - -/// Extra methods for implementations of `BufMut`. -pub trait BufMutExt: BufMut { - /// Creates an adaptor which can write at most `limit` bytes to `self`. - /// - /// # Examples - /// - /// ``` - /// use bytes::{BufMut, buf::BufMutExt}; - /// - /// let arr = &mut [0u8; 128][..]; - /// assert_eq!(arr.remaining_mut(), 128); - /// - /// let dst = arr.limit(10); - /// assert_eq!(dst.remaining_mut(), 10); - /// ``` - fn limit(self, limit: usize) -> Limit - where - Self: Sized, - { - limit::new(self, limit) - } - - /// Creates an adaptor which implements the `Write` trait for `self`. - /// - /// This function returns a new value which implements `Write` by adapting - /// the `Write` trait functions to the `BufMut` trait functions. Given that - /// `BufMut` operations are infallible, none of the `Write` functions will - /// return with `Err`. - /// - /// # Examples - /// - /// ``` - /// use bytes::buf::BufMutExt; - /// use std::io::Write; - /// - /// let mut buf = vec![].writer(); - /// - /// let num = buf.write(&b"hello world"[..]).unwrap(); - /// assert_eq!(11, num); - /// - /// let buf = buf.into_inner(); - /// - /// assert_eq!(*buf, b"hello world"[..]); - /// ``` - #[cfg(feature = "std")] - fn writer(self) -> Writer - where - Self: Sized, - { - writer::new(self) - } - - /// Creates an adapter which will chain this buffer with another. - /// - /// The returned `BufMut` instance will first write to all bytes from - /// `self`. Afterwards, it will write to `next`. - /// - /// # Examples - /// - /// ``` - /// use bytes::{BufMut, buf::BufMutExt}; - /// - /// let mut a = [0u8; 5]; - /// let mut b = [0u8; 6]; - /// - /// let mut chain = (&mut a[..]).chain_mut(&mut b[..]); - /// - /// chain.put_slice(b"hello world"); - /// - /// assert_eq!(&a[..], b"hello"); - /// assert_eq!(&b[..], b" world"); - /// ``` - fn chain_mut(self, next: U) -> Chain - where - Self: Sized, - { - Chain::new(self, next) - } -} - -impl BufMutExt for B {} diff --git a/src/buf/ext/limit.rs b/src/buf/limit.rs similarity index 100% rename from src/buf/ext/limit.rs rename to src/buf/limit.rs diff --git a/src/buf/mod.rs b/src/buf/mod.rs index ac2868127..5c6d5f9d5 100644 --- a/src/buf/mod.rs +++ b/src/buf/mod.rs @@ -18,11 +18,22 @@ mod buf_impl; mod buf_mut; -pub mod ext; +mod chain; mod iter; +mod limit; +#[cfg(feature = "std")] +mod reader; +mod take; mod vec_deque; +#[cfg(feature = "std")] +mod writer; pub use self::buf_impl::Buf; pub use self::buf_mut::BufMut; -pub use self::ext::{BufExt, BufMutExt}; +pub use self::chain::Chain; pub use self::iter::IntoIter; +pub use self::limit::Limit; +pub use self::take::Take; + +#[cfg(feature = "std")] +pub use self::{reader::Reader, writer::Writer}; diff --git a/src/buf/ext/reader.rs b/src/buf/reader.rs similarity index 96% rename from src/buf/ext/reader.rs rename to src/buf/reader.rs index dde3548bf..135db4172 100644 --- a/src/buf/ext/reader.rs +++ b/src/buf/reader.rs @@ -24,7 +24,7 @@ impl Reader { /// # Examples /// /// ```rust - /// use bytes::buf::BufExt; + /// use bytes::Buf; /// /// let buf = b"hello world".reader(); /// @@ -46,7 +46,7 @@ impl Reader { /// # Examples /// /// ```rust - /// use bytes::{Buf, buf::BufExt}; + /// use bytes::Buf; /// use std::io; /// /// let mut buf = b"hello world".reader(); diff --git a/src/buf/ext/take.rs b/src/buf/take.rs similarity index 94% rename from src/buf/ext/take.rs rename to src/buf/take.rs index 1d84868bf..57b9f4547 100644 --- a/src/buf/ext/take.rs +++ b/src/buf/take.rs @@ -22,7 +22,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::buf::{BufMut, BufExt}; + /// use bytes::{Buf, BufMut}; /// /// let mut buf = b"hello world".take(2); /// let mut dst = vec![]; @@ -47,7 +47,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::{Buf, buf::BufExt}; + /// use bytes::Buf; /// /// let buf = b"hello world".take(2); /// @@ -64,7 +64,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::{Buf, BufMut, buf::BufExt}; + /// use bytes::{Buf, BufMut}; /// /// let mut buf = b"hello world".take(2); /// let mut dst = vec![]; @@ -88,7 +88,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::{Buf, buf::BufExt}; + /// use bytes::Buf; /// /// let mut buf = b"hello world".take(2); /// @@ -110,7 +110,7 @@ impl Take { /// # Examples /// /// ```rust - /// use bytes::{BufMut, buf::BufExt}; + /// use bytes::{Buf, BufMut}; /// /// let mut buf = b"hello world".take(2); /// let mut dst = vec![]; diff --git a/src/buf/ext/writer.rs b/src/buf/writer.rs similarity index 94% rename from src/buf/ext/writer.rs rename to src/buf/writer.rs index a14197c81..261d7cd09 100644 --- a/src/buf/ext/writer.rs +++ b/src/buf/writer.rs @@ -24,7 +24,7 @@ impl Writer { /// # Examples /// /// ```rust - /// use bytes::buf::BufMutExt; + /// use bytes::BufMut; /// /// let buf = Vec::with_capacity(1024).writer(); /// @@ -41,7 +41,7 @@ impl Writer { /// # Examples /// /// ```rust - /// use bytes::buf::BufMutExt; + /// use bytes::BufMut; /// /// let mut buf = vec![].writer(); /// @@ -58,7 +58,7 @@ impl Writer { /// # Examples /// /// ```rust - /// use bytes::buf::BufMutExt; + /// use bytes::BufMut; /// use std::io; /// /// let mut buf = vec![].writer(); diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 6dbc45d04..9be434f37 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,6 +1,5 @@ #![warn(rust_2018_idioms)] -use bytes::buf::{BufExt, BufMutExt}; use bytes::{Buf, BufMut, Bytes}; #[cfg(feature = "std")] use std::io::IoSlice; diff --git a/tests/test_reader.rs b/tests/test_reader.rs index 10b480fcc..897aff645 100644 --- a/tests/test_reader.rs +++ b/tests/test_reader.rs @@ -3,13 +3,13 @@ use std::io::{BufRead, Read}; -use bytes::buf::BufExt; +use bytes::Buf; #[test] fn read() { let buf1 = &b"hello "[..]; let buf2 = &b"world"[..]; - let buf = BufExt::chain(buf1, buf2); // Disambiguate with Read::chain + let buf = Buf::chain(buf1, buf2); // Disambiguate with Read::chain let mut buffer = Vec::new(); buf.reader().read_to_end(&mut buffer).unwrap(); assert_eq!(b"hello world", &buffer[..]); @@ -19,7 +19,7 @@ fn read() { fn buf_read() { let buf1 = &b"hell"[..]; let buf2 = &b"o\nworld"[..]; - let mut reader = BufExt::chain(buf1, buf2).reader(); + let mut reader = Buf::chain(buf1, buf2).reader(); let mut line = String::new(); reader.read_line(&mut line).unwrap(); assert_eq!("hello\n", &line); diff --git a/tests/test_take.rs b/tests/test_take.rs index 0afb28bb4..40a1fa53d 100644 --- a/tests/test_take.rs +++ b/tests/test_take.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms)] -use bytes::buf::{Buf, BufExt}; +use bytes::buf::Buf; #[test] fn long_take() { From ced050730c7a5f9d322c0cf9aedea67f16151e90 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 16 Oct 2020 15:45:38 -0700 Subject: [PATCH 026/203] Make BufMut an unsafe trait (#432) Users of `BufMut` are unable to defend against incorrect implementations of `BufMut`, this makes the trait unsafe to implement. Fixes #329 --- src/buf/buf_mut.rs | 10 +++++----- src/buf/chain.rs | 2 +- src/buf/limit.rs | 2 +- src/bytes_mut.rs | 2 +- tests/test_buf_mut.rs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 4f6e47d32..026bcad02 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -30,7 +30,7 @@ use alloc::{boxed::Box, vec::Vec}; /// /// assert_eq!(buf, b"hello world"); /// ``` -pub trait BufMut { +pub unsafe trait BufMut { /// Returns the number of bytes that can be written from the current /// position until the end of the buffer is reached. /// @@ -992,15 +992,15 @@ macro_rules! deref_forward_bufmut { }; } -impl BufMut for &mut T { +unsafe impl BufMut for &mut T { deref_forward_bufmut!(); } -impl BufMut for Box { +unsafe impl BufMut for Box { deref_forward_bufmut!(); } -impl BufMut for &mut [u8] { +unsafe impl BufMut for &mut [u8] { #[inline] fn remaining_mut(&self) -> usize { self.len() @@ -1020,7 +1020,7 @@ impl BufMut for &mut [u8] { } } -impl BufMut for Vec { +unsafe impl BufMut for Vec { #[inline] fn remaining_mut(&self) -> usize { usize::MAX - self.len() diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 020bf085e..cc2c944b7 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -174,7 +174,7 @@ where } } -impl BufMut for Chain +unsafe impl BufMut for Chain where T: BufMut, U: BufMut, diff --git a/src/buf/limit.rs b/src/buf/limit.rs index a36eceeef..c6ed3c7b1 100644 --- a/src/buf/limit.rs +++ b/src/buf/limit.rs @@ -55,7 +55,7 @@ impl Limit { } } -impl BufMut for Limit { +unsafe impl BufMut for Limit { fn remaining_mut(&self) -> usize { cmp::min(self.inner.remaining_mut(), self.limit) } diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index a7a8e5798..16cb72c2b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -966,7 +966,7 @@ impl Buf for BytesMut { } } -impl BufMut for BytesMut { +unsafe impl BufMut for BytesMut { #[inline] fn remaining_mut(&self) -> usize { usize::MAX - self.len() diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 10c526d11..e9948839a 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -75,7 +75,7 @@ fn test_mut_slice() { fn test_deref_bufmut_forwards() { struct Special; - impl BufMut for Special { + unsafe impl BufMut for Special { fn remaining_mut(&self) -> usize { unreachable!("remaining_mut"); } From 4724c7e8a0fbb81164f3ca992475181e776867ec Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sun, 18 Oct 2020 09:55:19 -0700 Subject: [PATCH 027/203] remove `new` fns from combinator structs (#434) This is not idiomatic. --- src/buf/chain.rs | 2 +- src/buf/iter.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/buf/chain.rs b/src/buf/chain.rs index cc2c944b7..894d8b75e 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -38,7 +38,7 @@ pub struct Chain { impl Chain { /// Creates a new `Chain` sequencing the provided values. - pub fn new(a: T, b: U) -> Chain { + pub(crate) fn new(a: T, b: U) -> Chain { Chain { a, b } } diff --git a/src/buf/iter.rs b/src/buf/iter.rs index 0f9bdc04f..16a2c5543 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -34,17 +34,16 @@ impl IntoIter { /// /// ``` /// use bytes::Bytes; - /// use bytes::buf::IntoIter; /// /// let buf = Bytes::from_static(b"abc"); - /// let mut iter = IntoIter::new(buf); + /// let mut iter = buf.into_iter(); /// /// assert_eq!(iter.next(), Some(b'a')); /// assert_eq!(iter.next(), Some(b'b')); /// assert_eq!(iter.next(), Some(b'c')); /// assert_eq!(iter.next(), None); /// ``` - pub fn new(inner: T) -> IntoIter { + pub(crate) fn new(inner: T) -> IntoIter { IntoIter { inner } } From 5a11c783ecf7c308e7b145f56d5c05e635b99269 Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Mon, 19 Oct 2020 12:32:20 -0700 Subject: [PATCH 028/203] De-emphasize `Arc` implementation in `Bytes` description (#436) The previous description focussed a lot on the `Arc` based implementation of `Bytes`. Given the vtable based implemetation, this is however not the only valid implementation. This changes the description a bit in order to de-emaphasize the `Arc` part, and to describe that other implementations are possible. This should also be necessary if the vtable gets public. --- src/bytes.rs | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 79a09f398..96af7d6af 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -10,16 +10,23 @@ use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; use crate::Buf; -/// A reference counted contiguous slice of memory. +/// A cheaply cloneable and sliceable chunk of contiguous memory. /// /// `Bytes` is an efficient container for storing and operating on contiguous /// slices of memory. It is intended for use primarily in networking code, but /// could have applications elsewhere as well. /// /// `Bytes` values facilitate zero-copy network programming by allowing multiple -/// `Bytes` objects to point to the same underlying memory. This is managed by -/// using a reference count to track when the memory is no longer needed and can -/// be freed. +/// `Bytes` objects to point to the same underlying memory. +/// +/// `Bytes` does not have a single implementation. It is an interface, whose +/// exact behavior is implemented through dynamic dispatch in several underlying +/// implementations of `Bytes`. +/// +/// 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. /// /// ``` /// use bytes::Bytes; @@ -41,17 +48,33 @@ use crate::Buf; /// to track information about which segment of the underlying memory the /// `Bytes` handle has access to. /// -/// `Bytes` keeps both a pointer to the shared `Arc` containing the full memory +/// `Bytes` keeps both a pointer to the shared state containing the full memory /// slice and a pointer to the start of the region visible by the handle. /// `Bytes` also tracks the length of its view into the memory. /// /// # Sharing /// -/// The memory itself is reference counted, and multiple `Bytes` objects may -/// point to the same region. Each `Bytes` handle point to different sections within -/// the memory region, and `Bytes` handle may or may not have overlapping views +/// `Bytes` contains a vtable, which allows implementations of `Bytes` to define +/// how sharing/cloneing 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. +/// +/// For `Bytes` implementations which refer to constant memory (e.g. created +/// via `Bytes::from_static()`) the cloning implementation will be a no-op. +/// +/// 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. +/// +/// Due to this mechanism, multiple `Bytes` instances may point to the same +/// shared memory region. +/// Each `Bytes` instance can point to different sections within that +/// memory region, and `Bytes` instances may or may not have overlapping views /// into the memory. /// +/// The following diagram visualizes a scenario where 2 `Bytes` instances make +/// use of an `Arc`-based backing storage, and provide access to different views: /// /// ```text /// @@ -175,7 +198,7 @@ impl Bytes { self.len == 0 } - ///Creates `Bytes` instance from slice, by copying it. + /// Creates `Bytes` instance from slice, by copying it. pub fn copy_from_slice(data: &[u8]) -> Self { data.to_vec().into() } From e0d8413d53e3774177b040a4f9adda40ec15ce8f Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Mon, 19 Oct 2020 15:48:23 -0700 Subject: [PATCH 029/203] Switch `BufMut::bytes_mut` to`&mut UninitSlice` (#433) The way BufMut uses MaybeUninit can lead to unsoundness. This replaces MaybeUnit with a type owned by bytes so we can ensure the usage patterns are sound. Refs: #328 --- src/buf/buf_mut.rs | 53 +++++------- src/buf/chain.rs | 6 +- src/buf/limit.rs | 5 +- src/buf/mod.rs | 2 + src/buf/uninit_slice.rs | 176 ++++++++++++++++++++++++++++++++++++++++ src/bytes_mut.rs | 12 +-- tests/test_buf_mut.rs | 30 ++++++- tests/test_bytes.rs | 4 +- 8 files changed, 241 insertions(+), 47 deletions(-) create mode 100644 src/buf/uninit_slice.rs diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 026bcad02..fb3623d25 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1,12 +1,8 @@ -use crate::buf::{limit, Chain, Limit}; +use crate::buf::{limit, Chain, Limit, UninitSlice}; #[cfg(feature = "std")] use crate::buf::{writer, Writer}; -use core::{ - cmp, - mem::{self, MaybeUninit}, - ptr, usize, -}; +use core::{cmp, mem, ptr, usize}; use alloc::{boxed::Box, vec::Vec}; @@ -73,19 +69,14 @@ pub unsafe trait BufMut { /// /// let mut buf = Vec::with_capacity(16); /// - /// unsafe { - /// // MaybeUninit::as_mut_ptr - /// buf.bytes_mut()[0].as_mut_ptr().write(b'h'); - /// buf.bytes_mut()[1].as_mut_ptr().write(b'e'); + /// // Write some data + /// buf.bytes_mut()[0..2].copy_from_slice(b"he"); + /// unsafe { buf.advance_mut(2) }; /// - /// buf.advance_mut(2); + /// // write more bytes + /// buf.bytes_mut()[0..3].copy_from_slice(b"llo"); /// - /// buf.bytes_mut()[0].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[1].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[2].as_mut_ptr().write(b'o'); - /// - /// buf.advance_mut(3); - /// } + /// unsafe { buf.advance_mut(3); } /// /// assert_eq!(5, buf.len()); /// assert_eq!(buf, b"hello"); @@ -144,14 +135,14 @@ pub unsafe trait BufMut { /// /// unsafe { /// // MaybeUninit::as_mut_ptr - /// buf.bytes_mut()[0].as_mut_ptr().write(b'h'); - /// buf.bytes_mut()[1].as_mut_ptr().write(b'e'); + /// buf.bytes_mut()[0..].as_mut_ptr().write(b'h'); + /// buf.bytes_mut()[1..].as_mut_ptr().write(b'e'); /// /// buf.advance_mut(2); /// - /// buf.bytes_mut()[0].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[1].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[2].as_mut_ptr().write(b'o'); + /// buf.bytes_mut()[0..].as_mut_ptr().write(b'l'); + /// buf.bytes_mut()[1..].as_mut_ptr().write(b'l'); + /// buf.bytes_mut()[2..].as_mut_ptr().write(b'o'); /// /// buf.advance_mut(3); /// } @@ -167,7 +158,7 @@ pub unsafe trait BufMut { /// `bytes_mut` returning an empty slice implies that `remaining_mut` will /// return 0 and `remaining_mut` returning 0 implies that `bytes_mut` will /// return an empty slice. - fn bytes_mut(&mut self) -> &mut [MaybeUninit]; + fn bytes_mut(&mut self) -> &mut UninitSlice; /// Transfer bytes into `self` from `src` and advance the cursor by the /// number of bytes written. @@ -922,7 +913,7 @@ macro_rules! deref_forward_bufmut { (**self).remaining_mut() } - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { + fn bytes_mut(&mut self) -> &mut UninitSlice { (**self).bytes_mut() } @@ -1007,9 +998,9 @@ unsafe impl BufMut for &mut [u8] { } #[inline] - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { - // MaybeUninit is repr(transparent), so safe to transmute - unsafe { mem::transmute(&mut **self) } + fn bytes_mut(&mut self) -> &mut UninitSlice { + // UninitSlice is repr(transparent), so safe to transmute + unsafe { &mut *(*self as *mut [u8] as *mut _) } } #[inline] @@ -1042,9 +1033,7 @@ unsafe impl BufMut for Vec { } #[inline] - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { - use core::slice; - + fn bytes_mut(&mut self) -> &mut UninitSlice { if self.capacity() == self.len() { self.reserve(64); // Grow the vec } @@ -1052,8 +1041,8 @@ unsafe impl BufMut for Vec { let cap = self.capacity(); let len = self.len(); - let ptr = self.as_mut_ptr() as *mut MaybeUninit; - unsafe { &mut slice::from_raw_parts_mut(ptr, cap)[len..] } + let ptr = self.as_mut_ptr(); + unsafe { &mut UninitSlice::from_raw_parts_mut(ptr, cap)[len..] } } // Specialize these methods so they can skip checking `remaining_mut` diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 894d8b75e..db6219281 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -1,8 +1,6 @@ -use crate::buf::IntoIter; +use crate::buf::{IntoIter, UninitSlice}; use crate::{Buf, BufMut}; -use core::mem::MaybeUninit; - #[cfg(feature = "std")] use std::io::IoSlice; @@ -183,7 +181,7 @@ where self.a.remaining_mut() + self.b.remaining_mut() } - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { + fn bytes_mut(&mut self) -> &mut UninitSlice { if self.a.has_remaining_mut() { self.a.bytes_mut() } else { diff --git a/src/buf/limit.rs b/src/buf/limit.rs index c6ed3c7b1..5cbbbfe6b 100644 --- a/src/buf/limit.rs +++ b/src/buf/limit.rs @@ -1,6 +1,7 @@ +use crate::buf::UninitSlice; use crate::BufMut; -use core::{cmp, mem::MaybeUninit}; +use core::cmp; /// A `BufMut` adapter which limits the amount of bytes that can be written /// to an underlying buffer. @@ -60,7 +61,7 @@ unsafe impl BufMut for Limit { cmp::min(self.inner.remaining_mut(), self.limit) } - fn bytes_mut(&mut self) -> &mut [MaybeUninit] { + fn bytes_mut(&mut self) -> &mut UninitSlice { let bytes = self.inner.bytes_mut(); let end = cmp::min(bytes.len(), self.limit); &mut bytes[..end] diff --git a/src/buf/mod.rs b/src/buf/mod.rs index 5c6d5f9d5..c4c0a5724 100644 --- a/src/buf/mod.rs +++ b/src/buf/mod.rs @@ -24,6 +24,7 @@ mod limit; #[cfg(feature = "std")] mod reader; mod take; +mod uninit_slice; mod vec_deque; #[cfg(feature = "std")] mod writer; @@ -34,6 +35,7 @@ pub use self::chain::Chain; pub use self::iter::IntoIter; pub use self::limit::Limit; pub use self::take::Take; +pub use self::uninit_slice::UninitSlice; #[cfg(feature = "std")] pub use self::{reader::Reader, writer::Writer}; diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs new file mode 100644 index 000000000..32ebde4c5 --- /dev/null +++ b/src/buf/uninit_slice.rs @@ -0,0 +1,176 @@ +use core::fmt; +use core::mem::MaybeUninit; +use core::ops::{ + Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, +}; + +/// Uninitialized byte slice. +/// +/// Returned by `BufMut::bytes_mut()`, the referenced byte slice may be +/// uninitialized. The wrapper provides safe access without introducing +/// undefined behavior. +/// +/// The safety invariants of this wrapper are: +/// +/// 1. Reading from an `UninitSlice` is undefined behavior. +/// 2. Writing uninitialized bytes to an `UninitSlice` is undefined behavior. +/// +/// The difference between `&mut UninitSlice` and `&mut [MaybeUninit]` is +/// that it is possible in safe code to write uninitialized bytes to an +/// `&mut [MaybeUninit]`, which this type prohibits. +#[repr(transparent)] +pub struct UninitSlice([MaybeUninit]); + +impl UninitSlice { + /// Create a `&mut UninitSlice` from a pointer and a length. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` references a valid memory region owned + /// by the caller representing a byte slice for the duration of `'a`. + /// + /// # Examples + /// + /// ``` + /// use bytes::buf::UninitSlice; + /// + /// let bytes = b"hello world".to_vec(); + /// let ptr = bytes.as_ptr() as *mut _; + /// let len = bytes.len(); + /// + /// let slice = unsafe { UninitSlice::from_raw_parts_mut(ptr, len) }; + /// ``` + 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) + } + + /// Write a single byte at the specified offset. + /// + /// # Panics + /// + /// The function panics if `index` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use bytes::buf::UninitSlice; + /// + /// let mut data = [b'f', b'o', b'o']; + /// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; + /// + /// slice.write_byte(0, b'b'); + /// + /// assert_eq!(b"boo", &data[..]); + /// ``` + pub fn write_byte(&mut self, index: usize, byte: u8) { + assert!(index < self.len()); + + unsafe { self[index..].as_mut_ptr().write(byte) } + } + + /// Copies bytes from `src` into `self`. + /// + /// The length of `src` must be the same as `self`. + /// + /// # Panics + /// + /// The function panics if `src` has a different length than `self`. + /// + /// # Examples + /// + /// ``` + /// use bytes::buf::UninitSlice; + /// + /// let mut data = [b'f', b'o', b'o']; + /// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; + /// + /// slice.copy_from_slice(b"bar"); + /// + /// assert_eq!(b"bar", &data[..]); + /// ``` + pub fn copy_from_slice(&mut self, src: &[u8]) { + use core::ptr; + + assert_eq!(self.len(), src.len()); + + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len()); + } + } + + /// Return a raw pointer to the slice's buffer. + /// + /// # Safety + /// + /// The caller **must not** read from the referenced memory and **must not** + /// write **uninitialized** bytes to the slice either. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut data = [0, 1, 2]; + /// let mut slice = &mut data[..]; + /// let ptr = BufMut::bytes_mut(&mut slice).as_mut_ptr(); + /// ``` + pub fn as_mut_ptr(&mut self) -> *mut u8 { + self.0.as_mut_ptr() as *mut _ + } + + /// Returns the number of bytes in the slice. + /// + /// # Examples + /// + /// ``` + /// use bytes::BufMut; + /// + /// let mut data = [0, 1, 2]; + /// let mut slice = &mut data[..]; + /// let len = BufMut::bytes_mut(&mut slice).len(); + /// + /// assert_eq!(len, 3); + /// ``` + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl fmt::Debug for UninitSlice { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("UninitSlice[...]").finish() + } +} + +macro_rules! impl_index { + ($($t:ty),*) => { + $( + impl Index<$t> for UninitSlice { + type Output = UninitSlice; + + fn index(&self, index: $t) -> &UninitSlice { + let maybe_uninit: &[MaybeUninit] = &self.0[index]; + unsafe { &*(maybe_uninit as *const [MaybeUninit] as *const UninitSlice) } + } + } + + impl IndexMut<$t> for UninitSlice { + 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) } + } + } + )* + }; +} + +impl_index!( + Range, + RangeFrom, + RangeFull, + RangeInclusive, + RangeTo, + RangeToInclusive +); diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 16cb72c2b..38f1ed53d 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -11,7 +11,7 @@ use alloc::{ vec::Vec, }; -use crate::buf::IntoIter; +use crate::buf::{IntoIter, UninitSlice}; use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; @@ -684,7 +684,7 @@ impl BytesMut { self.reserve(cnt); unsafe { - let dst = self.maybe_uninit_bytes(); + let dst = self.uninit_slice(); // Reserved above debug_assert!(dst.len() >= cnt); @@ -910,12 +910,12 @@ impl BytesMut { } #[inline] - fn maybe_uninit_bytes(&mut self) -> &mut [mem::MaybeUninit] { + fn uninit_slice(&mut self) -> &mut UninitSlice { unsafe { let ptr = self.ptr.as_ptr().offset(self.len as isize); let len = self.cap - self.len; - slice::from_raw_parts_mut(ptr as *mut mem::MaybeUninit, len) + UninitSlice::from_raw_parts_mut(ptr, len) } } } @@ -985,11 +985,11 @@ unsafe impl BufMut for BytesMut { } #[inline] - fn bytes_mut(&mut self) -> &mut [mem::MaybeUninit] { + fn bytes_mut(&mut self) -> &mut UninitSlice { if self.capacity() == self.len() { self.reserve(64); } - self.maybe_uninit_bytes() + self.uninit_slice() } // Specialize these methods so they can skip checking `remaining_mut` diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index e9948839a..406ec510c 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -1,5 +1,6 @@ #![warn(rust_2018_idioms)] +use bytes::buf::UninitSlice; use bytes::{BufMut, BytesMut}; use core::fmt::Write; use core::usize; @@ -80,7 +81,7 @@ fn test_deref_bufmut_forwards() { unreachable!("remaining_mut"); } - fn bytes_mut(&mut self) -> &mut [std::mem::MaybeUninit] { + fn bytes_mut(&mut self) -> &mut UninitSlice { unreachable!("bytes_mut"); } @@ -99,3 +100,30 @@ fn test_deref_bufmut_forwards() { (Box::new(Special) as Box).put_u8(b'x'); Box::new(Special).put_u8(b'x'); } + +#[test] +#[should_panic] +fn write_byte_panics_if_out_of_bounds() { + let mut data = [b'b', b'a', b'r']; + + let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; + slice.write_byte(4, b'f'); +} + +#[test] +#[should_panic] +fn copy_from_slice_panics_if_different_length_1() { + let mut data = [b'b', b'a', b'r']; + + let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; + slice.copy_from_slice(b"a"); +} + +#[test] +#[should_panic] +fn copy_from_slice_panics_if_different_length_2() { + let mut data = [b'b', b'a', b'r']; + + let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; + slice.copy_from_slice(b"abcd"); +} diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 6b106a6bc..b97cce6cb 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -912,12 +912,12 @@ fn bytes_buf_mut_advance() { let mut bytes = BytesMut::with_capacity(1024); unsafe { - let ptr = bytes.bytes_mut().as_ptr(); + let ptr = bytes.bytes_mut().as_mut_ptr(); assert_eq!(1024, bytes.bytes_mut().len()); bytes.advance_mut(10); - let next = bytes.bytes_mut().as_ptr(); + let next = bytes.bytes_mut().as_mut_ptr(); assert_eq!(1024 - 10, bytes.bytes_mut().len()); assert_eq!(ptr.offset(10), next); From 5866839e4527784600f1b1d6db27dd4e82c1a536 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 20 Oct 2020 10:26:38 -0700 Subject: [PATCH 030/203] use checked addition with range (#438) --- src/bytes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes.rs b/src/bytes.rs index 96af7d6af..66b941d99 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -237,7 +237,7 @@ impl Bytes { }; let end = match range.end_bound() { - Bound::Included(&n) => n + 1, + Bound::Included(&n) => n.checked_add(1).expect("out of range"), Bound::Excluded(&n) => n, Bound::Unbounded => len, }; From 39de065a1ed7005b5b7e85fba612bf049a510807 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 20 Oct 2020 11:00:35 -0700 Subject: [PATCH 031/203] Add `Buf::copy_to_bytes(len)` (#439) This method replaces `Buf::to_bytes()`, providing a method that copies a subset of the remaining buffer into a `Bytes` value. As this is strictly more flexible, `to_bytes()` is removed. Fixes: #129, #398 --- src/buf/buf_impl.rs | 26 +++++++++++++++++--------- src/buf/chain.rs | 6 +++--- src/bytes.rs | 10 ++++++++-- src/bytes_mut.rs | 4 ++-- tests/test_buf.rs | 17 +++++++++++++++++ tests/test_chain.rs | 2 +- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index c26681fb0..04c2f7c40 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -795,20 +795,28 @@ pub trait Buf { f64::from_bits(Self::get_u64_le(self)) } - /// Consumes remaining bytes inside self and returns new instance of `Bytes` + /// Consumes `len` bytes inside self and returns new instance of `Bytes` + /// with this data. + /// + /// This function may be optimized by the underlying type to avoid actual + /// copies. For example, `Bytes` implementation will do a shallow copy + /// (ref-count increment). /// /// # Examples /// /// ``` /// use bytes::Buf; /// - /// let bytes = (&b"hello world"[..]).to_bytes(); - /// assert_eq!(&bytes[..], &b"hello world"[..]); + /// let bytes = (&b"hello world"[..]).copy_to_bytes(5); + /// assert_eq!(&bytes[..], &b"hello"[..]); /// ``` - fn to_bytes(&mut self) -> crate::Bytes { + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { use super::BufMut; - let mut ret = crate::BytesMut::with_capacity(self.remaining()); - ret.put(self); + + assert!(len <= self.remaining(), "`len` greater than remaining"); + + let mut ret = crate::BytesMut::with_capacity(len); + ret.put(self.take(len)); ret.freeze() } @@ -852,7 +860,7 @@ pub trait Buf { /// /// let mut chain = b"hello "[..].chain(&b"world"[..]); /// - /// let full = chain.to_bytes(); + /// let full = chain.copy_to_bytes(11); /// assert_eq!(full.bytes(), b"hello world"); /// ``` fn chain(self, next: U) -> Chain @@ -993,8 +1001,8 @@ macro_rules! deref_forward_buf { (**self).get_int_le(nbytes) } - fn to_bytes(&mut self) -> crate::Bytes { - (**self).to_bytes() + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + (**self).copy_to_bytes(len) } }; } diff --git a/src/buf/chain.rs b/src/buf/chain.rs index db6219281..c21fd35ae 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -21,7 +21,7 @@ use std::io::IoSlice; /// let mut buf = (&b"hello "[..]) /// .chain(&b"world"[..]); /// -/// let full: Bytes = buf.to_bytes(); +/// let full: Bytes = buf.copy_to_bytes(11); /// assert_eq!(full[..], b"hello world"[..]); /// ``` /// @@ -68,7 +68,7 @@ impl Chain { /// /// buf.first_mut().advance(1); /// - /// let full = buf.to_bytes(); + /// let full = buf.copy_to_bytes(9); /// assert_eq!(full, b"elloworld"[..]); /// ``` pub fn first_mut(&mut self) -> &mut T { @@ -103,7 +103,7 @@ impl Chain { /// /// buf.last_mut().advance(1); /// - /// let full = buf.to_bytes(); + /// let full = buf.copy_to_bytes(10); /// assert_eq!(full, b"hello orld"[..]); /// ``` pub fn last_mut(&mut self) -> &mut U { diff --git a/src/bytes.rs b/src/bytes.rs index 66b941d99..867f86bb8 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -548,8 +548,14 @@ impl Buf for Bytes { } } - fn to_bytes(&mut self) -> crate::Bytes { - core::mem::replace(self, Bytes::new()) + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + if len == self.remaining() { + core::mem::replace(self, Bytes::new()) + } else { + let ret = self.slice(..len); + self.advance(len); + ret + } } } diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 38f1ed53d..c3abadf14 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -961,8 +961,8 @@ impl Buf for BytesMut { } } - fn to_bytes(&mut self) -> crate::Bytes { - self.split().freeze() + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + self.split_to(len).freeze() } } diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 17bdd54e8..bc1287323 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -101,3 +101,20 @@ fn test_deref_buf_forwards() { assert_eq!((Box::new(Special) as Box).get_u8(), b'x'); assert_eq!(Box::new(Special).get_u8(), b'x'); } + +#[test] +fn copy_to_bytes_less() { + let mut buf = &b"hello world"[..]; + + let bytes = buf.copy_to_bytes(5); + assert_eq!(bytes, &b"hello"[..]); + assert_eq!(buf, &b" world"[..]) +} + +#[test] +#[should_panic] +fn copy_to_bytes_overflow() { + let mut buf = &b"hello world"[..]; + + let _bytes = buf.copy_to_bytes(12); +} diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 9be434f37..0f362846e 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -9,7 +9,7 @@ fn collect_two_bufs() { let a = Bytes::from(&b"hello"[..]); let b = Bytes::from(&b"world"[..]); - let res = a.chain(b).to_bytes(); + let res = a.chain(b).copy_to_bytes(10); assert_eq!(res, &b"helloworld"[..]); } From b7f75829ab521663b8906ae5bdfeadf47427d7b5 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 20 Oct 2020 16:08:25 -0700 Subject: [PATCH 032/203] prepare v0.6.0 release (#440) --- CHANGELOG.md | 14 +++++++++++++- Cargo.toml | 1 - 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9fa202e..cebb06168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ -# 0.6.0 (unreleased) +# 0.6.0 (October 21, 2020) +API polish in preparation for a 1.0 release. +### Changed +- `BufMut` is now an `unsafe` trait (#432). +- `BufMut::bytes_mut()` returns `&mut UninitSlice`, a type owned by `bytes` to + avoid undefined behavior (#433). +- `Buf::copy_to_bytes(len)` replaces `Buf::into_bytes()` (#439). +- `Buf`/`BufMut` utility methods are moved onto the trait and `*Ext` traits are + removed (#431). + +### Removed +- `BufMut::bytes_vectored_mut()` (#430). +- `new` methods on combinator types (#434). # 0.5.6 (July 13, 2020) diff --git a/Cargo.toml b/Cargo.toml index bb928de14..3d1d73df1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] edition = "2018" -publish = false [features] default = ["std"] From 39c6d7a51212dcfa339e62af153e7eacf33b5ac9 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sun, 1 Nov 2020 10:10:51 -0500 Subject: [PATCH 033/203] Upgrade bytes version from 0.5 to 0.6.0 in README (#441) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 73c43abc8..9ccc7852e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To use `bytes`, first add this to your `Cargo.toml`: ```toml [dependencies] -bytes = "0.5" +bytes = "0.6" ``` Next, add this to your crate: @@ -33,7 +33,7 @@ Serde support is optional and disabled by default. To enable use the feature `se ```toml [dependencies] -bytes = { version = "0.5", features = ["serde"] } +bytes = { version = "0.6", features = ["serde"] } ``` ## License From bd78f193935bc53539a1c0399712968f630f0678 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sat, 12 Dec 2020 08:25:14 -0800 Subject: [PATCH 034/203] chore: prepare for v1.0.0 work (#448) --- Cargo.toml | 7 ++++--- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3d1d73df1..8013bff13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,20 +5,21 @@ name = "bytes" # - Update html_root_url. # - Update CHANGELOG.md. # - Update doc URL. -# - Create "v0.6.x" git tag. -version = "0.6.0" +# - Create "v1.0.x" git tag. +version = "1.0.0" license = "MIT" authors = [ "Carl Lerche ", "Sean McArthur ", ] description = "Types and traits for working with bytes" -documentation = "https://docs.rs/bytes/0.6.0/bytes/" +documentation = "https://docs.rs/bytes/1.0.0/bytes/" repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] edition = "2018" +publish = false [features] default = ["std"] diff --git a/src/lib.rs b/src/lib.rs index cfa626a81..940d17d7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/0.6.0")] +#![doc(html_root_url = "https://docs.rs/bytes/1.0.0")] #![no_std] //! Provides abstractions for working with bytes. From 54f5ced6c58c47f721836a9444654de4c8d687a1 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Wed, 16 Dec 2020 21:51:13 -0800 Subject: [PATCH 035/203] remove unused Buf implementation. (#449) The implementation of `Buf` for `Option<[u8; 1]>` was added to support `IntoBuf`. The `IntoBuf` trait has since been removed. Closes #444 --- benches/buf.rs | 1 - src/buf/buf_impl.rs | 29 ----------------------------- 2 files changed, 30 deletions(-) diff --git a/benches/buf.rs b/benches/buf.rs index 77b0633ee..4b5d2864a 100644 --- a/benches/buf.rs +++ b/benches/buf.rs @@ -159,7 +159,6 @@ macro_rules! bench_group { mod get_u8 { use super::*; bench_group!(get_u8); - bench!(option, option); } mod get_u16 { use super::*; diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 04c2f7c40..3c596f164 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -1032,35 +1032,6 @@ impl Buf for &[u8] { } } -impl Buf for Option<[u8; 1]> { - fn remaining(&self) -> usize { - if self.is_some() { - 1 - } else { - 0 - } - } - - fn bytes(&self) -> &[u8] { - self.as_ref() - .map(AsRef::as_ref) - .unwrap_or(Default::default()) - } - - fn advance(&mut self, cnt: usize) { - if cnt == 0 { - return; - } - - if self.is_none() { - panic!("overflow"); - } else { - assert_eq!(1, cnt); - *self = None; - } - } -} - #[cfg(feature = "std")] impl> Buf for std::io::Cursor { fn remaining(&self) -> usize { From 06907f3e7badc98b83902c27ab19661693b0979a Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 18 Dec 2020 11:04:31 -0800 Subject: [PATCH 036/203] Rename Buf/BufMut, methods to chunk/chunk_mut (#450) The `bytes()` / `bytes_mut()` name implies the method returns the full set of bytes represented by `Buf`/`BufMut`. To rectify this, the methods are renamed to `chunk()` and `chunk_mut()` to reflect the partial nature of the returned byte slice. `bytes_vectored()` is renamed `chunks_vectored()`. Closes #447 --- .github/workflows/ci.yml | 3 ++- benches/buf.rs | 6 ++--- src/buf/buf_impl.rs | 48 ++++++++++++++++++++-------------------- src/buf/buf_mut.rs | 44 ++++++++++++++++++------------------ src/buf/chain.rs | 18 +++++++-------- src/buf/iter.rs | 2 +- src/buf/limit.rs | 4 ++-- src/buf/reader.rs | 2 +- src/buf/take.rs | 4 ++-- src/buf/uninit_slice.rs | 6 ++--- src/buf/vec_deque.rs | 2 +- src/bytes.rs | 2 +- src/bytes_mut.rs | 8 +++---- tests/test_buf.rs | 16 +++++++------- tests/test_buf_mut.rs | 6 ++--- tests/test_bytes.rs | 10 ++++----- tests/test_chain.rs | 8 +++---- tests/test_take.rs | 2 +- 18 files changed, 96 insertions(+), 95 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b99832a5..a9111221f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ on: env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 + nightly: nightly-2020-12-17 defaults: run: @@ -120,7 +121,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Rust - run: rustup update nightly && rustup default nightly + run: rustup update $nightly && rustup default $nightly - name: Install rust-src run: rustup component add rust-src - name: ASAN / TSAN diff --git a/benches/buf.rs b/benches/buf.rs index 4b5d2864a..6dc8516dd 100644 --- a/benches/buf.rs +++ b/benches/buf.rs @@ -53,7 +53,7 @@ impl Buf for TestBuf { assert!(self.pos <= self.buf.len()); self.next_readlen(); } - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { if self.readlen == 0 { Default::default() } else { @@ -87,8 +87,8 @@ impl Buf for TestBufC { self.inner.advance(cnt) } #[inline(never)] - fn bytes(&self) -> &[u8] { - self.inner.bytes() + fn chunk(&self) -> &[u8] { + self.inner.chunk() } } diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 3c596f164..16ad8a7ee 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -16,7 +16,7 @@ macro_rules! buf_get_impl { // this Option trick is to avoid keeping a borrow on self // when advance() is called (mut borrow) and to call bytes() only once let ret = $this - .bytes() + .chunk() .get(..SIZE) .map(|src| unsafe { $typ::$conv(*(src as *const _ as *const [_; SIZE])) }); @@ -78,7 +78,7 @@ pub trait Buf { /// the buffer. /// /// This value is greater than or equal to the length of the slice returned - /// by `bytes`. + /// by `chunk()`. /// /// # Examples /// @@ -115,31 +115,31 @@ pub trait Buf { /// /// let mut buf = &b"hello world"[..]; /// - /// assert_eq!(buf.bytes(), &b"hello world"[..]); + /// assert_eq!(buf.chunk(), &b"hello world"[..]); /// /// buf.advance(6); /// - /// assert_eq!(buf.bytes(), &b"world"[..]); + /// assert_eq!(buf.chunk(), &b"world"[..]); /// ``` /// /// # Implementer notes /// /// This function should never panic. Once the end of the buffer is reached, - /// i.e., `Buf::remaining` returns 0, calls to `bytes` should return an + /// i.e., `Buf::remaining` returns 0, calls to `chunk()` should return an /// empty slice. - fn bytes(&self) -> &[u8]; + fn chunk(&self) -> &[u8]; /// Fills `dst` with potentially multiple slices starting at `self`'s /// current position. /// - /// If the `Buf` is backed by disjoint slices of bytes, `bytes_vectored` enables + /// If the `Buf` is backed by disjoint slices of bytes, `chunk_vectored` enables /// fetching more than one slice at once. `dst` is a slice of `IoSlice` /// references, enabling the slice to be directly used with [`writev`] /// without any further conversion. The sum of the lengths of all the /// buffers in `dst` will be less than or equal to `Buf::remaining()`. /// /// The entries in `dst` will be overwritten, but the data **contained** by - /// the slices **will not** be modified. If `bytes_vectored` does not fill every + /// the slices **will not** be modified. If `chunk_vectored` does not fill every /// entry in `dst`, then `dst` is guaranteed to contain all remaining slices /// in `self. /// @@ -149,7 +149,7 @@ pub trait Buf { /// # Implementer notes /// /// This function should never panic. Once the end of the buffer is reached, - /// i.e., `Buf::remaining` returns 0, calls to `bytes_vectored` must return 0 + /// i.e., `Buf::remaining` returns 0, calls to `chunk_vectored` must return 0 /// without mutating `dst`. /// /// Implementations should also take care to properly handle being called @@ -157,13 +157,13 @@ pub trait Buf { /// /// [`writev`]: http://man7.org/linux/man-pages/man2/readv.2.html #[cfg(feature = "std")] - fn bytes_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { + fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { if dst.is_empty() { return 0; } if self.has_remaining() { - dst[0] = IoSlice::new(self.bytes()); + dst[0] = IoSlice::new(self.chunk()); 1 } else { 0 @@ -172,7 +172,7 @@ pub trait Buf { /// Advance the internal cursor of the Buf /// - /// The next call to `bytes` will return a slice starting `cnt` bytes + /// The next call to `chunk()` will return a slice starting `cnt` bytes /// further into the underlying buffer. /// /// # Examples @@ -182,11 +182,11 @@ pub trait Buf { /// /// let mut buf = &b"hello world"[..]; /// - /// assert_eq!(buf.bytes(), &b"hello world"[..]); + /// assert_eq!(buf.chunk(), &b"hello world"[..]); /// /// buf.advance(6); /// - /// assert_eq!(buf.bytes(), &b"world"[..]); + /// assert_eq!(buf.chunk(), &b"world"[..]); /// ``` /// /// # Panics @@ -253,7 +253,7 @@ pub trait Buf { let cnt; unsafe { - let src = self.bytes(); + let src = self.chunk(); cnt = cmp::min(src.len(), dst.len() - off); ptr::copy_nonoverlapping(src.as_ptr(), dst[off..].as_mut_ptr(), cnt); @@ -283,7 +283,7 @@ pub trait Buf { /// This function panics if there is no more remaining data in `self`. fn get_u8(&mut self) -> u8 { assert!(self.remaining() >= 1); - let ret = self.bytes()[0]; + let ret = self.chunk()[0]; self.advance(1); ret } @@ -306,7 +306,7 @@ pub trait Buf { /// This function panics if there is no more remaining data in `self`. fn get_i8(&mut self) -> i8 { assert!(self.remaining() >= 1); - let ret = self.bytes()[0] as i8; + let ret = self.chunk()[0] as i8; self.advance(1); ret } @@ -861,7 +861,7 @@ pub trait Buf { /// let mut chain = b"hello "[..].chain(&b"world"[..]); /// /// let full = chain.copy_to_bytes(11); - /// assert_eq!(full.bytes(), b"hello world"); + /// assert_eq!(full.chunk(), b"hello world"); /// ``` fn chain(self, next: U) -> Chain where @@ -908,13 +908,13 @@ macro_rules! deref_forward_buf { (**self).remaining() } - fn bytes(&self) -> &[u8] { - (**self).bytes() + fn chunk(&self) -> &[u8] { + (**self).chunk() } #[cfg(feature = "std")] - fn bytes_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { - (**self).bytes_vectored(dst) + fn chunks_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { + (**self).chunks_vectored(dst) } fn advance(&mut self, cnt: usize) { @@ -1022,7 +1022,7 @@ impl Buf for &[u8] { } #[inline] - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { self } @@ -1045,7 +1045,7 @@ impl> Buf for std::io::Cursor { len - pos as usize } - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { let len = self.get_ref().as_ref().len(); let pos = self.position(); diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index fb3623d25..16e3ffaff 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -31,7 +31,7 @@ pub unsafe trait BufMut { /// position until the end of the buffer is reached. /// /// This value is greater than or equal to the length of the slice returned - /// by `bytes_mut`. + /// by `chunk_mut()`. /// /// # Examples /// @@ -56,7 +56,7 @@ pub unsafe trait BufMut { /// Advance the internal cursor of the BufMut /// - /// The next call to `bytes_mut` will return a slice starting `cnt` bytes + /// The next call to `chunk_mut` will return a slice starting `cnt` bytes /// further into the underlying buffer. /// /// This function is unsafe because there is no guarantee that the bytes @@ -70,11 +70,11 @@ pub unsafe trait BufMut { /// let mut buf = Vec::with_capacity(16); /// /// // Write some data - /// buf.bytes_mut()[0..2].copy_from_slice(b"he"); + /// buf.chunk_mut()[0..2].copy_from_slice(b"he"); /// unsafe { buf.advance_mut(2) }; /// /// // write more bytes - /// buf.bytes_mut()[0..3].copy_from_slice(b"llo"); + /// buf.chunk_mut()[0..3].copy_from_slice(b"llo"); /// /// unsafe { buf.advance_mut(3); } /// @@ -135,14 +135,14 @@ pub unsafe trait BufMut { /// /// unsafe { /// // MaybeUninit::as_mut_ptr - /// buf.bytes_mut()[0..].as_mut_ptr().write(b'h'); - /// buf.bytes_mut()[1..].as_mut_ptr().write(b'e'); + /// buf.chunk_mut()[0..].as_mut_ptr().write(b'h'); + /// buf.chunk_mut()[1..].as_mut_ptr().write(b'e'); /// /// buf.advance_mut(2); /// - /// buf.bytes_mut()[0..].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[1..].as_mut_ptr().write(b'l'); - /// buf.bytes_mut()[2..].as_mut_ptr().write(b'o'); + /// buf.chunk_mut()[0..].as_mut_ptr().write(b'l'); + /// buf.chunk_mut()[1..].as_mut_ptr().write(b'l'); + /// buf.chunk_mut()[2..].as_mut_ptr().write(b'o'); /// /// buf.advance_mut(3); /// } @@ -153,12 +153,12 @@ pub unsafe trait BufMut { /// /// # Implementer notes /// - /// This function should never panic. `bytes_mut` should return an empty - /// slice **if and only if** `remaining_mut` returns 0. In other words, - /// `bytes_mut` returning an empty slice implies that `remaining_mut` will - /// return 0 and `remaining_mut` returning 0 implies that `bytes_mut` will + /// This function should never panic. `chunk_mut` should return an empty + /// slice **if and only if** `remaining_mut()` returns 0. In other words, + /// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will + /// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will /// return an empty slice. - fn bytes_mut(&mut self) -> &mut UninitSlice; + fn chunk_mut(&mut self) -> &mut UninitSlice; /// Transfer bytes into `self` from `src` and advance the cursor by the /// number of bytes written. @@ -190,8 +190,8 @@ pub unsafe trait BufMut { let l; unsafe { - let s = src.bytes(); - let d = self.bytes_mut(); + let s = src.chunk(); + let d = self.chunk_mut(); l = cmp::min(s.len(), d.len()); ptr::copy_nonoverlapping(s.as_ptr(), d.as_mut_ptr() as *mut u8, l); @@ -237,7 +237,7 @@ pub unsafe trait BufMut { let cnt; unsafe { - let dst = self.bytes_mut(); + let dst = self.chunk_mut(); cnt = cmp::min(dst.len(), src.len() - off); ptr::copy_nonoverlapping(src[off..].as_ptr(), dst.as_mut_ptr() as *mut u8, cnt); @@ -913,8 +913,8 @@ macro_rules! deref_forward_bufmut { (**self).remaining_mut() } - fn bytes_mut(&mut self) -> &mut UninitSlice { - (**self).bytes_mut() + fn chunk_mut(&mut self) -> &mut UninitSlice { + (**self).chunk_mut() } unsafe fn advance_mut(&mut self, cnt: usize) { @@ -998,7 +998,7 @@ unsafe impl BufMut for &mut [u8] { } #[inline] - fn bytes_mut(&mut self) -> &mut UninitSlice { + fn chunk_mut(&mut self) -> &mut UninitSlice { // UninitSlice is repr(transparent), so safe to transmute unsafe { &mut *(*self as *mut [u8] as *mut _) } } @@ -1033,7 +1033,7 @@ unsafe impl BufMut for Vec { } #[inline] - fn bytes_mut(&mut self) -> &mut UninitSlice { + fn chunk_mut(&mut self) -> &mut UninitSlice { if self.capacity() == self.len() { self.reserve(64); // Grow the vec } @@ -1060,7 +1060,7 @@ unsafe impl BufMut for Vec { // a block to contain the src.bytes() borrow { - let s = src.bytes(); + let s = src.chunk(); l = s.len(); self.extend_from_slice(s); } diff --git a/src/buf/chain.rs b/src/buf/chain.rs index c21fd35ae..d68bc2d0e 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -138,11 +138,11 @@ where self.a.remaining() + self.b.remaining() } - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { if self.a.has_remaining() { - self.a.bytes() + self.a.chunk() } else { - self.b.bytes() + self.b.chunk() } } @@ -165,9 +165,9 @@ where } #[cfg(feature = "std")] - fn bytes_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { - let mut n = self.a.bytes_vectored(dst); - n += self.b.bytes_vectored(&mut dst[n..]); + fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { + let mut n = self.a.chunks_vectored(dst); + n += self.b.chunks_vectored(&mut dst[n..]); n } } @@ -181,11 +181,11 @@ where self.a.remaining_mut() + self.b.remaining_mut() } - fn bytes_mut(&mut self) -> &mut UninitSlice { + fn chunk_mut(&mut self) -> &mut UninitSlice { if self.a.has_remaining_mut() { - self.a.bytes_mut() + self.a.chunk_mut() } else { - self.b.bytes_mut() + self.b.chunk_mut() } } diff --git a/src/buf/iter.rs b/src/buf/iter.rs index 16a2c5543..8914a40e8 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -117,7 +117,7 @@ impl Iterator for IntoIter { return None; } - let b = self.inner.bytes()[0]; + let b = self.inner.chunk()[0]; self.inner.advance(1); Some(b) diff --git a/src/buf/limit.rs b/src/buf/limit.rs index 5cbbbfe6b..b422be538 100644 --- a/src/buf/limit.rs +++ b/src/buf/limit.rs @@ -61,8 +61,8 @@ unsafe impl BufMut for Limit { cmp::min(self.inner.remaining_mut(), self.limit) } - fn bytes_mut(&mut self) -> &mut UninitSlice { - let bytes = self.inner.bytes_mut(); + fn chunk_mut(&mut self) -> &mut UninitSlice { + let bytes = self.inner.chunk_mut(); let end = cmp::min(bytes.len(), self.limit); &mut bytes[..end] } diff --git a/src/buf/reader.rs b/src/buf/reader.rs index 135db4172..f2b4d98f7 100644 --- a/src/buf/reader.rs +++ b/src/buf/reader.rs @@ -73,7 +73,7 @@ impl io::Read for Reader { impl io::BufRead for Reader { fn fill_buf(&mut self) -> io::Result<&[u8]> { - Ok(self.buf.bytes()) + Ok(self.buf.chunk()) } fn consume(&mut self, amt: usize) { self.buf.advance(amt) diff --git a/src/buf/take.rs b/src/buf/take.rs index 57b9f4547..1747f6e83 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -134,8 +134,8 @@ impl Buf for Take { cmp::min(self.inner.remaining(), self.limit) } - fn bytes(&self) -> &[u8] { - let bytes = self.inner.bytes(); + fn chunk(&self) -> &[u8] { + let bytes = self.inner.chunk(); &bytes[..cmp::min(bytes.len(), self.limit)] } diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 32ebde4c5..73f4e8924 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -6,7 +6,7 @@ use core::ops::{ /// Uninitialized byte slice. /// -/// Returned by `BufMut::bytes_mut()`, the referenced byte slice may be +/// Returned by `BufMut::chunk_mut()`, the referenced byte slice may be /// uninitialized. The wrapper provides safe access without introducing /// undefined behavior. /// @@ -114,7 +114,7 @@ impl UninitSlice { /// /// let mut data = [0, 1, 2]; /// let mut slice = &mut data[..]; - /// let ptr = BufMut::bytes_mut(&mut slice).as_mut_ptr(); + /// let ptr = BufMut::chunk_mut(&mut slice).as_mut_ptr(); /// ``` pub fn as_mut_ptr(&mut self) -> *mut u8 { self.0.as_mut_ptr() as *mut _ @@ -129,7 +129,7 @@ impl UninitSlice { /// /// let mut data = [0, 1, 2]; /// let mut slice = &mut data[..]; - /// let len = BufMut::bytes_mut(&mut slice).len(); + /// let len = BufMut::chunk_mut(&mut slice).len(); /// /// assert_eq!(len, 3); /// ``` diff --git a/src/buf/vec_deque.rs b/src/buf/vec_deque.rs index 195e6897f..263167e83 100644 --- a/src/buf/vec_deque.rs +++ b/src/buf/vec_deque.rs @@ -7,7 +7,7 @@ impl Buf for VecDeque { self.len() } - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { let (s1, s2) = self.as_slices(); if s1.is_empty() { s2 diff --git a/src/bytes.rs b/src/bytes.rs index 867f86bb8..0e10a95d2 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -530,7 +530,7 @@ impl Buf for Bytes { } #[inline] - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { self.as_slice() } diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index c3abadf14..61c0460ca 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -445,7 +445,7 @@ impl BytesMut { let additional = new_len - len; self.reserve(additional); unsafe { - let dst = self.bytes_mut().as_mut_ptr(); + let dst = self.chunk_mut().as_mut_ptr(); ptr::write_bytes(dst, value, additional); self.set_len(new_len); } @@ -944,7 +944,7 @@ impl Buf for BytesMut { } #[inline] - fn bytes(&self) -> &[u8] { + fn chunk(&self) -> &[u8] { self.as_slice() } @@ -985,7 +985,7 @@ unsafe impl BufMut for BytesMut { } #[inline] - fn bytes_mut(&mut self) -> &mut UninitSlice { + fn chunk_mut(&mut self) -> &mut UninitSlice { if self.capacity() == self.len() { self.reserve(64); } @@ -1000,7 +1000,7 @@ unsafe impl BufMut for BytesMut { Self: Sized, { while src.has_remaining() { - let s = src.bytes(); + let s = src.chunk(); let l = s.len(); self.extend_from_slice(s); src.advance(l); diff --git a/tests/test_buf.rs b/tests/test_buf.rs index bc1287323..fbad003a4 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -9,17 +9,17 @@ fn test_fresh_cursor_vec() { let mut buf = &b"hello"[..]; assert_eq!(buf.remaining(), 5); - assert_eq!(buf.bytes(), b"hello"); + assert_eq!(buf.chunk(), b"hello"); buf.advance(2); assert_eq!(buf.remaining(), 3); - assert_eq!(buf.bytes(), b"llo"); + assert_eq!(buf.chunk(), b"llo"); buf.advance(3); assert_eq!(buf.remaining(), 0); - assert_eq!(buf.bytes(), b""); + assert_eq!(buf.chunk(), b""); } #[test] @@ -53,7 +53,7 @@ fn test_bufs_vec() { let mut dst = [IoSlice::new(b1), IoSlice::new(b2)]; - assert_eq!(1, buf.bytes_vectored(&mut dst[..])); + assert_eq!(1, buf.chunks_vectored(&mut dst[..])); } #[test] @@ -63,9 +63,9 @@ fn test_vec_deque() { let mut buffer: VecDeque = VecDeque::new(); buffer.extend(b"hello world"); assert_eq!(11, buffer.remaining()); - assert_eq!(b"hello world", buffer.bytes()); + assert_eq!(b"hello world", buffer.chunk()); buffer.advance(6); - assert_eq!(b"world", buffer.bytes()); + assert_eq!(b"world", buffer.chunk()); buffer.extend(b" piece"); let mut out = [0; 11]; buffer.copy_to_slice(&mut out); @@ -81,8 +81,8 @@ fn test_deref_buf_forwards() { unreachable!("remaining"); } - fn bytes(&self) -> &[u8] { - unreachable!("bytes"); + fn chunk(&self) -> &[u8] { + unreachable!("chunk"); } fn advance(&mut self, _: usize) { diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 406ec510c..8d270e30a 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -11,7 +11,7 @@ fn test_vec_as_mut_buf() { assert_eq!(buf.remaining_mut(), usize::MAX); - assert!(buf.bytes_mut().len() >= 64); + assert!(buf.chunk_mut().len() >= 64); buf.put(&b"zomg"[..]); @@ -81,8 +81,8 @@ fn test_deref_bufmut_forwards() { unreachable!("remaining_mut"); } - fn bytes_mut(&mut self) -> &mut UninitSlice { - unreachable!("bytes_mut"); + fn chunk_mut(&mut self) -> &mut UninitSlice { + unreachable!("chunk_mut"); } unsafe fn advance_mut(&mut self, _: usize) { diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index b97cce6cb..78be4b7bf 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -912,20 +912,20 @@ fn bytes_buf_mut_advance() { let mut bytes = BytesMut::with_capacity(1024); unsafe { - let ptr = bytes.bytes_mut().as_mut_ptr(); - assert_eq!(1024, bytes.bytes_mut().len()); + let ptr = bytes.chunk_mut().as_mut_ptr(); + assert_eq!(1024, bytes.chunk_mut().len()); bytes.advance_mut(10); - let next = bytes.bytes_mut().as_mut_ptr(); - assert_eq!(1024 - 10, bytes.bytes_mut().len()); + let next = bytes.chunk_mut().as_mut_ptr(); + assert_eq!(1024 - 10, bytes.chunk_mut().len()); assert_eq!(ptr.offset(10), next); // advance to the end bytes.advance_mut(1024 - 10); // The buffer size is doubled - assert_eq!(1024, bytes.bytes_mut().len()); + assert_eq!(1024, bytes.chunk_mut().len()); } } diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 0f362846e..500ccd4a8 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -62,7 +62,7 @@ fn vectored_read() { IoSlice::new(b4), ]; - assert_eq!(2, buf.bytes_vectored(&mut iovecs)); + assert_eq!(2, buf.chunks_vectored(&mut iovecs)); assert_eq!(iovecs[0][..], b"hello"[..]); assert_eq!(iovecs[1][..], b"world"[..]); assert_eq!(iovecs[2][..], b""[..]); @@ -83,7 +83,7 @@ fn vectored_read() { IoSlice::new(b4), ]; - assert_eq!(2, buf.bytes_vectored(&mut iovecs)); + assert_eq!(2, buf.chunks_vectored(&mut iovecs)); assert_eq!(iovecs[0][..], b"llo"[..]); assert_eq!(iovecs[1][..], b"world"[..]); assert_eq!(iovecs[2][..], b""[..]); @@ -104,7 +104,7 @@ fn vectored_read() { IoSlice::new(b4), ]; - assert_eq!(1, buf.bytes_vectored(&mut iovecs)); + assert_eq!(1, buf.chunks_vectored(&mut iovecs)); assert_eq!(iovecs[0][..], b"world"[..]); assert_eq!(iovecs[1][..], b""[..]); assert_eq!(iovecs[2][..], b""[..]); @@ -125,7 +125,7 @@ fn vectored_read() { IoSlice::new(b4), ]; - assert_eq!(1, buf.bytes_vectored(&mut iovecs)); + assert_eq!(1, buf.chunks_vectored(&mut iovecs)); assert_eq!(iovecs[0][..], b"ld"[..]); assert_eq!(iovecs[1][..], b""[..]); assert_eq!(iovecs[2][..], b""[..]); diff --git a/tests/test_take.rs b/tests/test_take.rs index 40a1fa53d..a23a29edb 100644 --- a/tests/test_take.rs +++ b/tests/test_take.rs @@ -8,5 +8,5 @@ fn long_take() { // overrun the buffer. Regression test for #138. let buf = b"hello world".take(100); assert_eq!(11, buf.remaining()); - assert_eq!(b"hello world", buf.bytes()); + assert_eq!(b"hello world", buf.chunk()); } From e398b0a209dd5f71687eba4920c6ea87e40f0e70 Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Sun, 20 Dec 2020 16:31:22 +0100 Subject: [PATCH 037/203] Update readme / changelog (#451) --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cebb06168..16f153c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.0.0 (unreleased) + +- Rename Buf/BufMut, methods to chunk/chunk_mut (#450) +- remove unused Buf implementation. (#449) + # 0.6.0 (October 21, 2020) API polish in preparation for a 1.0 release. diff --git a/README.md b/README.md index 9ccc7852e..2064c2618 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To use `bytes`, first add this to your `Cargo.toml`: ```toml [dependencies] -bytes = "0.6" +bytes = "1.0" ``` Next, add this to your crate: @@ -33,7 +33,7 @@ Serde support is optional and disabled by default. To enable use the feature `se ```toml [dependencies] -bytes = { version = "0.6", features = ["serde"] } +bytes = { version = "1.0", features = ["serde"] } ``` ## License From ed1d1946604f97c35440536a4ddae970d665ce13 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 22 Dec 2020 18:30:50 +0900 Subject: [PATCH 038/203] deps: update loom to 0.4 (#452) --- .github/workflows/ci.yml | 12 ++++-------- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9111221f..39303d1d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,14 +75,11 @@ jobs: # Nightly nightly: name: nightly - env: - # Pin nightly to avoid being impacted by breakage - RUST_VERSION: nightly-2019-09-25 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust - run: rustup update $RUST_VERSION && rustup default $RUST_VERSION + run: rustup update $nightly && rustup default $nightly - name: Test run: . ci/test-stable.sh test @@ -130,14 +127,11 @@ jobs: # Loom loom: name: loom - env: - # Pin nightly to avoid being impacted by breakage - RUST_VERSION: nightly-2020-05-19 runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust - run: rustup update $RUST_VERSION && rustup default $RUST_VERSION + run: rustup update $nightly && rustup default $nightly - name: Loom tests run: RUSTFLAGS="--cfg loom -Dwarnings" cargo test --lib @@ -150,6 +144,8 @@ jobs: - nightly - minrust - cross + - tsan + - loom runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 8013bff13..d17a5a5d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,4 @@ serde = { version = "1.0.60", optional = true, default-features = false, feature serde_test = "1.0" [target.'cfg(loom)'.dev-dependencies] -loom = "0.3" +loom = "0.4" From 064ad9a1a07e3fb62fb0e8ec17a637f45c0890ed Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 22 Dec 2020 15:30:20 -0800 Subject: [PATCH 039/203] chore: prepare v1.0.0 release (#453) --- CHANGELOG.md | 5 ++++- Cargo.toml | 1 - README.md | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f153c79..5cabb8c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ -# 1.0.0 (unreleased) +# 1.0.0 (December 22, 2020) +### Changed - Rename Buf/BufMut, methods to chunk/chunk_mut (#450) + +### Removed - remove unused Buf implementation. (#449) # 0.6.0 (October 21, 2020) diff --git a/Cargo.toml b/Cargo.toml index d17a5a5d0..cec534c2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] edition = "2018" -publish = false [features] default = ["std"] diff --git a/README.md b/README.md index 2064c2618..468485d12 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To use `bytes`, first add this to your `Cargo.toml`: ```toml [dependencies] -bytes = "1.0" +bytes = "1" ``` Next, add this to your crate: @@ -33,7 +33,7 @@ Serde support is optional and disabled by default. To enable use the feature `se ```toml [dependencies] -bytes = { version = "1.0", features = ["serde"] } +bytes = { version = "1", features = ["serde"] } ``` ## License From ed71a7beb3fd0cc67cad08da2643cff2732f6b52 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 29 Dec 2020 22:46:39 +0100 Subject: [PATCH 040/203] Fix deprecation warning (#457) --- src/bytes.rs | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 0e10a95d2..a196e1e02 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1023,33 +1023,35 @@ unsafe fn shallow_clone_vec( // `Release` is used synchronize with other threads that // will load the `arc` field. // - // If the `compare_and_swap` fails, then the thread lost the + // If the `compare_exchange` fails, then the thread lost the // race to promote the buffer to shared. The `Acquire` - // ordering will synchronize with the `compare_and_swap` + // ordering will synchronize with the `compare_exchange` // that happened in the other thread and the `Shared` // pointed to by `actual` will be visible. - let actual = atom.compare_and_swap(ptr as _, shared as _, Ordering::AcqRel); - - if actual as usize == ptr as usize { - // The upgrade was successful, the new handle can be - // returned. - return Bytes { - ptr: offset, - len, - data: AtomicPtr::new(shared as _), - vtable: &SHARED_VTABLE, - }; + match atom.compare_exchange(ptr as _, shared as _, Ordering::AcqRel, Ordering::Acquire) { + Ok(actual) => { + debug_assert!(actual as usize == ptr as usize); + // The upgrade was successful, the new handle can be + // returned. + Bytes { + ptr: offset, + len, + data: AtomicPtr::new(shared as _), + vtable: &SHARED_VTABLE, + } + } + Err(actual) => { + // The upgrade failed, a concurrent clone happened. Release + // the allocation that was made in this thread, it will not + // be needed. + let shared = Box::from_raw(shared); + mem::forget(*shared); + + // Buffer already promoted to shared storage, so increment ref + // count. + shallow_clone_arc(actual as _, offset, len) + } } - - // The upgrade failed, a concurrent clone happened. Release - // the allocation that was made in this thread, it will not - // be needed. - let shared = Box::from_raw(shared); - mem::forget(*shared); - - // Buffer already promoted to shared storage, so increment ref - // count. - shallow_clone_arc(actual as _, offset, len) } unsafe fn release_shared(ptr: *mut Shared) { From 27a0f9ca6e01b953805684eff535a14eaff937d0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Dec 2020 22:54:48 +0100 Subject: [PATCH 041/203] CI: run test suite in Miri (#456) --- .github/workflows/ci.yml | 7 +++++++ ci/miri.sh | 11 +++++++++++ tests/test_bytes.rs | 7 ++++--- tests/test_bytes_odd_alloc.rs | 2 ++ 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100755 ci/miri.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39303d1d7..fc03588df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,6 +123,13 @@ jobs: run: rustup component add rust-src - name: ASAN / TSAN run: . ci/tsan.sh + miri: + name: miri + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Miri + run: ci/miri.sh # Loom loom: diff --git a/ci/miri.sh b/ci/miri.sh new file mode 100755 index 000000000..88d2b6a8c --- /dev/null +++ b/ci/miri.sh @@ -0,0 +1,11 @@ +#!/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 + +cargo miri test +cargo miri test --target mips64-unknown-linux-gnuabi64 diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 78be4b7bf..b9e6ce4d3 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -461,6 +461,7 @@ fn reserve_allocates_at_least_original_capacity() { } #[test] +#[cfg_attr(miri, ignore)] // Miri is too slow fn reserve_max_original_capacity_value() { const SIZE: usize = 128 * 1024; @@ -608,15 +609,15 @@ fn advance_past_len() { #[test] // Only run these tests on little endian systems. CI uses qemu for testing -// little endian... and qemu doesn't really support threading all that well. -#[cfg(target_endian = "little")] +// big endian... and qemu doesn't really support threading all that well. +#[cfg(any(miri, target_endian = "little"))] fn stress() { // Tests promoting a buffer from a vec -> shared in a concurrent situation use std::sync::{Arc, Barrier}; use std::thread; const THREADS: usize = 8; - const ITERS: usize = 1_000; + const ITERS: usize = if cfg!(miri) { 100 } else { 1_000 }; for i in 0..ITERS { let data = [i as u8; 256]; diff --git a/tests/test_bytes_odd_alloc.rs b/tests/test_bytes_odd_alloc.rs index 4ce424b7c..04ba7c2f1 100644 --- a/tests/test_bytes_odd_alloc.rs +++ b/tests/test_bytes_odd_alloc.rs @@ -1,6 +1,8 @@ //! Test using `Bytes` with an allocator that hands out "odd" pointers for //! vectors (pointers where the LSB is set). +#![cfg(not(miri))] // Miri does not support custom allocators (also, Miri is "odd" by default with 50% chance) + use std::alloc::{GlobalAlloc, Layout, System}; use std::ptr; From 8758a1aba594ad0e721614dff5c523f130f8eb82 Mon Sep 17 00:00:00 2001 From: laizy Date: Thu, 31 Dec 2020 19:34:39 +0800 Subject: [PATCH 042/203] add inline for Vec::put_slice (#459) --- src/buf/buf_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 16e3ffaff..f736727b4 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1047,7 +1047,6 @@ unsafe impl BufMut for Vec { // Specialize these methods so they can skip checking `remaining_mut` // and `advance_mut`. - fn put(&mut self, mut src: T) where Self: Sized, @@ -1069,6 +1068,7 @@ unsafe impl BufMut for Vec { } } + #[inline] fn put_slice(&mut self, src: &[u8]) { self.extend_from_slice(src); } From df20a683567909f3e33512a22862b0de954881f4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 31 Dec 2020 15:07:28 +0100 Subject: [PATCH 043/203] use Box::into_raw instead of mem-forget-in-disguise (#458) --- src/bytes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index a196e1e02..b1b35ea83 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -806,8 +806,7 @@ impl From> for Bytes { let slice = vec.into_boxed_slice(); let len = slice.len(); - let ptr = slice.as_ptr(); - drop(Box::into_raw(slice)); + let ptr = Box::into_raw(slice) as *mut u8; if ptr as usize & 0x1 == 0 { let data = ptr as usize | KIND_VEC; From 7b18c1c076adf9dff4dca28ffc1bbd8759860798 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 11 Jan 2021 18:07:46 +0100 Subject: [PATCH 044/203] prepare 1.0.1 release (#460) --- CHANGELOG.md | 9 +++++++++ Cargo.toml | 4 ++-- src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cabb8c97..a53a165e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 1.0.1 (January 11, 2021) + +### Changed +- mark `Vec::put_slice` with `#[inline]` (#459) + +### Fixed +- Fix deprecation warning (#457) +- use `Box::into_raw` instead of `mem::forget`-in-disguise (#458) + # 1.0.0 (December 22, 2020) ### Changed diff --git a/Cargo.toml b/Cargo.toml index cec534c2b..8f4e22496 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,14 @@ name = "bytes" # - Update CHANGELOG.md. # - Update doc URL. # - Create "v1.0.x" git tag. -version = "1.0.0" +version = "1.0.1" license = "MIT" authors = [ "Carl Lerche ", "Sean McArthur ", ] description = "Types and traits for working with bytes" -documentation = "https://docs.rs/bytes/1.0.0/bytes/" +documentation = "https://docs.rs/bytes/1.0.1/bytes/" repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] diff --git a/src/lib.rs b/src/lib.rs index 940d17d7e..dd8cc9661 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/1.0.0")] +#![doc(html_root_url = "https://docs.rs/bytes/1.0.1")] #![no_std] //! Provides abstractions for working with bytes. From 8daf43e9bde134e32fc0b8938abc92695f6850d1 Mon Sep 17 00:00:00 2001 From: Christopher Bunn Date: Tue, 19 Jan 2021 21:39:23 -0800 Subject: [PATCH 045/203] docs: fix broken Take link (#466) --- src/buf/take.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buf/take.rs b/src/buf/take.rs index 1747f6e83..2f4c436ac 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -5,7 +5,7 @@ use core::cmp; /// A `Buf` adapter which limits the bytes read from an underlying buffer. /// /// This struct is generally created by calling `take()` on `Buf`. See -/// documentation of [`take()`](trait.BufExt.html#method.take) for more details. +/// documentation of [`take()`](trait.Buf.html#method.take) for more details. #[derive(Debug)] pub struct Take { inner: T, From e4182808df772e331fbd25c48f2f1ab79c0d7c20 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 23 Jan 2021 07:22:43 +0100 Subject: [PATCH 046/203] Make bytes_mut -> chunk_mut rename more easily discoverable (#471) --- .github/workflows/ci.yml | 2 ++ CHANGELOG.md | 2 +- Cargo.toml | 3 +++ src/buf/buf_impl.rs | 3 +++ src/buf/buf_mut.rs | 3 +++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc03588df..b88ed7fc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,6 +160,8 @@ jobs: run: rustup update stable && rustup default stable - name: Build documentation run: cargo doc --no-deps --all-features + env: + RUSTDOCFLAGS: --cfg docsrs - name: Publish documentation run: | cd target/doc diff --git a/CHANGELOG.md b/CHANGELOG.md index a53a165e1..99975bf89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ # 1.0.0 (December 22, 2020) ### Changed -- Rename Buf/BufMut, methods to chunk/chunk_mut (#450) +- Rename `Buf`/`BufMut` methods `bytes()` and `bytes_mut()` to `chunk()` and `chunk_mut()` (#450) ### Removed - remove unused Buf implementation. (#449) diff --git a/Cargo.toml b/Cargo.toml index 8f4e22496..bf588a165 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,6 @@ serde_test = "1.0" [target.'cfg(loom)'.dev-dependencies] loom = "0.4" + +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 16ad8a7ee..a33c8a42d 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -127,6 +127,9 @@ pub trait Buf { /// This function should never panic. Once the end of the buffer is reached, /// i.e., `Buf::remaining` returns 0, calls to `chunk()` should return an /// empty slice. + // The `chunk` method was previously called `bytes`. This alias makes the rename + // more easily discoverable. + #[cfg_attr(docsrs, doc(alias = "bytes"))] fn chunk(&self) -> &[u8]; /// Fills `dst` with potentially multiple slices starting at `self`'s diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index f736727b4..eccd5096d 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -158,6 +158,9 @@ pub unsafe trait BufMut { /// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will /// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will /// return an empty slice. + // The `chunk_mut` method was previously called `bytes_mut`. This alias makes the + // rename more easily discoverable. + #[cfg_attr(docsrs, doc(alias = "bytes_mut"))] fn chunk_mut(&mut self) -> &mut UninitSlice; /// Transfer bytes into `self` from `src` and advance the cursor by the From 268f6f80b4685b53f57706abc6aef31f46b54738 Mon Sep 17 00:00:00 2001 From: Zettroke Date: Tue, 16 Feb 2021 03:35:01 +0300 Subject: [PATCH 047/203] override put_slice for &mut [u8] (#483) --- src/buf/buf_mut.rs | 8 ++++++++ tests/test_buf_mut.rs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index eccd5096d..844474fb9 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1012,6 +1012,14 @@ unsafe impl BufMut 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[..src.len()].copy_from_slice(src); + unsafe { + self.advance_mut(src.len()); + } + } } unsafe impl BufMut for Vec { diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 8d270e30a..b85ab9c67 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -70,6 +70,9 @@ fn test_mut_slice() { let mut v = vec![0, 0, 0, 0]; let mut s = &mut v[..]; s.put_u32(42); + + assert_eq!(s.len(), 0); + assert_eq!(&v, &[0, 0, 0, 42]); } #[test] From 2428c152a67c06057a98d9d29b08389cb3429c1f Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Tue, 16 Feb 2021 20:44:33 +0000 Subject: [PATCH 048/203] Panic on integer overflow in Chain::remaining (#482) Make it safer. --- src/buf/chain.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/buf/chain.rs b/src/buf/chain.rs index d68bc2d0e..8a1598ccf 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -135,7 +135,7 @@ where U: Buf, { fn remaining(&self) -> usize { - self.a.remaining() + self.b.remaining() + self.a.remaining().checked_add(self.b.remaining()).unwrap() } fn chunk(&self) -> &[u8] { @@ -178,7 +178,10 @@ where U: BufMut, { fn remaining_mut(&self) -> usize { - self.a.remaining_mut() + self.b.remaining_mut() + self.a + .remaining_mut() + .checked_add(self.b.remaining_mut()) + .unwrap() } fn chunk_mut(&mut self) -> &mut UninitSlice { From 3d5624a452af67e323ead2855e26b583163e5b99 Mon Sep 17 00:00:00 2001 From: Dan Burkert Date: Sat, 10 Apr 2021 11:19:30 -0700 Subject: [PATCH 049/203] Add inline tags to UninitSlice methods (#443) This appears to be the primary cause of significant performance regressions in the `prost` test suite in the 0.5 to 0.6 transition. See danburkert/prost#381. --- src/buf/uninit_slice.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 73f4e8924..fb67c0afd 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -40,6 +40,7 @@ impl UninitSlice { /// /// let slice = unsafe { UninitSlice::from_raw_parts_mut(ptr, len) }; /// ``` + #[inline] 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); @@ -64,6 +65,7 @@ impl UninitSlice { /// /// assert_eq!(b"boo", &data[..]); /// ``` + #[inline] pub fn write_byte(&mut self, index: usize, byte: u8) { assert!(index < self.len()); @@ -90,6 +92,7 @@ impl UninitSlice { /// /// assert_eq!(b"bar", &data[..]); /// ``` + #[inline] pub fn copy_from_slice(&mut self, src: &[u8]) { use core::ptr; @@ -116,6 +119,7 @@ impl UninitSlice { /// let mut slice = &mut data[..]; /// let ptr = BufMut::chunk_mut(&mut slice).as_mut_ptr(); /// ``` + #[inline] pub fn as_mut_ptr(&mut self) -> *mut u8 { self.0.as_mut_ptr() as *mut _ } @@ -133,6 +137,7 @@ impl UninitSlice { /// /// assert_eq!(len, 3); /// ``` + #[inline] pub fn len(&self) -> usize { self.0.len() } @@ -150,6 +155,7 @@ macro_rules! impl_index { impl Index<$t> for UninitSlice { type Output = UninitSlice; + #[inline] fn index(&self, index: $t) -> &UninitSlice { let maybe_uninit: &[MaybeUninit] = &self.0[index]; unsafe { &*(maybe_uninit as *const [MaybeUninit] as *const UninitSlice) } @@ -157,6 +163,7 @@ macro_rules! impl_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) } From b9eade12a57b923a54376762ae908da1cff6bcd4 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sat, 10 Apr 2021 19:56:35 +0100 Subject: [PATCH 050/203] Specialize copy_to_bytes for Chain and Take (#481) Avoid allocation when `Take` or `Chain` is composed of `Bytes` objects. This works now for `Take`. `Chain` it works if the requested bytes does not cross boundary between `Chain` members. --- src/buf/chain.rs | 20 +++++++++++++++++++- src/buf/take.rs | 10 +++++++++- tests/test_chain.rs | 21 +++++++++++++++++++++ tests/test_take.rs | 20 ++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 8a1598ccf..9ce5f23aa 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -1,5 +1,5 @@ use crate::buf::{IntoIter, UninitSlice}; -use crate::{Buf, BufMut}; +use crate::{Buf, BufMut, Bytes}; #[cfg(feature = "std")] use std::io::IoSlice; @@ -170,6 +170,24 @@ where n += self.b.chunks_vectored(&mut dst[n..]); n } + + fn copy_to_bytes(&mut self, len: usize) -> Bytes { + let a_rem = self.a.remaining(); + if a_rem >= len { + self.a.copy_to_bytes(len) + } else if a_rem == 0 { + self.b.copy_to_bytes(len) + } else { + assert!( + len - a_rem <= self.b.remaining(), + "`len` greater than remaining" + ); + let mut ret = crate::BytesMut::with_capacity(len); + ret.put(&mut self.a); + ret.put((&mut self.b).take(len - a_rem)); + ret.freeze() + } + } } unsafe impl BufMut for Chain diff --git a/src/buf/take.rs b/src/buf/take.rs index 2f4c436ac..d3cb10ab6 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -1,4 +1,4 @@ -use crate::Buf; +use crate::{Buf, Bytes}; use core::cmp; @@ -144,4 +144,12 @@ impl Buf for Take { self.inner.advance(cnt); self.limit -= cnt; } + + fn copy_to_bytes(&mut self, len: usize) -> Bytes { + assert!(len <= self.remaining(), "`len` greater than remaining"); + + let r = self.inner.copy_to_bytes(len); + self.limit -= len; + r + } } diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 500ccd4a8..affaf7a9e 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -132,3 +132,24 @@ fn vectored_read() { assert_eq!(iovecs[3][..], b""[..]); } } + +#[test] +fn chain_get_bytes() { + let mut ab = Bytes::copy_from_slice(b"ab"); + let mut cd = Bytes::copy_from_slice(b"cd"); + let ab_ptr = ab.as_ptr(); + let cd_ptr = cd.as_ptr(); + let mut chain = (&mut ab).chain(&mut cd); + let a = chain.copy_to_bytes(1); + let bc = chain.copy_to_bytes(2); + let d = chain.copy_to_bytes(1); + + assert_eq!(Bytes::copy_from_slice(b"a"), a); + assert_eq!(Bytes::copy_from_slice(b"bc"), bc); + assert_eq!(Bytes::copy_from_slice(b"d"), d); + + // assert `get_bytes` did not allocate + assert_eq!(ab_ptr, a.as_ptr()); + // assert `get_bytes` did not allocate + assert_eq!(cd_ptr.wrapping_offset(1), d.as_ptr()); +} diff --git a/tests/test_take.rs b/tests/test_take.rs index a23a29edb..51df91d14 100644 --- a/tests/test_take.rs +++ b/tests/test_take.rs @@ -1,6 +1,7 @@ #![warn(rust_2018_idioms)] use bytes::buf::Buf; +use bytes::Bytes; #[test] fn long_take() { @@ -10,3 +11,22 @@ fn long_take() { assert_eq!(11, buf.remaining()); assert_eq!(b"hello world", buf.chunk()); } + +#[test] +fn take_copy_to_bytes() { + let mut abcd = Bytes::copy_from_slice(b"abcd"); + let abcd_ptr = abcd.as_ptr(); + let mut take = (&mut abcd).take(2); + let a = take.copy_to_bytes(1); + assert_eq!(Bytes::copy_from_slice(b"a"), a); + // assert `to_bytes` did not allocate + assert_eq!(abcd_ptr, a.as_ptr()); + assert_eq!(Bytes::copy_from_slice(b"bcd"), abcd); +} + +#[test] +#[should_panic] +fn take_copy_to_bytes_panics() { + let abcd = Bytes::copy_from_slice(b"abcd"); + abcd.take(2).copy_to_bytes(3); +} From 9c770188fdd0029d10bf21d7805f2ff0158c09bb Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:49:33 -0500 Subject: [PATCH 051/203] Fully inline BytesMut::new (#493) --- src/bytes_mut.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 61c0460ca..2af58e300 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1250,6 +1250,7 @@ impl Shared { } } +#[inline] fn original_capacity_to_repr(cap: usize) -> usize { let width = PTR_WIDTH - ((cap >> MIN_ORIGINAL_CAPACITY_WIDTH).leading_zeros() as usize); cmp::min( @@ -1476,6 +1477,7 @@ impl PartialEq for BytesMut { } } +#[inline] fn vptr(ptr: *mut u8) -> NonNull { if cfg!(debug_assertions) { NonNull::new(ptr).expect("Vec pointer should be non-null") From b89247c713c6c77089cfd722dc4632db4ae4ccb3 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 13 Apr 2021 23:58:18 +0900 Subject: [PATCH 052/203] Update loom to 0.5 (#494) --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b88ed7fc4..4f4d52677 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 - nightly: nightly-2020-12-17 + nightly: nightly-2021-04-13 defaults: run: diff --git a/Cargo.toml b/Cargo.toml index bf588a165..4a008f1ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ serde = { version = "1.0.60", optional = true, default-features = false, feature serde_test = "1.0" [target.'cfg(loom)'.dev-dependencies] -loom = "0.4" +loom = "0.5" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] From ed1d24e57079a86c6201b5fd6be6f789265f0e6a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 23 May 2021 23:09:26 +0900 Subject: [PATCH 053/203] Use ubuntu-latest instead of ubuntu-16.04 (#497) --- .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 4f4d52677..00a4414eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: - powerpc-unknown-linux-gnu - powerpc64-unknown-linux-gnu - wasm32-unknown-unknown - runs-on: ubuntu-16.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust From baaf12d22a844350998d2f60ab3bc78df12ab29a Mon Sep 17 00:00:00 2001 From: Gbillou <7978569+Gbillou@users.noreply.github.com> Date: Mon, 5 Jul 2021 16:46:17 +0200 Subject: [PATCH 054/203] Keep capacity when unsplit on empty other buf (#502) --- src/bytes_mut.rs | 2 +- tests/test_bytes.rs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 2af58e300..8e42079e6 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -819,7 +819,7 @@ impl BytesMut { } fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> { - if other.is_empty() { + if other.capacity() == 0 { return Ok(()); } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index b9e6ce4d3..2d22fdddb 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -784,6 +784,31 @@ fn bytes_mut_unsplit_empty_self() { assert_eq!(b"aaabbbcccddd", &buf[..]); } +#[test] +fn bytes_mut_unsplit_other_keeps_capacity() { + let mut buf = BytesMut::with_capacity(64); + buf.extend_from_slice(b"aabb"); + + // non empty other created "from" buf + let mut other = buf.split_off(buf.len()); + other.extend_from_slice(b"ccddee"); + buf.unsplit(other); + + assert_eq!(buf.capacity(), 64); +} + +#[test] +fn bytes_mut_unsplit_empty_other_keeps_capacity() { + let mut buf = BytesMut::with_capacity(64); + buf.extend_from_slice(b"aabbccddee"); + + // empty other created "from" buf + let other = buf.split_off(buf.len()); + buf.unsplit(other); + + assert_eq!(buf.capacity(), 64); +} + #[test] fn bytes_mut_unsplit_arc_different() { let mut buf = BytesMut::with_capacity(64); From f34dc5c3f9e6a3a11e315631055194a733ac1d08 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 7 Aug 2021 02:06:57 +0900 Subject: [PATCH 055/203] Remove doc URLs (#498) --- Cargo.toml | 3 --- src/lib.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4a008f1ff..34d70f8f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,9 +2,7 @@ name = "bytes" # When releasing to crates.io: -# - Update html_root_url. # - Update CHANGELOG.md. -# - Update doc URL. # - Create "v1.0.x" git tag. version = "1.0.1" license = "MIT" @@ -13,7 +11,6 @@ authors = [ "Sean McArthur ", ] description = "Types and traits for working with bytes" -documentation = "https://docs.rs/bytes/1.0.1/bytes/" repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] diff --git a/src/lib.rs b/src/lib.rs index dd8cc9661..706735e3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] -#![doc(html_root_url = "https://docs.rs/bytes/1.0.1")] #![no_std] //! Provides abstractions for working with bytes. From ab8e3c01a8fae128971cea43eca18da03482cb29 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 7 Aug 2021 08:22:18 +0200 Subject: [PATCH 056/203] Clarify BufMut allocation guarantees (#501) --- src/buf/buf_mut.rs | 10 +++++++++- tests/test_buf_mut.rs | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 844474fb9..bf33fe635 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -33,6 +33,10 @@ pub unsafe trait BufMut { /// This value is greater than or equal to the length of the slice returned /// by `chunk_mut()`. /// + /// Writing to a `BufMut` may involve allocating more memory on the fly. + /// Implementations may fail before reaching the number of bytes indicated + /// by this method if they encounter an allocation failure. + /// /// # Examples /// /// ``` @@ -158,6 +162,9 @@ pub unsafe trait BufMut { /// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will /// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will /// return an empty slice. + /// + /// This function may trigger an out-of-memory abort if it tries to allocate + /// memory and fails to do so. // The `chunk_mut` method was previously called `bytes_mut`. This alias makes the // rename more easily discoverable. #[cfg_attr(docsrs, doc(alias = "bytes_mut"))] @@ -1025,7 +1032,8 @@ unsafe impl BufMut for &mut [u8] { unsafe impl BufMut for Vec { #[inline] fn remaining_mut(&self) -> usize { - usize::MAX - self.len() + // A vector can never have more than isize::MAX bytes + core::isize::MAX as usize - self.len() } #[inline] diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index b85ab9c67..f63198226 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -9,7 +9,7 @@ use core::usize; fn test_vec_as_mut_buf() { let mut buf = Vec::with_capacity(64); - assert_eq!(buf.remaining_mut(), usize::MAX); + assert_eq!(buf.remaining_mut(), isize::MAX as usize); assert!(buf.chunk_mut().len() >= 64); @@ -17,7 +17,7 @@ fn test_vec_as_mut_buf() { assert_eq!(&buf, b"zomg"); - assert_eq!(buf.remaining_mut(), usize::MAX - 4); + assert_eq!(buf.remaining_mut(), isize::MAX as usize - 4); assert_eq!(buf.capacity(), 64); for _ in 0..16 { From fa9cbf1258cea7812d4009639ed8700b987d8d4e Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Sun, 8 Aug 2021 18:43:09 +0100 Subject: [PATCH 057/203] 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 058/203] 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 059/203] 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 060/203] 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 061/203] 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 062/203] 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 063/203] 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 064/203] `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 065/203] 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 066/203] 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 067/203] 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 068/203] 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 069/203] 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 070/203] 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 071/203] 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 072/203] 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 073/203] 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 074/203] 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 075/203] 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 076/203] 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 077/203] 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 078/203] 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 079/203] 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 080/203] 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 081/203] 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 082/203] 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 083/203] 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 084/203] 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 085/203] 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 086/203] 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 087/203] 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 088/203] 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 089/203] 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 090/203] 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 091/203] 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 092/203] 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 093/203] 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 094/203] 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 095/203] 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 096/203] 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 097/203] 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 098/203] 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 099/203] 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 100/203] 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 101/203] 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 102/203] 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 103/203] 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 104/203] 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] From c7756c3e5976246926a5c8227c181a97d80cf22b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 5 Jun 2023 01:04:59 +0900 Subject: [PATCH 105/203] Fix CI failure (#616) --- tests/test_buf.rs | 1 + tests/test_buf_mut.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/test_buf.rs b/tests/test_buf.rs index fbad003a4..3940f9247 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -72,6 +72,7 @@ fn test_vec_deque() { assert_eq!(b"world piece", &out[..]); } +#[allow(unused_allocation)] // This is intentional. #[test] fn test_deref_buf_forwards() { struct Special; diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 865cccc33..33aa68038 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -218,6 +218,7 @@ fn test_maybe_uninit_buf_mut_put_bytes_overflow() { do_test_slice_put_bytes_panics(make_maybe_uninit_slice); } +#[allow(unused_allocation)] // This is intentional. #[test] fn test_deref_bufmut_forwards() { struct Special; From 64c4fa286771ad9e522ffbefc576bcf7b76933d0 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 20 Jun 2023 10:19:56 +0200 Subject: [PATCH 106/203] Rename UninitSlice constructors for consistency with ReadBuf (#599) tokio::io::ReadBuf uses names `new` and `uninit` for its constructors. For consistency with that, rename recently introduced UninitSlice constructors to match those names. --- src/buf/buf_mut.rs | 2 +- src/buf/uninit_slice.rs | 46 ++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 2a3c24325..c2ac39df3 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1427,7 +1427,7 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] fn chunk_mut(&mut self) -> &mut UninitSlice { - UninitSlice::from_uninit_slice(self) + UninitSlice::uninit(self) } #[inline] diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 84b1d8820..c5d86e85d 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -22,42 +22,42 @@ use core::ops::{ pub struct UninitSlice([MaybeUninit]); impl UninitSlice { - /// Creates a `&mut UninitSlice` wrapping slice of uninitialised memory. + /// Creates a `&mut UninitSlice` wrapping a slice of initialised 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(); + /// let mut buffer = [0u8; 64]; + /// let slice = UninitSlice::new(&mut buffer[..]); /// ``` #[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) } + pub fn new(slice: &mut [u8]) -> &mut UninitSlice { + unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit] as *mut UninitSlice) } } - /// Creates a `&mut UninitSlice` wrapping slice of initialised memory. + /// Creates a `&mut UninitSlice` wrapping a slice of uninitialised memory. /// /// # Examples /// /// ``` /// use bytes::buf::UninitSlice; + /// use core::mem::MaybeUninit; /// - /// let mut buffer = [0u8; 64]; - /// let slice = UninitSlice::from_slice(&mut buffer[..]); + /// let mut buffer = [MaybeUninit::uninit(); 64]; + /// let slice = UninitSlice::uninit(&mut buffer[..]); + /// + /// let mut vec = Vec::with_capacity(1024); + /// let spare: &mut UninitSlice = vec.spare_capacity_mut().into(); /// ``` #[inline] - pub fn from_slice(slice: &mut [u8]) -> &mut UninitSlice { - unsafe { &mut *(slice as *mut [u8] as *mut [MaybeUninit] as *mut UninitSlice) } + pub fn uninit(slice: &mut [MaybeUninit]) -> &mut UninitSlice { + unsafe { &mut *(slice as *mut [MaybeUninit] as *mut UninitSlice) } + } + + fn uninit_ref(slice: &[MaybeUninit]) -> &UninitSlice { + unsafe { &*(slice as *const [MaybeUninit] as *const UninitSlice) } } /// Create a `&mut UninitSlice` from a pointer and a length. @@ -82,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_uninit_slice(maybe_init) + Self::uninit(maybe_init) } /// Write a single byte at the specified offset. @@ -215,13 +215,13 @@ 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) + UninitSlice::new(slice) } } impl<'a> From<&'a mut [MaybeUninit]> for &'a mut UninitSlice { fn from(slice: &'a mut [MaybeUninit]) -> Self { - UninitSlice::from_uninit_slice(slice) + UninitSlice::uninit(slice) } } @@ -233,14 +233,14 @@ macro_rules! impl_index { #[inline] fn index(&self, index: $t) -> &UninitSlice { - UninitSlice::from_uninit_slice_ref(&self.0[index]) + UninitSlice::uninit_ref(&self.0[index]) } } impl IndexMut<$t> for UninitSlice { #[inline] fn index_mut(&mut self, index: $t) -> &mut UninitSlice { - UninitSlice::from_uninit_slice(&mut self.0[index]) + UninitSlice::uninit(&mut self.0[index]) } } )* From 74e6e200fd671340d4d4a874f83776def04f6c7b Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 7 Sep 2023 10:52:15 +0200 Subject: [PATCH 107/203] chore: prepare bytes v1.5.0 (#627) --- CHANGELOG.md | 11 +++++++++++ Cargo.toml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bad4a40..47e48802c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 1.5.0 (September 7, 2023) + +### Added + +- Add `UninitSlice::{new,init}` (#598, #599) +- Implement `BufMut` for `&mut [MaybeUninit]` (#597) + +### Changed + +- Mark `BytesMut::extend_from_slice` as inline (#595) + # 1.4.0 (January 31, 2023) ### Added diff --git a/Cargo.toml b/Cargo.toml index 4a96ec1ed..06b19e671 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.4.0" +version = "1.5.0" license = "MIT" authors = [ "Carl Lerche ", From bd9c164cb65cf9b80436c3229a6753dc9c4e73eb Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 7 Sep 2023 14:10:55 +0200 Subject: [PATCH 108/203] doc: fix changelog typo (#628) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47e48802c..67b9f673a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Added -- Add `UninitSlice::{new,init}` (#598, #599) +- Add `UninitSlice::{new,uninit}` (#598, #599) - Implement `BufMut` for `&mut [MaybeUninit]` (#597) ### Changed From a14ef4617c1d041cd4b479cd7f5453054b8e639a Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Mon, 11 Sep 2023 19:12:11 +1000 Subject: [PATCH 109/203] Move comment to correct constant (#629) --- src/bytes_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index c5c2e52fc..79a8877d7 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -96,11 +96,11 @@ const MIN_ORIGINAL_CAPACITY_WIDTH: usize = 10; const ORIGINAL_CAPACITY_MASK: usize = 0b11100; const ORIGINAL_CAPACITY_OFFSET: usize = 2; +const VEC_POS_OFFSET: usize = 5; // When the storage is in the `Vec` representation, the pointer can be advanced // at most this value. This is due to the amount of storage available to track // the offset is usize - number of KIND bits and number of ORIGINAL_CAPACITY // bits. -const VEC_POS_OFFSET: usize = 5; const MAX_VEC_POS: usize = usize::MAX >> VEC_POS_OFFSET; const NOT_VEC_POS_MASK: usize = 0b11111; From a4e16a552bf5bee0b75d549a98551d5ab1b074e4 Mon Sep 17 00:00:00 2001 From: mxsm Date: Mon, 25 Sep 2023 16:47:02 +0800 Subject: [PATCH 110/203] docs: fix some spelling mistakes (#633) --- src/buf/uninit_slice.rs | 2 +- src/bytes_mut.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index c5d86e85d..b0eeed410 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -168,7 +168,7 @@ impl UninitSlice { /// /// 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 + /// that created the `UninitSlice` knows which parts are initialized. Writing uninitialized /// bytes to the slice may cause the `BufMut` to read those bytes and trigger undefined /// behavior. /// diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 79a8877d7..b4be074d9 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1684,7 +1684,7 @@ fn invalid_ptr(addr: usize) -> *mut T { /// self.ptr.as_ptr().offset_from(ptr) as usize; /// ``` /// -/// But due to min rust is 1.39 and it is only stablised +/// But due to min rust is 1.39 and it is only stabilized /// in 1.47, we cannot use it. #[inline] fn offset_from(dst: *mut u8, original: *mut u8) -> usize { From fd9243f9e2fe2027243b1a23d518c723c24a17b7 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 2 Oct 2023 15:40:02 +0200 Subject: [PATCH 111/203] Various cleanup (#635) --- src/buf/buf_impl.rs | 168 ++++++++++++++++++-------- src/buf/buf_mut.rs | 261 ++++++++++++++++++++++++++++------------ src/buf/chain.rs | 2 +- src/buf/uninit_slice.rs | 2 +- src/bytes.rs | 2 +- src/bytes_mut.rs | 14 +-- src/lib.rs | 37 ++++++ tests/test_buf_mut.rs | 2 +- 8 files changed, 351 insertions(+), 137 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 366cfc989..b4ebf408a 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -1,8 +1,9 @@ #[cfg(feature = "std")] use crate::buf::{reader, Reader}; use crate::buf::{take, Chain, Take}; - -use core::{cmp, mem, ptr}; +#[cfg(feature = "std")] +use crate::{min_u64_usize, saturating_sub_usize_u64}; +use crate::{panic_advance, panic_does_not_fit}; #[cfg(feature = "std")] use std::io::IoSlice; @@ -11,7 +12,12 @@ use alloc::boxed::Box; macro_rules! buf_get_impl { ($this:ident, $typ:tt::$conv:tt) => {{ - const SIZE: usize = mem::size_of::<$typ>(); + const SIZE: usize = core::mem::size_of::<$typ>(); + + if $this.remaining() < SIZE { + panic_advance(SIZE, $this.remaining()); + } + // try to convert directly from the bytes // this Option trick is to avoid keeping a borrow on self // when advance() is called (mut borrow) and to call bytes() only once @@ -32,19 +38,30 @@ macro_rules! buf_get_impl { } }}; (le => $this:ident, $typ:tt, $len_to_read:expr) => {{ - debug_assert!(mem::size_of::<$typ>() >= $len_to_read); + const SIZE: usize = core::mem::size_of::<$typ>(); // The same trick as above does not improve the best case speed. // It seems to be linked to the way the method is optimised by the compiler - let mut buf = [0; (mem::size_of::<$typ>())]; - $this.copy_to_slice(&mut buf[..($len_to_read)]); + let mut buf = [0; SIZE]; + + let subslice = match buf.get_mut(..$len_to_read) { + Some(subslice) => subslice, + None => panic_does_not_fit(SIZE, $len_to_read), + }; + + $this.copy_to_slice(subslice); return $typ::from_le_bytes(buf); }}; (be => $this:ident, $typ:tt, $len_to_read:expr) => {{ - debug_assert!(mem::size_of::<$typ>() >= $len_to_read); + const SIZE: usize = core::mem::size_of::<$typ>(); + + let slice_at = match SIZE.checked_sub($len_to_read) { + Some(slice_at) => slice_at, + None => panic_does_not_fit(SIZE, $len_to_read), + }; - let mut buf = [0; (mem::size_of::<$typ>())]; - $this.copy_to_slice(&mut buf[mem::size_of::<$typ>() - ($len_to_read)..]); + let mut buf = [0; SIZE]; + $this.copy_to_slice(&mut buf[slice_at..]); return $typ::from_be_bytes(buf); }}; } @@ -247,23 +264,18 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if `self.remaining() < dst.len()` - fn copy_to_slice(&mut self, dst: &mut [u8]) { - let mut off = 0; - - assert!(self.remaining() >= dst.len()); - - while off < dst.len() { - let cnt; - - unsafe { - let src = self.chunk(); - cnt = cmp::min(src.len(), dst.len() - off); + /// This function panics if `self.remaining() < dst.len()`. + fn copy_to_slice(&mut self, mut dst: &mut [u8]) { + if self.remaining() < dst.len() { + panic_advance(dst.len(), self.remaining()); + } - ptr::copy_nonoverlapping(src.as_ptr(), dst[off..].as_mut_ptr(), cnt); + while !dst.is_empty() { + let src = self.chunk(); + let cnt = usize::min(src.len(), dst.len()); - off += cnt; - } + dst[..cnt].copy_from_slice(&src[..cnt]); + dst = &mut dst[cnt..]; self.advance(cnt); } @@ -286,7 +298,9 @@ pub trait Buf { /// /// This function panics if there is no more remaining data in `self`. fn get_u8(&mut self) -> u8 { - assert!(self.remaining() >= 1); + if self.remaining() < 1 { + panic_advance(1, 0); + } let ret = self.chunk()[0]; self.advance(1); ret @@ -309,7 +323,9 @@ pub trait Buf { /// /// This function panics if there is no more remaining data in `self`. fn get_i8(&mut self) -> i8 { - assert!(self.remaining() >= 1); + if self.remaining() < 1 { + panic_advance(1, 0); + } let ret = self.chunk()[0] as i8; self.advance(1); ret @@ -877,7 +893,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_uint_ne(&mut self, nbytes: usize) -> u64 { if cfg!(target_endian = "big") { self.get_uint(nbytes) @@ -901,7 +918,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_int(&mut self, nbytes: usize) -> i64 { buf_get_impl!(be => self, i64, nbytes); } @@ -921,7 +939,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_int_le(&mut self, nbytes: usize) -> i64 { buf_get_impl!(le => self, i64, nbytes); } @@ -944,7 +963,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_int_ne(&mut self, nbytes: usize) -> i64 { if cfg!(target_endian = "big") { self.get_int(nbytes) @@ -1103,7 +1123,9 @@ pub trait Buf { fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { use super::BufMut; - assert!(len <= self.remaining(), "`len` greater than remaining"); + if self.remaining() < len { + panic_advance(len, self.remaining()); + } let mut ret = crate::BytesMut::with_capacity(len); ret.put(self.take(len)); @@ -1195,135 +1217,168 @@ pub trait Buf { macro_rules! deref_forward_buf { () => { + #[inline] fn remaining(&self) -> usize { (**self).remaining() } + #[inline] fn chunk(&self) -> &[u8] { (**self).chunk() } #[cfg(feature = "std")] + #[inline] fn chunks_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { (**self).chunks_vectored(dst) } + #[inline] fn advance(&mut self, cnt: usize) { (**self).advance(cnt) } + #[inline] fn has_remaining(&self) -> bool { (**self).has_remaining() } + #[inline] fn copy_to_slice(&mut self, dst: &mut [u8]) { (**self).copy_to_slice(dst) } + #[inline] fn get_u8(&mut self) -> u8 { (**self).get_u8() } + #[inline] fn get_i8(&mut self) -> i8 { (**self).get_i8() } + #[inline] fn get_u16(&mut self) -> u16 { (**self).get_u16() } + #[inline] fn get_u16_le(&mut self) -> u16 { (**self).get_u16_le() } + #[inline] fn get_u16_ne(&mut self) -> u16 { (**self).get_u16_ne() } + #[inline] fn get_i16(&mut self) -> i16 { (**self).get_i16() } + #[inline] fn get_i16_le(&mut self) -> i16 { (**self).get_i16_le() } + #[inline] fn get_i16_ne(&mut self) -> i16 { (**self).get_i16_ne() } + #[inline] fn get_u32(&mut self) -> u32 { (**self).get_u32() } + #[inline] fn get_u32_le(&mut self) -> u32 { (**self).get_u32_le() } + #[inline] fn get_u32_ne(&mut self) -> u32 { (**self).get_u32_ne() } + #[inline] fn get_i32(&mut self) -> i32 { (**self).get_i32() } + #[inline] fn get_i32_le(&mut self) -> i32 { (**self).get_i32_le() } + #[inline] fn get_i32_ne(&mut self) -> i32 { (**self).get_i32_ne() } + #[inline] fn get_u64(&mut self) -> u64 { (**self).get_u64() } + #[inline] fn get_u64_le(&mut self) -> u64 { (**self).get_u64_le() } + #[inline] fn get_u64_ne(&mut self) -> u64 { (**self).get_u64_ne() } + #[inline] fn get_i64(&mut self) -> i64 { (**self).get_i64() } + #[inline] fn get_i64_le(&mut self) -> i64 { (**self).get_i64_le() } + #[inline] fn get_i64_ne(&mut self) -> i64 { (**self).get_i64_ne() } + #[inline] fn get_uint(&mut self, nbytes: usize) -> u64 { (**self).get_uint(nbytes) } + #[inline] fn get_uint_le(&mut self, nbytes: usize) -> u64 { (**self).get_uint_le(nbytes) } + #[inline] fn get_uint_ne(&mut self, nbytes: usize) -> u64 { (**self).get_uint_ne(nbytes) } + #[inline] fn get_int(&mut self, nbytes: usize) -> i64 { (**self).get_int(nbytes) } + #[inline] fn get_int_le(&mut self, nbytes: usize) -> i64 { (**self).get_int_le(nbytes) } + #[inline] fn get_int_ne(&mut self, nbytes: usize) -> i64 { (**self).get_int_ne(nbytes) } + #[inline] fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { (**self).copy_to_bytes(len) } @@ -1351,41 +1406,52 @@ impl Buf for &[u8] { #[inline] fn advance(&mut self, cnt: usize) { + if self.len() < cnt { + panic_advance(cnt, self.len()); + } + *self = &self[cnt..]; } + + #[inline] + fn copy_to_slice(&mut self, dst: &mut [u8]) { + if self.len() < dst.len() { + panic_advance(dst.len(), self.len()); + } + + dst.copy_from_slice(&self[..dst.len()]); + self.advance(dst.len()); + } } #[cfg(feature = "std")] impl> Buf for std::io::Cursor { + #[inline] fn remaining(&self) -> usize { - let len = self.get_ref().as_ref().len(); - let pos = self.position(); - - if pos >= len as u64 { - return 0; - } - - len - pos as usize + saturating_sub_usize_u64(self.get_ref().as_ref().len(), self.position()) } + #[inline] fn chunk(&self) -> &[u8] { + let slice = self.get_ref().as_ref(); + let pos = min_u64_usize(self.position(), slice.len()); + &slice[pos..] + } + + #[inline] + fn advance(&mut self, cnt: usize) { let len = self.get_ref().as_ref().len(); let pos = self.position(); - if pos >= len as u64 { - return &[]; + // We intentionally allow `cnt == 0` here even if `pos > len`. + let max_cnt = saturating_sub_usize_u64(len, pos); + if cnt > max_cnt { + panic_advance(cnt, max_cnt); } - &self.get_ref().as_ref()[pos as usize..] - } - - fn advance(&mut self, cnt: usize) { - let pos = (self.position() as usize) - .checked_add(cnt) - .expect("overflow"); - - assert!(pos <= self.get_ref().as_ref().len()); - self.set_position(pos as u64); + // This will not overflow because either `cnt == 0` or the sum is not + // greater than `len`. + self.set_position(pos + cnt as u64); } } diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index c2ac39df3..304e11b13 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1,8 +1,9 @@ use crate::buf::{limit, Chain, Limit, UninitSlice}; #[cfg(feature = "std")] use crate::buf::{writer, Writer}; +use crate::{panic_advance, panic_does_not_fit}; -use core::{cmp, mem, ptr, usize}; +use core::{mem, ptr, usize}; use alloc::{boxed::Box, vec::Vec}; @@ -67,8 +68,10 @@ pub unsafe trait BufMut { /// The next call to `chunk_mut` will return a slice starting `cnt` bytes /// further into the underlying buffer. /// - /// This function is unsafe because there is no guarantee that the bytes - /// being advanced past have been initialized. + /// # Safety + /// + /// The caller must ensure that the next `cnt` bytes of `chunk` are + /// initialized. /// /// # Examples /// @@ -121,6 +124,7 @@ pub unsafe trait BufMut { /// /// assert!(!buf.has_remaining_mut()); /// ``` + #[inline] fn has_remaining_mut(&self) -> bool { self.remaining_mut() > 0 } @@ -194,27 +198,25 @@ pub unsafe trait BufMut { /// # Panics /// /// Panics if `self` does not have enough capacity to contain `src`. + #[inline] fn put(&mut self, mut src: T) where Self: Sized, { - assert!(self.remaining_mut() >= src.remaining()); + if self.remaining_mut() < src.remaining() { + panic_advance(src.remaining(), self.remaining_mut()); + } while src.has_remaining() { - let l; - - unsafe { - let s = src.chunk(); - let d = self.chunk_mut(); - l = cmp::min(s.len(), d.len()); + let s = src.chunk(); + let d = self.chunk_mut(); + let cnt = usize::min(s.len(), d.len()); - ptr::copy_nonoverlapping(s.as_ptr(), d.as_mut_ptr() as *mut u8, l); - } + d[..cnt].copy_from_slice(&s[..cnt]); - src.advance(l); - unsafe { - self.advance_mut(l); - } + // SAFETY: We just initialized `cnt` bytes in `self`. + unsafe { self.advance_mut(cnt) }; + src.advance(cnt); } } @@ -237,31 +239,21 @@ pub unsafe trait BufMut { /// /// assert_eq!(b"hello\0", &dst); /// ``` - fn put_slice(&mut self, src: &[u8]) { - let mut off = 0; - - assert!( - self.remaining_mut() >= src.len(), - "buffer overflow; remaining = {}; src = {}", - self.remaining_mut(), - src.len() - ); - - while off < src.len() { - let cnt; - - unsafe { - let dst = self.chunk_mut(); - cnt = cmp::min(dst.len(), src.len() - off); + #[inline] + fn put_slice(&mut self, mut src: &[u8]) { + if self.remaining_mut() < src.len() { + panic_advance(src.len(), self.remaining_mut()); + } - ptr::copy_nonoverlapping(src[off..].as_ptr(), dst.as_mut_ptr() as *mut u8, cnt); + while !src.is_empty() { + let dst = self.chunk_mut(); + let cnt = usize::min(src.len(), dst.len()); - off += cnt; - } + dst[..cnt].copy_from_slice(&src[..cnt]); + src = &src[cnt..]; - unsafe { - self.advance_mut(cnt); - } + // SAFETY: We just initialized `cnt` bytes in `self`. + unsafe { self.advance_mut(cnt) }; } } @@ -290,9 +282,20 @@ pub unsafe trait BufMut { /// /// 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); + #[inline] + fn put_bytes(&mut self, val: u8, mut cnt: usize) { + if self.remaining_mut() < cnt { + panic_advance(cnt, self.remaining_mut()); + } + + while cnt > 0 { + let dst = self.chunk_mut(); + let dst_len = usize::min(dst.len(), cnt); + // SAFETY: The pointer is valid for `dst_len <= dst.len()` bytes. + unsafe { core::ptr::write_bytes(dst.as_mut_ptr(), val, dst_len) }; + // SAFETY: We just initialized `dst_len` bytes in `self`. + unsafe { self.advance_mut(dst_len) }; + cnt -= dst_len; } } @@ -314,6 +317,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u8(&mut self, n: u8) { let src = [n]; self.put_slice(&src); @@ -337,6 +341,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i8(&mut self, n: i8) { let src = [n as u8]; self.put_slice(&src) @@ -360,6 +365,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u16(&mut self, n: u16) { self.put_slice(&n.to_be_bytes()) } @@ -382,6 +388,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u16_le(&mut self, n: u16) { self.put_slice(&n.to_le_bytes()) } @@ -408,6 +415,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u16_ne(&mut self, n: u16) { self.put_slice(&n.to_ne_bytes()) } @@ -430,6 +438,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i16(&mut self, n: i16) { self.put_slice(&n.to_be_bytes()) } @@ -452,6 +461,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i16_le(&mut self, n: i16) { self.put_slice(&n.to_le_bytes()) } @@ -478,6 +488,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i16_ne(&mut self, n: i16) { self.put_slice(&n.to_ne_bytes()) } @@ -500,6 +511,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u32(&mut self, n: u32) { self.put_slice(&n.to_be_bytes()) } @@ -522,6 +534,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u32_le(&mut self, n: u32) { self.put_slice(&n.to_le_bytes()) } @@ -548,6 +561,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u32_ne(&mut self, n: u32) { self.put_slice(&n.to_ne_bytes()) } @@ -570,6 +584,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i32(&mut self, n: i32) { self.put_slice(&n.to_be_bytes()) } @@ -592,6 +607,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i32_le(&mut self, n: i32) { self.put_slice(&n.to_le_bytes()) } @@ -618,6 +634,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i32_ne(&mut self, n: i32) { self.put_slice(&n.to_ne_bytes()) } @@ -640,6 +657,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u64(&mut self, n: u64) { self.put_slice(&n.to_be_bytes()) } @@ -662,6 +680,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u64_le(&mut self, n: u64) { self.put_slice(&n.to_le_bytes()) } @@ -688,6 +707,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u64_ne(&mut self, n: u64) { self.put_slice(&n.to_ne_bytes()) } @@ -710,6 +730,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i64(&mut self, n: i64) { self.put_slice(&n.to_be_bytes()) } @@ -732,6 +753,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i64_le(&mut self, n: i64) { self.put_slice(&n.to_le_bytes()) } @@ -758,6 +780,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i64_ne(&mut self, n: i64) { self.put_slice(&n.to_ne_bytes()) } @@ -780,6 +803,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u128(&mut self, n: u128) { self.put_slice(&n.to_be_bytes()) } @@ -802,6 +826,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u128_le(&mut self, n: u128) { self.put_slice(&n.to_le_bytes()) } @@ -828,6 +853,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_u128_ne(&mut self, n: u128) { self.put_slice(&n.to_ne_bytes()) } @@ -850,6 +876,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i128(&mut self, n: i128) { self.put_slice(&n.to_be_bytes()) } @@ -872,6 +899,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i128_le(&mut self, n: i128) { self.put_slice(&n.to_le_bytes()) } @@ -898,6 +926,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_i128_ne(&mut self, n: i128) { self.put_slice(&n.to_ne_bytes()) } @@ -919,9 +948,15 @@ pub unsafe trait BufMut { /// # Panics /// /// This function panics if there is not enough remaining capacity in - /// `self`. + /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_uint(&mut self, n: u64, nbytes: usize) { - self.put_slice(&n.to_be_bytes()[mem::size_of_val(&n) - nbytes..]); + let start = match mem::size_of_val(&n).checked_sub(nbytes) { + Some(start) => start, + None => panic_does_not_fit(nbytes, mem::size_of_val(&n)), + }; + + self.put_slice(&n.to_be_bytes()[start..]); } /// Writes an unsigned n-byte integer to `self` in the little-endian byte order. @@ -941,9 +976,16 @@ pub unsafe trait BufMut { /// # Panics /// /// This function panics if there is not enough remaining capacity in - /// `self`. + /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_uint_le(&mut self, n: u64, nbytes: usize) { - self.put_slice(&n.to_le_bytes()[0..nbytes]); + let slice = n.to_le_bytes(); + let slice = match slice.get(..nbytes) { + Some(slice) => slice, + None => panic_does_not_fit(nbytes, slice.len()), + }; + + self.put_slice(slice); } /// Writes an unsigned n-byte integer to `self` in the native-endian byte order. @@ -967,7 +1009,8 @@ pub unsafe trait BufMut { /// # Panics /// /// This function panics if there is not enough remaining capacity in - /// `self`. + /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_uint_ne(&mut self, n: u64, nbytes: usize) { if cfg!(target_endian = "big") { self.put_uint(n, nbytes) @@ -994,8 +1037,14 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_int(&mut self, n: i64, nbytes: usize) { - self.put_slice(&n.to_be_bytes()[mem::size_of_val(&n) - nbytes..]); + let start = match mem::size_of_val(&n).checked_sub(nbytes) { + Some(start) => start, + None => panic_does_not_fit(nbytes, mem::size_of_val(&n)), + }; + + self.put_slice(&n.to_be_bytes()[start..]); } /// Writes low `nbytes` of a signed integer to `self` in little-endian byte order. @@ -1016,8 +1065,15 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_int_le(&mut self, n: i64, nbytes: usize) { - self.put_slice(&n.to_le_bytes()[0..nbytes]); + let slice = n.to_le_bytes(); + let slice = match slice.get(..nbytes) { + Some(slice) => slice, + None => panic_does_not_fit(nbytes, slice.len()), + }; + + self.put_slice(slice); } /// Writes low `nbytes` of a signed integer to `self` in native-endian byte order. @@ -1042,6 +1098,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self` or if `nbytes` is greater than 8. + #[inline] fn put_int_ne(&mut self, n: i64, nbytes: usize) { if cfg!(target_endian = "big") { self.put_int(n, nbytes) @@ -1069,6 +1126,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f32(&mut self, n: f32) { self.put_u32(n.to_bits()); } @@ -1092,6 +1150,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f32_le(&mut self, n: f32) { self.put_u32_le(n.to_bits()); } @@ -1119,6 +1178,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f32_ne(&mut self, n: f32) { self.put_u32_ne(n.to_bits()); } @@ -1142,6 +1202,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f64(&mut self, n: f64) { self.put_u64(n.to_bits()); } @@ -1165,6 +1226,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f64_le(&mut self, n: f64) { self.put_u64_le(n.to_bits()); } @@ -1192,6 +1254,7 @@ pub unsafe trait BufMut { /// /// This function panics if there is not enough remaining capacity in /// `self`. + #[inline] fn put_f64_ne(&mut self, n: f64) { self.put_u64_ne(n.to_bits()); } @@ -1209,6 +1272,7 @@ pub unsafe trait BufMut { /// let dst = arr.limit(10); /// assert_eq!(dst.remaining_mut(), 10); /// ``` + #[inline] fn limit(self, limit: usize) -> Limit where Self: Sized, @@ -1240,6 +1304,7 @@ pub unsafe trait BufMut { /// ``` #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + #[inline] fn writer(self) -> Writer where Self: Sized, @@ -1267,6 +1332,7 @@ pub unsafe trait BufMut { /// assert_eq!(&a[..], b"hello"); /// assert_eq!(&b[..], b" world"); /// ``` + #[inline] fn chain_mut(self, next: U) -> Chain where Self: Sized, @@ -1277,98 +1343,122 @@ pub unsafe trait BufMut { macro_rules! deref_forward_bufmut { () => { + #[inline] fn remaining_mut(&self) -> usize { (**self).remaining_mut() } + #[inline] fn chunk_mut(&mut self) -> &mut UninitSlice { (**self).chunk_mut() } + #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { (**self).advance_mut(cnt) } + #[inline] fn put_slice(&mut self, src: &[u8]) { (**self).put_slice(src) } + #[inline] fn put_u8(&mut self, n: u8) { (**self).put_u8(n) } + #[inline] fn put_i8(&mut self, n: i8) { (**self).put_i8(n) } + #[inline] fn put_u16(&mut self, n: u16) { (**self).put_u16(n) } + #[inline] fn put_u16_le(&mut self, n: u16) { (**self).put_u16_le(n) } + #[inline] fn put_u16_ne(&mut self, n: u16) { (**self).put_u16_ne(n) } + #[inline] fn put_i16(&mut self, n: i16) { (**self).put_i16(n) } + #[inline] fn put_i16_le(&mut self, n: i16) { (**self).put_i16_le(n) } + #[inline] fn put_i16_ne(&mut self, n: i16) { (**self).put_i16_ne(n) } + #[inline] fn put_u32(&mut self, n: u32) { (**self).put_u32(n) } + #[inline] fn put_u32_le(&mut self, n: u32) { (**self).put_u32_le(n) } + #[inline] fn put_u32_ne(&mut self, n: u32) { (**self).put_u32_ne(n) } + #[inline] fn put_i32(&mut self, n: i32) { (**self).put_i32(n) } + #[inline] fn put_i32_le(&mut self, n: i32) { (**self).put_i32_le(n) } + #[inline] fn put_i32_ne(&mut self, n: i32) { (**self).put_i32_ne(n) } + #[inline] fn put_u64(&mut self, n: u64) { (**self).put_u64(n) } + #[inline] fn put_u64_le(&mut self, n: u64) { (**self).put_u64_le(n) } + #[inline] fn put_u64_ne(&mut self, n: u64) { (**self).put_u64_ne(n) } + #[inline] fn put_i64(&mut self, n: i64) { (**self).put_i64(n) } + #[inline] fn put_i64_le(&mut self, n: i64) { (**self).put_i64_le(n) } + #[inline] fn put_i64_ne(&mut self, n: i64) { (**self).put_i64_ne(n) } @@ -1391,12 +1481,15 @@ unsafe impl BufMut for &mut [u8] { #[inline] fn chunk_mut(&mut self) -> &mut UninitSlice { - // UninitSlice is repr(transparent), so safe to transmute - unsafe { &mut *(*self as *mut [u8] as *mut _) } + UninitSlice::new(self) } #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { + if self.len() < cnt { + panic_advance(cnt, self.len()); + } + // Lifetime dance taken from `impl Write for &mut [u8]`. let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt); *self = b; @@ -1404,14 +1497,22 @@ unsafe impl BufMut for &mut [u8] { #[inline] fn put_slice(&mut self, src: &[u8]) { - self[..src.len()].copy_from_slice(src); - unsafe { - self.advance_mut(src.len()); + if self.len() < src.len() { + panic_advance(src.len(), self.len()); } + + self[..src.len()].copy_from_slice(src); + // SAFETY: We just initialized `src.len()` bytes. + unsafe { self.advance_mut(src.len()) }; } + #[inline] fn put_bytes(&mut self, val: u8, cnt: usize) { - assert!(self.remaining_mut() >= cnt); + if self.len() < cnt { + panic_advance(cnt, self.len()); + } + + // SAFETY: We just checked that the pointer is valid for `cnt` bytes. unsafe { ptr::write_bytes(self.as_mut_ptr(), val, cnt); self.advance_mut(cnt); @@ -1432,6 +1533,10 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { + if self.len() < cnt { + panic_advance(cnt, self.len()); + } + // Lifetime dance taken from `impl Write for &mut [u8]`. let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt); *self = b; @@ -1439,14 +1544,24 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] fn put_slice(&mut self, src: &[u8]) { - self.chunk_mut()[..src.len()].copy_from_slice(src); + if self.len() < src.len() { + panic_advance(src.len(), self.len()); + } + + // SAFETY: We just checked that the pointer is valid for `src.len()` bytes. unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr().cast(), src.len()); self.advance_mut(src.len()); } } + #[inline] fn put_bytes(&mut self, val: u8, cnt: usize) { - assert!(self.remaining_mut() >= cnt); + if self.len() < cnt { + panic_advance(cnt, self.len()); + } + + // SAFETY: We just checked that the pointer is valid for `cnt` bytes. unsafe { ptr::write_bytes(self.as_mut_ptr() as *mut u8, val, cnt); self.advance_mut(cnt); @@ -1466,13 +1581,11 @@ unsafe impl BufMut for Vec { let len = self.len(); let remaining = self.capacity() - len; - assert!( - cnt <= remaining, - "cannot advance past `remaining_mut`: {:?} <= {:?}", - cnt, - remaining - ); + if remaining < cnt { + panic_advance(cnt, remaining); + } + // Addition will not overflow since the sum is at most the capacity. self.set_len(len + cnt); } @@ -1486,28 +1599,26 @@ unsafe impl BufMut for Vec { let len = self.len(); let ptr = self.as_mut_ptr(); - unsafe { &mut UninitSlice::from_raw_parts_mut(ptr, cap)[len..] } + // SAFETY: Since `ptr` is valid for `cap` bytes, `ptr.add(len)` must be + // valid for `cap - len` bytes. The subtraction will not underflow since + // `len <= cap`. + unsafe { UninitSlice::from_raw_parts_mut(ptr.add(len), cap - len) } } // Specialize these methods so they can skip checking `remaining_mut` // and `advance_mut`. + #[inline] fn put(&mut self, mut src: T) where Self: Sized, { - // In case the src isn't contiguous, reserve upfront + // In case the src isn't contiguous, reserve upfront. self.reserve(src.remaining()); while src.has_remaining() { - let l; - - // a block to contain the src.bytes() borrow - { - let s = src.chunk(); - l = s.len(); - self.extend_from_slice(s); - } - + let s = src.chunk(); + let l = s.len(); + self.extend_from_slice(s); src.advance(l); } } @@ -1517,8 +1628,10 @@ unsafe impl BufMut for Vec { self.extend_from_slice(src); } + #[inline] fn put_bytes(&mut self, val: u8, cnt: usize) { - let new_len = self.len().checked_add(cnt).unwrap(); + // If the addition overflows, then the `resize` will fail. + let new_len = self.len().saturating_add(cnt); self.resize(new_len, val); } } diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 78979a123..a2dac93a5 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -135,7 +135,7 @@ where U: Buf, { fn remaining(&self) -> usize { - self.a.remaining().checked_add(self.b.remaining()).unwrap() + self.a.remaining().saturating_add(self.b.remaining()) } fn chunk(&self) -> &[u8] { diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index b0eeed410..0715ad233 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -184,7 +184,7 @@ impl UninitSlice { /// }; /// ``` #[inline] - pub unsafe fn as_uninit_slice_mut<'a>(&'a mut self) -> &'a mut [MaybeUninit] { + pub unsafe fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { &mut *(self as *mut _ as *mut [MaybeUninit]) } diff --git a/src/bytes.rs b/src/bytes.rs index 0404a72db..d8911bba1 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -242,7 +242,7 @@ impl Bytes { let begin = match range.start_bound() { Bound::Included(&n) => n, - Bound::Excluded(&n) => n + 1, + Bound::Excluded(&n) => n.checked_add(1).expect("out of range"), Bound::Unbounded => 0, }; diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index b4be074d9..c8607201b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1087,14 +1087,12 @@ unsafe impl BufMut for BytesMut { #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { - let new_len = self.len() + cnt; - assert!( - new_len <= self.cap, - "new_len = {}; capacity = {}", - new_len, - self.cap - ); - self.len = new_len; + let remaining = self.cap - self.len(); + if cnt > remaining { + super::panic_advance(cnt, remaining); + } + // Addition won't overflow since it is at most `self.cap`. + self.len = self.len() + cnt; } #[inline] diff --git a/src/lib.rs b/src/lib.rs index af436b316..d2d970ba0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,3 +115,40 @@ fn abort() -> ! { panic!("abort"); } } + +#[inline(always)] +#[cfg(feature = "std")] +fn saturating_sub_usize_u64(a: usize, b: u64) -> usize { + use core::convert::TryFrom; + match usize::try_from(b) { + Ok(b) => a.saturating_sub(b), + Err(_) => 0, + } +} + +#[inline(always)] +#[cfg(feature = "std")] +fn min_u64_usize(a: u64, b: usize) -> usize { + use core::convert::TryFrom; + match usize::try_from(a) { + Ok(a) => usize::min(a, b), + Err(_) => b, + } +} + +/// Panic with a nice error message. +#[cold] +fn panic_advance(idx: usize, len: usize) -> ! { + panic!( + "advance out of bounds: the len is {} but advancing by {}", + len, idx + ); +} + +#[cold] +fn panic_does_not_fit(size: usize, nbytes: usize) -> ! { + panic!( + "size too large: the integer type can fit {} bytes, but nbytes is {}", + size, nbytes + ); +} diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 33aa68038..0abeb9f7a 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -83,7 +83,7 @@ fn test_put_int_le_nbytes_overflow() { } #[test] -#[should_panic(expected = "cannot advance")] +#[should_panic(expected = "advance out of bounds: the len is 8 but advancing by 12")] fn test_vec_advance_mut() { // Verify fix for #354 let mut buf = Vec::with_capacity(8); From bde8c50703869f54b905560eb62c2478a0111885 Mon Sep 17 00:00:00 2001 From: DanielB Date: Thu, 19 Oct 2023 12:50:24 -0700 Subject: [PATCH 112/203] docs: typo fix (#637) Co-authored-by: Daniel Bauman --- src/bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index d8911bba1..58cd1fc8c 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -63,8 +63,8 @@ use crate::Buf; /// `Bytes` contains a vtable, which allows implementations of `Bytes` to define /// 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. +/// cloning the backing storage in order to share it behind multiple `Bytes` +/// instances. /// /// For `Bytes` implementations which refer to constant memory (e.g. created /// via `Bytes::from_static()`) the cloning implementation will be a no-op. From 72cbb92e0e53680c67c27b56fabbe1f3ed5dbae9 Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Thu, 16 Nov 2023 12:24:21 +0100 Subject: [PATCH 113/203] docs: fix broken links (#639) Fixed a few broken links and converted a lot of them from the html-link to intra-doc links. --- src/buf/chain.rs | 4 +--- src/buf/iter.rs | 3 --- src/buf/mod.rs | 2 -- src/buf/reader.rs | 2 +- src/buf/take.rs | 2 +- src/buf/writer.rs | 2 +- src/bytes.rs | 4 +--- src/bytes_mut.rs | 4 +--- src/lib.rs | 11 +++-------- 9 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/buf/chain.rs b/src/buf/chain.rs index a2dac93a5..97ac2eca5 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -25,9 +25,7 @@ use std::io::IoSlice; /// assert_eq!(full[..], b"hello world"[..]); /// ``` /// -/// [`Buf::chain`]: trait.Buf.html#method.chain -/// [`Buf`]: trait.Buf.html -/// [`BufMut`]: trait.BufMut.html +/// [`Buf::chain`]: Buf::chain #[derive(Debug)] pub struct Chain { a: T, diff --git a/src/buf/iter.rs b/src/buf/iter.rs index c694e3d41..74f9b991e 100644 --- a/src/buf/iter.rs +++ b/src/buf/iter.rs @@ -17,9 +17,6 @@ use crate::Buf; /// assert_eq!(iter.next(), Some(b'c')); /// assert_eq!(iter.next(), None); /// ``` -/// -/// [`iter`]: trait.Buf.html#method.iter -/// [`Buf`]: trait.Buf.html #[derive(Debug)] pub struct IntoIter { inner: T, diff --git a/src/buf/mod.rs b/src/buf/mod.rs index c4c0a5724..1bf0a47e8 100644 --- a/src/buf/mod.rs +++ b/src/buf/mod.rs @@ -13,8 +13,6 @@ //! See [`Buf`] and [`BufMut`] for more details. //! //! [rope]: https://en.wikipedia.org/wiki/Rope_(data_structure) -//! [`Buf`]: trait.Buf.html -//! [`BufMut`]: trait.BufMut.html mod buf_impl; mod buf_mut; diff --git a/src/buf/reader.rs b/src/buf/reader.rs index f2b4d98f7..521494958 100644 --- a/src/buf/reader.rs +++ b/src/buf/reader.rs @@ -5,7 +5,7 @@ use std::{cmp, io}; /// A `Buf` adapter which implements `io::Read` for the inner value. /// /// This struct is generally created by calling `reader()` on `Buf`. See -/// documentation of [`reader()`](trait.Buf.html#method.reader) for more +/// documentation of [`reader()`](Buf::reader) for more /// details. #[derive(Debug)] pub struct Reader { diff --git a/src/buf/take.rs b/src/buf/take.rs index d3cb10ab6..a16a434ee 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -5,7 +5,7 @@ use core::cmp; /// A `Buf` adapter which limits the bytes read from an underlying buffer. /// /// This struct is generally created by calling `take()` on `Buf`. See -/// documentation of [`take()`](trait.Buf.html#method.take) for more details. +/// documentation of [`take()`](Buf::take) for more details. #[derive(Debug)] pub struct Take { inner: T, diff --git a/src/buf/writer.rs b/src/buf/writer.rs index 261d7cd09..08f15d20e 100644 --- a/src/buf/writer.rs +++ b/src/buf/writer.rs @@ -5,7 +5,7 @@ use std::{cmp, io}; /// A `BufMut` adapter which implements `io::Write` for the inner value. /// /// This struct is generally created by calling `writer()` on `BufMut`. See -/// documentation of [`writer()`](trait.BufMut.html#method.writer) for more +/// documentation of [`writer()`](BufMut::writer) for more /// details. #[derive(Debug)] pub struct Writer { diff --git a/src/bytes.rs b/src/bytes.rs index 58cd1fc8c..9fed3d287 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -438,7 +438,7 @@ impl Bytes { /// If `len` is greater than the buffer's current length, this has no /// effect. /// - /// The [`split_off`] method can emulate `truncate`, but this causes the + /// The [split_off](`Self::split_off()`) method can emulate `truncate`, but this causes the /// excess bytes to be returned instead of dropped. /// /// # Examples @@ -450,8 +450,6 @@ impl Bytes { /// buf.truncate(5); /// assert_eq!(buf, b"hello"[..]); /// ``` - /// - /// [`split_off`]: #method.split_off #[inline] pub fn truncate(&mut self, len: usize) { if len < self.len { diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index c8607201b..57fd33e4b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -399,7 +399,7 @@ impl BytesMut { /// /// Existing underlying capacity is preserved. /// - /// The [`split_off`] method can emulate `truncate`, but this causes the + /// The [split_off](`Self::split_off()`) method can emulate `truncate`, but this causes the /// excess bytes to be returned instead of dropped. /// /// # Examples @@ -411,8 +411,6 @@ impl BytesMut { /// buf.truncate(5); /// assert_eq!(buf, b"hello"[..]); /// ``` - /// - /// [`split_off`]: #method.split_off pub fn truncate(&mut self, len: usize) { if len <= self.len() { unsafe { diff --git a/src/lib.rs b/src/lib.rs index d2d970ba0..1b3e6fc40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,12 +9,9 @@ //! Provides abstractions for working with bytes. //! //! The `bytes` crate provides an efficient byte buffer structure -//! ([`Bytes`](struct.Bytes.html)) and traits for working with buffer +//! ([`Bytes`]) and traits for working with buffer //! implementations ([`Buf`], [`BufMut`]). //! -//! [`Buf`]: trait.Buf.html -//! [`BufMut`]: trait.BufMut.html -//! //! # `Bytes` //! //! `Bytes` is an efficient container for storing and operating on contiguous @@ -52,9 +49,7 @@ //! `a` and `b` will share the underlying buffer and maintain indices tracking //! the view into the buffer represented by the handle. //! -//! See the [struct docs] for more details. -//! -//! [struct docs]: struct.Bytes.html +//! See the [struct docs](`Bytes`) for more details. //! //! # `Buf`, `BufMut` //! @@ -70,7 +65,7 @@ //! ## Relation with `Read` and `Write` //! //! At first glance, it may seem that `Buf` and `BufMut` overlap in -//! functionality with `std::io::Read` and `std::io::Write`. However, they +//! functionality with [`std::io::Read`] and [`std::io::Write`]. However, they //! serve different purposes. A buffer is the value that is provided as an //! argument to `Read::read` and `Write::write`. `Read` and `Write` may then //! perform a syscall, which has the potential of failing. Operations on `Buf` From f73c6c8e8543ee15741c788d105e2b4235f1bc7b Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Thu, 28 Dec 2023 06:17:53 +0100 Subject: [PATCH 114/203] Simplify UninitSlice::as_uninit_slice_mut() logic (#644) This reworks `UninitSlice::as_uninit_slice_mut()` using equivalent simpler logic. --- src/buf/uninit_slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 0715ad233..82ebdbbb3 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -185,7 +185,7 @@ impl UninitSlice { /// ``` #[inline] pub unsafe fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit] { - &mut *(self as *mut _ as *mut [MaybeUninit]) + &mut self.0 } /// Returns the number of bytes in the slice. From dbbdb63d7691066922ac4c7753e6dd95c07f8fbf Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Thu, 28 Dec 2023 00:20:13 -0500 Subject: [PATCH 115/203] Use `self.` instead of `Self::` (#642) I was a little confused about these calls using `Self::` instead of `self.` here. Is there a reason to use the former instead of the latter? --- src/buf/buf_impl.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index b4ebf408a..9367eb2df 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -991,7 +991,7 @@ pub trait Buf { /// /// This function panics if there is not enough remaining data in `self`. fn get_f32(&mut self) -> f32 { - f32::from_bits(Self::get_u32(self)) + f32::from_bits(self.get_u32()) } /// Gets an IEEE754 single-precision (4 bytes) floating point number from @@ -1012,7 +1012,7 @@ pub trait Buf { /// /// This function panics if there is not enough remaining data in `self`. fn get_f32_le(&mut self) -> f32 { - f32::from_bits(Self::get_u32_le(self)) + f32::from_bits(self.get_u32_le()) } /// Gets an IEEE754 single-precision (4 bytes) floating point number from @@ -1036,7 +1036,7 @@ pub trait Buf { /// /// 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)) + f32::from_bits(self.get_u32_ne()) } /// Gets an IEEE754 double-precision (8 bytes) floating point number from @@ -1057,7 +1057,7 @@ pub trait Buf { /// /// This function panics if there is not enough remaining data in `self`. fn get_f64(&mut self) -> f64 { - f64::from_bits(Self::get_u64(self)) + f64::from_bits(self.get_u64()) } /// Gets an IEEE754 double-precision (8 bytes) floating point number from @@ -1078,7 +1078,7 @@ pub trait Buf { /// /// This function panics if there is not enough remaining data in `self`. fn get_f64_le(&mut self) -> f64 { - f64::from_bits(Self::get_u64_le(self)) + f64::from_bits(self.get_u64_le()) } /// Gets an IEEE754 double-precision (8 bytes) floating point number from @@ -1102,7 +1102,7 @@ pub trait Buf { /// /// 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)) + f64::from_bits(self.get_u64_ne()) } /// Consumes `len` bytes inside self and returns new instance of `Bytes` From 3bf6583b5cece02526b9b225e6ace0552a36ded3 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 3 Jan 2024 17:22:39 +0100 Subject: [PATCH 116/203] readme: add security policy (#649) --- SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..b74a831cb --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +Bytes is part of the Tokio project and uses the same security policy as [Tokio][tokio-security]. + +## Report a security issue + +The process for reporting an issue is the same as for [Tokio][tokio-security]. This includes private reporting via security@tokio.rs. + +[tokio-security]: https://github.com/tokio-rs/tokio/security/policy From fbc64bcc6713b51fa1253cf18fc80c904796ddb5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 8 Jan 2024 01:10:48 +0900 Subject: [PATCH 117/203] Update loom to 0.7 (#651) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 06b19e671..cf72180fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ serde = { version = "1.0.60", optional = true, default-features = false, feature serde_test = "1.0" [target.'cfg(loom)'.dev-dependencies] -loom = "0.5" +loom = "0.7" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] From 09214ba51bdace6f6cb91740cee9514fc08d55ce Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 8 Jan 2024 01:11:02 +0900 Subject: [PATCH 118/203] Update CI config (#650) --- .github/workflows/ci.yml | 54 +++++++++++++++++++++++----------------- Cargo.toml | 3 ++- ci/test-stable.sh | 6 ----- ci/tsan.sh | 0 4 files changed, 33 insertions(+), 30 deletions(-) mode change 100644 => 100755 ci/test-stable.sh mode change 100644 => 100755 ci/tsan.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4f7b1d93..c0658a142 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: push: branches: - master + schedule: + - cron: '0 2 * * 0' env: RUSTFLAGS: -Dwarnings @@ -23,11 +25,11 @@ jobs: name: rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust - run: rustup update stable && rustup default stable + run: rustup update stable - name: Check formatting - run: cargo fmt --all -- --check + run: cargo fmt --all --check # TODO # # Apply clippy lints @@ -35,7 +37,7 @@ jobs: # name: clippy # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v3 + # - uses: actions/checkout@v4 # - name: Apply clippy lints # run: cargo clippy --all-features @@ -48,11 +50,11 @@ jobs: name: minrust runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Install Rust - run: rustup update 1.39.0 && rustup default 1.39.0 + - uses: actions/checkout@v4 + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack - name: Check - run: . ci/test-stable.sh check + run: cargo hack check --feature-powerset --optional-deps --rust-version # Stable stable: @@ -65,23 +67,27 @@ jobs: - windows-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - 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 + run: rustup update stable --no-self-update + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack - name: Test - run: . ci/test-stable.sh test + run: ci/test-stable.sh test # Nightly nightly: name: nightly runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update $nightly && rustup default $nightly + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack - name: Test - run: . ci/test-stable.sh test + run: ci/test-stable.sh test # Run tests on some extra platforms cross: @@ -96,13 +102,14 @@ jobs: - wasm32-unknown-unknown runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust - run: rustup update stable && rustup default stable + run: rustup update stable + - name: Install cross + uses: taiki-e/install-action@cross + if: matrix.target != 'wasm32-unknown-unknown' - name: cross build --target ${{ matrix.target }} - run: | - cargo install cross - cross build --target ${{ matrix.target }} + run: cross build --target ${{ matrix.target }} if: matrix.target != 'wasm32-unknown-unknown' # WASM support - name: cargo build --target ${{ matrix.target }} @@ -116,18 +123,19 @@ jobs: name: tsan runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Install rust-src run: rustup component add rust-src - name: ASAN / TSAN - run: . ci/tsan.sh + run: ci/tsan.sh + miri: name: miri runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Miri run: ci/miri.sh @@ -136,7 +144,7 @@ jobs: name: loom runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Loom tests @@ -155,7 +163,7 @@ jobs: - loom runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Rust run: rustup update $nightly && rustup default $nightly - name: Build documentation diff --git a/Cargo.toml b/Cargo.toml index cf72180fd..127d81dd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ name = "bytes" # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. version = "1.5.0" +edition = "2018" +rust-version = "1.39" license = "MIT" authors = [ "Carl Lerche ", @@ -15,7 +17,6 @@ repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] -edition = "2018" [features] default = ["std"] diff --git a/ci/test-stable.sh b/ci/test-stable.sh old mode 100644 new mode 100755 index 4421f3a97..a8eaa3c65 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -4,10 +4,6 @@ set -ex cmd="${1:-test}" -# Install cargo-hack for feature flag test -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 # * --optional-deps is needed for serde feature @@ -15,8 +11,6 @@ cargo hack "${cmd}" --each-feature --optional-deps # Run with all features cargo "${cmd}" --all-features -cargo doc --no-deps --all-features - if [[ "${RUST_VERSION}" == "nightly"* ]]; then # Check benchmarks cargo check --benches diff --git a/ci/tsan.sh b/ci/tsan.sh old mode 100644 new mode 100755 From abb4a2e66cab68a6d1deb3d37377625443794cfd Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Fri, 19 Jan 2024 05:08:27 -0500 Subject: [PATCH 119/203] BytesMut: Assert alignment of Shared (#652) Back in #362, an assertion was added to ensure that the alignment of bytes::Shared is even so we can use the least significant bit as a flag. bytes_mut::Shared uses the same technique but has no such assertion so I've added one here. --- src/bytes_mut.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 57fd33e4b..dd4ff5031 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -80,6 +80,12 @@ struct Shared { ref_count: AtomicUsize, } +// Assert that the alignment of `Shared` is divisible by 2. +// This is a necessary invariant since we depend on allocating `Shared` a +// shared object to implicitly carry the `KIND_ARC` flag in its pointer. +// This flag is set when the LSB is 0. +const _: [(); 0 - mem::align_of::() % 2] = []; // Assert that the alignment of `Shared` is divisible by 2. + // Buffer storage strategy flags. const KIND_ARC: usize = 0b0; const KIND_VEC: usize = 0b1; From 0864aea9704ac12fa53ee96a7f968e51c9dabba1 Mon Sep 17 00:00:00 2001 From: Cyborus04 Date: Fri, 19 Jan 2024 17:59:30 -0500 Subject: [PATCH 120/203] add `Bytes::is_unique` (#643) --- src/bytes.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/bytes_mut.rs | 1 + tests/test_bytes.rs | 33 ++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/bytes.rs b/src/bytes.rs index 9fed3d287..9eda9f463 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -112,6 +112,8 @@ pub(crate) struct Vtable { /// /// takes `Bytes` to value pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec, + /// fn(data) + pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool, /// fn(data, ptr, len) pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize), } @@ -208,6 +210,28 @@ impl Bytes { self.len == 0 } + /// Returns true if this is the only reference to the data. + /// + /// Always returns false if the data is backed by a static slice. + /// + /// The result of this method may be invalidated immediately if another + /// thread clones this value while this is being called. Ensure you have + /// unique access to this value (`&mut Bytes`) first if you need to be + /// certain the result is valid (i.e. for safety reasons) + /// # Examples + /// + /// ``` + /// use bytes::Bytes; + /// + /// let a = Bytes::from(vec![1, 2, 3]); + /// assert!(a.is_unique()); + /// let b = a.clone(); + /// assert!(!a.is_unique()); + /// ``` + pub fn is_unique(&self) -> bool { + unsafe { (self.vtable.is_unique)(&self.data) } + } + /// Creates `Bytes` instance from slice, by copying it. pub fn copy_from_slice(data: &[u8]) -> Self { data.to_vec().into() @@ -898,6 +922,7 @@ impl fmt::Debug for Vtable { const STATIC_VTABLE: Vtable = Vtable { clone: static_clone, to_vec: static_to_vec, + is_unique: static_is_unique, drop: static_drop, }; @@ -911,6 +936,10 @@ unsafe fn static_to_vec(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec) -> bool { + false +} + unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { // nothing to drop for &'static [u8] } @@ -920,12 +949,14 @@ 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, + is_unique: promotable_is_unique, drop: promotable_even_drop, }; static PROMOTABLE_ODD_VTABLE: Vtable = Vtable { clone: promotable_odd_clone, to_vec: promotable_odd_to_vec, + is_unique: promotable_is_unique, drop: promotable_odd_drop, }; @@ -1020,6 +1051,18 @@ unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usi }); } +unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool { + let shared = data.load(Ordering::Acquire); + let kind = shared as usize & KIND_MASK; + + if kind == KIND_ARC { + let ref_cnt = (*shared.cast::()).ref_cnt.load(Ordering::Relaxed); + ref_cnt == 1 + } else { + true + } +} + unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) { let cap = (offset as usize - buf as usize) + len; dealloc(buf, Layout::from_size_align(cap, 1).unwrap()) @@ -1049,6 +1092,7 @@ const _: [(); 0 - mem::align_of::() % 2] = []; // Assert that the alignm static SHARED_VTABLE: Vtable = Vtable { clone: shared_clone, to_vec: shared_to_vec, + is_unique: shared_is_unique, drop: shared_drop, }; @@ -1094,6 +1138,12 @@ unsafe fn shared_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec shared_to_vec_impl(data.load(Ordering::Relaxed).cast(), ptr, len) } +pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool { + let shared = data.load(Ordering::Acquire); + let ref_cnt = (*shared.cast::()).ref_cnt.load(Ordering::Relaxed); + ref_cnt == 1 +} + 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 dd4ff5031..88d596cf6 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1708,6 +1708,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, + is_unique: crate::bytes::shared_is_unique, drop: shared_v_drop, }; diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 5ec60a5b0..76adfdbf4 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1208,3 +1208,36 @@ fn test_bytes_capacity_len() { } } } + +#[test] +fn static_is_unique() { + let b = Bytes::from_static(LONG); + assert!(!b.is_unique()); +} + +#[test] +fn vec_is_unique() { + let v: Vec = LONG.to_vec(); + let b = Bytes::from(v); + assert!(b.is_unique()); +} + +#[test] +fn arc_is_unique() { + let v: Vec = LONG.to_vec(); + let b = Bytes::from(v); + let c = b.clone(); + assert!(!b.is_unique()); + drop(c); + assert!(b.is_unique()); +} + +#[test] +fn shared_is_unique() { + let v: Vec = LONG.to_vec(); + let b = Bytes::from(v); + let c = b.clone(); + assert!(!c.is_unique()); + drop(b); + assert!(c.is_unique()); +} From 0ba3b4c4cd74a0ad8566277e1a1533fa9e895756 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sun, 28 Jan 2024 05:37:11 -0500 Subject: [PATCH 121/203] Remove unnecessary namespace qualifier (#660) --- src/bytes.rs | 2 +- src/bytes_mut.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 9eda9f463..3da747d44 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -580,7 +580,7 @@ impl Buf for Bytes { } } - fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + fn copy_to_bytes(&mut self, len: usize) -> Self { if len == self.remaining() { core::mem::replace(self, Bytes::new()) } else { diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 88d596cf6..1628a85a5 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1078,7 +1078,7 @@ impl Buf for BytesMut { } } - fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + fn copy_to_bytes(&mut self, len: usize) -> Bytes { self.split_to(len).freeze() } } @@ -1110,7 +1110,7 @@ unsafe impl BufMut for BytesMut { // Specialize these methods so they can skip checking `remaining_mut` // and `advance_mut`. - fn put(&mut self, mut src: T) + fn put(&mut self, mut src: T) where Self: Sized, { From 9257a6ea0852c03f4672e5f8346d3d614543e270 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sun, 28 Jan 2024 05:50:56 -0500 Subject: [PATCH 122/203] Remove an unnecessary else branch (#662) --- src/bytes_mut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 1628a85a5..d143f605c 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -726,11 +726,11 @@ impl BytesMut { } return; - } else { - new_cap = cmp::max(new_cap, original_capacity); } } + new_cap = cmp::max(new_cap, original_capacity); + // Create a new vector to store the data let mut v = ManuallyDrop::new(Vec::with_capacity(new_cap)); From e24587dd6197dbc58d6c2b6eb7186df99b04d881 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sun, 28 Jan 2024 07:00:30 -0500 Subject: [PATCH 123/203] Remove unreachable else branch (#661) --- src/bytes_mut.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d143f605c..8783ae7f3 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1628,7 +1628,7 @@ impl From for Vec { let (off, _) = bytes.get_vec_pos(); rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off) } - } else if kind == KIND_ARC { + } else { let shared = bytes.data as *mut Shared; if unsafe { (*shared).is_unique() } { @@ -1640,8 +1640,6 @@ impl From for Vec { } else { return bytes.deref().to_vec(); } - } else { - return bytes.deref().to_vec(); }; let len = bytes.len; From d2e7abdb290e663f025a22a7d9e14e019b6abdb2 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 31 Jan 2024 09:41:23 -0500 Subject: [PATCH 124/203] refactor: make parameter mut in From (#667) Instead of re-declaring `vec`, we can just use a mut parameter. --- src/bytes.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 3da747d44..0b443c85b 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -828,8 +828,7 @@ impl From<&'static str> for Bytes { } impl From> for Bytes { - fn from(vec: Vec) -> Bytes { - let mut vec = vec; + fn from(mut vec: Vec) -> Bytes { let ptr = vec.as_mut_ptr(); let len = vec.len(); let cap = vec.capacity(); From 8bcac21cb44c112f20e8dd31475033ff448e35ce Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 6 Feb 2024 13:17:01 -0500 Subject: [PATCH 125/203] Restore commented tests (#665) These seem to have been commented by accident in #298, and are still passing. --- src/bytes_mut.rs | 79 +++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 8783ae7f3..5566f2d1a 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1409,56 +1409,59 @@ fn original_capacity_from_repr(repr: usize) -> usize { 1 << (repr + (MIN_ORIGINAL_CAPACITY_WIDTH - 1)) } -/* -#[test] -fn test_original_capacity_to_repr() { - assert_eq!(original_capacity_to_repr(0), 0); +#[cfg(test)] +mod tests { + use super::*; - let max_width = 32; + #[test] + fn test_original_capacity_to_repr() { + assert_eq!(original_capacity_to_repr(0), 0); - for width in 1..(max_width + 1) { - let cap = 1 << width - 1; + let max_width = 32; - let expected = if width < MIN_ORIGINAL_CAPACITY_WIDTH { - 0 - } else if width < MAX_ORIGINAL_CAPACITY_WIDTH { - width - MIN_ORIGINAL_CAPACITY_WIDTH - } else { - MAX_ORIGINAL_CAPACITY_WIDTH - MIN_ORIGINAL_CAPACITY_WIDTH - }; + for width in 1..(max_width + 1) { + let cap = 1 << width - 1; - assert_eq!(original_capacity_to_repr(cap), expected); + let expected = if width < MIN_ORIGINAL_CAPACITY_WIDTH { + 0 + } else if width < MAX_ORIGINAL_CAPACITY_WIDTH { + width - MIN_ORIGINAL_CAPACITY_WIDTH + } else { + MAX_ORIGINAL_CAPACITY_WIDTH - MIN_ORIGINAL_CAPACITY_WIDTH + }; - if width > 1 { - assert_eq!(original_capacity_to_repr(cap + 1), expected); - } + assert_eq!(original_capacity_to_repr(cap), expected); - // MIN_ORIGINAL_CAPACITY_WIDTH must be bigger than 7 to pass tests below - if width == MIN_ORIGINAL_CAPACITY_WIDTH + 1 { - assert_eq!(original_capacity_to_repr(cap - 24), expected - 1); - assert_eq!(original_capacity_to_repr(cap + 76), expected); - } else if width == MIN_ORIGINAL_CAPACITY_WIDTH + 2 { - assert_eq!(original_capacity_to_repr(cap - 1), expected - 1); - assert_eq!(original_capacity_to_repr(cap - 48), expected - 1); + if width > 1 { + assert_eq!(original_capacity_to_repr(cap + 1), expected); + } + + // MIN_ORIGINAL_CAPACITY_WIDTH must be bigger than 7 to pass tests below + if width == MIN_ORIGINAL_CAPACITY_WIDTH + 1 { + assert_eq!(original_capacity_to_repr(cap - 24), expected - 1); + assert_eq!(original_capacity_to_repr(cap + 76), expected); + } else if width == MIN_ORIGINAL_CAPACITY_WIDTH + 2 { + assert_eq!(original_capacity_to_repr(cap - 1), expected - 1); + assert_eq!(original_capacity_to_repr(cap - 48), expected - 1); + } } } -} -#[test] -fn test_original_capacity_from_repr() { - assert_eq!(0, original_capacity_from_repr(0)); + #[test] + fn test_original_capacity_from_repr() { + assert_eq!(0, original_capacity_from_repr(0)); - let min_cap = 1 << MIN_ORIGINAL_CAPACITY_WIDTH; + let min_cap = 1 << MIN_ORIGINAL_CAPACITY_WIDTH; - assert_eq!(min_cap, original_capacity_from_repr(1)); - assert_eq!(min_cap * 2, original_capacity_from_repr(2)); - assert_eq!(min_cap * 4, original_capacity_from_repr(3)); - assert_eq!(min_cap * 8, original_capacity_from_repr(4)); - assert_eq!(min_cap * 16, original_capacity_from_repr(5)); - assert_eq!(min_cap * 32, original_capacity_from_repr(6)); - assert_eq!(min_cap * 64, original_capacity_from_repr(7)); + assert_eq!(min_cap, original_capacity_from_repr(1)); + assert_eq!(min_cap * 2, original_capacity_from_repr(2)); + assert_eq!(min_cap * 4, original_capacity_from_repr(3)); + assert_eq!(min_cap * 8, original_capacity_from_repr(4)); + assert_eq!(min_cap * 16, original_capacity_from_repr(5)); + assert_eq!(min_cap * 32, original_capacity_from_repr(6)); + assert_eq!(min_cap * 64, original_capacity_from_repr(7)); + } } -*/ unsafe impl Send for BytesMut {} unsafe impl Sync for BytesMut {} From 47e83056f28e15e4ca68056a0136f3920b753783 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 6 Feb 2024 13:41:44 -0500 Subject: [PATCH 126/203] Use sub instead of offset (#668) We're always subtracting here, and we already have a usize, so `sub` seems like a more appropriate usage to me. --- src/bytes_mut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 5566f2d1a..d1c141122 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -617,7 +617,7 @@ impl BytesMut { // // Just move the pointer back to the start after copying // data back. - let base_ptr = self.ptr.as_ptr().offset(-(off as isize)); + let base_ptr = self.ptr.as_ptr().sub(off); // 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); @@ -1697,7 +1697,7 @@ fn offset_from(dst: *mut u8, original: *mut u8) -> usize { } unsafe fn rebuild_vec(ptr: *mut u8, mut len: usize, mut cap: usize, off: usize) -> Vec { - let ptr = ptr.offset(-(off as isize)); + let ptr = ptr.sub(off); len += off; cap += off; From c6972d61328be113ec8e80c207815a4b84fe616c Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 6 Feb 2024 13:46:49 -0500 Subject: [PATCH 127/203] Calculate original capacity only if necessary (#666) We don't need the original capacity if the shared data is unique, so let's not calculate it until after that check. --- src/bytes_mut.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d1c141122..619defc21 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -652,13 +652,7 @@ impl BytesMut { // Compute the new capacity let mut new_cap = len.checked_add(additional).expect("overflow"); - let original_capacity; - let original_capacity_repr; - unsafe { - original_capacity_repr = (*shared).original_capacity_repr; - original_capacity = original_capacity_from_repr(original_capacity_repr); - // First, try to reclaim the buffer. This is possible if the current // handle is the only outstanding handle pointing to the buffer. if (*shared).is_unique() { @@ -729,6 +723,9 @@ impl BytesMut { } } + let original_capacity_repr = unsafe { (*shared).original_capacity_repr }; + let original_capacity = original_capacity_from_repr(original_capacity_repr); + new_cap = cmp::max(new_cap, original_capacity); // Create a new vector to store the data From f586ffc52589f01be1b4a44d6544b3d0226773d6 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 6 Feb 2024 14:03:37 -0500 Subject: [PATCH 128/203] set_vec_pos does not need a second parameter (#672) The second argument to `set_vec_pos` always contains the value of `self.data`. Let's just use `self.data` and remove the second parameter altogether. --- src/bytes_mut.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 619defc21..bb72a2107 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -247,7 +247,7 @@ impl BytesMut { if self.kind() == KIND_VEC { // Just re-use `Bytes` internal Vec vtable unsafe { - let (off, _) = self.get_vec_pos(); + let off = self.get_vec_pos(); let vec = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off); mem::forget(self); let mut b: Bytes = vec.into(); @@ -596,7 +596,7 @@ impl BytesMut { // 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(); + let off = self.get_vec_pos(); // Only reuse space if we can satisfy the requested additional space. // @@ -621,7 +621,7 @@ impl BytesMut { // 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); + self.set_vec_pos(0); // Length stays constant, but since we moved backwards we // can gain capacity back. @@ -867,11 +867,10 @@ impl BytesMut { // complicated. First, we have to track how far ahead the // "start" of the byte buffer from the beginning of the vec. We // also have to ensure that we don't exceed the maximum shift. - let (mut pos, prev) = self.get_vec_pos(); - pos += start; + let pos = self.get_vec_pos() + start; if pos <= MAX_VEC_POS { - self.set_vec_pos(pos, prev); + self.set_vec_pos(pos); } else { // The repr must be upgraded to ARC. This will never happen // on 64 bit systems and will only happen on 32 bit systems @@ -979,19 +978,18 @@ impl BytesMut { } #[inline] - unsafe fn get_vec_pos(&mut self) -> (usize, usize) { + unsafe fn get_vec_pos(&mut self) -> usize { debug_assert_eq!(self.kind(), KIND_VEC); - let prev = self.data as usize; - (prev >> VEC_POS_OFFSET, prev) + self.data as usize >> VEC_POS_OFFSET } #[inline] - unsafe fn set_vec_pos(&mut self, pos: usize, prev: usize) { + unsafe fn set_vec_pos(&mut self, pos: usize) { debug_assert_eq!(self.kind(), KIND_VEC); debug_assert!(pos <= MAX_VEC_POS); - self.data = invalid_ptr((pos << VEC_POS_OFFSET) | (prev & NOT_VEC_POS_MASK)); + self.data = invalid_ptr((pos << VEC_POS_OFFSET) | (self.data as usize & NOT_VEC_POS_MASK)); } /// Returns the remaining spare capacity of the buffer as a slice of `MaybeUninit`. @@ -1040,7 +1038,7 @@ impl Drop for BytesMut { if kind == KIND_VEC { unsafe { - let (off, _) = self.get_vec_pos(); + let off = self.get_vec_pos(); // Vector storage, free the vector let _ = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off); @@ -1625,7 +1623,7 @@ impl From for Vec { let mut vec = if kind == KIND_VEC { unsafe { - let (off, _) = bytes.get_vec_pos(); + let off = bytes.get_vec_pos(); rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off) } } else { From 1bcd2129d195a0722d8b5b1a16c7d33698701f2e Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 6 Feb 2024 17:34:30 -0500 Subject: [PATCH 129/203] get_vec_pos: use &self instead of &mut self (#670) I can't see any reason that get_vec_pos needs a &mut self. --- src/bytes_mut.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index bb72a2107..901889671 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -243,7 +243,7 @@ impl BytesMut { /// th.join().unwrap(); /// ``` #[inline] - pub fn freeze(mut self) -> Bytes { + pub fn freeze(self) -> Bytes { if self.kind() == KIND_VEC { // Just re-use `Bytes` internal Vec vtable unsafe { @@ -978,7 +978,7 @@ impl BytesMut { } #[inline] - unsafe fn get_vec_pos(&mut self) -> usize { + unsafe fn get_vec_pos(&self) -> usize { debug_assert_eq!(self.kind(), KIND_VEC); self.data as usize >> VEC_POS_OFFSET @@ -1618,7 +1618,7 @@ impl PartialEq for BytesMut { } impl From for Vec { - fn from(mut bytes: BytesMut) -> Self { + fn from(bytes: BytesMut) -> Self { let kind = bytes.kind(); let mut vec = if kind == KIND_VEC { From 46289278f52a26c12298779f4aaebad1dcb26d35 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Fri, 23 Feb 2024 17:22:58 -0500 Subject: [PATCH 130/203] Refactor split_at/split_to (#663) * set len a little more concisely * inline set_end * remove kind assertions * remove a duplicate assertion * remove redundant assertion and min * rename set_start to advance_unchecked --- src/bytes_mut.rs | 50 +++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 901889671..220bdb005 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -317,8 +317,10 @@ impl BytesMut { ); unsafe { let mut other = self.shallow_clone(); - other.set_start(at); - self.set_end(at); + // SAFETY: We've checked that `at` <= `self.capacity()` above. + other.advance_unchecked(at); + self.cap = at; + self.len = cmp::min(self.len, at); other } } @@ -391,8 +393,11 @@ impl BytesMut { unsafe { let mut other = self.shallow_clone(); - other.set_end(at); - self.set_start(at); + // SAFETY: We've checked that `at` <= `self.len()` and we know that `self.len()` <= + // `self.capacity()`. + self.advance_unchecked(at); + other.cap = at; + other.len = at; other } } @@ -851,14 +856,19 @@ impl BytesMut { unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } } - unsafe fn set_start(&mut self, start: usize) { + /// Advance the buffer without bounds checking. + /// + /// # SAFETY + /// + /// The caller must ensure that `count` <= `self.cap`. + unsafe fn advance_unchecked(&mut self, count: usize) { // Setting the start to 0 is a no-op, so return early if this is the // case. - if start == 0 { + if count == 0 { return; } - debug_assert!(start <= self.cap, "internal: set_start out of bounds"); + debug_assert!(count <= self.cap, "internal: set_start out of bounds"); let kind = self.kind(); @@ -867,7 +877,7 @@ impl BytesMut { // complicated. First, we have to track how far ahead the // "start" of the byte buffer from the beginning of the vec. We // also have to ensure that we don't exceed the maximum shift. - let pos = self.get_vec_pos() + start; + let pos = self.get_vec_pos() + count; if pos <= MAX_VEC_POS { self.set_vec_pos(pos); @@ -883,23 +893,9 @@ 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().add(start)); - - if self.len >= start { - self.len -= start; - } else { - self.len = 0; - } - - self.cap -= start; - } - - unsafe fn set_end(&mut self, end: usize) { - debug_assert_eq!(self.kind(), KIND_ARC); - assert!(end <= self.cap, "set_end out of bounds"); - - self.cap = end; - self.len = cmp::min(self.len, end); + self.ptr = vptr(self.ptr.as_ptr().add(count)); + self.len = self.len.checked_sub(count).unwrap_or(0); + self.cap -= count; } fn try_unsplit(&mut self, other: BytesMut) -> Result<(), BytesMut> { @@ -1069,7 +1065,9 @@ impl Buf for BytesMut { self.remaining(), ); unsafe { - self.set_start(cnt); + // SAFETY: We've checked that `cnt` <= `self.remaining()` and we know that + // `self.remaining()` <= `self.cap`. + self.advance_unchecked(cnt); } } From 99584cc10d66cb6880a20c5ac9b9a960f9c17823 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sat, 2 Mar 2024 10:40:17 -0500 Subject: [PATCH 131/203] Use Iterator from the prelude (#673) CI is [failing][failure] due to unused_imports because Iterator is already in the prelude. Removing it fixes things up. [failure]: https://github.com/tokio-rs/bytes/actions/runs/8034858583/job/21946873895 --- src/bytes_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 220bdb005..734f4df92 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1,4 +1,4 @@ -use core::iter::{FromIterator, Iterator}; +use core::iter::FromIterator; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; From c5fae00c76dbd1af7ea7b6cde7a9281d82ee7cd2 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sun, 3 Mar 2024 08:59:30 -0500 Subject: [PATCH 132/203] copy_to_bytes: Add panic section to docs (#676) Fixes #454. --- src/buf/buf_impl.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 9367eb2df..38ecf4bd9 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -1120,6 +1120,10 @@ pub trait Buf { /// let bytes = (&b"hello world"[..]).copy_to_bytes(5); /// assert_eq!(&bytes[..], &b"hello"[..]); /// ``` + /// + /// # Panics + /// + /// This function panics if `len > self.remaining()`. fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { use super::BufMut; From 7968f6f83d17175683e04ce56aa48e44ed7d0d98 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 4 Mar 2024 03:04:40 -0500 Subject: [PATCH 133/203] Remove redundant reserve call (#674) --- src/bytes_mut.rs | 2 -- tests/test_bytes.rs | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 734f4df92..1b4a4d9ae 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1283,9 +1283,7 @@ impl Extend for BytesMut { // TODO: optimize // 1. If self.kind() == KIND_VEC, use Vec::extend - // 2. Make `reserve` inline-able for b in iter { - self.reserve(1); self.put_u8(b); } } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 76adfdbf4..e3820d76e 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -598,6 +598,28 @@ fn extend_mut_from_bytes() { assert_eq!(*bytes, LONG[..]); } +#[test] +fn extend_past_lower_limit_of_size_hint() { + // See https://github.com/tokio-rs/bytes/pull/674#pullrequestreview-1913035700 + struct Iter(I); + + impl> Iterator for Iter { + type Item = u8; + + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + (5, None) + } + } + + let mut bytes = BytesMut::with_capacity(5); + bytes.extend(Iter(std::iter::repeat(0).take(10))); + assert_eq!(bytes.len(), 10); +} + #[test] fn extend_mut_without_size_hint() { let mut bytes = BytesMut::with_capacity(0); From ca004117f86afccd36148dee7c8413cfaf9de6a4 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 4 Mar 2024 03:05:00 -0500 Subject: [PATCH 134/203] Remove commented tests for Bytes::unsplit (#677) Bytes doesn't have an unsplit method anymore. We can always retrieve these from git history if necessary. --- tests/test_bytes.rs | 91 --------------------------------------------- 1 file changed, 91 deletions(-) diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index e3820d76e..84c3d5a43 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -732,97 +732,6 @@ fn partial_eq_bytesmut() { assert!(bytesmut != bytes2); } -/* -#[test] -fn bytes_unsplit_basic() { - let buf = Bytes::from(&b"aaabbbcccddd"[..]); - - let splitted = buf.split_off(6); - assert_eq!(b"aaabbb", &buf[..]); - assert_eq!(b"cccddd", &splitted[..]); - - buf.unsplit(splitted); - assert_eq!(b"aaabbbcccddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_empty_other() { - let buf = Bytes::from(&b"aaabbbcccddd"[..]); - - // empty other - let other = Bytes::new(); - - buf.unsplit(other); - assert_eq!(b"aaabbbcccddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_empty_self() { - // empty self - let mut buf = Bytes::new(); - - let mut other = Bytes::with_capacity(64); - other.extend_from_slice(b"aaabbbcccddd"); - - buf.unsplit(other); - assert_eq!(b"aaabbbcccddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_arc_different() { - let mut buf = Bytes::with_capacity(64); - buf.extend_from_slice(b"aaaabbbbeeee"); - - buf.split_off(8); //arc - - let mut buf2 = Bytes::with_capacity(64); - buf2.extend_from_slice(b"ccccddddeeee"); - - buf2.split_off(8); //arc - - buf.unsplit(buf2); - assert_eq!(b"aaaabbbbccccdddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_arc_non_contiguous() { - let mut buf = Bytes::with_capacity(64); - buf.extend_from_slice(b"aaaabbbbeeeeccccdddd"); - - let mut buf2 = buf.split_off(8); //arc - - let buf3 = buf2.split_off(4); //arc - - buf.unsplit(buf3); - assert_eq!(b"aaaabbbbccccdddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_two_split_offs() { - let mut buf = Bytes::with_capacity(64); - buf.extend_from_slice(b"aaaabbbbccccdddd"); - - let mut buf2 = buf.split_off(8); //arc - let buf3 = buf2.split_off(4); //arc - - buf2.unsplit(buf3); - buf.unsplit(buf2); - assert_eq!(b"aaaabbbbccccdddd", &buf[..]); -} - -#[test] -fn bytes_unsplit_overlapping_references() { - let mut buf = Bytes::with_capacity(64); - buf.extend_from_slice(b"abcdefghijklmnopqrstuvwxyz"); - let mut buf0010 = buf.slice(0..10); - let buf1020 = buf.slice(10..20); - let buf0515 = buf.slice(5..15); - buf0010.unsplit(buf1020); - assert_eq!(b"abcdefghijklmnopqrst", &buf0010[..]); - assert_eq!(b"fghijklmno", &buf0515[..]); -} -*/ - #[test] fn bytes_mut_unsplit_basic() { let mut buf = BytesMut::with_capacity(64); From 536db06f168bdef967afbeac0561bf774e9a1315 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Thu, 14 Mar 2024 09:40:03 -0400 Subject: [PATCH 135/203] Use ManuallyDrop instead of mem::forget (#675) --- src/bytes_mut.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 1b4a4d9ae..282aaa710 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -244,23 +244,22 @@ impl BytesMut { /// ``` #[inline] pub fn freeze(self) -> Bytes { - if self.kind() == KIND_VEC { + let bytes = ManuallyDrop::new(self); + if bytes.kind() == KIND_VEC { // Just re-use `Bytes` internal Vec vtable unsafe { - let off = self.get_vec_pos(); - let vec = rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off); - mem::forget(self); + let off = bytes.get_vec_pos(); + let vec = rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off); let mut b: Bytes = vec.into(); b.advance(off); b } } else { - debug_assert_eq!(self.kind(), KIND_ARC); + debug_assert_eq!(bytes.kind(), KIND_ARC); - let ptr = self.ptr.as_ptr(); - let len = self.len; - let data = AtomicPtr::new(self.data.cast()); - mem::forget(self); + let ptr = bytes.ptr.as_ptr(); + let len = bytes.len; + let data = AtomicPtr::new(bytes.data.cast()); unsafe { Bytes::with_vtable(ptr, len, data, &SHARED_VTABLE) } } } @@ -829,11 +828,11 @@ impl BytesMut { // internal change could make a simple pattern (`BytesMut::from(vec)`) // suddenly a lot more expensive. #[inline] - pub(crate) fn from_vec(mut vec: Vec) -> BytesMut { + pub(crate) fn from_vec(vec: Vec) -> BytesMut { + let mut vec = ManuallyDrop::new(vec); let ptr = vptr(vec.as_mut_ptr()); let len = vec.len(); let cap = vec.capacity(); - mem::forget(vec); let original_capacity_repr = original_capacity_to_repr(cap); let data = (original_capacity_repr << ORIGINAL_CAPACITY_OFFSET) | KIND_VEC; @@ -1616,6 +1615,7 @@ impl PartialEq for BytesMut { impl From for Vec { fn from(bytes: BytesMut) -> Self { let kind = bytes.kind(); + let bytes = ManuallyDrop::new(bytes); let mut vec = if kind == KIND_VEC { unsafe { @@ -1632,7 +1632,7 @@ impl From for Vec { vec } else { - return bytes.deref().to_vec(); + return ManuallyDrop::into_inner(bytes).deref().to_vec(); } }; @@ -1643,8 +1643,6 @@ impl From for Vec { vec.set_len(len); } - mem::forget(bytes); - vec } } From ce8d8a0a029c0d296ade752ecc8c3e1ce9eee47f Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Fri, 22 Mar 2024 15:55:20 -0400 Subject: [PATCH 136/203] chore: prepare bytes v1.6.0 (#681) --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b9f673a..23357174b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +# 1.6.0 (March 22, 2024) + +### Added + +- Add `Bytes::is_unique` (#643) + +### Documented + +- Fix changelog typo (#628) +- Fix some spelling mistakes (#633) +- Typo fix (#637) +- Fix broken links (#639) +- Add security policy (#649) + +### Internal changes + +- Move comment to correct constant (#629) +- Various cleanup (#635) +- Simplify `UninitSlice::as_uninit_slice_mut()` logic (#644) +- Use `self.` instead of `Self::` (#642) +- `BytesMut`: Assert alignment of `Shared` (#652) +- Remove unnecessary namespace qualifier (#660) +- Remove an unnecessary else branch (#662) +- Remove unreachable else branch (#661) +- make parameter mut in `From` (#667) +- Restore commented tests (#665) +- Use `sub` instead of `offset` (#668) +- Calculate original capacity only if necessary (#666) +- `set_vec_pos` does not need a second parameter (#672) +- `get_vec_pos`: use `&self` instead of `&mut self` (#670) +- Refactor `split_at`/`split_to` (#663) +- Use `Iterator` from the prelude (#673) +- `copy_to_bytes`: Add panic section to docs (#676) +- Remove redundant reserve call (#674) +- Use `ManuallyDrop` instead of `mem::forget` (#675) + + # 1.5.0 (September 7, 2023) ### Added diff --git a/Cargo.toml b/Cargo.toml index 127d81dd5..793582af1 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.5.0" +version = "1.6.0" edition = "2018" rust-version = "1.39" license = "MIT" From 0d4cc7ffed2eadfb2028bade65b9ac0b6d231fc4 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 8 Apr 2024 11:05:04 -0400 Subject: [PATCH 137/203] Bytes: Use ManuallyDrop instead of mem::forget (#678) --- src/bytes.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 0b443c85b..4a0a94fa1 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,6 +1,7 @@ use core::iter::FromIterator; +use core::mem::{self, ManuallyDrop}; use core::ops::{Deref, RangeBounds}; -use core::{cmp, fmt, hash, mem, ptr, slice, usize}; +use core::{cmp, fmt, hash, ptr, slice, usize}; use alloc::{ alloc::{dealloc, Layout}, @@ -828,13 +829,15 @@ impl From<&'static str> for Bytes { } impl From> for Bytes { - fn from(mut vec: Vec) -> Bytes { + fn from(vec: Vec) -> Bytes { + let mut vec = ManuallyDrop::new(vec); let ptr = vec.as_mut_ptr(); let len = vec.len(); let cap = vec.capacity(); // Avoid an extra allocation if possible. if len == cap { + let vec = ManuallyDrop::into_inner(vec); return Bytes::from(vec.into_boxed_slice()); } @@ -843,7 +846,6 @@ impl From> for Bytes { cap, ref_cnt: AtomicUsize::new(1), }); - mem::forget(vec); let shared = Box::into_raw(shared); // The pointer should be aligned, so this assert should @@ -900,7 +902,7 @@ impl From for Bytes { impl From for Vec { fn from(bytes: Bytes) -> Vec { - let bytes = mem::ManuallyDrop::new(bytes); + let bytes = ManuallyDrop::new(bytes); unsafe { (bytes.vtable.to_vec)(&bytes.data, bytes.ptr, bytes.len) } } } @@ -1116,11 +1118,11 @@ unsafe fn shared_to_vec_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> .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)); + // Deallocate the `Shared` instance without running its destructor. + let shared = *Box::from_raw(shared); + let shared = ManuallyDrop::new(shared); + let buf = shared.buf; + let cap = shared.cap; // Copy back buffer ptr::copy(ptr, buf, len); From e4af48633cec419e8274571d353fe166d5e23a3e Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 9 Apr 2024 08:35:54 -0400 Subject: [PATCH 138/203] Don't set `len` in `BytesMut::reserve` (#682) A fundamental invariant of `reserve` is that it can extend capacity while the stored data remains the same, even if it's moved to a new allocation. As a result, `len` can never change during a call to `reserve`. --- src/bytes_mut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 282aaa710..c9f563430 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -639,8 +639,8 @@ impl BytesMut { // Update the info self.ptr = vptr(v.as_mut_ptr().add(off)); - self.len = v.len() - off; self.cap = v.capacity() - off; + debug_assert_eq!(self.len, v.len() - off); } return; @@ -746,8 +746,8 @@ impl BytesMut { let data = (original_capacity_repr << ORIGINAL_CAPACITY_OFFSET) | KIND_VEC; self.data = invalid_ptr(data); self.ptr = vptr(v.as_mut_ptr()); - self.len = v.len(); self.cap = v.capacity(); + debug_assert_eq!(self.len, v.len()); } /// Appends given bytes to this `BytesMut`. From 4eb62b912a199bef711e7e12243d972f4f0cdca8 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 10 Apr 2024 04:09:09 -0400 Subject: [PATCH 139/203] Bytes::split_to - check fast path first (#689) If `at == self.len()` then we already know `at <= self.len()`. If `at == 0`, it can't be greater than `self.len()`. --- src/bytes.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 4a0a94fa1..63c06cee7 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -434,13 +434,6 @@ 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) -> Self { - assert!( - at <= self.len(), - "split_to out of bounds: {:?} <= {:?}", - at, - self.len(), - ); - if at == self.len() { return mem::replace(self, Bytes::new()); } @@ -449,6 +442,13 @@ impl Bytes { return Bytes::new(); } + assert!( + at <= self.len(), + "split_to out of bounds: {:?} <= {:?}", + at, + self.len(), + ); + let mut ret = self.clone(); unsafe { self.inc_start(at) }; From b5fbfc3edb35a03ca560d29a0911e0495299575e Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 10 Apr 2024 22:45:31 +0800 Subject: [PATCH 140/203] perf: improve Bytes::copy_to_bytes (#688) Signed-off-by: tison --- src/bytes.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 63c06cee7..c3240ce09 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -582,13 +582,7 @@ impl Buf for Bytes { } fn copy_to_bytes(&mut self, len: usize) -> Self { - if len == self.remaining() { - core::mem::replace(self, Bytes::new()) - } else { - let ret = self.slice(..len); - self.advance(len); - ret - } + self.split_to(len) } } From 327615e5d4ba27e9647734d83ef9ad88d7dd8a38 Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Thu, 11 Apr 2024 11:45:18 +0200 Subject: [PATCH 141/203] test(benches): encloses bytes into `test::black_box` for clone benches (#691) Closes #690 Without it, it seems to me that compiler is able to inline the vtable, resulting in similar results for `clone_shared` and `clone_arg_vec`. --- benches/bytes.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benches/bytes.rs b/benches/bytes.rs index 61d1e832a..8782d0066 100644 --- a/benches/bytes.rs +++ b/benches/bytes.rs @@ -47,7 +47,7 @@ fn clone_static(b: &mut Bencher) { b.iter(|| { for _ in 0..1024 { - test::black_box(&bytes.clone()); + test::black_box(test::black_box(&bytes).clone()); } }) } @@ -58,7 +58,7 @@ fn clone_shared(b: &mut Bencher) { b.iter(|| { for _ in 0..1024 { - test::black_box(&bytes.clone()); + test::black_box(test::black_box(&bytes).clone()); } }) } @@ -70,7 +70,7 @@ fn clone_arc_vec(b: &mut Bencher) { b.iter(|| { for _ in 0..1024 { - test::black_box(&bytes.clone()); + test::black_box(test::black_box(&bytes).clone()); } }) } From 4e2c9c065a06bf9cb5d7dd46e3b29f62a1c20057 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 17 Apr 2024 05:27:00 -0400 Subject: [PATCH 142/203] Truncate tweaks (#694) --- src/bytes_mut.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index c9f563430..0248df856 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -422,8 +422,9 @@ impl BytesMut { /// assert_eq!(buf, b"hello"[..]); /// ``` pub fn truncate(&mut self, len: usize) { - if len <= self.len() { + if len < self.len() { unsafe { + // SAFETY: Shrinking the buffer cannot expose uninitialized bytes. self.set_len(len); } } From 9d3ec1cffb76141b4706bb289beced8b04ecac4a Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 24 Apr 2024 05:49:53 -0400 Subject: [PATCH 143/203] Resize refactor (#696) * use checked_sub * return when additional == 0 * move safe operation out of unsafe block * use spare_capacity_mut instead of chunk_mut We don't need to check capacity because it's already been reserved above. * Add safety comments * refactor to use guard clauses This would be better written with let-else, but we won't get that until `MSRV >= 1.65.x`. * use if-let instead of unwrap * reduce scope of unsafe blocks Co-authored-by: Alice Ryhl --------- Co-authored-by: Alice Ryhl --- src/bytes_mut.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 0248df856..0ea0272c5 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -468,18 +468,26 @@ impl BytesMut { /// assert_eq!(&buf[..], &[0x1, 0x1, 0x3, 0x3]); /// ``` pub fn resize(&mut self, new_len: usize, value: u8) { - let len = self.len(); - if new_len > len { - let additional = new_len - len; - self.reserve(additional); - unsafe { - let dst = self.chunk_mut().as_mut_ptr(); - ptr::write_bytes(dst, value, additional); - self.set_len(new_len); - } + let additional = if let Some(additional) = new_len.checked_sub(self.len()) { + additional } else { self.truncate(new_len); + return; + }; + + if additional == 0 { + return; } + + self.reserve(additional); + let dst = self.spare_capacity_mut().as_mut_ptr(); + // SAFETY: `spare_capacity_mut` returns a valid, properly aligned pointer and we've + // reserved enough space to write `additional` bytes. + unsafe { ptr::write_bytes(dst, value, additional) }; + + // SAFETY: There are at least `new_len` initialized bytes in the buffer so no + // uninitialized bytes are being exposed. + unsafe { self.set_len(new_len) }; } /// Sets the length of the buffer. From ce09d7d358ab1d1d31ed9d0b52a747c0a21ea401 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 24 Apr 2024 08:23:39 -0400 Subject: [PATCH 144/203] Bytes::split_off - check fast path first (#693) Follow up to https://github.com/tokio-rs/bytes/pull/689 * If `at == self.len()`, we already know `at <= self.len()`. * If `at == 0`, we already know `at <= self.len()`. --- src/bytes.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index c3240ce09..908cee9ad 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -385,13 +385,6 @@ 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) -> Self { - assert!( - at <= self.len(), - "split_off out of bounds: {:?} <= {:?}", - at, - self.len(), - ); - if at == self.len() { return Bytes::new(); } @@ -400,6 +393,13 @@ impl Bytes { return mem::replace(self, Bytes::new()); } + assert!( + at <= self.len(), + "split_off out of bounds: {:?} <= {:?}", + at, + self.len(), + ); + let mut ret = self.clone(); self.len = at; From baa5053572ed9e88ca1058ec2b5a3f08046c5a40 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Thu, 25 Apr 2024 09:08:16 +0200 Subject: [PATCH 145/203] Reuse capacity when possible in ::advance impl (#698) --- src/bytes_mut.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 0ea0272c5..35e19005a 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1066,6 +1066,14 @@ impl Buf for BytesMut { #[inline] fn advance(&mut self, cnt: usize) { + // Advancing by the length is the same as resetting the length to 0, + // except this way we get to reuse the full capacity. + if cnt == self.remaining() { + // SAFETY: Zero is not greater than the capacity. + unsafe { self.set_len(0) }; + return; + } + assert!( cnt <= self.remaining(), "cannot advance past `remaining`: {:?} <= {:?}", From a8806c245700e583134e67b7e0b87f1256b95bfa Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Thu, 25 Apr 2024 10:43:15 +0200 Subject: [PATCH 146/203] Improve BytesMut::split suggestion (#699) --- src/bytes_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 35e19005a..75762996a 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -349,7 +349,7 @@ impl BytesMut { /// /// assert_eq!(other, b"hello world"[..]); /// ``` - #[must_use = "consider BytesMut::advance(len()) if you don't need the other half"] + #[must_use = "consider BytesMut::clear if you don't need the other half"] pub fn split(&mut self) -> BytesMut { let len = self.len(); self.split_to(len) From cb7f8449b5efc7022dc592b3a1d7dd33079f4c8f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 26 Apr 2024 09:24:05 +0200 Subject: [PATCH 147/203] Tweak clear and truncate length modifications (#700) --- src/bytes_mut.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 75762996a..b01bb1adc 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -422,11 +422,9 @@ impl BytesMut { /// assert_eq!(buf, b"hello"[..]); /// ``` pub fn truncate(&mut self, len: usize) { - if len < self.len() { - unsafe { - // SAFETY: Shrinking the buffer cannot expose uninitialized bytes. - self.set_len(len); - } + if len <= self.len() { + // SAFETY: Shrinking the buffer cannot expose uninitialized bytes. + unsafe { self.set_len(len) }; } } @@ -442,7 +440,8 @@ impl BytesMut { /// assert!(buf.is_empty()); /// ``` pub fn clear(&mut self) { - self.truncate(0); + // SAFETY: Setting the length to zero cannot expose uninitialized bytes. + unsafe { self.set_len(0) }; } /// Resizes the buffer so that `len` is equal to `new_len`. @@ -1069,8 +1068,7 @@ impl Buf for BytesMut { // Advancing by the length is the same as resetting the length to 0, // except this way we get to reuse the full capacity. if cnt == self.remaining() { - // SAFETY: Zero is not greater than the capacity. - unsafe { self.set_len(0) }; + self.clear(); return; } From 0c17e99283185b94ab68cdf0fb62da53cbd765ee Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sun, 5 May 2024 14:19:18 +0200 Subject: [PATCH 148/203] ci: silence unexpected-cfgs warnings due to `#[cfg(loom)]` (#703) --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 1b3e6fc40..4dd118007 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(unknown_lints, unexpected_cfgs)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![doc(test( no_crate_inject, From 86694b05649c0c1666044b2ba5c386c2328aac18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Fugulin?= Date: Sun, 5 May 2024 11:58:00 -0400 Subject: [PATCH 149/203] Add zero-copy make_mut (#695) --- src/bytes.rs | 150 +++++++++++++++++++++++++++++++++- src/bytes_mut.rs | 32 +++++++- tests/test_bytes.rs | 111 +++++++++++++++++++++++++ tests/test_bytes_odd_alloc.rs | 50 ++++++++++++ 4 files changed, 341 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 908cee9ad..b4359b08d 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -15,7 +15,7 @@ use crate::buf::IntoIter; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::Buf; +use crate::{Buf, BytesMut}; /// A cheaply cloneable and sliceable chunk of contiguous memory. /// @@ -113,6 +113,7 @@ pub(crate) struct Vtable { /// /// takes `Bytes` to value pub to_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec, + pub to_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> BytesMut, /// fn(data) pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool, /// fn(data, ptr, len) @@ -507,6 +508,49 @@ impl Bytes { self.truncate(0); } + /// Try to convert self into `BytesMut`. + /// + /// If `self` is unique for the entire original buffer, this will succeed + /// and return a `BytesMut` with the contents of `self` without copying. + /// If `self` is not unique for the entire original buffer, this will fail + /// and return self. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Bytes, BytesMut}; + /// + /// let bytes = Bytes::from(b"hello".to_vec()); + /// assert_eq!(bytes.try_into_mut(), Ok(BytesMut::from(&b"hello"[..]))); + /// ``` + pub fn try_into_mut(self) -> Result { + if self.is_unique() { + Ok(self.make_mut()) + } else { + Err(self) + } + } + + /// Convert self into `BytesMut`. + /// + /// If `self` is unique for the entire original buffer, this will return a + /// `BytesMut` with the contents of `self` without copying. + /// If `self` is not unique for the entire original buffer, this will make + /// a copy of `self` subset of the original buffer in a new `BytesMut`. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Bytes, BytesMut}; + /// + /// let bytes = Bytes::from(b"hello".to_vec()); + /// assert_eq!(bytes.make_mut(), BytesMut::from(&b"hello"[..])); + /// ``` + pub fn make_mut(self) -> BytesMut { + let bytes = ManuallyDrop::new(self); + unsafe { (bytes.vtable.to_mut)(&bytes.data, bytes.ptr, bytes.len) } + } + #[inline] pub(crate) unsafe fn with_vtable( ptr: *const u8, @@ -917,6 +961,7 @@ impl fmt::Debug for Vtable { const STATIC_VTABLE: Vtable = Vtable { clone: static_clone, to_vec: static_to_vec, + to_mut: static_to_mut, is_unique: static_is_unique, drop: static_drop, }; @@ -931,6 +976,11 @@ unsafe fn static_to_vec(_: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec, ptr: *const u8, len: usize) -> BytesMut { + let slice = slice::from_raw_parts(ptr, len); + BytesMut::from(slice) +} + fn static_is_unique(_: &AtomicPtr<()>) -> bool { false } @@ -944,6 +994,7 @@ 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, + to_mut: promotable_even_to_mut, is_unique: promotable_is_unique, drop: promotable_even_drop, }; @@ -951,6 +1002,7 @@ static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable { static PROMOTABLE_ODD_VTABLE: Vtable = Vtable { clone: promotable_odd_clone, to_vec: promotable_odd_to_vec, + to_mut: promotable_odd_to_mut, is_unique: promotable_is_unique, drop: promotable_odd_drop, }; @@ -994,12 +1046,47 @@ unsafe fn promotable_to_vec( } } +unsafe fn promotable_to_mut( + data: &AtomicPtr<()>, + ptr: *const u8, + len: usize, + f: fn(*mut ()) -> *mut u8, +) -> BytesMut { + let shared = data.load(Ordering::Acquire); + let kind = shared as usize & KIND_MASK; + + if kind == KIND_ARC { + shared_to_mut_impl(shared.cast(), ptr, len) + } else { + // KIND_VEC is a view of an underlying buffer at a certain offset. + // The ptr + len always represents the end of that buffer. + // Before truncating it, it is first promoted to KIND_ARC. + // Thus, we can safely reconstruct a Vec from it without leaking memory. + debug_assert_eq!(kind, KIND_VEC); + + let buf = f(shared); + let off = offset_from(ptr, buf); + let cap = off + len; + let v = Vec::from_raw_parts(buf, cap, cap); + + let mut b = BytesMut::from_vec(v); + b.advance_unchecked(off); + b + } +} + 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_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + promotable_to_mut(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; @@ -1031,6 +1118,10 @@ unsafe fn promotable_odd_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize promotable_to_vec(data, ptr, len, |shared| shared.cast()) } +unsafe fn promotable_odd_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + promotable_to_mut(data, ptr, len, |shared| shared.cast()) +} + unsafe fn promotable_odd_drop(data: &mut AtomicPtr<()>, ptr: *const u8, len: usize) { data.with_mut(|shared| { let shared = *shared; @@ -1087,6 +1178,7 @@ const _: [(); 0 - mem::align_of::() % 2] = []; // Assert that the alignm static SHARED_VTABLE: Vtable = Vtable { clone: shared_clone, to_vec: shared_to_vec, + to_mut: shared_to_mut, is_unique: shared_is_unique, drop: shared_drop, }; @@ -1133,6 +1225,45 @@ 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_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> BytesMut { + // The goal is to check if the current handle is the only handle + // that currently has access to the buffer. This is done by + // checking if the `ref_cnt` is currently 1. + // + // The `Acquire` ordering synchronizes with the `Release` as + // part of the `fetch_sub` in `release_shared`. The `fetch_sub` + // operation guarantees that any mutations done in other threads + // are ordered before the `ref_cnt` is decremented. As such, + // this `Acquire` will guarantee that those mutations are + // visible to the current thread. + // + // Otherwise, we take the other branch, copy the data and call `release_shared`. + if (*shared).ref_cnt.load(Ordering::Acquire) == 1 { + // Deallocate the `Shared` instance without running its destructor. + let shared = *Box::from_raw(shared); + let shared = ManuallyDrop::new(shared); + let buf = shared.buf; + let cap = shared.cap; + + // Rebuild Vec + let off = offset_from(ptr, buf); + let v = Vec::from_raw_parts(buf, len + off, cap); + + let mut b = BytesMut::from_vec(v); + b.advance_unchecked(off); + b + } else { + // Copy the data from Shared in a new Vec, then release it + let v = slice::from_raw_parts(ptr, len).to_vec(); + release_shared(shared); + BytesMut::from_vec(v) + } +} + +unsafe fn shared_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + shared_to_mut_impl(data.load(Ordering::Relaxed).cast(), ptr, len) +} + pub(crate) unsafe fn shared_is_unique(data: &AtomicPtr<()>) -> bool { let shared = data.load(Ordering::Acquire); let ref_cnt = (*shared.cast::()).ref_cnt.load(Ordering::Relaxed); @@ -1291,6 +1422,23 @@ where new_addr as *mut u8 } +/// 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 stabilized +/// in 1.47, we cannot use it. +#[inline] +fn offset_from(dst: *const u8, original: *const u8) -> usize { + debug_assert!(dst >= original); + + dst as usize - original as usize +} + // compile-fails /// ```compile_fail diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index b01bb1adc..569f8be63 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -868,7 +868,7 @@ impl BytesMut { /// # SAFETY /// /// The caller must ensure that `count` <= `self.cap`. - unsafe fn advance_unchecked(&mut self, count: usize) { + pub(crate) unsafe fn advance_unchecked(&mut self, count: usize) { // Setting the start to 0 is a no-op, so return early if this is the // case. if count == 0 { @@ -1713,6 +1713,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, + to_mut: shared_v_to_mut, is_unique: crate::bytes::shared_is_unique, drop: shared_v_drop, }; @@ -1747,6 +1748,35 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V } } +unsafe fn shared_v_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + let shared: *mut Shared = data.load(Ordering::Relaxed).cast(); + + if (*shared).is_unique() { + let shared = &mut *shared; + + // The capacity is always the original capacity of the buffer + // minus the offset from the start of the buffer + let v = &mut shared.vec; + let v_capacity = v.capacity(); + let v_ptr = v.as_mut_ptr(); + let offset = offset_from(ptr as *mut u8, v_ptr); + let cap = v_capacity - offset; + + let ptr = vptr(ptr as *mut u8); + + BytesMut { + ptr, + len, + cap, + data: shared, + } + } else { + let v = slice::from_raw_parts(ptr, len).to_vec(); + release_shared(shared); + BytesMut::from_vec(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 84c3d5a43..2f283af2f 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1172,3 +1172,114 @@ fn shared_is_unique() { drop(b); assert!(c.is_unique()); } + +#[test] +fn test_bytes_make_mut_static() { + let bs = b"1b23exfcz3r"; + + // Test STATIC_VTABLE.to_mut + let bytes_mut = Bytes::from_static(bs).make_mut(); + assert_eq!(bytes_mut, bs[..]); +} + +#[test] +fn test_bytes_make_mut_bytes_mut_vec() { + let bs = b"1b23exfcz3r"; + let bs_long = b"1b23exfcz3r1b23exfcz3r"; + + // Test case where kind == KIND_VEC + let mut bytes_mut: BytesMut = bs[..].into(); + bytes_mut = bytes_mut.freeze().make_mut(); + assert_eq!(bytes_mut, bs[..]); + bytes_mut.extend_from_slice(&bs[..]); + assert_eq!(bytes_mut, bs_long[..]); +} + +#[test] +fn test_bytes_make_mut_bytes_mut_shared() { + let bs = b"1b23exfcz3r"; + + // Set kind to KIND_ARC so that after freeze, Bytes will use bytes_mut.SHARED_VTABLE + let mut bytes_mut: BytesMut = bs[..].into(); + drop(bytes_mut.split_off(bs.len())); + + let b1 = bytes_mut.freeze(); + let b2 = b1.clone(); + + // shared.is_unique() = False + let mut b1m = b1.make_mut(); + assert_eq!(b1m, bs[..]); + b1m[0] = b'9'; + + // shared.is_unique() = True + let b2m = b2.make_mut(); + assert_eq!(b2m, bs[..]); +} + +#[test] +fn test_bytes_make_mut_bytes_mut_offset() { + let bs = b"1b23exfcz3r"; + + // Test bytes_mut.SHARED_VTABLE.to_mut 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(); + + let b1m = b1.make_mut(); + let b2m = b2.make_mut(); + + assert_eq!(b2m, bs[9..]); + assert_eq!(b1m, bs[..9]); +} + +#[test] +fn test_bytes_make_mut_promotable_even_vec() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_VEC + let b1 = Bytes::from(vec.clone()); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); +} + +#[test] +fn test_bytes_make_mut_promotable_even_arc_1() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_ARC, ref_cnt == 1 + let b1 = Bytes::from(vec.clone()); + drop(b1.clone()); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); +} + +#[test] +fn test_bytes_make_mut_promotable_even_arc_2() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_ARC, ref_cnt == 2 + let b1 = Bytes::from(vec.clone()); + let b2 = b1.clone(); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); + + // Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 + let b2m = b2.make_mut(); + assert_eq!(b2m, vec); +} + +#[test] +fn test_bytes_make_mut_promotable_even_arc_offset() { + let vec = vec![33u8; 1024]; + + // Test case where offset != 0 + let mut b1 = Bytes::from(vec.clone()); + let b2 = b1.split_off(20); + let b1m = b1.make_mut(); + let b2m = b2.make_mut(); + + assert_eq!(b2m, vec[20..]); + assert_eq!(b1m, vec[..20]); +} diff --git a/tests/test_bytes_odd_alloc.rs b/tests/test_bytes_odd_alloc.rs index 27ed87736..8008a0e47 100644 --- a/tests/test_bytes_odd_alloc.rs +++ b/tests/test_bytes_odd_alloc.rs @@ -95,3 +95,53 @@ fn test_bytes_into_vec() { assert_eq!(Vec::from(b2), vec[20..]); assert_eq!(Vec::from(b1), vec[..20]); } + +#[test] +fn test_bytes_make_mut_vec() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_VEC + let b1 = Bytes::from(vec.clone()); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); +} + +#[test] +fn test_bytes_make_mut_arc_1() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_ARC, ref_cnt == 1 + let b1 = Bytes::from(vec.clone()); + drop(b1.clone()); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); +} + +#[test] +fn test_bytes_make_mut_arc_2() { + let vec = vec![33u8; 1024]; + + // Test case where kind == KIND_ARC, ref_cnt == 2 + let b1 = Bytes::from(vec.clone()); + let b2 = b1.clone(); + let b1m = b1.make_mut(); + assert_eq!(b1m, vec); + + // Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 + let b2m = b2.make_mut(); + assert_eq!(b2m, vec); +} + +#[test] +fn test_bytes_make_mut_arc_offset() { + let vec = vec![33u8; 1024]; + + // Test case where offset != 0 + let mut b1 = Bytes::from(vec.clone()); + let b2 = b1.split_off(20); + let b1m = b1.make_mut(); + let b2m = b2.make_mut(); + + assert_eq!(b2m, vec[20..]); + assert_eq!(b1m, vec[..20]); +} From 4950c503768fcebce6f9ab9dbaac2a7da30b35ba Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sat, 11 May 2024 13:41:50 -0400 Subject: [PATCH 150/203] Offset from (#705) --- src/bytes.rs | 25 ++++--------------------- src/bytes_mut.rs | 19 +------------------ src/lib.rs | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index b4359b08d..e23d9a81f 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -15,7 +15,7 @@ use crate::buf::IntoIter; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::{Buf, BytesMut}; +use crate::{offset_from, Buf, BytesMut}; /// A cheaply cloneable and sliceable chunk of contiguous memory. /// @@ -1037,7 +1037,7 @@ unsafe fn promotable_to_vec( let buf = f(shared); - let cap = (ptr as usize - buf as usize) + len; + let cap = offset_from(ptr, buf) + len; // Copy back buffer ptr::copy(ptr, buf, len); @@ -1150,7 +1150,7 @@ unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool { } unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) { - let cap = (offset as usize - buf as usize) + len; + let cap = offset_from(offset, buf) + len; dealloc(buf, Layout::from_size_align(cap, 1).unwrap()) } @@ -1312,7 +1312,7 @@ unsafe fn shallow_clone_vec( // vector. let shared = Box::new(Shared { buf, - cap: (offset as usize - buf as usize) + len, + cap: offset_from(offset, buf) + len, // Initialize refcount to 2. One for this reference, and one // for the new clone that will be returned from // `shallow_clone`. @@ -1422,23 +1422,6 @@ where new_addr as *mut u8 } -/// 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 stabilized -/// in 1.47, we cannot use it. -#[inline] -fn offset_from(dst: *const u8, original: *const u8) -> usize { - debug_assert!(dst >= original); - - dst as usize - original as usize -} - // compile-fails /// ```compile_fail diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 569f8be63..537f01ad3 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -17,7 +17,7 @@ use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::{Buf, BufMut, Bytes}; +use crate::{offset_from, Buf, BufMut, Bytes}; /// A unique reference to a contiguous slice of memory. /// @@ -1683,23 +1683,6 @@ 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 stabilized -/// 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.sub(off); len += off; diff --git a/src/lib.rs b/src/lib.rs index 4dd118007..7ddd2205b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,3 +148,18 @@ fn panic_does_not_fit(size: usize, nbytes: usize) -> ! { size, nbytes ); } + +/// 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 stabilized +/// in 1.47, we cannot use it. +#[inline] +fn offset_from(dst: *const u8, original: *const u8) -> usize { + dst as usize - original as usize +} From caf520ac7f2c466d26bd88eca33ddc53c408e17e Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 19 May 2024 21:28:03 +0200 Subject: [PATCH 151/203] Fix iter tests to use the actual bytes IntoIter instead of std (#707) --- tests/test_iter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_iter.rs b/tests/test_iter.rs index a5bfddddf..bad901860 100644 --- a/tests/test_iter.rs +++ b/tests/test_iter.rs @@ -1,11 +1,11 @@ #![warn(rust_2018_idioms)] -use bytes::Bytes; +use bytes::{buf::IntoIter, Bytes}; #[test] fn iter_len() { let buf = Bytes::from_static(b"hello world"); - let iter = buf.iter(); + let iter = IntoIter::new(buf); assert_eq!(iter.size_hint(), (11, Some(11))); assert_eq!(iter.len(), 11); @@ -13,8 +13,8 @@ fn iter_len() { #[test] fn empty_iter_len() { - let buf = Bytes::from_static(b""); - let iter = buf.iter(); + let buf = Bytes::new(); + let iter = IntoIter::new(buf); assert_eq!(iter.size_hint(), (0, Some(0))); assert_eq!(iter.len(), 0); From fa1daac3ae1dcb07dffe3a41a041dffd6edf177b Mon Sep 17 00:00:00 2001 From: Anthony Ramine <123095+nox@users.noreply.github.com> Date: Tue, 28 May 2024 10:14:02 +0200 Subject: [PATCH 152/203] Change Bytes::make_mut to impl From for BytesMut (closes #709) (#710) >::make_mut returns a &mut T, such an API is doable for Bytes too and thus we should reserve Bytes::make_mut for that. Furthermore, it would be helpful to use From as a trait bound in some cases with other traits such as Hyper's body trait, where Hyper gives you Bytes values. Finally, making it impl From for BytesMut means the API is more easily discoverable as it appears on both Bytes and BytesMut. --- src/bytes.rs | 44 ++++++++++++++++++----------------- src/bytes/promotable.rs | 0 tests/test_bytes.rs | 40 +++++++++++++++---------------- tests/test_bytes_odd_alloc.rs | 22 +++++++++--------- 4 files changed, 54 insertions(+), 52 deletions(-) create mode 100644 src/bytes/promotable.rs diff --git a/src/bytes.rs b/src/bytes.rs index e23d9a81f..e0c33b3e6 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -525,32 +525,12 @@ impl Bytes { /// ``` pub fn try_into_mut(self) -> Result { if self.is_unique() { - Ok(self.make_mut()) + Ok(self.into()) } else { Err(self) } } - /// Convert self into `BytesMut`. - /// - /// If `self` is unique for the entire original buffer, this will return a - /// `BytesMut` with the contents of `self` without copying. - /// If `self` is not unique for the entire original buffer, this will make - /// a copy of `self` subset of the original buffer in a new `BytesMut`. - /// - /// # Examples - /// - /// ``` - /// use bytes::{Bytes, BytesMut}; - /// - /// let bytes = Bytes::from(b"hello".to_vec()); - /// assert_eq!(bytes.make_mut(), BytesMut::from(&b"hello"[..])); - /// ``` - pub fn make_mut(self) -> BytesMut { - let bytes = ManuallyDrop::new(self); - unsafe { (bytes.vtable.to_mut)(&bytes.data, bytes.ptr, bytes.len) } - } - #[inline] pub(crate) unsafe fn with_vtable( ptr: *const u8, @@ -932,6 +912,28 @@ impl From> for Bytes { } } +impl From for BytesMut { + /// Convert self into `BytesMut`. + /// + /// If `bytes` is unique for the entire original buffer, this will return a + /// `BytesMut` with the contents of `bytes` without copying. + /// If `bytes` is not unique for the entire original buffer, this will make + /// a copy of `bytes` subset of the original buffer in a new `BytesMut`. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Bytes, BytesMut}; + /// + /// let bytes = Bytes::from(b"hello".to_vec()); + /// assert_eq!(BytesMut::from(bytes), BytesMut::from(&b"hello"[..])); + /// ``` + fn from(bytes: Bytes) -> Self { + let bytes = ManuallyDrop::new(bytes); + unsafe { (bytes.vtable.to_mut)(&bytes.data, bytes.ptr, bytes.len) } + } +} + impl From for Bytes { fn from(s: String) -> Bytes { Bytes::from(s.into_bytes()) diff --git a/src/bytes/promotable.rs b/src/bytes/promotable.rs new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 2f283af2f..3ac429832 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1174,29 +1174,29 @@ fn shared_is_unique() { } #[test] -fn test_bytes_make_mut_static() { +fn test_bytesmut_from_bytes_static() { let bs = b"1b23exfcz3r"; // Test STATIC_VTABLE.to_mut - let bytes_mut = Bytes::from_static(bs).make_mut(); + let bytes_mut = BytesMut::from(Bytes::from_static(bs)); assert_eq!(bytes_mut, bs[..]); } #[test] -fn test_bytes_make_mut_bytes_mut_vec() { +fn test_bytesmut_from_bytes_bytes_mut_vec() { let bs = b"1b23exfcz3r"; let bs_long = b"1b23exfcz3r1b23exfcz3r"; // Test case where kind == KIND_VEC let mut bytes_mut: BytesMut = bs[..].into(); - bytes_mut = bytes_mut.freeze().make_mut(); + bytes_mut = BytesMut::from(bytes_mut.freeze()); assert_eq!(bytes_mut, bs[..]); bytes_mut.extend_from_slice(&bs[..]); assert_eq!(bytes_mut, bs_long[..]); } #[test] -fn test_bytes_make_mut_bytes_mut_shared() { +fn test_bytesmut_from_bytes_bytes_mut_shared() { let bs = b"1b23exfcz3r"; // Set kind to KIND_ARC so that after freeze, Bytes will use bytes_mut.SHARED_VTABLE @@ -1207,17 +1207,17 @@ fn test_bytes_make_mut_bytes_mut_shared() { let b2 = b1.clone(); // shared.is_unique() = False - let mut b1m = b1.make_mut(); + let mut b1m = BytesMut::from(b1); assert_eq!(b1m, bs[..]); b1m[0] = b'9'; // shared.is_unique() = True - let b2m = b2.make_mut(); + let b2m = BytesMut::from(b2); assert_eq!(b2m, bs[..]); } #[test] -fn test_bytes_make_mut_bytes_mut_offset() { +fn test_bytesmut_from_bytes_bytes_mut_offset() { let bs = b"1b23exfcz3r"; // Test bytes_mut.SHARED_VTABLE.to_mut impl where offset != 0 @@ -1227,58 +1227,58 @@ fn test_bytes_make_mut_bytes_mut_offset() { let b1 = bytes_mut1.freeze(); let b2 = bytes_mut2.freeze(); - let b1m = b1.make_mut(); - let b2m = b2.make_mut(); + let b1m = BytesMut::from(b1); + let b2m = BytesMut::from(b2); assert_eq!(b2m, bs[9..]); assert_eq!(b1m, bs[..9]); } #[test] -fn test_bytes_make_mut_promotable_even_vec() { +fn test_bytesmut_from_bytes_promotable_even_vec() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_VEC let b1 = Bytes::from(vec.clone()); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); } #[test] -fn test_bytes_make_mut_promotable_even_arc_1() { +fn test_bytesmut_from_bytes_promotable_even_arc_1() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_ARC, ref_cnt == 1 let b1 = Bytes::from(vec.clone()); drop(b1.clone()); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); } #[test] -fn test_bytes_make_mut_promotable_even_arc_2() { +fn test_bytesmut_from_bytes_promotable_even_arc_2() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_ARC, ref_cnt == 2 let b1 = Bytes::from(vec.clone()); let b2 = b1.clone(); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); // Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 - let b2m = b2.make_mut(); + let b2m = BytesMut::from(b2); assert_eq!(b2m, vec); } #[test] -fn test_bytes_make_mut_promotable_even_arc_offset() { +fn test_bytesmut_from_bytes_promotable_even_arc_offset() { let vec = vec![33u8; 1024]; // Test case where offset != 0 let mut b1 = Bytes::from(vec.clone()); let b2 = b1.split_off(20); - let b1m = b1.make_mut(); - let b2m = b2.make_mut(); + let b1m = BytesMut::from(b1); + let b2m = BytesMut::from(b2); assert_eq!(b2m, vec[20..]); assert_eq!(b1m, vec[..20]); diff --git a/tests/test_bytes_odd_alloc.rs b/tests/test_bytes_odd_alloc.rs index 8008a0e47..4758dc2f9 100644 --- a/tests/test_bytes_odd_alloc.rs +++ b/tests/test_bytes_odd_alloc.rs @@ -6,7 +6,7 @@ use std::alloc::{GlobalAlloc, Layout, System}; use std::ptr; -use bytes::Bytes; +use bytes::{Bytes, BytesMut}; #[global_allocator] static ODD: Odd = Odd; @@ -97,50 +97,50 @@ fn test_bytes_into_vec() { } #[test] -fn test_bytes_make_mut_vec() { +fn test_bytesmut_from_bytes_vec() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_VEC let b1 = Bytes::from(vec.clone()); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); } #[test] -fn test_bytes_make_mut_arc_1() { +fn test_bytesmut_from_bytes_arc_1() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_ARC, ref_cnt == 1 let b1 = Bytes::from(vec.clone()); drop(b1.clone()); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); } #[test] -fn test_bytes_make_mut_arc_2() { +fn test_bytesmut_from_bytes_arc_2() { let vec = vec![33u8; 1024]; // Test case where kind == KIND_ARC, ref_cnt == 2 let b1 = Bytes::from(vec.clone()); let b2 = b1.clone(); - let b1m = b1.make_mut(); + let b1m = BytesMut::from(b1); assert_eq!(b1m, vec); // Test case where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1 - let b2m = b2.make_mut(); + let b2m = BytesMut::from(b2); assert_eq!(b2m, vec); } #[test] -fn test_bytes_make_mut_arc_offset() { +fn test_bytesmut_from_bytes_arc_offset() { let vec = vec![33u8; 1024]; // Test case where offset != 0 let mut b1 = Bytes::from(vec.clone()); let b2 = b1.split_off(20); - let b1m = b1.make_mut(); - let b2m = b2.make_mut(); + let b1m = BytesMut::from(b1); + let b2m = BytesMut::from(b2); assert_eq!(b2m, vec[20..]); assert_eq!(b1m, vec[..20]); From 7a5154ba8b54970b7bb07c4902bc8a7981f4e57c Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 21 Jun 2024 14:07:25 +0200 Subject: [PATCH 153/203] Clarify how `BytesMut::zeroed` works and advantages to manual impl (#714) --- src/bytes_mut.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 537f01ad3..a01b29634 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -264,7 +264,14 @@ impl BytesMut { } } - /// Creates a new `BytesMut`, which is initialized with zero. + /// Creates a new `BytesMut` containing `len` zeros. + /// + /// The resulting object has a length of `len` and a capacity greater + /// than or equal to `len`. The entire length of the object will be filled + /// with zeros. + /// + /// On some platforms or allocators this function may be faster than + /// a manual implementation. /// /// # Examples /// @@ -273,6 +280,7 @@ impl BytesMut { /// /// let zeros = BytesMut::zeroed(42); /// + /// assert!(zeros.capacity() >= 42); /// assert_eq!(zeros.len(), 42); /// zeros.into_iter().for_each(|x| assert_eq!(x, 0)); /// ``` From 8cc940779fd6a489a2d7ca53fbbc44f84210083e Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Fri, 28 Jun 2024 14:26:32 +0200 Subject: [PATCH 154/203] Allow reclaiming the current allocation (#686) --- src/bytes_mut.rs | 89 +++++++++++++++++++++++++++++++++++++++++---- tests/test_bytes.rs | 70 +++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index a01b29634..f2a3dab5c 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -597,12 +597,13 @@ impl BytesMut { return; } - self.reserve_inner(additional); + // will always succeed + let _ = self.reserve_inner(additional, true); } - // In separate function to allow the short-circuits in `reserve` to - // be inline-able. Significant helps performance. - fn reserve_inner(&mut self, additional: usize) { + // In separate function to allow the short-circuits in `reserve` and `try_reclaim` to + // be inline-able. Significantly helps performance. Returns false if it did not succeed. + fn reserve_inner(&mut self, additional: usize, allocate: bool) -> bool { let len = self.len(); let kind = self.kind(); @@ -647,6 +648,9 @@ impl BytesMut { // can gain capacity back. self.cap += off; } else { + if !allocate { + return false; + } // Not enough space, or reusing might be too much overhead: // allocate more space! let mut v = @@ -659,7 +663,7 @@ impl BytesMut { debug_assert_eq!(self.len, v.len() - off); } - return; + return true; } } @@ -670,7 +674,11 @@ impl BytesMut { // allocating a new vector with the requested capacity. // // Compute the new capacity - let mut new_cap = len.checked_add(additional).expect("overflow"); + let mut new_cap = match len.checked_add(additional) { + Some(new_cap) => new_cap, + None if !allocate => return false, + None => panic!("overflow"), + }; unsafe { // First, try to reclaim the buffer. This is possible if the current @@ -701,6 +709,9 @@ impl BytesMut { self.ptr = vptr(ptr); self.cap = v.capacity(); } else { + if !allocate { + return false; + } // calculate offset let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize); @@ -739,9 +750,12 @@ impl BytesMut { self.cap = v.capacity() - off; } - return; + return true; } } + if !allocate { + return false; + } let original_capacity_repr = unsafe { (*shared).original_capacity_repr }; let original_capacity = original_capacity_from_repr(original_capacity_repr); @@ -764,6 +778,67 @@ impl BytesMut { self.ptr = vptr(v.as_mut_ptr()); self.cap = v.capacity(); debug_assert_eq!(self.len, v.len()); + return true; + } + + /// Attempts to cheaply reclaim already allocated capacity for at least `additional` more + /// bytes to be inserted into the given `BytesMut` and returns `true` if it succeeded. + /// + /// `try_reclaim` behaves exactly like `reserve`, except that it never allocates new storage + /// and returns a `bool` indicating whether it was successful in doing so: + /// + /// `try_reclaim` returns false under these conditions: + /// - The spare capacity left is less than `additional` bytes AND + /// - The existing allocation cannot be reclaimed cheaply or it was less than + /// `additional` bytes in size + /// + /// Reclaiming the allocation cheaply is possible if the `BytesMut` has no outstanding + /// references through other `BytesMut`s or `Bytes` which point to the same underlying + /// storage. + /// + /// # Examples + /// + /// ``` + /// use bytes::BytesMut; + /// + /// let mut buf = BytesMut::with_capacity(64); + /// assert_eq!(true, buf.try_reclaim(64)); + /// assert_eq!(64, buf.capacity()); + /// + /// buf.extend_from_slice(b"abcd"); + /// let mut split = buf.split(); + /// assert_eq!(60, buf.capacity()); + /// assert_eq!(4, split.capacity()); + /// assert_eq!(false, split.try_reclaim(64)); + /// assert_eq!(false, buf.try_reclaim(64)); + /// // The split buffer is filled with "abcd" + /// assert_eq!(false, split.try_reclaim(4)); + /// // buf is empty and has capacity for 60 bytes + /// assert_eq!(true, buf.try_reclaim(60)); + /// + /// drop(buf); + /// assert_eq!(false, split.try_reclaim(64)); + /// + /// split.clear(); + /// assert_eq!(4, split.capacity()); + /// assert_eq!(true, split.try_reclaim(64)); + /// assert_eq!(64, split.capacity()); + /// ``` + // I tried splitting out try_reclaim_inner after the short circuits, but it was inlined + // regardless with Rust 1.78.0 so probably not worth it + #[inline] + #[must_use = "consider BytesMut::reserve if you need an infallible reservation"] + pub fn try_reclaim(&mut self, additional: usize) -> bool { + let len = self.len(); + let rem = self.capacity() - len; + + if additional <= rem { + // The handle can already store at least `additional` more bytes, so + // there is no further work needed to be done. + return true; + } + + self.reserve_inner(additional, false) } /// Appends given bytes to this `BytesMut`. diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 3ac429832..017ce66ea 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1283,3 +1283,73 @@ fn test_bytesmut_from_bytes_promotable_even_arc_offset() { assert_eq!(b2m, vec[20..]); assert_eq!(b1m, vec[..20]); } + +#[test] +fn try_reclaim_empty() { + let mut buf = BytesMut::new(); + assert_eq!(false, buf.try_reclaim(6)); + buf.reserve(6); + assert_eq!(true, buf.try_reclaim(6)); + let cap = buf.capacity(); + assert!(cap >= 6); + assert_eq!(false, buf.try_reclaim(cap + 1)); + + let mut buf = BytesMut::new(); + buf.reserve(6); + let cap = buf.capacity(); + assert!(cap >= 6); + let mut split = buf.split(); + drop(buf); + assert_eq!(0, split.capacity()); + assert_eq!(true, split.try_reclaim(6)); + assert_eq!(false, split.try_reclaim(cap + 1)); +} + +#[test] +fn try_reclaim_vec() { + let mut buf = BytesMut::with_capacity(6); + buf.put_slice(b"abc"); + // Reclaiming a ludicrous amount of space should calmly return false + assert_eq!(false, buf.try_reclaim(usize::MAX)); + + assert_eq!(false, buf.try_reclaim(6)); + buf.advance(2); + assert_eq!(4, buf.capacity()); + // We can reclaim 5 bytes, because the byte in the buffer can be moved to the front. 6 bytes + // cannot be reclaimed because there is already one byte stored + assert_eq!(false, buf.try_reclaim(6)); + assert_eq!(true, buf.try_reclaim(5)); + buf.advance(1); + assert_eq!(true, buf.try_reclaim(6)); + assert_eq!(6, buf.capacity()); +} + +#[test] +fn try_reclaim_arc() { + let mut buf = BytesMut::with_capacity(6); + buf.put_slice(b"abc"); + let x = buf.split().freeze(); + buf.put_slice(b"def"); + // Reclaiming a ludicrous amount of space should calmly return false + assert_eq!(false, buf.try_reclaim(usize::MAX)); + + let y = buf.split().freeze(); + let z = y.clone(); + assert_eq!(false, buf.try_reclaim(6)); + drop(x); + drop(z); + assert_eq!(false, buf.try_reclaim(6)); + drop(y); + assert_eq!(true, buf.try_reclaim(6)); + assert_eq!(6, buf.capacity()); + assert_eq!(0, buf.len()); + buf.put_slice(b"abc"); + buf.put_slice(b"def"); + assert_eq!(6, buf.capacity()); + assert_eq!(6, buf.len()); + assert_eq!(false, buf.try_reclaim(6)); + buf.advance(4); + assert_eq!(true, buf.try_reclaim(4)); + buf.advance(2); + assert_eq!(true, buf.try_reclaim(6)); +} From 3443ca5a0be21cdb2424bd20d49746bd622ed195 Mon Sep 17 00:00:00 2001 From: vvvviiv Date: Tue, 9 Jul 2024 20:13:09 +0800 Subject: [PATCH 155/203] docs: clarify the behavior of `Buf::chunk` (#717) --- src/buf/buf_impl.rs | 8 +++++--- src/buf/buf_mut.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 38ecf4bd9..c44d4fb38 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -141,9 +141,11 @@ pub trait Buf { /// /// # Implementer notes /// - /// This function should never panic. Once the end of the buffer is reached, - /// i.e., `Buf::remaining` returns 0, calls to `chunk()` should return an - /// empty slice. + /// This function should never panic. `chunk()` should return an empty + /// slice **if and only if** `remaining()` returns 0. In other words, + /// `chunk()` returning an empty slice implies that `remaining()` will + /// return 0 and `remaining()` returning 0 implies that `chunk()` will + /// return an empty slice. // The `chunk` method was previously called `bytes`. This alias makes the rename // more easily discoverable. #[cfg_attr(docsrs, doc(alias = "bytes"))] diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 304e11b13..e13278d2c 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -165,7 +165,7 @@ pub unsafe trait BufMut { /// /// # Implementer notes /// - /// This function should never panic. `chunk_mut` should return an empty + /// This function should never panic. `chunk_mut()` should return an empty /// slice **if and only if** `remaining_mut()` returns 0. In other words, /// `chunk_mut()` returning an empty slice implies that `remaining_mut()` will /// return 0 and `remaining_mut()` returning 0 implies that `chunk_mut()` will From 9965a04b5684079bb614addd750340ffc165a9f5 Mon Sep 17 00:00:00 2001 From: EXPLOSION Date: Wed, 10 Jul 2024 19:08:47 +0900 Subject: [PATCH 156/203] Remove unnecessary file (#719) --- src/bytes/promotable.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/bytes/promotable.rs diff --git a/src/bytes/promotable.rs b/src/bytes/promotable.rs deleted file mode 100644 index e69de29bb..000000000 From 6b4b0eda2980f09df18380c80f8ae6109ae70d83 Mon Sep 17 00:00:00 2001 From: Emily Crandall Fleischman Date: Fri, 12 Jul 2024 19:09:46 -0400 Subject: [PATCH 157/203] Fix `Bytes::is_unique` when created from shared `BytesMut` (#718) The `is_unique` entry in the vtable for `Bytes` created from a shared `BytesMut` just called the `shared_is_unique` function from the `bytes` module. However, that function dereferences the `data` argument` as `bytes::Shared`, but the actual underlying type is `bytes_mut::Shared`. --- src/bytes_mut.rs | 8 +++++++- tests/test_bytes.rs | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 282aaa710..2342077d2 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1698,7 +1698,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, - is_unique: crate::bytes::shared_is_unique, + is_unique: shared_v_is_unique, drop: shared_v_drop, }; @@ -1732,6 +1732,12 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V } } +unsafe fn shared_v_is_unique(data: &AtomicPtr<()>) -> bool { + let shared = data.load(Ordering::Acquire); + let ref_count = (*shared.cast::()).ref_count.load(Ordering::Relaxed); + ref_count == 1 +} + unsafe fn shared_v_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { data.with_mut(|shared| { release_shared(*shared as *mut Shared); diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 84c3d5a43..b2135905d 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1172,3 +1172,12 @@ fn shared_is_unique() { drop(b); assert!(c.is_unique()); } + +#[test] +fn mut_shared_is_unique() { + let mut b = BytesMut::from(LONG); + let c = b.split().freeze(); + assert!(!c.is_unique()); + drop(b); + assert!(c.is_unique()); +} From fd13c7dcdb840653bf81294d141da77d3f1f9e1f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Sat, 13 Jul 2024 07:45:33 +0000 Subject: [PATCH 158/203] chore: prepare bytes v1.6.1 (#720) --- CHANGELOG.md | 5 +++++ Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23357174b..ac5d8d5ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.6.1 (July 13, 2024) + +This release fixes a bug where `Bytes::is_unique` returns incorrect values when +the `Bytes` originates from a shared `BytesMut`. (#718) + # 1.6.0 (March 22, 2024) ### Added diff --git a/Cargo.toml b/Cargo.toml index 793582af1..ef44fbb9d 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.6.0" +version = "1.6.1" edition = "2018" rust-version = "1.39" license = "MIT" From 03fdde9dcfe69caf681ecaa1d97f8105a9c9a6c1 Mon Sep 17 00:00:00 2001 From: Motoyuki Kimura Date: Wed, 31 Jul 2024 20:33:47 +0900 Subject: [PATCH 159/203] chore: prepare v1.7.0 (#724) --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac5d8d5ce..7a4c5ba08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 1.7.0 (July 31, 2024) + +### Added + +- Add conversion from `Bytes` to `BytesMut` (#695, #710) +- Add reclaim method without additional allocation (#686) + +### Documented + +- Clarify how `BytesMut::zeroed` works (#714) +- Clarify the behavior of `Buf::chunk` (#717) + +### Changed + +- Change length condition of `BytesMut::truncate` +- Reuse capacity when possible in `::advance` impl (#698) +- Improve `must_use` suggestion of `BytesMut::split` (#699) + +### Internal changes + +- Use `ManuallyDrop` instead of `mem::forget` (#678) +- Don't set `len` in `BytesMut::reserve` (#682) +- Optimize `Bytes::copy_to_bytes` (#688) +- Refactor `BytesMut::truncate` (#694) +- Refactor `BytesMut::resize` (#696) +- Reorder assertion in `Bytes::split_to`, `Bytes::split_off` (#689, #693) +- Use `offset_from` in more places (#705) +- Correct the wrong usage of `IntoIter` (#707) + # 1.6.1 (July 13, 2024) This release fixes a bug where `Bytes::is_unique` returns incorrect values when diff --git a/Cargo.toml b/Cargo.toml index ef44fbb9d..ac8bdf7d6 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.6.1" +version = "1.7.0" edition = "2018" rust-version = "1.39" license = "MIT" From f488be48d07d899dc428c5cd7f5c11a95bf7716c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 1 Aug 2024 21:38:17 +0100 Subject: [PATCH 160/203] Revert "Reuse capacity when possible in ::advance impl" (#726) This reverts commit baa5053572ed9e88ca1058ec2b5a3f08046c5a40. --- src/bytes_mut.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 2829a7cfb..ec74c4e97 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1148,13 +1148,6 @@ impl Buf for BytesMut { #[inline] fn advance(&mut self, cnt: usize) { - // Advancing by the length is the same as resetting the length to 0, - // except this way we get to reuse the full capacity. - if cnt == self.remaining() { - self.clear(); - return; - } - assert!( cnt <= self.remaining(), "cannot advance past `remaining`: {:?} <= {:?}", From dc4fb3e8f45650500187f8cdbad5ac8ffdb7df0a Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 1 Aug 2024 21:55:24 +0100 Subject: [PATCH 161/203] chore: prepare bytes v1.7.1 (#727) --- CHANGELOG.md | 8 ++++++++ Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a4c5ba08..93c1419cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# 1.7.1 (August 1, 2024) + +This release reverts the following change due to a regression: + +- Reuse capacity when possible in `::advance` impl (#698) + +The revert can be found at #726. + # 1.7.0 (July 31, 2024) ### Added diff --git a/Cargo.toml b/Cargo.toml index ac8bdf7d6..e07253942 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.7.0" +version = "1.7.1" edition = "2018" rust-version = "1.39" license = "MIT" From ed7d5ff39e39c2802c0fa9e2fc308f6a3e0beda7 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Fri, 2 Aug 2024 13:07:45 -0600 Subject: [PATCH 162/203] test: ensure BytesMut::advance reduces capacity (#728) --- tests/test_bytes.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 8e5d0ae1f..59c967b66 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -676,6 +676,43 @@ fn advance_bytes_mut() { assert_eq!(a, b"d zomg wat wat"[..]); } +// Ensures BytesMut::advance reduces always capacity +// +// See https://github.com/tokio-rs/bytes/issues/725 +#[test] +fn advance_bytes_mut_remaining_capacity() { + // reduce the search space under miri + let max_capacity = if cfg!(miri) { 16 } else { 256 }; + for capacity in 0..=max_capacity { + for len in 0..=capacity { + for advance in 0..=len { + eprintln!("testing capacity={capacity}, len={len}, advance={advance}"); + let mut buf = BytesMut::with_capacity(capacity); + + buf.resize(len, 42); + assert_eq!(buf.len(), len, "resize should write `len` bytes"); + assert_eq!( + buf.remaining(), + len, + "Buf::remaining() should equal BytesMut::len" + ); + + buf.advance(advance); + assert_eq!( + buf.remaining(), + len - advance, + "Buf::advance should reduce the remaining len" + ); + assert_eq!( + buf.capacity(), + capacity - advance, + "Buf::advance should reduce the remaining capacity" + ); + } + } + } +} + #[test] #[should_panic] fn advance_past_len() { From 291df5acc94b82a48765e67eeb1c1a2074539e68 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Mon, 19 Aug 2024 10:19:35 +0200 Subject: [PATCH 163/203] Fix double spaces in comments and doc comments (#731) --- src/buf/buf_mut.rs | 10 +++++----- src/buf/uninit_slice.rs | 2 +- src/bytes.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index e13278d2c..2a3b5e9ee 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1107,7 +1107,7 @@ pub unsafe trait BufMut { } } - /// Writes an IEEE754 single-precision (4 bytes) floating point number to + /// Writes an IEEE754 single-precision (4 bytes) floating point number to /// `self` in big-endian byte order. /// /// The current position is advanced by 4. @@ -1131,7 +1131,7 @@ pub unsafe trait BufMut { self.put_u32(n.to_bits()); } - /// Writes an IEEE754 single-precision (4 bytes) floating point number to + /// Writes an IEEE754 single-precision (4 bytes) floating point number to /// `self` in little-endian byte order. /// /// The current position is advanced by 4. @@ -1183,7 +1183,7 @@ pub unsafe trait BufMut { self.put_u32_ne(n.to_bits()); } - /// Writes an IEEE754 double-precision (8 bytes) floating point number to + /// Writes an IEEE754 double-precision (8 bytes) floating point number to /// `self` in big-endian byte order. /// /// The current position is advanced by 8. @@ -1207,7 +1207,7 @@ pub unsafe trait BufMut { self.put_u64(n.to_bits()); } - /// Writes an IEEE754 double-precision (8 bytes) floating point number to + /// Writes an IEEE754 double-precision (8 bytes) floating point number to /// `self` in little-endian byte order. /// /// The current position is advanced by 8. @@ -1231,7 +1231,7 @@ pub unsafe trait BufMut { self.put_u64_le(n.to_bits()); } - /// Writes an IEEE754 double-precision (8 bytes) floating point number to + /// Writes an IEEE754 double-precision (8 bytes) floating point number to /// `self` in native-endian byte order. /// /// The current position is advanced by 8. diff --git a/src/buf/uninit_slice.rs b/src/buf/uninit_slice.rs index 82ebdbbb3..aea096ae6 100644 --- a/src/buf/uninit_slice.rs +++ b/src/buf/uninit_slice.rs @@ -110,7 +110,7 @@ impl UninitSlice { unsafe { self[index..].as_mut_ptr().write(byte) } } - /// Copies bytes from `src` into `self`. + /// Copies bytes from `src` into `self`. /// /// The length of `src` must be the same as `self`. /// diff --git a/src/bytes.rs b/src/bytes.rs index e0c33b3e6..da0ba3a6f 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1301,7 +1301,7 @@ unsafe fn shallow_clone_vec( offset: *const u8, len: usize, ) -> Bytes { - // If the buffer is still tracked in a `Vec`. It is time to + // If the buffer is still tracked in a `Vec`. It is time to // promote the vec to an `Arc`. This could potentially be called // concurrently, so some care must be taken. From 79fb85323cf4cf14d9b85f487b65fc147030cf4b Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 30 Aug 2024 14:20:29 +0200 Subject: [PATCH 164/203] fix: apply sign extension when decoding int (#732) Closes #730 --- src/buf/buf_impl.rs | 10 ++++++++-- tests/test_buf.rs | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index c44d4fb38..9ef464075 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -66,6 +66,12 @@ macro_rules! buf_get_impl { }}; } +// https://en.wikipedia.org/wiki/Sign_extension +fn sign_extend(val: u64, nbytes: usize) -> i64 { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift +} + /// Read bytes from a buffer. /// /// A buffer stores bytes in memory such that read operations are infallible. @@ -923,7 +929,7 @@ pub trait Buf { /// This function panics if there is not enough remaining data in `self`, or /// if `nbytes` is greater than 8. fn get_int(&mut self, nbytes: usize) -> i64 { - buf_get_impl!(be => self, i64, nbytes); + sign_extend(self.get_uint(nbytes), nbytes) } /// Gets a signed n-byte integer from `self` in little-endian byte order. @@ -944,7 +950,7 @@ pub trait Buf { /// This function panics if there is not enough remaining data in `self`, or /// if `nbytes` is greater than 8. fn get_int_le(&mut self, nbytes: usize) -> i64 { - buf_get_impl!(le => self, i64, nbytes); + sign_extend(self.get_uint_le(nbytes), nbytes) } /// Gets a signed n-byte integer from `self` in native-endian byte order. diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 3940f9247..5aadea43e 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -36,6 +36,19 @@ fn test_get_u16() { assert_eq!(0x5421, buf.get_u16_le()); } +#[test] +fn test_get_int() { + let mut buf = &b"\xd6zomg"[..]; + assert_eq!(-42, buf.get_int(1)); + let mut buf = &b"\xd6zomg"[..]; + assert_eq!(-42, buf.get_int_le(1)); + + let mut buf = &b"\xfe\x1d\xc0zomg"[..]; + assert_eq!(0xffffffffffc01dfeu64 as i64, buf.get_int_le(3)); + let mut buf = &b"\xfe\x1d\xc0zomg"[..]; + assert_eq!(0xfffffffffffe1dc0u64 as i64, buf.get_int(3)); +} + #[test] #[should_panic] fn test_get_u16_buffer_underflow() { From ac46ebdd464144cecd7463a96ca9eb347458ae94 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 18 Sep 2024 00:02:45 +0900 Subject: [PATCH 165/203] ci: update nightly to nightly-2024-09-15 (#734) --- .github/workflows/ci.yml | 5 ++++- ci/miri.sh | 3 +-- src/bytes.rs | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0658a142..d0136c47b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ on: env: RUSTFLAGS: -Dwarnings RUST_BACKTRACE: 1 - nightly: nightly-2022-11-12 + nightly: nightly-2024-09-15 defaults: run: @@ -136,6 +136,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update $nightly && rustup default $nightly - name: Miri run: ci/miri.sh @@ -160,6 +162,7 @@ jobs: - minrust - cross - tsan + - miri - loom runs-on: ubuntu-latest steps: diff --git a/ci/miri.sh b/ci/miri.sh index 0158756cd..7df29f360 100755 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -1,8 +1,7 @@ #!/bin/bash set -e -rustup toolchain install nightly --component miri -rustup override set nightly +rustup component add miri cargo miri setup export MIRIFLAGS="-Zmiri-strict-provenance" diff --git a/src/bytes.rs b/src/bytes.rs index da0ba3a6f..ec95e802d 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -142,6 +142,7 @@ impl Bytes { Bytes::from_static(EMPTY) } + /// Creates a new empty `Bytes`. #[cfg(all(loom, test))] pub fn new() -> Self { const EMPTY: &[u8] = &[]; @@ -172,6 +173,7 @@ impl Bytes { } } + /// Creates a new `Bytes` from a static slice. #[cfg(all(loom, test))] pub fn from_static(bytes: &'static [u8]) -> Self { Bytes { From d7c1d658d90c922eeee1dbc29bc6f6fd9a1a1a66 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Wed, 18 Sep 2024 07:39:32 +0200 Subject: [PATCH 166/203] chore: prepare bytes v1.7.2 (#736) --- CHANGELOG.md | 14 ++++++++++++++ Cargo.toml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93c1419cc..91c0fa47e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.7.2 (September 17, 2024) + +### Fixed + +- Fix default impl of `Buf::{get_int, get_int_le}` (#732) + +### Documented + +- Fix double spaces in comments and doc comments (#731) + +### Internal changes + +- Ensure BytesMut::advance reduces capacity (#728) + # 1.7.1 (August 1, 2024) This release reverts the following change due to a regression: diff --git a/Cargo.toml b/Cargo.toml index e07253942..8083901a1 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.7.1" +version = "1.7.2" edition = "2018" rust-version = "1.39" license = "MIT" From 0ac54ca706dfc039cc738962581bba4793860605 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 18 Oct 2024 19:46:49 +0200 Subject: [PATCH 167/203] Guarantee address in split_off/split_to for empty slices (#740) Signed-off-by: Alice Ryhl --- src/bytes.rs | 33 ++++++++++++++++--- src/bytes_mut.rs | 4 ++- tests/test_bytes.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index ec95e802d..8d7abd040 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -184,6 +184,22 @@ impl Bytes { } } + /// Creates a new `Bytes` with length zero and the given pointer as the address. + fn new_empty_with_ptr(ptr: *const u8) -> Self { + debug_assert!(!ptr.is_null()); + + // Detach this pointer's provenance from whichever allocation it came from, and reattach it + // to the provenance of the fake ZST [u8;0] at the same address. + let ptr = without_provenance(ptr as usize); + + Bytes { + ptr, + len: 0, + data: AtomicPtr::new(ptr::null_mut()), + vtable: &STATIC_VTABLE, + } + } + /// Returns the number of bytes contained in this `Bytes`. /// /// # Examples @@ -366,7 +382,9 @@ impl Bytes { /// Splits the bytes into two at the given index. /// /// Afterwards `self` contains elements `[0, at)`, and the returned `Bytes` - /// contains elements `[at, len)`. + /// contains elements `[at, len)`. It's guaranteed that the memory does not + /// move, that is, the address of `self` does not change, and the address of + /// the returned slice is `at` bytes after that. /// /// This is an `O(1)` operation that just increases the reference count and /// sets a few indices. @@ -389,11 +407,11 @@ impl Bytes { #[must_use = "consider Bytes::truncate if you don't need the other half"] pub fn split_off(&mut self, at: usize) -> Self { if at == self.len() { - return Bytes::new(); + return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(at)); } if at == 0 { - return mem::replace(self, Bytes::new()); + return mem::replace(self, Bytes::new_empty_with_ptr(self.ptr)); } assert!( @@ -438,11 +456,12 @@ impl Bytes { #[must_use = "consider Bytes::advance if you don't need the other half"] pub fn split_to(&mut self, at: usize) -> Self { if at == self.len() { - return mem::replace(self, Bytes::new()); + let end_ptr = self.ptr.wrapping_add(at); + return mem::replace(self, Bytes::new_empty_with_ptr(end_ptr)); } if at == 0 { - return Bytes::new(); + return Bytes::new_empty_with_ptr(self.ptr); } assert!( @@ -1426,6 +1445,10 @@ where new_addr as *mut u8 } +fn without_provenance(ptr: usize) -> *const u8 { + core::ptr::null::().wrapping_add(ptr) +} + // compile-fails /// ```compile_fail diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index ec74c4e97..a0d77bc24 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -291,7 +291,9 @@ impl BytesMut { /// Splits the bytes into two at the given index. /// /// Afterwards `self` contains elements `[0, at)`, and the returned - /// `BytesMut` contains elements `[at, capacity)`. + /// `BytesMut` contains elements `[at, capacity)`. It's guaranteed that the + /// memory does not move, that is, the address of `self` does not change, + /// and the address of the returned slice is `at` bytes after that. /// /// This is an `O(1)` operation that just increases the reference count /// and sets a few indices. diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 59c967b66..71f0e6681 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1399,3 +1399,83 @@ fn try_reclaim_arc() { buf.advance(2); assert_eq!(true, buf.try_reclaim(6)); } + +#[test] +fn split_off_empty_addr() { + let mut buf = Bytes::from(vec![0; 1024]); + + let ptr_start = buf.as_ptr(); + let ptr_end = ptr_start.wrapping_add(1024); + + let empty_end = buf.split_off(1024); + assert_eq!(empty_end.len(), 0); + assert_eq!(empty_end.as_ptr(), ptr_end); + + let _ = buf.split_off(0); + assert_eq!(buf.len(), 0); + assert_eq!(buf.as_ptr(), ptr_start); + + // Is miri happy about the provenance? + let _ = &empty_end[..]; + let _ = &buf[..]; +} + +#[test] +fn split_to_empty_addr() { + let mut buf = Bytes::from(vec![0; 1024]); + + let ptr_start = buf.as_ptr(); + let ptr_end = ptr_start.wrapping_add(1024); + + let empty_start = buf.split_to(0); + assert_eq!(empty_start.len(), 0); + assert_eq!(empty_start.as_ptr(), ptr_start); + + let _ = buf.split_to(1024); + assert_eq!(buf.len(), 0); + assert_eq!(buf.as_ptr(), ptr_end); + + // Is miri happy about the provenance? + let _ = &empty_start[..]; + let _ = &buf[..]; +} + +#[test] +fn split_off_empty_addr_mut() { + let mut buf = BytesMut::from([0; 1024].as_slice()); + + let ptr_start = buf.as_ptr(); + let ptr_end = ptr_start.wrapping_add(1024); + + let empty_end = buf.split_off(1024); + assert_eq!(empty_end.len(), 0); + assert_eq!(empty_end.as_ptr(), ptr_end); + + let _ = buf.split_off(0); + assert_eq!(buf.len(), 0); + assert_eq!(buf.as_ptr(), ptr_start); + + // Is miri happy about the provenance? + let _ = &empty_end[..]; + let _ = &buf[..]; +} + +#[test] +fn split_to_empty_addr_mut() { + let mut buf = BytesMut::from([0; 1024].as_slice()); + + let ptr_start = buf.as_ptr(); + let ptr_end = ptr_start.wrapping_add(1024); + + let empty_start = buf.split_to(0); + assert_eq!(empty_start.len(), 0); + assert_eq!(empty_start.as_ptr(), ptr_start); + + let _ = buf.split_to(1024); + assert_eq!(buf.len(), 0); + assert_eq!(buf.as_ptr(), ptr_end); + + // Is miri happy about the provenance? + let _ = &empty_start[..]; + let _ = &buf[..]; +} From c45697ce4230777aa8467db7ef91e89f282a539f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 21 Oct 2024 21:54:34 +0200 Subject: [PATCH 168/203] chore: prepare bytes v1.8.0 (#741) --- CHANGELOG.md | 4 ++++ Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c0fa47e..e428ae0de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.8.0 (October 21, 2024) + +- Guarantee address in `split_off`/`split_to` for empty slices (#740) + # 1.7.2 (September 17, 2024) ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 8083901a1..de632dd1b 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.7.2" +version = "1.8.0" edition = "2018" rust-version = "1.39" license = "MIT" From 30ee8e9cba87c6d31774c100d2c9fd67ff2ad94c Mon Sep 17 00:00:00 2001 From: Adam Cimarosti Date: Tue, 29 Oct 2024 12:18:03 +0000 Subject: [PATCH 169/203] Add `Bytes::from_owner` (#742) --- src/bytes.rs | 177 +++++++++++++++++++++++++++++++++++++++++++- tests/test_bytes.rs | 152 +++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 3 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 8d7abd040..96f834f43 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,6 +1,7 @@ use core::iter::FromIterator; use core::mem::{self, ManuallyDrop}; use core::ops::{Deref, RangeBounds}; +use core::ptr::NonNull; use core::{cmp, fmt, hash, ptr, slice, usize}; use alloc::{ @@ -200,6 +201,94 @@ impl Bytes { } } + /// Create [Bytes] with a buffer whose lifetime is controlled + /// via an explicit owner. + /// + /// A common use case is to zero-copy construct from mapped memory. + /// + /// ``` + /// # struct File; + /// # + /// # impl File { + /// # pub fn open(_: &str) -> Result { + /// # Ok(Self) + /// # } + /// # } + /// # + /// # mod memmap2 { + /// # pub struct Mmap; + /// # + /// # impl Mmap { + /// # pub unsafe fn map(_file: &super::File) -> Result { + /// # Ok(Self) + /// # } + /// # } + /// # + /// # impl AsRef<[u8]> for Mmap { + /// # fn as_ref(&self) -> &[u8] { + /// # b"buf" + /// # } + /// # } + /// # } + /// use bytes::Bytes; + /// use memmap2::Mmap; + /// + /// # fn main() -> Result<(), ()> { + /// let file = File::open("upload_bundle.tar.gz")?; + /// let mmap = unsafe { Mmap::map(&file) }?; + /// let b = Bytes::from_owner(mmap); + /// # Ok(()) + /// # } + /// ``` + /// + /// The `owner` will be transferred to the constructed [Bytes] object, which + /// will ensure it is dropped once all remaining clones of the constructed + /// object are dropped. The owner will then be responsible for dropping the + /// specified region of memory as part of its [Drop] implementation. + /// + /// Note that converting [Bytes] constructed from an owner into a [BytesMut] + /// will always create a deep copy of the buffer into newly allocated memory. + pub fn from_owner(owner: T) -> Self + where + T: AsRef<[u8]> + Send + 'static, + { + // Safety & Miri: + // The ownership of `owner` is first transferred to the `Owned` wrapper and `Bytes` object. + // This ensures that the owner is pinned in memory, allowing us to call `.as_ref()` safely + // since the lifetime of the owner is controlled by the lifetime of the new `Bytes` object, + // and the lifetime of the resulting borrowed `&[u8]` matches that of the owner. + // Note that this remains safe so long as we only call `.as_ref()` once. + // + // There are some additional special considerations here: + // * We rely on Bytes's Drop impl to clean up memory should `.as_ref()` panic. + // * Setting the `ptr` and `len` on the bytes object last (after moving the owner to + // Bytes) allows Miri checks to pass since it avoids obtaining the `&[u8]` slice + // from a stack-owned Box. + // More details on this: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813375863 + // and: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813316032 + + let owned = Box::into_raw(Box::new(Owned { + lifetime: OwnedLifetime { + ref_cnt: AtomicUsize::new(1), + drop: owned_box_and_drop::, + }, + owner, + })); + + let mut ret = Bytes { + ptr: NonNull::dangling().as_ptr(), + len: 0, + data: AtomicPtr::new(owned.cast()), + vtable: &OWNED_VTABLE, + }; + + let buf = unsafe { &*owned }.owner.as_ref(); + ret.ptr = buf.as_ptr(); + ret.len = buf.len(); + + ret + } + /// Returns the number of bytes contained in this `Bytes`. /// /// # Examples @@ -230,14 +319,16 @@ impl Bytes { self.len == 0 } - /// Returns true if this is the only reference to the data. + /// Returns true if this is the only reference to the data and + /// `Into` would avoid cloning the underlying buffer. /// - /// Always returns false if the data is backed by a static slice. + /// Always returns false if the data is backed by a [static slice](Bytes::from_static), + /// or an [owner](Bytes::from_owner). /// /// The result of this method may be invalidated immediately if another /// thread clones this value while this is being called. Ensure you have /// unique access to this value (`&mut Bytes`) first if you need to be - /// certain the result is valid (i.e. for safety reasons) + /// certain the result is valid (i.e. for safety reasons). /// # Examples /// /// ``` @@ -536,6 +627,9 @@ impl Bytes { /// If `self` is not unique for the entire original buffer, this will fail /// and return self. /// + /// This will also always fail if the buffer was constructed via either + /// [from_owner](Bytes::from_owner) or [from_static](Bytes::from_static). + /// /// # Examples /// /// ``` @@ -1012,6 +1106,83 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { // nothing to drop for &'static [u8] } +// ===== impl OwnedVtable ===== + +#[repr(C)] +struct OwnedLifetime { + ref_cnt: AtomicUsize, + drop: unsafe fn(*mut ()), +} + +#[repr(C)] +struct Owned { + lifetime: OwnedLifetime, + owner: T, +} + +unsafe fn owned_box_and_drop(ptr: *mut ()) { + let b: Box> = Box::from_raw(ptr as _); + drop(b); +} + +unsafe fn owned_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { + let owned = data.load(Ordering::Relaxed); + let ref_cnt = &(*owned.cast::()).ref_cnt; + let old_cnt = ref_cnt.fetch_add(1, Ordering::Relaxed); + if old_cnt > usize::MAX >> 1 { + crate::abort() + } + + Bytes { + ptr, + len, + data: AtomicPtr::new(owned as _), + vtable: &OWNED_VTABLE, + } +} + +unsafe fn owned_to_vec(_data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { + let slice = slice::from_raw_parts(ptr, len); + slice.to_vec() +} + +unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + let bytes_mut = BytesMut::from_vec(owned_to_vec(data, ptr, len)); + owned_drop_impl(data.load(Ordering::Relaxed)); + bytes_mut +} + +unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool { + false +} + +unsafe fn owned_drop_impl(owned: *mut ()) { + let lifetime = owned.cast::(); + let ref_cnt = &(*lifetime).ref_cnt; + + let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release); + if old_cnt != 1 { + return; + } + ref_cnt.load(Ordering::Acquire); + + let drop_fn = &(*lifetime).drop; + drop_fn(owned) +} + +unsafe fn owned_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { + let owned = data.load(Ordering::Relaxed); + owned_drop_impl(owned); +} + +static OWNED_VTABLE: Vtable = Vtable { + clone: owned_clone, + to_vec: owned_to_vec, + to_mut: owned_to_mut, + is_unique: owned_is_unique, + drop: owned_drop, +}; + // ===== impl PromotableVtable ===== static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable { diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 71f0e6681..fdc36ce8d 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1,7 +1,10 @@ #![warn(rust_2018_idioms)] use bytes::{Buf, BufMut, Bytes, BytesMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::panic::{self, AssertUnwindSafe}; use std::usize; const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb"; @@ -1479,3 +1482,152 @@ fn split_to_empty_addr_mut() { let _ = &empty_start[..]; let _ = &buf[..]; } + +#[derive(Clone)] +struct SharedAtomicCounter(Arc); + +impl SharedAtomicCounter { + pub fn new() -> Self { + SharedAtomicCounter(Arc::new(AtomicUsize::new(0))) + } + + pub fn increment(&self) { + self.0.fetch_add(1, Ordering::AcqRel); + } + + pub fn get(&self) -> usize { + self.0.load(Ordering::Acquire) + } +} + +#[derive(Clone)] +struct OwnedTester { + buf: [u8; L], + drop_count: SharedAtomicCounter, + pub panic_as_ref: bool, +} + +impl OwnedTester { + fn new(buf: [u8; L], drop_count: SharedAtomicCounter) -> Self { + Self { + buf, + drop_count, + panic_as_ref: false, + } + } +} + +impl AsRef<[u8]> for OwnedTester { + fn as_ref(&self) -> &[u8] { + if self.panic_as_ref { + panic!("test-triggered panic in `AsRef<[u8]> for OwnedTester`"); + } + self.buf.as_slice() + } +} + +impl Drop for OwnedTester { + fn drop(&mut self) { + self.drop_count.increment(); + } +} + +#[test] +fn owned_is_unique_always_false() { + let b1 = Bytes::from_owner([1, 2, 3, 4, 5, 6, 7]); + assert!(!b1.is_unique()); // even if ref_cnt == 1 + let b2 = b1.clone(); + assert!(!b1.is_unique()); + assert!(!b2.is_unique()); + drop(b1); + assert!(!b2.is_unique()); // even if ref_cnt == 1 +} + +#[test] +fn owned_buf_sharing() { + let buf = [1, 2, 3, 4, 5, 6, 7]; + let b1 = Bytes::from_owner(buf); + let b2 = b1.clone(); + assert_eq!(&buf[..], &b1[..]); + assert_eq!(&buf[..], &b2[..]); + assert_eq!(b1.as_ptr(), b2.as_ptr()); + assert_eq!(b1.len(), b2.len()); + assert_eq!(b1.len(), buf.len()); +} + +#[test] +fn owned_buf_slicing() { + let b1 = Bytes::from_owner(SHORT); + assert_eq!(SHORT, &b1[..]); + let b2 = b1.slice(1..(b1.len() - 1)); + assert_eq!(&SHORT[1..(SHORT.len() - 1)], b2); + assert_eq!(unsafe { SHORT.as_ptr().add(1) }, b2.as_ptr()); + assert_eq!(SHORT.len() - 2, b2.len()); +} + +#[test] +fn owned_dropped_exactly_once() { + let buf: [u8; 5] = [1, 2, 3, 4, 5]; + let drop_counter = SharedAtomicCounter::new(); + let owner = OwnedTester::new(buf, drop_counter.clone()); + let b1 = Bytes::from_owner(owner); + let b2 = b1.clone(); + assert_eq!(drop_counter.get(), 0); + drop(b1); + assert_eq!(drop_counter.get(), 0); + let b3 = b2.slice(1..b2.len() - 1); + drop(b2); + assert_eq!(drop_counter.get(), 0); + drop(b3); + assert_eq!(drop_counter.get(), 1); +} + +#[test] +fn owned_to_mut() { + let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let drop_counter = SharedAtomicCounter::new(); + let owner = OwnedTester::new(buf, drop_counter.clone()); + let b1 = Bytes::from_owner(owner); + + // Holding an owner will fail converting to a BytesMut, + // even when the bytes instance has a ref_cnt == 1. + let b1 = b1.try_into_mut().unwrap_err(); + + // That said, it's still possible, just not cheap. + let bm1: BytesMut = b1.into(); + let new_buf = &bm1[..]; + assert_eq!(new_buf, &buf[..]); + + // `.into::()` has correctly dropped the owner + assert_eq!(drop_counter.get(), 1); +} + +#[test] +fn owned_to_vec() { + let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let drop_counter = SharedAtomicCounter::new(); + let owner = OwnedTester::new(buf, drop_counter.clone()); + let b1 = Bytes::from_owner(owner); + + let v1 = b1.to_vec(); + assert_eq!(&v1[..], &buf[..]); + assert_eq!(&v1[..], &b1[..]); + + drop(b1); + assert_eq!(drop_counter.get(), 1); +} + +#[test] +fn owned_safe_drop_on_as_ref_panic() { + let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let drop_counter = SharedAtomicCounter::new(); + let mut owner = OwnedTester::new(buf, drop_counter.clone()); + owner.panic_as_ref = true; + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let _ = Bytes::from_owner(owner); + })); + + assert!(result.is_err()); + assert_eq!(drop_counter.get(), 1); +} From 2d996a2b419ad1a29d65ba6c7e82cfe9c733595c Mon Sep 17 00:00:00 2001 From: katelyn martin Date: Fri, 1 Nov 2024 10:46:09 -0400 Subject: [PATCH 170/203] Fix typo in `Buf::chunk()` comment (#744) --- src/buf/buf_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 9ef464075..b09d356bd 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -125,8 +125,8 @@ pub trait Buf { fn remaining(&self) -> usize; /// Returns a slice starting at the current position and of length between 0 - /// and `Buf::remaining()`. Note that this *can* return shorter slice (this allows - /// non-continuous internal representation). + /// and `Buf::remaining()`. Note that this *can* return a shorter slice (this + /// allows non-continuous internal representation). /// /// This is a lower level function. Most operations are done with other /// functions. From 4cd8969e850d0a66b181c388ead281c37d360621 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 1 Nov 2024 19:22:07 +0100 Subject: [PATCH 171/203] Replace `BufMut::put` with `BufMut::put_slice` in Writer impl (#745) --- src/buf/writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buf/writer.rs b/src/buf/writer.rs index 08f15d20e..e72348f40 100644 --- a/src/buf/writer.rs +++ b/src/buf/writer.rs @@ -78,7 +78,7 @@ impl io::Write for Writer { fn write(&mut self, src: &[u8]) -> io::Result { let n = cmp::min(self.buf.remaining_mut(), src.len()); - self.buf.put(&src[0..n]); + self.buf.put_slice(&src[..n]); Ok(n) } From 54f1c26f694bb24220a846958d6e414e9298cda9 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 8 Nov 2024 10:23:02 +0100 Subject: [PATCH 172/203] Rename hex_impl! to fmt_impl! and reuse it for fmt::Debug (#743) --- src/fmt/debug.rs | 13 ++----------- src/fmt/hex.rs | 18 ++++-------------- src/fmt/mod.rs | 10 ++++++++++ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/fmt/debug.rs b/src/fmt/debug.rs index 83de695dd..82d0aa5e3 100644 --- a/src/fmt/debug.rs +++ b/src/fmt/debug.rs @@ -36,14 +36,5 @@ impl Debug for BytesRef<'_> { } } -impl Debug for Bytes { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&BytesRef(self.as_ref()), f) - } -} - -impl Debug for BytesMut { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - Debug::fmt(&BytesRef(self.as_ref()), f) - } -} +fmt_impl!(Debug, Bytes); +fmt_impl!(Debug, BytesMut); diff --git a/src/fmt/hex.rs b/src/fmt/hex.rs index 97a749a33..1203b4198 100644 --- a/src/fmt/hex.rs +++ b/src/fmt/hex.rs @@ -21,17 +21,7 @@ impl UpperHex for BytesRef<'_> { } } -macro_rules! hex_impl { - ($tr:ident, $ty:ty) => { - impl $tr for $ty { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - $tr::fmt(&BytesRef(self.as_ref()), f) - } - } - }; -} - -hex_impl!(LowerHex, Bytes); -hex_impl!(LowerHex, BytesMut); -hex_impl!(UpperHex, Bytes); -hex_impl!(UpperHex, BytesMut); +fmt_impl!(LowerHex, Bytes); +fmt_impl!(LowerHex, BytesMut); +fmt_impl!(UpperHex, Bytes); +fmt_impl!(UpperHex, BytesMut); diff --git a/src/fmt/mod.rs b/src/fmt/mod.rs index 676d15fc2..b8a0eafaf 100644 --- a/src/fmt/mod.rs +++ b/src/fmt/mod.rs @@ -1,3 +1,13 @@ +macro_rules! fmt_impl { + ($tr:ident, $ty:ty) => { + impl $tr for $ty { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + $tr::fmt(&BytesRef(self.as_ref()), f) + } + } + }; +} + mod debug; mod hex; From d0a14deeb5d94839f101a02cc650a3a14949305b Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 28 Nov 2024 07:43:45 -0500 Subject: [PATCH 173/203] chore: prepare bytes v1.9.0 (#748) --- CHANGELOG.md | 17 ++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e428ae0de..34b5145e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# 1.9.0 (November 27, 2024) + +### Added + +- Add `Bytes::from_owner` to enable externally-allocated memory (#742) + +### Documented + +- Fix typo in Buf::chunk() comment (#744) + +### Internal changes + +- Replace BufMut::put with BufMut::put_slice in Writer impl (#745) +- Rename hex_impl! to fmt_impl! and reuse it for fmt::Debug (#743) + # 1.8.0 (October 21, 2024) - Guarantee address in `split_off`/`split_to` for empty slices (#740) @@ -14,7 +29,7 @@ ### Internal changes -- Ensure BytesMut::advance reduces capacity (#728) +- Ensure BytesMut::advance reduces capacity (#728) # 1.7.1 (August 1, 2024) diff --git a/Cargo.toml b/Cargo.toml index de632dd1b..ed385d197 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.8.0" +version = "1.9.0" edition = "2018" rust-version = "1.39" license = "MIT" From f7072c9267f1058f5dd2ef4273e2a91f697a1e12 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 10 Dec 2024 22:30:27 +0100 Subject: [PATCH 174/203] Run tests under panic=abort (#749) --- .github/workflows/ci.yml | 10 ++++++++++ ci/panic-abort.sh | 4 ++++ tests/test_bytes.rs | 2 ++ tests/test_bytes_vec_alloc.rs | 6 ++++-- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 ci/panic-abort.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0136c47b..bc868bbbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,16 @@ jobs: - name: Test run: ci/test-stable.sh test + panic-abort: + name: panic=abort tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update $nightly && rustup default $nightly + - name: Run tests with -Cpanic=abort + run: ci/panic-abort.sh + # Run tests on some extra platforms cross: name: cross diff --git a/ci/panic-abort.sh b/ci/panic-abort.sh new file mode 100755 index 000000000..4284791f1 --- /dev/null +++ b/ci/panic-abort.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +set -ex +RUSTFLAGS="$RUSTFLAGS -Cpanic=abort -Zpanic-abort-tests" cargo test --all-features --test '*' diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index fdc36ce8d..6ab418329 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -291,6 +291,7 @@ fn split_to_uninitialized() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore)] fn split_off_to_at_gt_len() { fn make_bytes() -> Bytes { let mut bytes = BytesMut::with_capacity(100); @@ -1618,6 +1619,7 @@ fn owned_to_vec() { } #[test] +#[cfg_attr(not(panic = "unwind"), ignore)] fn owned_safe_drop_on_as_ref_panic() { let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let drop_counter = SharedAtomicCounter::new(); diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index 107e56e58..48fea1e9a 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -7,7 +7,7 @@ use bytes::{Buf, Bytes}; #[global_allocator] static LEDGER: Ledger = Ledger::new(); -const LEDGER_LENGTH: usize = 2048; +const LEDGER_LENGTH: usize = 1024 * 1024; struct Ledger { alloc_table: [(AtomicPtr, AtomicUsize); LEDGER_LENGTH], @@ -31,9 +31,11 @@ impl Ledger { .is_ok() { entry_size.store(size, Ordering::SeqCst); - break; + return; } } + + panic!("Ledger ran out of space."); } fn remove(&self, ptr: *mut u8) -> usize { From 0e9e4fc2f1776b6b052de9e614ac1336c94291e7 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Thu, 9 Jan 2025 17:13:47 +0100 Subject: [PATCH 175/203] Expand `Buf` tests (#747) --- tests/test_buf.rs | 457 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 381 insertions(+), 76 deletions(-) diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 5aadea43e..562e2821b 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -1,88 +1,410 @@ #![warn(rust_2018_idioms)] -use bytes::Buf; +use ::bytes::{Buf, Bytes, BytesMut}; +use core::{cmp, mem}; +use std::collections::VecDeque; #[cfg(feature = "std")] use std::io::IoSlice; -#[test] -fn test_fresh_cursor_vec() { - let mut buf = &b"hello"[..]; +// A random 64-byte ascii string, with the first 8 bytes altered to +// give valid representations of f32 and f64 (making them easier to compare) +// and negative signed numbers when interpreting as big endian +// (testing Sign Extension for `Buf::get_int' and `Buf::get_int_ne`). +const INPUT: &[u8] = b"\xffFqrjrDqPhvTc45vvq33f6bJrUtyHESuTeklWKgYd64xgzxJwvAkpYYnpNJyZSRn"; + +macro_rules! e { + ($big_endian_val:expr, $little_endian_val:expr) => { + if cfg!(target_endian = "big") { + $big_endian_val + } else { + $little_endian_val + } + }; +} + +macro_rules! buf_tests { + ($make_input:ident) => { + buf_tests!($make_input, true); + }; + ($make_input:ident, $checks_vectored_is_complete:expr) => { + use super::*; + + #[test] + fn empty_state() { + let buf = $make_input(&[]); + assert_eq!(buf.remaining(), 0); + assert!(!buf.has_remaining()); + assert!(buf.chunk().is_empty()); + } + + #[test] + fn fresh_state() { + let buf = $make_input(INPUT); + assert_eq!(buf.remaining(), 64); + assert!(buf.has_remaining()); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64); + assert!(INPUT.starts_with(chunk)); + } + + #[test] + fn advance() { + let mut buf = $make_input(INPUT); + buf.advance(8); + assert_eq!(buf.remaining(), 64 - 8); + assert!(buf.has_remaining()); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64 - 8); + assert!(INPUT[8..].starts_with(chunk)); + } + + #[test] + fn advance_to_end() { + let mut buf = $make_input(INPUT); + buf.advance(64); + assert_eq!(buf.remaining(), 0); + assert!(!buf.has_remaining()); + + let chunk = buf.chunk(); + assert!(chunk.is_empty()); + } + + #[test] + #[should_panic] + fn advance_past_end() { + let mut buf = $make_input(INPUT); + buf.advance(65); + } + + #[test] + #[cfg(feature = "std")] + fn chunks_vectored_empty() { + let buf = $make_input(&[]); + let mut bufs = [IoSlice::new(&[]); 16]; + + let n = buf.chunks_vectored(&mut bufs); + assert_eq!(n, 0); + assert!(bufs.iter().all(|buf| buf.is_empty())); + } + + #[test] + #[cfg(feature = "std")] + fn chunks_vectored_is_complete() { + let buf = $make_input(INPUT); + let mut bufs = [IoSlice::new(&[]); 16]; + + let n = buf.chunks_vectored(&mut bufs); + assert!(n > 0); + assert!(n <= 16); + + let bufs_concat = bufs[..n] + .iter() + .flat_map(|b| b.iter().copied()) + .collect::>(); + if $checks_vectored_is_complete { + assert_eq!(bufs_concat, INPUT); + } else { + // If this panics then `buf` implements `chunks_vectored`. + // Remove the `false` argument from `buf_tests!` for that type. + assert!(bufs_concat.len() < INPUT.len()); + assert!(INPUT.starts_with(&bufs_concat)); + } + + for i in n..16 { + assert!(bufs[i].is_empty()); + } + } + + #[test] + fn copy_to_slice() { + let mut buf = $make_input(INPUT); + + let mut chunk = [0u8; 8]; + buf.copy_to_slice(&mut chunk); + assert_eq!(buf.remaining(), 64 - 8); + assert!(buf.has_remaining()); + assert_eq!(chunk, INPUT[..8]); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64 - 8); + assert!(INPUT[8..].starts_with(chunk)); + } + + #[test] + fn copy_to_slice_big() { + let mut buf = $make_input(INPUT); + + let mut chunk = [0u8; 56]; + buf.copy_to_slice(&mut chunk); + assert_eq!(buf.remaining(), 64 - 56); + assert!(buf.has_remaining()); + assert_eq!(chunk, INPUT[..56]); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64 - 56); + assert!(INPUT[56..].starts_with(chunk)); + } + + #[test] + fn copy_to_slice_to_end() { + let mut buf = $make_input(INPUT); + + let mut chunk = [0u8; 64]; + buf.copy_to_slice(&mut chunk); + assert_eq!(buf.remaining(), 0); + assert!(!buf.has_remaining()); + assert_eq!(chunk, INPUT); + + assert!(buf.chunk().is_empty()); + } + + #[test] + #[should_panic] + fn copy_to_slice_overflow() { + let mut buf = $make_input(INPUT); + + let mut chunk = [0u8; 65]; + buf.copy_to_slice(&mut chunk); + } + + #[test] + fn copy_to_bytes() { + let mut buf = $make_input(INPUT); + + let chunk = buf.copy_to_bytes(8); + assert_eq!(buf.remaining(), 64 - 8); + assert!(buf.has_remaining()); + assert_eq!(chunk, INPUT[..8]); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64 - 8); + assert!(INPUT[8..].starts_with(chunk)); + } + + #[test] + fn copy_to_bytes_big() { + let mut buf = $make_input(INPUT); + + let chunk = buf.copy_to_bytes(56); + assert_eq!(buf.remaining(), 64 - 56); + assert!(buf.has_remaining()); + assert_eq!(chunk, INPUT[..56]); + + let chunk = buf.chunk(); + assert!(chunk.len() <= 64 - 56); + assert!(INPUT[56..].starts_with(chunk)); + } + + #[test] + fn copy_to_bytes_to_end() { + let mut buf = $make_input(INPUT); + + let chunk = buf.copy_to_bytes(64); + assert_eq!(buf.remaining(), 0); + assert!(!buf.has_remaining()); + assert_eq!(chunk, INPUT); + + assert!(buf.chunk().is_empty()); + } - assert_eq!(buf.remaining(), 5); - assert_eq!(buf.chunk(), b"hello"); + #[test] + #[should_panic] + fn copy_to_bytes_overflow() { + let mut buf = $make_input(INPUT); - buf.advance(2); + let _ = buf.copy_to_bytes(65); + } + + buf_tests!(number $make_input, get_u8, get_u8_overflow, u8, get_u8, 0xff); + buf_tests!(number $make_input, get_i8, get_i8_overflow, i8, get_i8, 0xffu8 as i8); + buf_tests!(number $make_input, get_u16_be, get_u16_be_overflow, u16, get_u16, 0xff46); + buf_tests!(number $make_input, get_u16_le, get_u16_le_overflow, u16, get_u16_le, 0x46ff); + buf_tests!(number $make_input, get_u16_ne, get_u16_ne_overflow, u16, get_u16_ne, e!(0xff46, 0x46ff)); + buf_tests!(number $make_input, get_i16_be, get_i16_be_overflow, i16, get_i16, 0xff46u16 as i16); + buf_tests!(number $make_input, get_i16_le, get_i16_le_overflow, i16, get_i16_le, 0x46ff); + buf_tests!(number $make_input, get_i16_ne, get_i16_ne_overflow, i16, get_i16_ne, e!(0xff46u16 as i16, 0x46ff)); + buf_tests!(number $make_input, get_u32_be, get_u32_be_overflow, u32, get_u32, 0xff467172); + buf_tests!(number $make_input, get_u32_le, get_u32_le_overflow, u32, get_u32_le, 0x727146ff); + buf_tests!(number $make_input, get_u32_ne, get_u32_ne_overflow, u32, get_u32_ne, e!(0xff467172, 0x727146ff)); + buf_tests!(number $make_input, get_i32_be, get_i32_be_overflow, i32, get_i32, 0xff467172u32 as i32); + buf_tests!(number $make_input, get_i32_le, get_i32_le_overflow, i32, get_i32_le, 0x727146ff); + buf_tests!(number $make_input, get_i32_ne, get_i32_ne_overflow, i32, get_i32_ne, e!(0xff467172u32 as i32, 0x727146ff)); + buf_tests!(number $make_input, get_u64_be, get_u64_be_overflow, u64, get_u64, 0xff4671726a724471); + buf_tests!(number $make_input, get_u64_le, get_u64_le_overflow, u64, get_u64_le, 0x7144726a727146ff); + buf_tests!(number $make_input, get_u64_ne, get_u64_ne_overflow, u64, get_u64_ne, e!(0xff4671726a724471, 0x7144726a727146ff)); + buf_tests!(number $make_input, get_i64_be, get_i64_be_overflow, i64, get_i64, 0xff4671726a724471u64 as i64); + buf_tests!(number $make_input, get_i64_le, get_i64_le_overflow, i64, get_i64_le, 0x7144726a727146ff); + buf_tests!(number $make_input, get_i64_ne, get_i64_ne_overflow, i64, get_i64_ne, e!(0xff4671726a724471u64 as i64, 0x7144726a727146ff)); + buf_tests!(number $make_input, get_u128_be, get_u128_be_overflow, u128, get_u128, 0xff4671726a7244715068765463343576); + buf_tests!(number $make_input, get_u128_le, get_u128_le_overflow, u128, get_u128_le, 0x76353463547668507144726a727146ff); + buf_tests!(number $make_input, get_u128_ne, get_u128_ne_overflow, u128, get_u128_ne, e!(0xff4671726a7244715068765463343576, 0x76353463547668507144726a727146ff)); + buf_tests!(number $make_input, get_i128_be, get_i128_be_overflow, i128, get_i128, 0xff4671726a7244715068765463343576u128 as i128); + buf_tests!(number $make_input, get_i128_le, get_i128_le_overflow, i128, get_i128_le, 0x76353463547668507144726a727146ff); + buf_tests!(number $make_input, get_i128_ne, get_i128_ne_overflow, i128, get_i128_ne, e!(0xff4671726a7244715068765463343576u128 as i128, 0x76353463547668507144726a727146ff)); + buf_tests!(number $make_input, get_f32_be, get_f32_be_overflow, f32, get_f32, f32::from_bits(0xff467172)); + buf_tests!(number $make_input, get_f32_le, get_f32_le_overflow, f32, get_f32_le, f32::from_bits(0x727146ff)); + buf_tests!(number $make_input, get_f32_ne, get_f32_ne_overflow, f32, get_f32_ne, f32::from_bits(e!(0xff467172, 0x727146ff))); + buf_tests!(number $make_input, get_f64_be, get_f64_be_overflow, f64, get_f64, f64::from_bits(0xff4671726a724471)); + buf_tests!(number $make_input, get_f64_le, get_f64_le_overflow, f64, get_f64_le, f64::from_bits(0x7144726a727146ff)); + buf_tests!(number $make_input, get_f64_ne, get_f64_ne_overflow, f64, get_f64_ne, f64::from_bits(e!(0xff4671726a724471, 0x7144726a727146ff))); + + buf_tests!(var_number $make_input, get_uint_be, get_uint_be_overflow, u64, get_uint, 3, 0xff4671); + buf_tests!(var_number $make_input, get_uint_le, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff); + buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff)); + buf_tests!(var_number $make_input, get_int_be, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64); + buf_tests!(var_number $make_input, get_int_le, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff); + buf_tests!(var_number $make_input, get_int_ne, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff)); + }; + (number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $value:expr) => { + #[test] + fn $ok_name() { + let mut buf = $make_input(INPUT); + + let value = buf.$method(); + assert_eq!(buf.remaining(), 64 - mem::size_of::<$number>()); + assert!(buf.has_remaining()); + assert_eq!(value, $value); + } + + #[test] + #[should_panic] + fn $panic_name() { + let mut buf = $make_input(&[]); + + let _ = buf.$method(); + } + }; + (var_number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => { + #[test] + fn $ok_name() { + let mut buf = $make_input(INPUT); - assert_eq!(buf.remaining(), 3); - assert_eq!(buf.chunk(), b"llo"); + let value = buf.$method($len); + assert_eq!(buf.remaining(), 64 - $len); + assert!(buf.has_remaining()); + assert_eq!(value, $value); + } - buf.advance(3); + #[test] + #[should_panic] + fn $panic_name() { + let mut buf = $make_input(&[]); - assert_eq!(buf.remaining(), 0); - assert_eq!(buf.chunk(), b""); + let _ = buf.$method($len); + } + }; } -#[test] -fn test_get_u8() { - let mut buf = &b"\x21zomg"[..]; - assert_eq!(0x21, buf.get_u8()); +mod u8_slice { + fn make_input(buf: &'static [u8]) -> &'static [u8] { + buf + } + + buf_tests!(make_input); } -#[test] -fn test_get_u16() { - let mut buf = &b"\x21\x54zomg"[..]; - assert_eq!(0x2154, buf.get_u16()); - let mut buf = &b"\x21\x54zomg"[..]; - assert_eq!(0x5421, buf.get_u16_le()); +mod bytes { + fn make_input(buf: &'static [u8]) -> impl Buf { + Bytes::from_static(buf) + } + + buf_tests!(make_input); } -#[test] -fn test_get_int() { - let mut buf = &b"\xd6zomg"[..]; - assert_eq!(-42, buf.get_int(1)); - let mut buf = &b"\xd6zomg"[..]; - assert_eq!(-42, buf.get_int_le(1)); - - let mut buf = &b"\xfe\x1d\xc0zomg"[..]; - assert_eq!(0xffffffffffc01dfeu64 as i64, buf.get_int_le(3)); - let mut buf = &b"\xfe\x1d\xc0zomg"[..]; - assert_eq!(0xfffffffffffe1dc0u64 as i64, buf.get_int(3)); +mod bytes_mut { + fn make_input(buf: &'static [u8]) -> impl Buf { + BytesMut::from(buf) + } + + buf_tests!(make_input); } -#[test] -#[should_panic] -fn test_get_u16_buffer_underflow() { - let mut buf = &b"\x21"[..]; - buf.get_u16(); +mod vec_deque { + fn make_input(buf: &'static [u8]) -> impl Buf { + let mut deque = VecDeque::new(); + + if !buf.is_empty() { + // Construct |b|some bytes|a| `VecDeque` + let mid = buf.len() / 2; + let (a, b) = buf.split_at(mid); + + deque.reserve_exact(buf.len() + 1); + + let extra_space = deque.capacity() - b.len() - 1; + deque.resize(extra_space, 0); + + deque.extend(a); + deque.drain(..extra_space); + deque.extend(b); + + let (a, b) = deque.as_slices(); + assert!( + !a.is_empty(), + "could not setup test - attempt to create discontiguous VecDeque failed" + ); + assert!( + !b.is_empty(), + "could not setup test - attempt to create discontiguous VecDeque failed" + ); + } + + deque + } + + buf_tests!(make_input, /* `VecDeque` does not do `chucks_vectored */ false); } #[cfg(feature = "std")] -#[test] -fn test_bufs_vec() { - let buf = &b"hello world"[..]; +mod cursor { + use std::io::Cursor; - let b1: &[u8] = &mut []; - let b2: &[u8] = &mut []; + fn make_input(buf: &'static [u8]) -> impl Buf { + Cursor::new(buf) + } - let mut dst = [IoSlice::new(b1), IoSlice::new(b2)]; + buf_tests!(make_input); +} + +mod box_bytes { + fn make_input(buf: &'static [u8]) -> impl Buf { + Box::new(Bytes::from_static(buf)) + } - assert_eq!(1, buf.chunks_vectored(&mut dst[..])); + buf_tests!(make_input); } -#[test] -fn test_vec_deque() { - use std::collections::VecDeque; - - let mut buffer: VecDeque = VecDeque::new(); - buffer.extend(b"hello world"); - assert_eq!(11, buffer.remaining()); - assert_eq!(b"hello world", buffer.chunk()); - buffer.advance(6); - assert_eq!(b"world", buffer.chunk()); - buffer.extend(b" piece"); - let mut out = [0; 11]; - buffer.copy_to_slice(&mut out); - assert_eq!(b"world piece", &out[..]); +mod chain_u8_slice { + fn make_input(buf: &'static [u8]) -> impl Buf { + let (a, b) = buf.split_at(buf.len() / 2); + Buf::chain(a, b) + } + + buf_tests!(make_input); +} + +mod chain_small_big_u8_slice { + fn make_input(buf: &'static [u8]) -> impl Buf { + let mid = cmp::min(1, buf.len()); + let (a, b) = buf.split_at(mid); + Buf::chain(a, b) + } + + buf_tests!(make_input); +} + +mod chain_limited_slices { + fn make_input(buf: &'static [u8]) -> impl Buf { + let buf3 = &buf[cmp::min(buf.len(), 3)..]; + let a = Buf::take(buf3, 0); + let b = Buf::take(buf, 3); + let c = Buf::take(buf3, usize::MAX); + let d = buf; + Buf::take(Buf::chain(Buf::chain(a, b), Buf::chain(c, d)), buf.len()) + } + + buf_tests!(make_input, /* `Limit` does not forward `chucks_vectored */ false); } #[allow(unused_allocation)] // This is intentional. @@ -115,20 +437,3 @@ fn test_deref_buf_forwards() { assert_eq!((Box::new(Special) as Box).get_u8(), b'x'); assert_eq!(Box::new(Special).get_u8(), b'x'); } - -#[test] -fn copy_to_bytes_less() { - let mut buf = &b"hello world"[..]; - - let bytes = buf.copy_to_bytes(5); - assert_eq!(bytes, &b"hello"[..]); - assert_eq!(buf, &b" world"[..]) -} - -#[test] -#[should_panic] -fn copy_to_bytes_overflow() { - let mut buf = &b"hello world"[..]; - - let _bytes = buf.copy_to_bytes(12); -} From 16cc574feae9057f9550f7802bb768ab0db759ab Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 10 Jan 2025 14:40:12 +0100 Subject: [PATCH 176/203] Implement `Buf::chunks_vectored` for `VecDeque` (#708) --- src/buf/vec_deque.rs | 18 ++++++++++++++++++ tests/test_buf.rs | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/buf/vec_deque.rs b/src/buf/vec_deque.rs index 263167e83..55d5636b1 100644 --- a/src/buf/vec_deque.rs +++ b/src/buf/vec_deque.rs @@ -1,4 +1,6 @@ use alloc::collections::VecDeque; +#[cfg(feature = "std")] +use std::io; use super::Buf; @@ -16,6 +18,22 @@ impl Buf for VecDeque { } } + #[cfg(feature = "std")] + fn chunks_vectored<'a>(&'a self, dst: &mut [io::IoSlice<'a>]) -> usize { + if self.is_empty() || dst.is_empty() { + return 0; + } + + let (s1, s2) = self.as_slices(); + dst[0] = io::IoSlice::new(s1); + if s2.is_empty() || dst.len() == 1 { + return 1; + } + + dst[1] = io::IoSlice::new(s2); + 2 + } + fn advance(&mut self, cnt: usize) { self.drain(..cnt); } diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 562e2821b..5a5ac7e80 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -353,7 +353,7 @@ mod vec_deque { deque } - buf_tests!(make_input, /* `VecDeque` does not do `chucks_vectored */ false); + buf_tests!(make_input, true); } #[cfg(feature = "std")] From 103d7bf9e030fd4ce009092e8da8f2ff3251e261 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 10 Jan 2025 23:13:52 +0100 Subject: [PATCH 177/203] Remove incorrect guarantee for `chunks_vectored` (#754) --- src/buf/buf_impl.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index b09d356bd..ab715e434 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -164,12 +164,12 @@ pub trait Buf { /// fetching more than one slice at once. `dst` is a slice of `IoSlice` /// references, enabling the slice to be directly used with [`writev`] /// without any further conversion. The sum of the lengths of all the - /// buffers in `dst` will be less than or equal to `Buf::remaining()`. + /// buffers written to `dst` will be less than or equal to `Buf::remaining()`. /// /// The entries in `dst` will be overwritten, but the data **contained** by - /// the slices **will not** be modified. If `chunk_vectored` does not fill every - /// entry in `dst`, then `dst` is guaranteed to contain all remaining slices - /// in `self. + /// the slices **will not** be modified. The return value is the number of + /// slices written to `dst`. If `Buf::remaining()` is non-zero, then this + /// writes at least one non-empty slice to `dst`. /// /// This is a lower level function. Most operations are done with other /// functions. From aae4969fde8a3b2bc091c1fbbad51071a3fa542c Mon Sep 17 00:00:00 2001 From: Sam Rijs Date: Mon, 13 Jan 2025 13:53:52 +0100 Subject: [PATCH 178/203] Add specialized `Buf::chunks_vectored` for `Take` (#617) Co-authored-by: Alice Ryhl Co-authored-by: Michal 'vorner' Vaner --- src/buf/take.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ tests/test_buf.rs | 2 +- tests/test_take.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/buf/take.rs b/src/buf/take.rs index a16a434ee..fc4e39dda 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -2,6 +2,9 @@ use crate::{Buf, Bytes}; use core::cmp; +#[cfg(feature = "std")] +use std::io::IoSlice; + /// A `Buf` adapter which limits the bytes read from an underlying buffer. /// /// This struct is generally created by calling `take()` on `Buf`. See @@ -152,4 +155,50 @@ impl Buf for Take { self.limit -= len; r } + + #[cfg(feature = "std")] + fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize { + if self.limit == 0 { + return 0; + } + + const LEN: usize = 16; + let mut slices: [IoSlice<'a>; LEN] = [ + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + IoSlice::new(&[]), + ]; + + let cnt = self + .inner + .chunks_vectored(&mut slices[..dst.len().min(LEN)]); + let mut limit = self.limit; + for (i, (dst, slice)) in dst[..cnt].iter_mut().zip(slices.iter()).enumerate() { + if let Some(buf) = slice.get(..limit) { + // SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV. + let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(buf) }; + *dst = IoSlice::new(buf); + return i + 1; + } else { + // SAFETY: We could do this safely with `IoSlice::advance` if we had a larger MSRV. + let buf = unsafe { std::mem::transmute::<&[u8], &'a [u8]>(slice) }; + *dst = IoSlice::new(buf); + limit -= slice.len(); + } + } + cnt + } } diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 5a5ac7e80..099016e24 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -404,7 +404,7 @@ mod chain_limited_slices { Buf::take(Buf::chain(Buf::chain(a, b), Buf::chain(c, d)), buf.len()) } - buf_tests!(make_input, /* `Limit` does not forward `chucks_vectored */ false); + buf_tests!(make_input, true); } #[allow(unused_allocation)] // This is intentional. diff --git a/tests/test_take.rs b/tests/test_take.rs index 51df91d14..0c0159be1 100644 --- a/tests/test_take.rs +++ b/tests/test_take.rs @@ -30,3 +30,55 @@ fn take_copy_to_bytes_panics() { let abcd = Bytes::copy_from_slice(b"abcd"); abcd.take(2).copy_to_bytes(3); } + +#[cfg(feature = "std")] +#[test] +fn take_chunks_vectored() { + fn chain() -> impl Buf { + Bytes::from([1, 2, 3].to_vec()).chain(Bytes::from([4, 5, 6].to_vec())) + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(0); + assert_eq!(take.chunks_vectored(&mut dst), 0); + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(1); + assert_eq!(take.chunks_vectored(&mut dst), 1); + assert_eq!(&*dst[0], &[1]); + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(3); + assert_eq!(take.chunks_vectored(&mut dst), 1); + assert_eq!(&*dst[0], &[1, 2, 3]); + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(4); + assert_eq!(take.chunks_vectored(&mut dst), 2); + assert_eq!(&*dst[0], &[1, 2, 3]); + assert_eq!(&*dst[1], &[4]); + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(6); + assert_eq!(take.chunks_vectored(&mut dst), 2); + assert_eq!(&*dst[0], &[1, 2, 3]); + assert_eq!(&*dst[1], &[4, 5, 6]); + } + + { + let mut dst = [std::io::IoSlice::new(&[]); 2]; + let take = chain().take(7); + assert_eq!(take.chunks_vectored(&mut dst), 2); + assert_eq!(&*dst[0], &[1, 2, 3]); + assert_eq!(&*dst[1], &[4, 5, 6]); + } +} From a1b120812709a5ef88b55871dddcc928279dfa99 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 13 Jan 2025 16:28:28 +0100 Subject: [PATCH 179/203] Disable test_bytes_vec_alloc test on miri (#755) --- tests/test_bytes_vec_alloc.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bytes_vec_alloc.rs b/tests/test_bytes_vec_alloc.rs index 48fea1e9a..9c7601717 100644 --- a/tests/test_bytes_vec_alloc.rs +++ b/tests/test_bytes_vec_alloc.rs @@ -1,3 +1,4 @@ +#![cfg(not(miri))] use std::alloc::{GlobalAlloc, Layout, System}; use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; From 3ab876fee66393b064c2adac83d28fca46186075 Mon Sep 17 00:00:00 2001 From: hpenne Date: Tue, 21 Jan 2025 13:41:42 +0100 Subject: [PATCH 180/203] Try get methods for Buf trait (#753) --- src/buf/buf_impl.rs | 1748 +++++++++++++++++++++++++++++++++++++++---- src/buf/buf_mut.rs | 52 +- src/bytes_mut.rs | 7 +- src/lib.rs | 39 +- 4 files changed, 1702 insertions(+), 144 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index ab715e434..192034fbe 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -3,19 +3,22 @@ use crate::buf::{reader, Reader}; use crate::buf::{take, Chain, Take}; #[cfg(feature = "std")] use crate::{min_u64_usize, saturating_sub_usize_u64}; -use crate::{panic_advance, panic_does_not_fit}; +use crate::{panic_advance, panic_does_not_fit, TryGetError}; #[cfg(feature = "std")] use std::io::IoSlice; use alloc::boxed::Box; -macro_rules! buf_get_impl { +macro_rules! buf_try_get_impl { ($this:ident, $typ:tt::$conv:tt) => {{ const SIZE: usize = core::mem::size_of::<$typ>(); if $this.remaining() < SIZE { - panic_advance(SIZE, $this.remaining()); + return Err(TryGetError { + requested: SIZE, + available: $this.remaining(), + }); } // try to convert directly from the bytes @@ -29,12 +32,12 @@ macro_rules! buf_get_impl { if let Some(ret) = ret { // if the direct conversion was possible, advance and return $this.advance(SIZE); - return ret; + return Ok(ret); } else { // if not we copy the bytes in a temp buffer then convert let mut buf = [0; SIZE]; $this.copy_to_slice(&mut buf); // (do the advance) - return $typ::$conv(buf); + return Ok($typ::$conv(buf)); } }}; (le => $this:ident, $typ:tt, $len_to_read:expr) => {{ @@ -49,8 +52,8 @@ macro_rules! buf_get_impl { None => panic_does_not_fit(SIZE, $len_to_read), }; - $this.copy_to_slice(subslice); - return $typ::from_le_bytes(buf); + $this.try_copy_to_slice(subslice)?; + return Ok($typ::from_le_bytes(buf)); }}; (be => $this:ident, $typ:tt, $len_to_read:expr) => {{ const SIZE: usize = core::mem::size_of::<$typ>(); @@ -61,8 +64,23 @@ macro_rules! buf_get_impl { }; let mut buf = [0; SIZE]; - $this.copy_to_slice(&mut buf[slice_at..]); - return $typ::from_be_bytes(buf); + $this.try_copy_to_slice(&mut buf[slice_at..])?; + return Ok($typ::from_be_bytes(buf)); + }}; +} + +macro_rules! buf_get_impl { + ($this:ident, $typ:tt::$conv:tt) => {{ + return (|| buf_try_get_impl!($this, $typ::$conv))() + .unwrap_or_else(|error| panic_advance(&error)); + }}; + (le => $this:ident, $typ:tt, $len_to_read:expr) => {{ + return (|| buf_try_get_impl!(le => $this, $typ, $len_to_read))() + .unwrap_or_else(|error| panic_advance(&error)); + }}; + (be => $this:ident, $typ:tt, $len_to_read:expr) => {{ + return (|| buf_try_get_impl!(be => $this, $typ, $len_to_read))() + .unwrap_or_else(|error| panic_advance(&error)); }}; } @@ -273,20 +291,9 @@ pub trait Buf { /// # Panics /// /// This function panics if `self.remaining() < dst.len()`. - fn copy_to_slice(&mut self, mut dst: &mut [u8]) { - if self.remaining() < dst.len() { - panic_advance(dst.len(), self.remaining()); - } - - while !dst.is_empty() { - let src = self.chunk(); - let cnt = usize::min(src.len(), dst.len()); - - dst[..cnt].copy_from_slice(&src[..cnt]); - dst = &mut dst[cnt..]; - - self.advance(cnt); - } + fn copy_to_slice(&mut self, dst: &mut [u8]) { + self.try_copy_to_slice(dst) + .unwrap_or_else(|error| panic_advance(&error)); } /// Gets an unsigned 8 bit integer from `self`. @@ -307,7 +314,10 @@ pub trait Buf { /// This function panics if there is no more remaining data in `self`. fn get_u8(&mut self) -> u8 { if self.remaining() < 1 { - panic_advance(1, 0); + panic_advance(&TryGetError { + requested: 1, + available: 0, + }) } let ret = self.chunk()[0]; self.advance(1); @@ -332,7 +342,10 @@ pub trait Buf { /// This function panics if there is no more remaining data in `self`. fn get_i8(&mut self) -> i8 { if self.remaining() < 1 { - panic_advance(1, 0); + panic_advance(&TryGetError { + requested: 1, + available: 0, + }); } let ret = self.chunk()[0] as i8; self.advance(1); @@ -858,7 +871,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_uint(&mut self, nbytes: usize) -> u64 { buf_get_impl!(be => self, u64, nbytes); } @@ -878,7 +892,8 @@ pub trait Buf { /// /// # Panics /// - /// This function panics if there is not enough remaining data in `self`. + /// This function panics if there is not enough remaining data in `self`, or + /// if `nbytes` is greater than 8. fn get_uint_le(&mut self, nbytes: usize) -> u64 { buf_get_impl!(le => self, u64, nbytes); } @@ -1113,156 +1128,1367 @@ pub trait Buf { f64::from_bits(self.get_u64_ne()) } - /// Consumes `len` bytes inside self and returns new instance of `Bytes` - /// with this data. + /// Copies bytes from `self` into `dst`. /// - /// This function may be optimized by the underlying type to avoid actual - /// copies. For example, `Bytes` implementation will do a shallow copy - /// (ref-count increment). + /// The cursor is advanced by the number of bytes copied. `self` must have + /// enough remaining bytes to fill `dst`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. /// /// # Examples /// /// ``` /// use bytes::Buf; /// - /// let bytes = (&b"hello world"[..]).copy_to_bytes(5); - /// assert_eq!(&bytes[..], &b"hello"[..]); + /// let mut buf = &b"hello world"[..]; + /// let mut dst = [0; 5]; + /// + /// assert_eq!(Ok(()), buf.try_copy_to_slice(&mut dst)); + /// assert_eq!(&b"hello"[..], &dst); + /// assert_eq!(6, buf.remaining()); /// ``` /// - /// # Panics + /// ``` + /// use bytes::{Buf, TryGetError}; /// - /// This function panics if `len > self.remaining()`. - fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { - use super::BufMut; - - if self.remaining() < len { - panic_advance(len, self.remaining()); + /// let mut buf = &b"hello world"[..]; + /// let mut dst = [0; 12]; + /// + /// assert_eq!(Err(TryGetError{requested: 12, available: 11}), buf.try_copy_to_slice(&mut dst)); + /// assert_eq!(11, buf.remaining()); + /// ``` + fn try_copy_to_slice(&mut self, mut dst: &mut [u8]) -> Result<(), TryGetError> { + if self.remaining() < dst.len() { + return Err(TryGetError { + requested: dst.len(), + available: self.remaining(), + }); } - let mut ret = crate::BytesMut::with_capacity(len); - ret.put(self.take(len)); - ret.freeze() + while !dst.is_empty() { + let src = self.chunk(); + let cnt = usize::min(src.len(), dst.len()); + + dst[..cnt].copy_from_slice(&src[..cnt]); + dst = &mut dst[cnt..]; + + self.advance(cnt); + } + Ok(()) } - /// Creates an adaptor which will read at most `limit` bytes from `self`. + /// Gets an unsigned 8 bit integer from `self`. /// - /// This function returns a new instance of `Buf` which will read at most - /// `limit` bytes. + /// The current position is advanced by 1. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. /// /// # Examples /// /// ``` - /// use bytes::{Buf, BufMut}; + /// use bytes::Buf; /// - /// let mut buf = b"hello world"[..].take(5); - /// let mut dst = vec![]; + /// let mut buf = &b"\x08 hello"[..]; + /// assert_eq!(Ok(0x08_u8), buf.try_get_u8()); + /// assert_eq!(6, buf.remaining()); + /// ``` /// - /// dst.put(&mut buf); - /// assert_eq!(dst, b"hello"); + /// ``` + /// use bytes::{Buf, TryGetError}; /// - /// let mut buf = buf.into_inner(); - /// dst.clear(); - /// dst.put(&mut buf); - /// assert_eq!(dst, b" world"); + /// let mut buf = &b""[..]; + /// assert_eq!(Err(TryGetError{requested: 1, available: 0}), buf.try_get_u8()); /// ``` - fn take(self, limit: usize) -> Take - where - Self: Sized, - { - take::new(self, limit) + fn try_get_u8(&mut self) -> Result { + if self.remaining() < 1 { + return Err(TryGetError { + requested: 1, + available: self.remaining(), + }); + } + let ret = self.chunk()[0]; + self.advance(1); + Ok(ret) } - /// Creates an adaptor which will chain this buffer with another. + /// Gets a signed 8 bit integer from `self`. /// - /// The returned `Buf` instance will first consume all bytes from `self`. - /// Afterwards the output is equivalent to the output of next. + /// The current position is advanced by 1. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. /// /// # Examples /// /// ``` /// use bytes::Buf; /// - /// let mut chain = b"hello "[..].chain(&b"world"[..]); + /// let mut buf = &b"\x08 hello"[..]; + /// assert_eq!(Ok(0x08_i8), buf.try_get_i8()); + /// assert_eq!(6, buf.remaining()); + /// ``` /// - /// let full = chain.copy_to_bytes(11); - /// assert_eq!(full.chunk(), b"hello world"); /// ``` - fn chain(self, next: U) -> Chain - where - Self: Sized, - { - Chain::new(self, next) + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b""[..]; + /// assert_eq!(Err(TryGetError{requested: 1, available: 0}), buf.try_get_i8()); + /// ``` + fn try_get_i8(&mut self) -> Result { + if self.remaining() < 1 { + return Err(TryGetError { + requested: 1, + available: self.remaining(), + }); + } + let ret = self.chunk()[0] as i8; + self.advance(1); + Ok(ret) } - /// Creates an adaptor which implements the `Read` trait for `self`. + /// Gets an unsigned 16 bit integer from `self` in big-endian byte order. /// - /// This function returns a new value which implements `Read` by adapting - /// the `Read` trait functions to the `Buf` trait functions. Given that - /// `Buf` operations are infallible, none of the `Read` functions will - /// return with `Err`. + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. /// /// # Examples /// /// ``` - /// use bytes::{Bytes, Buf}; - /// use std::io::Read; - /// - /// let buf = Bytes::from("hello world"); + /// use bytes::Buf; /// - /// let mut reader = buf.reader(); - /// let mut dst = [0; 1024]; + /// let mut buf = &b"\x08\x09 hello"[..]; + /// assert_eq!(Ok(0x0809_u16), buf.try_get_u16()); + /// assert_eq!(6, buf.remaining()); + /// ``` /// - /// let num = reader.read(&mut dst).unwrap(); + /// ``` + /// use bytes::{Buf, TryGetError}; /// - /// assert_eq!(11, num); - /// assert_eq!(&dst[..11], &b"hello world"[..]); + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_u16()); + /// assert_eq!(1, buf.remaining()); /// ``` - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - fn reader(self) -> Reader - where - Self: Sized, - { - reader::new(self) + fn try_get_u16(&mut self) -> Result { + buf_try_get_impl!(self, u16::from_be_bytes) } -} - -macro_rules! deref_forward_buf { - () => { - #[inline] - fn remaining(&self) -> usize { - (**self).remaining() - } - - #[inline] - fn chunk(&self) -> &[u8] { - (**self).chunk() - } - #[cfg(feature = "std")] - #[inline] - fn chunks_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { - (**self).chunks_vectored(dst) - } + /// Gets an unsigned 16 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x09\x08 hello"[..]; + /// assert_eq!(Ok(0x0809_u16), buf.try_get_u16_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_u16_le()); + /// assert_eq!(1, buf.remaining()); + /// ``` + fn try_get_u16_le(&mut self) -> Result { + buf_try_get_impl!(self, u16::from_le_bytes) + } - #[inline] - fn advance(&mut self, cnt: usize) { - (**self).advance(cnt) - } + /// Gets an unsigned 16 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0809_u16), buf.try_get_u16_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_u16_ne()); + /// assert_eq!(1, buf.remaining()); + /// ``` + fn try_get_u16_ne(&mut self) -> Result { + buf_try_get_impl!(self, u16::from_ne_bytes) + } - #[inline] - fn has_remaining(&self) -> bool { - (**self).has_remaining() - } + /// Gets a signed 16 bit integer from `self` in big-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x08\x09 hello"[..]; + /// assert_eq!(Ok(0x0809_i16), buf.try_get_i16()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_i16()); + /// assert_eq!(1, buf.remaining()); + /// ``` + fn try_get_i16(&mut self) -> Result { + buf_try_get_impl!(self, i16::from_be_bytes) + } - #[inline] - fn copy_to_slice(&mut self, dst: &mut [u8]) { - (**self).copy_to_slice(dst) - } + /// Gets an signed 16 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x09\x08 hello"[..]; + /// assert_eq!(Ok(0x0809_i16), buf.try_get_i16_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_i16_le()); + /// assert_eq!(1, buf.remaining()); + /// ``` + fn try_get_i16_le(&mut self) -> Result { + buf_try_get_impl!(self, i16::from_le_bytes) + } - #[inline] - fn get_u8(&mut self) -> u8 { - (**self).get_u8() + /// Gets a signed 16 bit integer from `self` in native-endian byte order. + /// + /// The current position is advanced by 2. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0809_i16), buf.try_get_i16_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08"[..]; + /// assert_eq!(Err(TryGetError{requested: 2, available: 1}), buf.try_get_i16_ne()); + /// assert_eq!(1, buf.remaining()); + /// ``` + fn try_get_i16_ne(&mut self) -> Result { + buf_try_get_impl!(self, i16::from_ne_bytes) + } + + /// Gets an unsigned 32 bit integer from `self` in big-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x08\x09\xA0\xA1 hello"[..]; + /// assert_eq!(Ok(0x0809A0A1), buf.try_get_u32()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_u32()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_u32(&mut self) -> Result { + buf_try_get_impl!(self, u32::from_be_bytes) + } + + /// Gets an unsigned 32 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\xA1\xA0\x09\x08 hello"[..]; + /// assert_eq!(Ok(0x0809A0A1_u32), buf.try_get_u32_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x09\xA0"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_u32_le()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_u32_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0809A0A1_u32), buf.try_get_u32_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x09\xA0"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_u32_ne()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_u32_ne(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x08\x09\xA0\xA1 hello"[..]; + /// assert_eq!(Ok(0x0809A0A1_i32), buf.try_get_i32()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_i32()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_i32(&mut self) -> Result { + buf_try_get_impl!(self, i32::from_be_bytes) + } + + /// Gets a signed 32 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\xA1\xA0\x09\x08 hello"[..]; + /// assert_eq!(Ok(0x0809A0A1_i32), buf.try_get_i32_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x09\xA0"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_i32_le()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_i32_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0809A0A1_i32), buf.try_get_i32_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x09\xA0"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_i32_ne()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_i32_ne(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08 hello"[..]; + /// assert_eq!(Ok(0x0102030405060708_u64), buf.try_get_u64()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_u64()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_u64(&mut self) -> Result { + buf_try_get_impl!(self, u64::from_be_bytes) + } + + /// Gets an unsigned 64 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x08\x07\x06\x05\x04\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x0102030405060708_u64), buf.try_get_u64_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x07\x06\x05\x04\x03\x02"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_u64_le()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_u64_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0102030405060708_u64), buf.try_get_u64_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_u64_ne()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_u64_ne(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08 hello"[..]; + /// assert_eq!(Ok(0x0102030405060708_i64), buf.try_get_i64()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_i64()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_i64(&mut self) -> Result { + buf_try_get_impl!(self, i64::from_be_bytes) + } + + /// Gets a signed 64 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x08\x07\x06\x05\x04\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x0102030405060708_i64), buf.try_get_i64_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x08\x07\x06\x05\x04\x03\x02"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_i64_le()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_i64_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x0102030405060708_i64), buf.try_get_i64_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_i64_ne()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_i64_ne(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16 hello"[..]; + /// assert_eq!(Ok(0x01020304050607080910111213141516_u128), buf.try_get_u128()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_u128()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_u128(&mut self) -> Result { + buf_try_get_impl!(self, u128::from_be_bytes) + } + + /// Gets an unsigned 128 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x01020304050607080910111213141516_u128), buf.try_get_u128_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_u128_le()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_u128_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x01020304050607080910111213141516_u128), buf.try_get_u128_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_u128_ne()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_u128_ne(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16 hello"[..]; + /// assert_eq!(Ok(0x01020304050607080910111213141516_i128), buf.try_get_i128()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_i128()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_i128(&mut self) -> Result { + buf_try_get_impl!(self, i128::from_be_bytes) + } + + /// Gets a signed 128 bit integer from `self` in little-endian byte order. + /// + /// The current position is advanced by 16. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x01020304050607080910111213141516_i128), buf.try_get_i128_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x16\x15\x14\x13\x12\x11\x10\x09\x08\x07\x06\x05\x04\x03\x02"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_i128_le()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_i128_le(&mut self) -> Result { + buf_try_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. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x01020304050607080910111213141516_i128), buf.try_get_i128_ne()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15"[..]; + /// assert_eq!(Err(TryGetError{requested: 16, available: 15}), buf.try_get_i128_ne()); + /// assert_eq!(15, buf.remaining()); + /// ``` + fn try_get_i128_ne(&mut self) -> Result { + buf_try_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`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03 hello"[..]; + /// assert_eq!(Ok(0x010203_u64), buf.try_get_uint(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_uint(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` > 8. + fn try_get_uint(&mut self, nbytes: usize) -> Result { + buf_try_get_impl!(be => self, u64, nbytes); + } + + /// Gets an unsigned n-byte integer from `self` in little-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x010203_u64), buf.try_get_uint_le(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_uint_le(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` > 8. + fn try_get_uint_le(&mut self, nbytes: usize) -> Result { + buf_try_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`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x010203_u64), buf.try_get_uint_ne(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03", + /// false => b"\x03\x02\x01", + /// }; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_uint_ne(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` is greater than 8. + fn try_get_uint_ne(&mut self, nbytes: usize) -> Result { + if cfg!(target_endian = "big") { + self.try_get_uint(nbytes) + } else { + self.try_get_uint_le(nbytes) + } + } + + /// Gets a signed n-byte integer from `self` in big-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x01\x02\x03 hello"[..]; + /// assert_eq!(Ok(0x010203_i64), buf.try_get_int(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_int(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` is greater than 8. + fn try_get_int(&mut self, nbytes: usize) -> Result { + buf_try_get_impl!(be => self, i64, nbytes); + } + + /// Gets a signed n-byte integer from `self` in little-endian byte order. + /// + /// The current position is advanced by `nbytes`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x03\x02\x01 hello"[..]; + /// assert_eq!(Ok(0x010203_i64), buf.try_get_int_le(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x01\x02\x03"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_int_le(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` is greater than 8. + fn try_get_int_le(&mut self, nbytes: usize) -> Result { + buf_try_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`. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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!(Ok(0x010203_i64), buf.try_get_int_ne(3)); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf: &[u8] = match cfg!(target_endian = "big") { + /// true => b"\x01\x02\x03", + /// false => b"\x03\x02\x01", + /// }; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_int_ne(4)); + /// assert_eq!(3, buf.remaining()); + /// ``` + /// + /// # Panics + /// + /// This function panics if `nbytes` is greater than 8. + fn try_get_int_ne(&mut self, nbytes: usize) -> Result { + if cfg!(target_endian = "big") { + self.try_get_int(nbytes) + } else { + self.try_get_int_le(nbytes) + } + } + + /// Gets an IEEE754 single-precision (4 bytes) floating point number from + /// `self` in big-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x3F\x99\x99\x9A hello"[..]; + /// assert_eq!(1.2f32, buf.get_f32()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\x99\x99"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_f32()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_f32(&mut self) -> Result { + Ok(f32::from_bits(self.try_get_u32()?)) + } + + /// Gets an IEEE754 single-precision (4 bytes) floating point number from + /// `self` in little-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x9A\x99\x99\x3F hello"[..]; + /// assert_eq!(1.2f32, buf.get_f32_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\x99\x99"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_f32_le()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_f32_le(&mut self) -> Result { + Ok(f32::from_bits(self.try_get_u32_le()?)) + } + + /// Gets an IEEE754 single-precision (4 bytes) floating point number from + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 4. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\x99\x99"[..]; + /// assert_eq!(Err(TryGetError{requested: 4, available: 3}), buf.try_get_f32_ne()); + /// assert_eq!(3, buf.remaining()); + /// ``` + fn try_get_f32_ne(&mut self) -> Result { + Ok(f32::from_bits(self.try_get_u32_ne()?)) + } + + /// Gets an IEEE754 double-precision (8 bytes) floating point number from + /// `self` in big-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x3F\xF3\x33\x33\x33\x33\x33\x33 hello"[..]; + /// assert_eq!(1.2f64, buf.get_f64()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\xF3\x33\x33\x33\x33\x33"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_f64()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_f64(&mut self) -> Result { + Ok(f64::from_bits(self.try_get_u64()?)) + } + + /// Gets an IEEE754 double-precision (8 bytes) floating point number from + /// `self` in little-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut buf = &b"\x33\x33\x33\x33\x33\x33\xF3\x3F hello"[..]; + /// assert_eq!(1.2f64, buf.get_f64_le()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\xF3\x33\x33\x33\x33\x33"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_f64_le()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_f64_le(&mut self) -> Result { + Ok(f64::from_bits(self.try_get_u64_le()?)) + } + + /// Gets an IEEE754 double-precision (8 bytes) floating point number from + /// `self` in native-endian byte order. + /// + /// The current position is advanced by 8. + /// + /// Returns `Err(TryGetError)` when there are not enough + /// remaining bytes to read the value. + /// + /// # 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()); + /// assert_eq!(6, buf.remaining()); + /// ``` + /// + /// ``` + /// use bytes::{Buf, TryGetError}; + /// + /// let mut buf = &b"\x3F\xF3\x33\x33\x33\x33\x33"[..]; + /// assert_eq!(Err(TryGetError{requested: 8, available: 7}), buf.try_get_f64_ne()); + /// assert_eq!(7, buf.remaining()); + /// ``` + fn try_get_f64_ne(&mut self) -> Result { + Ok(f64::from_bits(self.try_get_u64_ne()?)) + } + + /// Consumes `len` bytes inside self and returns new instance of `Bytes` + /// with this data. + /// + /// This function may be optimized by the underlying type to avoid actual + /// copies. For example, `Bytes` implementation will do a shallow copy + /// (ref-count increment). + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let bytes = (&b"hello world"[..]).copy_to_bytes(5); + /// assert_eq!(&bytes[..], &b"hello"[..]); + /// ``` + /// + /// # Panics + /// + /// This function panics if `len > self.remaining()`. + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { + use super::BufMut; + + if self.remaining() < len { + panic_advance(&TryGetError { + requested: len, + available: self.remaining(), + }); + } + + let mut ret = crate::BytesMut::with_capacity(len); + ret.put(self.take(len)); + ret.freeze() + } + + /// Creates an adaptor which will read at most `limit` bytes from `self`. + /// + /// This function returns a new instance of `Buf` which will read at most + /// `limit` bytes. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Buf, BufMut}; + /// + /// let mut buf = b"hello world"[..].take(5); + /// let mut dst = vec![]; + /// + /// dst.put(&mut buf); + /// assert_eq!(dst, b"hello"); + /// + /// let mut buf = buf.into_inner(); + /// dst.clear(); + /// dst.put(&mut buf); + /// assert_eq!(dst, b" world"); + /// ``` + fn take(self, limit: usize) -> Take + where + Self: Sized, + { + take::new(self, limit) + } + + /// Creates an adaptor which will chain this buffer with another. + /// + /// The returned `Buf` instance will first consume all bytes from `self`. + /// Afterwards the output is equivalent to the output of next. + /// + /// # Examples + /// + /// ``` + /// use bytes::Buf; + /// + /// let mut chain = b"hello "[..].chain(&b"world"[..]); + /// + /// let full = chain.copy_to_bytes(11); + /// assert_eq!(full.chunk(), b"hello world"); + /// ``` + fn chain(self, next: U) -> Chain + where + Self: Sized, + { + Chain::new(self, next) + } + + /// Creates an adaptor which implements the `Read` trait for `self`. + /// + /// This function returns a new value which implements `Read` by adapting + /// the `Read` trait functions to the `Buf` trait functions. Given that + /// `Buf` operations are infallible, none of the `Read` functions will + /// return with `Err`. + /// + /// # Examples + /// + /// ``` + /// use bytes::{Bytes, Buf}; + /// use std::io::Read; + /// + /// let buf = Bytes::from("hello world"); + /// + /// let mut reader = buf.reader(); + /// let mut dst = [0; 1024]; + /// + /// let num = reader.read(&mut dst).unwrap(); + /// + /// assert_eq!(11, num); + /// assert_eq!(&dst[..11], &b"hello world"[..]); + /// ``` + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + fn reader(self) -> Reader + where + Self: Sized, + { + reader::new(self) + } +} + +macro_rules! deref_forward_buf { + () => { + #[inline] + fn remaining(&self) -> usize { + (**self).remaining() + } + + #[inline] + fn chunk(&self) -> &[u8] { + (**self).chunk() + } + + #[cfg(feature = "std")] + #[inline] + fn chunks_vectored<'b>(&'b self, dst: &mut [IoSlice<'b>]) -> usize { + (**self).chunks_vectored(dst) + } + + #[inline] + fn advance(&mut self, cnt: usize) { + (**self).advance(cnt) + } + + #[inline] + fn has_remaining(&self) -> bool { + (**self).has_remaining() + } + + #[inline] + fn copy_to_slice(&mut self, dst: &mut [u8]) { + (**self).copy_to_slice(dst) + } + + #[inline] + fn get_u8(&mut self) -> u8 { + (**self).get_u8() } #[inline] @@ -1360,6 +2586,36 @@ macro_rules! deref_forward_buf { (**self).get_i64_ne() } + #[inline] + fn get_u128(&mut self) -> u128 { + (**self).get_u128() + } + + #[inline] + fn get_u128_le(&mut self) -> u128 { + (**self).get_u128_le() + } + + #[inline] + fn get_u128_ne(&mut self) -> u128 { + (**self).get_u128_ne() + } + + #[inline] + fn get_i128(&mut self) -> i128 { + (**self).get_i128() + } + + #[inline] + fn get_i128_le(&mut self) -> i128 { + (**self).get_i128_le() + } + + #[inline] + fn get_i128_ne(&mut self) -> i128 { + (**self).get_i128_ne() + } + #[inline] fn get_uint(&mut self, nbytes: usize) -> u64 { (**self).get_uint(nbytes) @@ -1390,6 +2646,231 @@ macro_rules! deref_forward_buf { (**self).get_int_ne(nbytes) } + #[inline] + fn get_f32(&mut self) -> f32 { + (**self).get_f32() + } + + #[inline] + fn get_f32_le(&mut self) -> f32 { + (**self).get_f32_le() + } + + #[inline] + fn get_f32_ne(&mut self) -> f32 { + (**self).get_f32_ne() + } + + #[inline] + fn get_f64(&mut self) -> f64 { + (**self).get_f64() + } + + #[inline] + fn get_f64_le(&mut self) -> f64 { + (**self).get_f64_le() + } + + #[inline] + fn get_f64_ne(&mut self) -> f64 { + (**self).get_f64_ne() + } + + #[inline] + fn try_copy_to_slice(&mut self, dst: &mut [u8]) -> Result<(), TryGetError> { + (**self).try_copy_to_slice(dst) + } + + #[inline] + fn try_get_u8(&mut self) -> Result { + (**self).try_get_u8() + } + + #[inline] + fn try_get_i8(&mut self) -> Result { + (**self).try_get_i8() + } + + #[inline] + fn try_get_u16(&mut self) -> Result { + (**self).try_get_u16() + } + + #[inline] + fn try_get_u16_le(&mut self) -> Result { + (**self).try_get_u16_le() + } + + #[inline] + fn try_get_u16_ne(&mut self) -> Result { + (**self).try_get_u16_ne() + } + + #[inline] + fn try_get_i16(&mut self) -> Result { + (**self).try_get_i16() + } + + #[inline] + fn try_get_i16_le(&mut self) -> Result { + (**self).try_get_i16_le() + } + + #[inline] + fn try_get_i16_ne(&mut self) -> Result { + (**self).try_get_i16_ne() + } + + #[inline] + fn try_get_u32(&mut self) -> Result { + (**self).try_get_u32() + } + + #[inline] + fn try_get_u32_le(&mut self) -> Result { + (**self).try_get_u32_le() + } + + #[inline] + fn try_get_u32_ne(&mut self) -> Result { + (**self).try_get_u32_ne() + } + + #[inline] + fn try_get_i32(&mut self) -> Result { + (**self).try_get_i32() + } + + #[inline] + fn try_get_i32_le(&mut self) -> Result { + (**self).try_get_i32_le() + } + + #[inline] + fn try_get_i32_ne(&mut self) -> Result { + (**self).try_get_i32_ne() + } + + #[inline] + fn try_get_u64(&mut self) -> Result { + (**self).try_get_u64() + } + + #[inline] + fn try_get_u64_le(&mut self) -> Result { + (**self).try_get_u64_le() + } + + #[inline] + fn try_get_u64_ne(&mut self) -> Result { + (**self).try_get_u64_ne() + } + + #[inline] + fn try_get_i64(&mut self) -> Result { + (**self).try_get_i64() + } + + #[inline] + fn try_get_i64_le(&mut self) -> Result { + (**self).try_get_i64_le() + } + + #[inline] + fn try_get_i64_ne(&mut self) -> Result { + (**self).try_get_i64_ne() + } + + #[inline] + fn try_get_u128(&mut self) -> Result { + (**self).try_get_u128() + } + + #[inline] + fn try_get_u128_le(&mut self) -> Result { + (**self).try_get_u128_le() + } + + #[inline] + fn try_get_u128_ne(&mut self) -> Result { + (**self).try_get_u128_ne() + } + + #[inline] + fn try_get_i128(&mut self) -> Result { + (**self).try_get_i128() + } + + #[inline] + fn try_get_i128_le(&mut self) -> Result { + (**self).try_get_i128_le() + } + + #[inline] + fn try_get_i128_ne(&mut self) -> Result { + (**self).try_get_i128_ne() + } + + #[inline] + fn try_get_uint(&mut self, nbytes: usize) -> Result { + (**self).try_get_uint(nbytes) + } + + #[inline] + fn try_get_uint_le(&mut self, nbytes: usize) -> Result { + (**self).try_get_uint_le(nbytes) + } + + #[inline] + fn try_get_uint_ne(&mut self, nbytes: usize) -> Result { + (**self).try_get_uint_ne(nbytes) + } + + #[inline] + fn try_get_int(&mut self, nbytes: usize) -> Result { + (**self).try_get_int(nbytes) + } + + #[inline] + fn try_get_int_le(&mut self, nbytes: usize) -> Result { + (**self).try_get_int_le(nbytes) + } + + #[inline] + fn try_get_int_ne(&mut self, nbytes: usize) -> Result { + (**self).try_get_int_ne(nbytes) + } + + #[inline] + fn try_get_f32(&mut self) -> Result { + (**self).try_get_f32() + } + + #[inline] + fn try_get_f32_le(&mut self) -> Result { + (**self).try_get_f32_le() + } + + #[inline] + fn try_get_f32_ne(&mut self) -> Result { + (**self).try_get_f32_ne() + } + + #[inline] + fn try_get_f64(&mut self) -> Result { + (**self).try_get_f64() + } + + #[inline] + fn try_get_f64_le(&mut self) -> Result { + (**self).try_get_f64_le() + } + + #[inline] + fn try_get_f64_ne(&mut self) -> Result { + (**self).try_get_f64_ne() + } + #[inline] fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { (**self).copy_to_bytes(len) @@ -1419,7 +2900,10 @@ impl Buf for &[u8] { #[inline] fn advance(&mut self, cnt: usize) { if self.len() < cnt { - panic_advance(cnt, self.len()); + panic_advance(&TryGetError { + requested: cnt, + available: self.len(), + }); } *self = &self[cnt..]; @@ -1428,7 +2912,10 @@ impl Buf for &[u8] { #[inline] fn copy_to_slice(&mut self, dst: &mut [u8]) { if self.len() < dst.len() { - panic_advance(dst.len(), self.len()); + panic_advance(&TryGetError { + requested: dst.len(), + available: self.len(), + }); } dst.copy_from_slice(&self[..dst.len()]); @@ -1458,7 +2945,10 @@ impl> Buf for std::io::Cursor { // We intentionally allow `cnt == 0` here even if `pos > len`. let max_cnt = saturating_sub_usize_u64(len, pos); if cnt > max_cnt { - panic_advance(cnt, max_cnt); + panic_advance(&TryGetError { + requested: cnt, + available: max_cnt, + }); } // This will not overflow because either `cnt == 0` or the sum is not diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 2a3b5e9ee..26645c6ae 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -1,7 +1,7 @@ use crate::buf::{limit, Chain, Limit, UninitSlice}; #[cfg(feature = "std")] use crate::buf::{writer, Writer}; -use crate::{panic_advance, panic_does_not_fit}; +use crate::{panic_advance, panic_does_not_fit, TryGetError}; use core::{mem, ptr, usize}; @@ -204,7 +204,10 @@ pub unsafe trait BufMut { Self: Sized, { if self.remaining_mut() < src.remaining() { - panic_advance(src.remaining(), self.remaining_mut()); + panic_advance(&TryGetError { + requested: src.remaining(), + available: self.remaining_mut(), + }); } while src.has_remaining() { @@ -242,7 +245,10 @@ pub unsafe trait BufMut { #[inline] fn put_slice(&mut self, mut src: &[u8]) { if self.remaining_mut() < src.len() { - panic_advance(src.len(), self.remaining_mut()); + panic_advance(&TryGetError { + requested: src.len(), + available: self.remaining_mut(), + }); } while !src.is_empty() { @@ -285,7 +291,10 @@ pub unsafe trait BufMut { #[inline] fn put_bytes(&mut self, val: u8, mut cnt: usize) { if self.remaining_mut() < cnt { - panic_advance(cnt, self.remaining_mut()); + panic_advance(&TryGetError { + requested: cnt, + available: self.remaining_mut(), + }) } while cnt > 0 { @@ -1487,7 +1496,10 @@ unsafe impl BufMut for &mut [u8] { #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { if self.len() < cnt { - panic_advance(cnt, self.len()); + panic_advance(&TryGetError { + requested: cnt, + available: self.len(), + }); } // Lifetime dance taken from `impl Write for &mut [u8]`. @@ -1498,7 +1510,10 @@ unsafe impl BufMut for &mut [u8] { #[inline] fn put_slice(&mut self, src: &[u8]) { if self.len() < src.len() { - panic_advance(src.len(), self.len()); + panic_advance(&TryGetError { + requested: src.len(), + available: self.len(), + }); } self[..src.len()].copy_from_slice(src); @@ -1509,7 +1524,10 @@ unsafe impl BufMut for &mut [u8] { #[inline] fn put_bytes(&mut self, val: u8, cnt: usize) { if self.len() < cnt { - panic_advance(cnt, self.len()); + panic_advance(&TryGetError { + requested: cnt, + available: self.len(), + }); } // SAFETY: We just checked that the pointer is valid for `cnt` bytes. @@ -1534,7 +1552,10 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] unsafe fn advance_mut(&mut self, cnt: usize) { if self.len() < cnt { - panic_advance(cnt, self.len()); + panic_advance(&TryGetError { + requested: cnt, + available: self.len(), + }); } // Lifetime dance taken from `impl Write for &mut [u8]`. @@ -1545,7 +1566,10 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] fn put_slice(&mut self, src: &[u8]) { if self.len() < src.len() { - panic_advance(src.len(), self.len()); + panic_advance(&TryGetError { + requested: src.len(), + available: self.len(), + }); } // SAFETY: We just checked that the pointer is valid for `src.len()` bytes. @@ -1558,7 +1582,10 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { #[inline] fn put_bytes(&mut self, val: u8, cnt: usize) { if self.len() < cnt { - panic_advance(cnt, self.len()); + panic_advance(&TryGetError { + requested: cnt, + available: self.len(), + }); } // SAFETY: We just checked that the pointer is valid for `cnt` bytes. @@ -1582,7 +1609,10 @@ unsafe impl BufMut for Vec { let remaining = self.capacity() - len; if remaining < cnt { - panic_advance(cnt, remaining); + panic_advance(&TryGetError { + requested: cnt, + available: remaining, + }); } // Addition will not overflow since the sum is at most the capacity. diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index a0d77bc24..d5db5124b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -17,7 +17,7 @@ use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::{offset_from, Buf, BufMut, Bytes}; +use crate::{offset_from, Buf, BufMut, Bytes, TryGetError}; /// A unique reference to a contiguous slice of memory. /// @@ -1178,7 +1178,10 @@ unsafe impl BufMut for BytesMut { unsafe fn advance_mut(&mut self, cnt: usize) { let remaining = self.cap - self.len(); if cnt > remaining { - super::panic_advance(cnt, remaining); + super::panic_advance(&TryGetError { + requested: cnt, + available: remaining, + }); } // Addition won't overflow since it is at most `self.cap`. self.len = self.len() + cnt; diff --git a/src/lib.rs b/src/lib.rs index 7ddd2205b..cfc828130 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,12 +132,47 @@ fn min_u64_usize(a: u64, b: usize) -> usize { } } +/// Error type for the `try_get_` methods of [`Buf`]. +/// Indicates that there were not enough remaining +/// bytes in the buffer while attempting +/// to get a value from a [`Buf`] with one +/// of the `try_get_` methods. +#[derive(Debug, PartialEq, Eq)] +pub struct TryGetError { + /// The number of bytes necessary to get the value + pub requested: usize, + + /// The number of bytes available in the buffer + pub available: usize, +} + +impl core::fmt::Display for TryGetError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + write!( + f, + "Not enough bytes remaining in buffer to read value (requested {} but only {} available)", + self.requested, + self.available + ) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryGetError {} + +#[cfg(feature = "std")] +impl From for std::io::Error { + fn from(error: TryGetError) -> Self { + std::io::Error::new(std::io::ErrorKind::Other, error) + } +} + /// Panic with a nice error message. #[cold] -fn panic_advance(idx: usize, len: usize) -> ! { +fn panic_advance(error_info: &TryGetError) -> ! { panic!( "advance out of bounds: the len is {} but advancing by {}", - len, idx + error_info.available, error_info.requested ); } From 7a876609a23eb9c140fffeae625dd9272e4e48e0 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 27 Jan 2025 18:56:32 +0900 Subject: [PATCH 181/203] Use [lints] to address unexpected_cfgs lint (#762) --- Cargo.toml | 5 +++++ src/lib.rs | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed385d197..7dfc48e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,8 @@ loom = "0.7" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(loom)', +] } diff --git a/src/lib.rs b/src/lib.rs index cfc828130..08c424942 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#![allow(unknown_lints, unexpected_cfgs)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![doc(test( no_crate_inject, From 71824b095c4150b3af0776ac158795c00ff9d53f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 27 Jan 2025 18:58:12 +0900 Subject: [PATCH 182/203] ci: test AArch64 and run tests instead of build in cross job (#763) --- .github/workflows/ci.yml | 37 ++++++++++++++++++------------------- tests/test_bytes.rs | 1 + 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc868bbbc..6973ccd55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,7 @@ jobs: matrix: os: - ubuntu-latest + - ubuntu-22.04-arm # TODO: update to 24.04 when https://github.com/rust-lang/rust/issues/135867 solved - macos-latest - windows-latest runs-on: ${{ matrix.os }} @@ -104,29 +105,27 @@ jobs: name: cross strategy: matrix: - target: - - i686-unknown-linux-gnu - - armv7-unknown-linux-gnueabihf - - powerpc-unknown-linux-gnu - - powerpc64-unknown-linux-gnu - - wasm32-unknown-unknown - runs-on: ubuntu-latest + include: + - target: i686-unknown-linux-gnu + os: ubuntu-latest + - target: armv7-unknown-linux-gnueabihf + os: ubuntu-22.04-arm # TODO: update to 24.04 when https://github.com/rust-lang/rust/issues/135867 solved + - target: powerpc-unknown-linux-gnu + os: ubuntu-latest + - target: powerpc64-unknown-linux-gnu + os: ubuntu-latest + - target: wasm32-wasip1 + os: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Install Rust run: rustup update stable - - name: Install cross - uses: taiki-e/install-action@cross - if: matrix.target != 'wasm32-unknown-unknown' - - name: cross build --target ${{ matrix.target }} - run: cross build --target ${{ matrix.target }} - if: matrix.target != 'wasm32-unknown-unknown' - # WASM support - - name: cargo build --target ${{ matrix.target }} - run: | - rustup target add ${{ matrix.target }} - cargo build --target ${{ matrix.target }} - if: matrix.target == 'wasm32-unknown-unknown' + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: ${{ matrix.target }} + - name: Test + run: cargo test --target ${{ matrix.target }} # Sanitizers tsan: diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 6ab418329..c283a8081 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -728,6 +728,7 @@ fn advance_past_len() { // Only run these tests on little endian systems. CI uses qemu for testing // big endian... and qemu doesn't really support threading all that well. #[cfg(any(miri, target_endian = "little"))] +#[cfg(not(target_family = "wasm"))] // wasm without experimental threads proposal doesn't support threads fn stress() { // Tests promoting a buffer from a vec -> shared in a concurrent situation use std::sync::{Arc, Barrier}; From 16fd473d5c6ca20787e157ec19afc469438fc66b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 31 Jan 2025 18:52:54 +0900 Subject: [PATCH 183/203] Add feature to support platforms without atomic CAS (#467) --- .github/workflows/ci.yml | 19 +++++++++++++++++++ Cargo.toml | 5 +++++ README.md | 17 +++++++++++++++++ ci/test-stable.sh | 7 +++++-- src/buf/chain.rs | 4 ++-- src/buf/take.rs | 4 ++-- src/loom.rs | 3 +++ 7 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6973ccd55..79b57dafd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,6 +127,25 @@ jobs: - name: Test run: cargo test --target ${{ matrix.target }} + # Build for no_std environment. + no-std: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - name: Install cargo-hack + uses: taiki-e/install-action@cargo-hack + # thumbv6m-none-eabi supports atomic, but not atomic CAS. + # thumbv7m-none-eabi supports atomic CAS. + - run: rustup target add thumbv6m-none-eabi thumbv7m-none-eabi + # * --optional-deps is needed for serde feature + # * --no-dev-deps is needed to avoid https://github.com/rust-lang/cargo/issues/4866 + - run: cargo hack build --target thumbv7m-none-eabi --feature-powerset --skip std,default --optional-deps --no-dev-deps + # A sound way to provide atomic CAS on platforms without native atomic CAS is system-dependent. + # portable-atomic provides major ways via cfgs and accepts user-defined implementations via critical-section feature. + - run: cargo hack build --target thumbv6m-none-eabi --feature-powerset --skip std,default --optional-deps --no-dev-deps --features extra-platforms,extra-platforms/critical-section + # Sanitizers tsan: name: tsan diff --git a/Cargo.toml b/Cargo.toml index 7dfc48e07..39def561e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,11 @@ std = [] [dependencies] serde = { version = "1.0.60", optional = true, default-features = false, features = ["alloc"] } +# Use portable-atomic crate to support platforms without atomic CAS. +# See "no_std support" section in readme for more information. +# +# Enable require-cas feature to provide a better error message if the end user forgets to use the cfg or feature. +extra-platforms = { package = "portable-atomic", version = "1.3", optional = true, default-features = false, features = ["require-cas"] } [dev-dependencies] serde_test = "1.0" diff --git a/README.md b/README.md index be46642a4..90d631c97 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,21 @@ Next, add this to your crate: use bytes::{Bytes, BytesMut, Buf, BufMut}; ``` +## no_std support + +To use `bytes` with no_std environment, disable the (enabled by default) `std` feature. + +```toml +[dependencies] +bytes = { version = "1", default-features = false } +``` + +To use `bytes` with no_std environment without atomic CAS, such as thumbv6m, you also need to enable +the `extra-platforms` feature. See the [documentation for the `portable-atomic` +crate](https://docs.rs/portable-atomic) for more information. + +The MSRV when `extra-platforms` feature is enabled depends on the MSRV of `portable-atomic`. + ## Serde support Serde support is optional and disabled by default. To enable use the feature `serde`. @@ -36,6 +51,8 @@ Serde support is optional and disabled by default. To enable use the feature `se bytes = { version = "1", features = ["serde"] } ``` +The MSRV when `serde` feature is enabled depends on the MSRV of `serde`. + ## Building documentation When building the `bytes` documentation the `docsrs` option should be used, otherwise diff --git a/ci/test-stable.sh b/ci/test-stable.sh index a8eaa3c65..ad9757449 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -16,7 +16,10 @@ if [[ "${RUST_VERSION}" == "nightly"* ]]; then cargo check --benches # Check minimal versions - cargo clean - cargo update -Zminimal-versions + # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update` + # from determining minimal versions based on dev-dependencies. + cargo hack --remove-dev-deps --workspace + # Update Cargo.lock to minimal version dependencies. + cargo update -Z minimal-versions cargo check --all-features fi diff --git a/src/buf/chain.rs b/src/buf/chain.rs index 97ac2eca5..c8bc36de9 100644 --- a/src/buf/chain.rs +++ b/src/buf/chain.rs @@ -1,5 +1,5 @@ use crate::buf::{IntoIter, UninitSlice}; -use crate::{Buf, BufMut, Bytes}; +use crate::{Buf, BufMut}; #[cfg(feature = "std")] use std::io::IoSlice; @@ -169,7 +169,7 @@ where n } - fn copy_to_bytes(&mut self, len: usize) -> Bytes { + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { let a_rem = self.a.remaining(); if a_rem >= len { self.a.copy_to_bytes(len) diff --git a/src/buf/take.rs b/src/buf/take.rs index fc4e39dda..acfeef6e1 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -1,4 +1,4 @@ -use crate::{Buf, Bytes}; +use crate::Buf; use core::cmp; @@ -148,7 +148,7 @@ impl Buf for Take { self.limit -= cnt; } - fn copy_to_bytes(&mut self, len: usize) -> Bytes { + fn copy_to_bytes(&mut self, len: usize) -> crate::Bytes { assert!(len <= self.remaining(), "`len` greater than remaining"); let r = self.inner.copy_to_bytes(len); diff --git a/src/loom.rs b/src/loom.rs index 9e6b2d5e2..c80929092 100644 --- a/src/loom.rs +++ b/src/loom.rs @@ -1,7 +1,10 @@ #[cfg(not(all(test, loom)))] pub(crate) mod sync { pub(crate) mod atomic { + #[cfg(not(feature = "extra-platforms"))] pub(crate) use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; + #[cfg(feature = "extra-platforms")] + pub(crate) use extra_platforms::{AtomicPtr, AtomicUsize, Ordering}; pub(crate) trait AtomicMut { fn with_mut(&mut self, f: F) -> R From e0f3a25faeca8babae5b4c5a810b8655adbcb96e Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 3 Feb 2025 12:49:49 +0100 Subject: [PATCH 184/203] chore: prepare bytes v1.10.0 (#766) --- CHANGELOG.md | 14 ++++++++++++++ Cargo.toml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34b5145e0..7f5eaa67e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# 1.10.0 (February 3rd, 2025) + +### Added + +- Add feature to support platforms without atomic CAS (#467) +- `try_get_*` methods for `Buf` trait (#753) +- Implement `Buf::chunks_vectored` for `Take` (#617) +- Implement `Buf::chunks_vectored` for `VecDeque` (#708) + +### Fixed + +- Remove incorrect guarantee for `chunks_vectored` (#754) +- Ensure that tests pass under `panic=abort` (#749) + # 1.9.0 (November 27, 2024) ### Added diff --git a/Cargo.toml b/Cargo.toml index 39def561e..df86ed256 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.9.0" +version = "1.10.0" edition = "2018" rust-version = "1.39" license = "MIT" From 36675436cc343fc0e828033278d668020bd897b9 Mon Sep 17 00:00:00 2001 From: venomousmoog Date: Wed, 5 Mar 2025 01:44:40 -0800 Subject: [PATCH 185/203] Fix memory leak in `owned_to_vec` (#773) --- src/bytes.rs | 14 +++++++++----- tests/test_bytes.rs | 13 +++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 96f834f43..cdb6ea559 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1141,15 +1141,15 @@ unsafe fn owned_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes } } -unsafe fn owned_to_vec(_data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { +unsafe fn owned_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { let slice = slice::from_raw_parts(ptr, len); - slice.to_vec() + let vec = slice.to_vec(); + owned_drop_impl(data.load(Ordering::Relaxed)); + vec } unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { - let bytes_mut = BytesMut::from_vec(owned_to_vec(data, ptr, len)); - owned_drop_impl(data.load(Ordering::Relaxed)); - bytes_mut + BytesMut::from_vec(owned_to_vec(data, ptr, len)) } unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool { @@ -1161,6 +1161,10 @@ unsafe fn owned_drop_impl(owned: *mut ()) { let ref_cnt = &(*lifetime).ref_cnt; let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release); + debug_assert!( + old_cnt > 0 && old_cnt <= usize::MAX >> 1, + "expected non-zero refcount and no underflow" + ); if old_cnt != 1 { return; } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index c283a8081..613efc886 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1619,6 +1619,19 @@ fn owned_to_vec() { assert_eq!(drop_counter.get(), 1); } +#[test] +fn owned_into_vec() { + let drop_counter = SharedAtomicCounter::new(); + let buf: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let owner = OwnedTester::new(buf, drop_counter.clone()); + let b1 = Bytes::from_owner(owner); + + let v1: Vec = b1.into(); + assert_eq!(&v1[..], &buf[..]); + // into() vec will copy out of the owner and drop it + assert_eq!(drop_counter.get(), 1); +} + #[test] #[cfg_attr(not(panic = "unwind"), ignore)] fn owned_safe_drop_on_as_ref_panic() { From 19d1427c971f6b619356966163459e43f797de2f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 5 Mar 2025 13:11:49 +0100 Subject: [PATCH 186/203] chore: prepare bytes v1.10.1 (#774) --- CHANGELOG.md | 6 ++++++ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f5eaa67e..a98a5c981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.10.1 (March 5th, 2025) + +### Fixed + +- Fix memory leak when using `to_vec` with `Bytes::from_owner` (#773) + # 1.10.0 (February 3rd, 2025) ### Added diff --git a/Cargo.toml b/Cargo.toml index df86ed256..7ef12eafc 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.10.0" +version = "1.10.1" edition = "2018" rust-version = "1.39" license = "MIT" From 141cbc22cbd04c4f3cf3d2af0a0f449322918d69 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Fri, 7 Mar 2025 05:58:07 -0800 Subject: [PATCH 187/203] Rename `Vtable::to_*` -> `Vtable::into_*` (#776) --- src/bytes.rs | 30 +++++++++++++++--------------- src/bytes_mut.rs | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index cdb6ea559..e2c187cc1 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -112,9 +112,9 @@ pub(crate) struct Vtable { 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, - pub to_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> BytesMut, + /// `into_*` consumes the `Bytes`, returning the respective value. + pub into_vec: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Vec, + pub into_mut: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> BytesMut, /// fn(data) pub is_unique: unsafe fn(&AtomicPtr<()>) -> bool, /// fn(data, ptr, len) @@ -1045,7 +1045,7 @@ impl From for BytesMut { /// ``` fn from(bytes: Bytes) -> Self { let bytes = ManuallyDrop::new(bytes); - unsafe { (bytes.vtable.to_mut)(&bytes.data, bytes.ptr, bytes.len) } + unsafe { (bytes.vtable.into_mut)(&bytes.data, bytes.ptr, bytes.len) } } } @@ -1058,7 +1058,7 @@ impl From for Bytes { impl From for Vec { fn from(bytes: Bytes) -> Vec { let bytes = ManuallyDrop::new(bytes); - unsafe { (bytes.vtable.to_vec)(&bytes.data, bytes.ptr, bytes.len) } + unsafe { (bytes.vtable.into_vec)(&bytes.data, bytes.ptr, bytes.len) } } } @@ -1077,8 +1077,8 @@ impl fmt::Debug for Vtable { const STATIC_VTABLE: Vtable = Vtable { clone: static_clone, - to_vec: static_to_vec, - to_mut: static_to_mut, + into_vec: static_to_vec, + into_mut: static_to_mut, is_unique: static_is_unique, drop: static_drop, }; @@ -1181,8 +1181,8 @@ unsafe fn owned_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { static OWNED_VTABLE: Vtable = Vtable { clone: owned_clone, - to_vec: owned_to_vec, - to_mut: owned_to_mut, + into_vec: owned_to_vec, + into_mut: owned_to_mut, is_unique: owned_is_unique, drop: owned_drop, }; @@ -1191,16 +1191,16 @@ static OWNED_VTABLE: Vtable = Vtable { static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable { clone: promotable_even_clone, - to_vec: promotable_even_to_vec, - to_mut: promotable_even_to_mut, + into_vec: promotable_even_to_vec, + into_mut: promotable_even_to_mut, is_unique: promotable_is_unique, drop: promotable_even_drop, }; static PROMOTABLE_ODD_VTABLE: Vtable = Vtable { clone: promotable_odd_clone, - to_vec: promotable_odd_to_vec, - to_mut: promotable_odd_to_mut, + into_vec: promotable_odd_to_vec, + into_mut: promotable_odd_to_mut, is_unique: promotable_is_unique, drop: promotable_odd_drop, }; @@ -1375,8 +1375,8 @@ const _: [(); 0 - mem::align_of::() % 2] = []; // Assert that the alignm static SHARED_VTABLE: Vtable = Vtable { clone: shared_clone, - to_vec: shared_to_vec, - to_mut: shared_to_mut, + into_vec: shared_to_vec, + into_mut: shared_to_mut, is_unique: shared_is_unique, drop: shared_drop, }; diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d5db5124b..739b36ed4 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1776,8 +1776,8 @@ 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, - to_mut: shared_v_to_mut, + into_vec: shared_v_to_vec, + into_mut: shared_v_to_mut, is_unique: shared_v_is_unique, drop: shared_v_drop, }; From 5c90b1141533c5c83dc61eebdfa85a33fecd2985 Mon Sep 17 00:00:00 2001 From: AbeZbm <128758303@qq.com> Date: Thu, 15 May 2025 14:52:54 +0800 Subject: [PATCH 188/203] Add missing tests in test_bytes --- tests/test_bytes.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 613efc886..f36abaa2a 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -157,6 +157,13 @@ fn slice_oob_2() { a.slice(44..49); } +#[test] +#[should_panic] +fn slice_start_greater_than_end() { + let a = Bytes::from(&b"hello world"[..]); + a.slice(5..3); +} + #[test] fn split_off() { let mut hello = Bytes::from(&b"helloworld"[..]); @@ -724,6 +731,15 @@ fn advance_past_len() { a.advance(20); } +#[test] +#[should_panic] +fn mut_advance_past_len() { + let mut a = BytesMut::from("hello world"); + unsafe { + a.advance_mut(20); + } +} + #[test] // Only run these tests on little endian systems. CI uses qemu for testing // big endian... and qemu doesn't really support threading all that well. From d9d5c59ef81038d7b50f61c4250a132c2f5dc91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20F=C3=A4rber?= <01mf02@gmail.com> Date: Tue, 20 May 2025 14:52:05 +0200 Subject: [PATCH 189/203] Guarantee address in `slice()` for empty slices. (#780) --- src/bytes.rs | 2 +- tests/test_bytes.rs | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/bytes.rs b/src/bytes.rs index e2c187cc1..5c2ca18ad 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -401,7 +401,7 @@ impl Bytes { ); if end == begin { - return Bytes::new(); + return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(begin)); } let mut ret = self.clone(); diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index f36abaa2a..e14778300 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1421,6 +1421,26 @@ fn try_reclaim_arc() { assert_eq!(true, buf.try_reclaim(6)); } +#[test] +fn slice_empty_addr() { + let buf = Bytes::from(vec![0; 1024]); + + let ptr_start = buf.as_ptr(); + let ptr_end = ptr_start.wrapping_add(1024); + + let empty_end = buf.slice(1024..); + assert_eq!(empty_end.len(), 0); + assert_eq!(empty_end.as_ptr(), ptr_end); + + let empty_start = buf.slice(..0); + assert_eq!(empty_start.len(), 0); + assert_eq!(empty_start.as_ptr(), ptr_start); + + // Is miri happy about the provenance? + let _ = &empty_end[..]; + let _ = &empty_start[..]; +} + #[test] fn split_off_empty_addr() { let mut buf = Bytes::from(vec![0; 1024]); From f29e93951da599095f54d57667c1988960ceff71 Mon Sep 17 00:00:00 2001 From: Hange Shi - NJU <61647848+AbeZbm@users.noreply.github.com> Date: Wed, 28 May 2025 18:56:41 +0800 Subject: [PATCH 190/203] Add some tests for Limit, BytesMut and Reader (#785) --- tests/test_bytes.rs | 7 ++++ tests/test_limit.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_reader.rs | 9 +++++ 3 files changed, 102 insertions(+) create mode 100644 tests/test_limit.rs diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index e14778300..fb4a0dc17 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -186,6 +186,13 @@ fn split_off_oob() { let _ = hello.split_off(44); } +#[test] +#[should_panic = "split_off out of bounds"] +fn bytes_mut_split_off_oob() { + let mut hello = BytesMut::from(&b"helloworld"[..]); + let _ = hello.split_off(44); +} + #[test] fn split_off_uninitialized() { let mut bytes = BytesMut::with_capacity(1024); diff --git a/tests/test_limit.rs b/tests/test_limit.rs new file mode 100644 index 000000000..dc40cc5f0 --- /dev/null +++ b/tests/test_limit.rs @@ -0,0 +1,86 @@ +#![warn(rust_2018_idioms)] + +use bytes::{buf::Limit, BufMut}; + +#[test] +fn long_limit() { + let buf = &mut [0u8; 10]; + let limit = buf.limit(100); + assert_eq!(10, limit.remaining_mut()); + assert_eq!(&[0u8; 10], &limit.get_ref()[..]); +} + +#[test] +fn limit_get_mut() { + let buf = &mut [0u8; 128]; + let mut limit = buf.limit(10); + assert_eq!(10, limit.remaining_mut()); + assert_eq!(&mut [0u8; 128], &limit.get_mut()[..]); +} + +#[test] +fn limit_set_limit() { + let buf = &mut [0u8; 128]; + let mut limit = buf.limit(10); + assert_eq!(10, Limit::limit(&limit)); + limit.set_limit(5); + assert_eq!(5, Limit::limit(&limit)); +} + +#[test] +fn limit_chunk_mut() { + let buf = &mut [0u8; 20]; + let mut limit = buf.limit(10); + assert_eq!(10, limit.chunk_mut().len()); + + let buf = &mut [0u8; 10]; + let mut limit = buf.limit(20); + assert_eq!(10, limit.chunk_mut().len()); +} + +#[test] +#[should_panic = "advance out of bounds"] +fn limit_advance_mut_panic_1() { + let buf = &mut [0u8; 10]; + let mut limit = buf.limit(100); + unsafe { + limit.advance_mut(50); + } +} + +#[test] +#[should_panic = "cnt <= self.limit"] +fn limit_advance_mut_panic_2() { + let buf = &mut [0u8; 100]; + let mut limit = buf.limit(10); + unsafe { + limit.advance_mut(50); + } +} + +#[test] +fn limit_advance_mut() { + let buf = &mut [0u8; 100]; + let mut limit = buf.limit(10); + unsafe { + limit.advance_mut(5); + } + assert_eq!(5, limit.remaining_mut()); + assert_eq!(5, limit.chunk_mut().len()); +} + +#[test] +fn limit_into_inner() { + let buf_arr = *b"hello world"; + let buf: &mut [u8] = &mut buf_arr.clone(); + let mut limit = buf.limit(4); + let mut dst = vec![]; + + unsafe { + limit.advance_mut(2); + } + + let buf = limit.into_inner(); + dst.put(&buf[..]); + assert_eq!(*dst, b"llo world"[..]); +} diff --git a/tests/test_reader.rs b/tests/test_reader.rs index 897aff645..19a16324e 100644 --- a/tests/test_reader.rs +++ b/tests/test_reader.rs @@ -27,3 +27,12 @@ fn buf_read() { reader.read_line(&mut line).unwrap(); assert_eq!("world", &line); } + +#[test] +fn get_mut() { + let buf = &b"hello world"[..]; + let mut reader = buf.reader(); + let buf_mut = reader.get_mut(); + assert_eq!(11, buf_mut.remaining()); + assert_eq!(b"hello world", buf_mut); +} From 3e44f88f5fae6dfcd3aa0779b804b3ff18afdee3 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Wed, 16 Jul 2025 10:33:07 +0200 Subject: [PATCH 191/203] Bump MSRV to 1.57 (#788) --- Cargo.toml | 4 ++-- clippy.toml | 2 +- src/buf/buf_mut.rs | 8 ++++---- src/buf/take.rs | 19 +------------------ src/bytes.rs | 15 +++++++-------- src/bytes_mut.rs | 13 ++++++------- src/lib.rs | 17 ----------------- tests/test_buf_mut.rs | 1 - tests/test_bytes.rs | 2 -- 9 files changed, 21 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ef12eafc..e7c246649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,8 @@ name = "bytes" # - Update CHANGELOG.md. # - Create "v1.x.y" git tag. version = "1.10.1" -edition = "2018" -rust-version = "1.39" +edition = "2021" +rust-version = "1.57" license = "MIT" authors = [ "Carl Lerche ", diff --git a/clippy.toml b/clippy.toml index 53095b15d..5cccb362c 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.39" +msrv = "1.57" diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs index 26645c6ae..3154dfeca 100644 --- a/src/buf/buf_mut.rs +++ b/src/buf/buf_mut.rs @@ -3,7 +3,7 @@ use crate::buf::{limit, Chain, Limit, UninitSlice}; use crate::buf::{writer, Writer}; use crate::{panic_advance, panic_does_not_fit, TryGetError}; -use core::{mem, ptr, usize}; +use core::{mem, ptr}; use alloc::{boxed::Box, vec::Vec}; @@ -1503,7 +1503,7 @@ unsafe impl BufMut for &mut [u8] { } // Lifetime dance taken from `impl Write for &mut [u8]`. - let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt); + let (_, b) = core::mem::take(self).split_at_mut(cnt); *self = b; } @@ -1559,7 +1559,7 @@ unsafe impl BufMut for &mut [core::mem::MaybeUninit] { } // Lifetime dance taken from `impl Write for &mut [u8]`. - let (_, b) = core::mem::replace(self, &mut []).split_at_mut(cnt); + let (_, b) = core::mem::take(self).split_at_mut(cnt); *self = b; } @@ -1600,7 +1600,7 @@ unsafe impl BufMut for Vec { #[inline] fn remaining_mut(&self) -> usize { // A vector can never have more than isize::MAX bytes - core::isize::MAX as usize - self.len() + isize::MAX as usize - self.len() } #[inline] diff --git a/src/buf/take.rs b/src/buf/take.rs index acfeef6e1..d8621f3f3 100644 --- a/src/buf/take.rs +++ b/src/buf/take.rs @@ -163,24 +163,7 @@ impl Buf for Take { } const LEN: usize = 16; - let mut slices: [IoSlice<'a>; LEN] = [ - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - IoSlice::new(&[]), - ]; + let mut slices: [IoSlice<'a>; LEN] = [IoSlice::new(&[]); LEN]; let cnt = self .inner diff --git a/src/bytes.rs b/src/bytes.rs index 5c2ca18ad..fdc879cd2 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,8 +1,7 @@ -use core::iter::FromIterator; use core::mem::{self, ManuallyDrop}; use core::ops::{Deref, RangeBounds}; use core::ptr::NonNull; -use core::{cmp, fmt, hash, ptr, slice, usize}; +use core::{cmp, fmt, hash, ptr, slice}; use alloc::{ alloc::{dealloc, Layout}, @@ -16,7 +15,7 @@ use crate::buf::IntoIter; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::{offset_from, Buf, BytesMut}; +use crate::{Buf, BytesMut}; /// A cheaply cloneable and sliceable chunk of contiguous memory. /// @@ -1235,7 +1234,7 @@ unsafe fn promotable_to_vec( let buf = f(shared); - let cap = offset_from(ptr, buf) + len; + let cap = ptr.offset_from(buf) as usize + len; // Copy back buffer ptr::copy(ptr, buf, len); @@ -1263,7 +1262,7 @@ unsafe fn promotable_to_mut( debug_assert_eq!(kind, KIND_VEC); let buf = f(shared); - let off = offset_from(ptr, buf); + let off = ptr.offset_from(buf) as usize; let cap = off + len; let v = Vec::from_raw_parts(buf, cap, cap); @@ -1348,7 +1347,7 @@ unsafe fn promotable_is_unique(data: &AtomicPtr<()>) -> bool { } unsafe fn free_boxed_slice(buf: *mut u8, offset: *const u8, len: usize) { - let cap = offset_from(offset, buf) + len; + let cap = offset.offset_from(buf) as usize + len; dealloc(buf, Layout::from_size_align(cap, 1).unwrap()) } @@ -1444,7 +1443,7 @@ unsafe fn shared_to_mut_impl(shared: *mut Shared, ptr: *const u8, len: usize) -> let cap = shared.cap; // Rebuild Vec - let off = offset_from(ptr, buf); + let off = ptr.offset_from(buf) as usize; let v = Vec::from_raw_parts(buf, len + off, cap); let mut b = BytesMut::from_vec(v); @@ -1510,7 +1509,7 @@ unsafe fn shallow_clone_vec( // vector. let shared = Box::new(Shared { buf, - cap: offset_from(offset, buf) + len, + cap: offset.offset_from(buf) as usize + len, // Initialize refcount to 2. One for this reference, and one // for the new clone that will be returned from // `shallow_clone`. diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 739b36ed4..befe16bca 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1,8 +1,7 @@ -use core::iter::FromIterator; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{Deref, DerefMut}; use core::ptr::{self, NonNull}; -use core::{cmp, fmt, hash, isize, slice, usize}; +use core::{cmp, fmt, hash, slice}; use alloc::{ borrow::{Borrow, BorrowMut}, @@ -17,7 +16,7 @@ use crate::bytes::Vtable; #[allow(unused)] use crate::loom::sync::atomic::AtomicMut; use crate::loom::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; -use crate::{offset_from, Buf, BufMut, Bytes, TryGetError}; +use crate::{Buf, BufMut, Bytes, TryGetError}; /// A unique reference to a contiguous slice of memory. /// @@ -694,7 +693,7 @@ impl BytesMut { let v_capacity = v.capacity(); let ptr = v.as_mut_ptr(); - let offset = offset_from(self.ptr.as_ptr(), ptr); + let offset = self.ptr.as_ptr().offset_from(ptr) as usize; // Compare the condition in the `kind == KIND_VEC` case above // for more details. @@ -1722,7 +1721,7 @@ impl From for Vec { let shared = bytes.data as *mut Shared; if unsafe { (*shared).is_unique() } { - let vec = mem::replace(unsafe { &mut (*shared).vec }, Vec::new()); + let vec = core::mem::take(unsafe { &mut (*shared).vec }); unsafe { release_shared(shared) }; @@ -1797,7 +1796,7 @@ unsafe fn shared_v_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> V let shared = &mut *shared; // Drop shared - let mut vec = mem::replace(&mut shared.vec, Vec::new()); + let mut vec = core::mem::take(&mut shared.vec); release_shared(shared); // Copy back buffer @@ -1823,7 +1822,7 @@ unsafe fn shared_v_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> B let v = &mut shared.vec; let v_capacity = v.capacity(); let v_ptr = v.as_mut_ptr(); - let offset = offset_from(ptr as *mut u8, v_ptr); + let offset = ptr.offset_from(v_ptr) as usize; let cap = v_capacity - offset; let ptr = vptr(ptr as *mut u8); diff --git a/src/lib.rs b/src/lib.rs index 08c424942..fb5c506e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,6 @@ fn abort() -> ! { #[inline(always)] #[cfg(feature = "std")] fn saturating_sub_usize_u64(a: usize, b: u64) -> usize { - use core::convert::TryFrom; match usize::try_from(b) { Ok(b) => a.saturating_sub(b), Err(_) => 0, @@ -124,7 +123,6 @@ fn saturating_sub_usize_u64(a: usize, b: u64) -> usize { #[inline(always)] #[cfg(feature = "std")] fn min_u64_usize(a: u64, b: usize) -> usize { - use core::convert::TryFrom; match usize::try_from(a) { Ok(a) => usize::min(a, b), Err(_) => b, @@ -182,18 +180,3 @@ fn panic_does_not_fit(size: usize, nbytes: usize) -> ! { size, nbytes ); } - -/// 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 stabilized -/// in 1.47, we cannot use it. -#[inline] -fn offset_from(dst: *const u8, original: *const u8) -> usize { - dst as usize - original as usize -} diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 0abeb9f7a..9eb0bffdc 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -4,7 +4,6 @@ use bytes::buf::UninitSlice; use bytes::{BufMut, BytesMut}; use core::fmt::Write; use core::mem::MaybeUninit; -use core::usize; #[test] fn test_vec_as_mut_buf() { diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index fb4a0dc17..85c007961 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -5,7 +5,6 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::panic::{self, AssertUnwindSafe}; -use std::usize; const LONG: &[u8] = b"mary had a little lamb, little lamb, little lamb"; const SHORT: &[u8] = b"hello world"; @@ -82,7 +81,6 @@ fn fmt() { #[test] fn fmt_write() { use std::fmt::Write; - use std::iter::FromIterator; let s = String::from_iter((0..10).map(|_| "abcdefg")); let mut a = BytesMut::with_capacity(64); From 16132ad2593402aa7dbd882b0ce22a40bfacfd96 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Wed, 16 Jul 2025 10:53:50 +0200 Subject: [PATCH 192/203] Fix latest clippy warnings (#787) --- src/bytes.rs | 4 ++-- src/bytes_mut.rs | 8 ++++---- tests/test_chain.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index fdc879cd2..b9264bd33 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -789,7 +789,7 @@ impl PartialEq for Bytes { impl PartialOrd for Bytes { fn partial_cmp(&self, other: &Bytes) -> Option { - self.as_slice().partial_cmp(other.as_slice()) + Some(self.cmp(other)) } } @@ -1536,7 +1536,7 @@ unsafe fn shallow_clone_vec( // pointed to by `actual` will be visible. match atom.compare_exchange(ptr as _, shared as _, Ordering::AcqRel, Ordering::Acquire) { Ok(actual) => { - debug_assert!(actual as usize == ptr as usize); + debug_assert!(core::ptr::eq(actual, ptr)); // The upgrade was successful, the new handle can be // returned. Bytes { diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index befe16bca..4d6e80eab 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -779,7 +779,7 @@ impl BytesMut { self.ptr = vptr(v.as_mut_ptr()); self.cap = v.capacity(); debug_assert_eq!(self.len, v.len()); - return true; + true } /// Attempts to cheaply reclaim already allocated capacity for at least `additional` more @@ -985,7 +985,7 @@ impl BytesMut { // new start and updating the `len` field to reflect the new length // of the view. self.ptr = vptr(self.ptr.as_ptr().add(count)); - self.len = self.len.checked_sub(count).unwrap_or(0); + self.len = self.len.saturating_sub(count); self.cap -= count; } @@ -1283,7 +1283,7 @@ impl PartialEq for BytesMut { impl PartialOrd for BytesMut { fn partial_cmp(&self, other: &BytesMut) -> Option { - self.as_slice().partial_cmp(other.as_slice()) + Some(self.cmp(other)) } } @@ -1718,7 +1718,7 @@ impl From for Vec { rebuild_vec(bytes.ptr.as_ptr(), bytes.len, bytes.cap, off) } } else { - let shared = bytes.data as *mut Shared; + let shared = bytes.data; if unsafe { (*shared).is_unique() } { let vec = core::mem::take(unsafe { &mut (*shared).vec }); diff --git a/tests/test_chain.rs b/tests/test_chain.rs index cfda6b8dc..e809f44f1 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -135,7 +135,7 @@ fn vectored_read() { #[test] fn chain_growing_buffer() { - let mut buff = [' ' as u8; 10]; + let mut buff = [b' '; 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 From 8b4f54d0f317724bfeab29232f8a9333b3d8625e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 10 Aug 2025 19:34:43 +0900 Subject: [PATCH 193/203] Ignore BytesMut::freeze doctest on wasm (#790) --- src/bytes_mut.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 4d6e80eab..6a4b9e7b2 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -225,7 +225,7 @@ impl BytesMut { /// /// # Examples /// - /// ``` + /// ```ignore-wasm /// use bytes::{BytesMut, BufMut}; /// use std::thread; /// From ef7f25736cfe48db5b42f2aa3225cace4126d2ac Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Thu, 14 Aug 2025 09:55:18 +0100 Subject: [PATCH 194/203] Specialize BytesMut::put:: (#793) --- src/bytes_mut.rs | 20 +++++++++++++++----- tests/test_bytes.rs | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 6a4b9e7b2..ad3aa775d 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1201,11 +1201,21 @@ unsafe impl BufMut for BytesMut { where Self: Sized, { - while src.has_remaining() { - let s = src.chunk(); - let l = s.len(); - self.extend_from_slice(s); - src.advance(l); + // When capacity is zero, try reusing allocation of `src`. + if self.capacity() == 0 { + let src_copy = src.copy_to_bytes(src.remaining()); + drop(src); + match src_copy.try_into_mut() { + Ok(bytes_mut) => *self = bytes_mut, + Err(bytes) => self.extend_from_slice(&bytes), + } + } else { + while src.has_remaining() { + let s = src.chunk(); + let l = s.len(); + self.extend_from_slice(s); + src.advance(l); + } } } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 85c007961..ec9a60e6c 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1688,3 +1688,22 @@ fn owned_safe_drop_on_as_ref_panic() { assert!(result.is_err()); assert_eq!(drop_counter.get(), 1); } + +/// Test `BytesMut::put` reuses allocation of `Bytes`. +#[test] +fn bytes_mut_put_bytes_specialization() { + let mut vec = Vec::with_capacity(1234); + vec.push(10); + let capacity = vec.capacity(); + assert!(capacity >= 1234); + + // Make `Bytes` backed by `Vec`. + let bytes = Bytes::from(vec); + let mut bytes_mut = BytesMut::new(); + bytes_mut.put(bytes); + + // Check contents is correct. + assert_eq!(&[10], bytes_mut.as_ref()); + // If allocation is reused, capacity should be equal to original vec capacity. + assert_eq!(bytes_mut.capacity(), capacity); +} From 016fdbdc7a95f6764186dacfcaeef94d77e89cd1 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Tue, 19 Aug 2025 11:50:38 +0100 Subject: [PATCH 195/203] Reserve capacity in BytesMut::put (#794) --- src/bytes_mut.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index ad3aa775d..d8d04d37b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1210,6 +1210,9 @@ unsafe impl BufMut for BytesMut { Err(bytes) => self.extend_from_slice(&bytes), } } else { + // In case the src isn't contiguous, reserve upfront. + self.reserve(src.remaining()); + while src.has_remaining() { let s = src.chunk(); let l = s.len(); From 4b53a29eb26716592ef2f00f925ef58ccb182e61 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Wed, 20 Aug 2025 10:57:49 +0100 Subject: [PATCH 196/203] Tweak BytesMut::remaining_mut (#795) --- src/bytes_mut.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index d8d04d37b..333a0ae1e 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1170,7 +1170,8 @@ impl Buf for BytesMut { unsafe impl BufMut for BytesMut { #[inline] fn remaining_mut(&self) -> usize { - usize::MAX - self.len() + // Max allocation size is isize::MAX. + isize::MAX as usize - self.len() } #[inline] From 7ce330f5192a322fb1efefe7ac9a3850b27162a1 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 10 Nov 2025 15:16:58 +0100 Subject: [PATCH 197/203] Move drop_fn of from_owner into vtable (#801) --- src/bytes.rs | 81 ++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index b9264bd33..441ca80d7 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -267,10 +267,7 @@ impl Bytes { // and: https://github.com/tokio-rs/bytes/pull/742/#discussion_r1813316032 let owned = Box::into_raw(Box::new(Owned { - lifetime: OwnedLifetime { - ref_cnt: AtomicUsize::new(1), - drop: owned_box_and_drop::, - }, + ref_cnt: AtomicUsize::new(1), owner, })); @@ -278,7 +275,7 @@ impl Bytes { ptr: NonNull::dangling().as_ptr(), len: 0, data: AtomicPtr::new(owned.cast()), - vtable: &OWNED_VTABLE, + vtable: &Owned::::VTABLE, }; let buf = unsafe { &*owned }.owner.as_ref(); @@ -1107,85 +1104,75 @@ unsafe fn static_drop(_: &mut AtomicPtr<()>, _: *const u8, _: usize) { // ===== impl OwnedVtable ===== -#[repr(C)] -struct OwnedLifetime { - ref_cnt: AtomicUsize, - drop: unsafe fn(*mut ()), -} - #[repr(C)] struct Owned { - lifetime: OwnedLifetime, + ref_cnt: AtomicUsize, owner: T, } -unsafe fn owned_box_and_drop(ptr: *mut ()) { - let b: Box> = Box::from_raw(ptr as _); - drop(b); +impl Owned { + const VTABLE: Vtable = Vtable { + clone: owned_clone::, + into_vec: owned_to_vec::, + into_mut: owned_to_mut::, + is_unique: owned_is_unique, + drop: owned_drop::, + }; } -unsafe fn owned_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { +unsafe fn owned_clone(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Bytes { let owned = data.load(Ordering::Relaxed); - let ref_cnt = &(*owned.cast::()).ref_cnt; - let old_cnt = ref_cnt.fetch_add(1, Ordering::Relaxed); + let old_cnt = (*owned.cast::()).fetch_add(1, Ordering::Relaxed); if old_cnt > usize::MAX >> 1 { - crate::abort() + crate::abort(); } Bytes { ptr, len, data: AtomicPtr::new(owned as _), - vtable: &OWNED_VTABLE, + vtable: &Owned::::VTABLE, } } -unsafe fn owned_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { +unsafe fn owned_to_vec(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> Vec { let slice = slice::from_raw_parts(ptr, len); let vec = slice.to_vec(); - owned_drop_impl(data.load(Ordering::Relaxed)); + owned_drop_impl::(data.load(Ordering::Relaxed)); vec } -unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { - BytesMut::from_vec(owned_to_vec(data, ptr, len)) +unsafe fn owned_to_mut(data: &AtomicPtr<()>, ptr: *const u8, len: usize) -> BytesMut { + BytesMut::from_vec(owned_to_vec::(data, ptr, len)) } unsafe fn owned_is_unique(_data: &AtomicPtr<()>) -> bool { false } -unsafe fn owned_drop_impl(owned: *mut ()) { - let lifetime = owned.cast::(); - let ref_cnt = &(*lifetime).ref_cnt; +unsafe fn owned_drop_impl(owned: *mut ()) { + { + let ref_cnt = &*owned.cast::(); - let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release); - debug_assert!( - old_cnt > 0 && old_cnt <= usize::MAX >> 1, - "expected non-zero refcount and no underflow" - ); - if old_cnt != 1 { - return; + let old_cnt = ref_cnt.fetch_sub(1, Ordering::Release); + debug_assert!( + old_cnt > 0 && old_cnt <= usize::MAX >> 1, + "expected non-zero refcount and no underflow" + ); + if old_cnt != 1 { + return; + } + ref_cnt.load(Ordering::Acquire); } - ref_cnt.load(Ordering::Acquire); - let drop_fn = &(*lifetime).drop; - drop_fn(owned) + drop(Box::>::from_raw(owned.cast())); } -unsafe fn owned_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { +unsafe fn owned_drop(data: &mut AtomicPtr<()>, _ptr: *const u8, _len: usize) { let owned = data.load(Ordering::Relaxed); - owned_drop_impl(owned); + owned_drop_impl::(owned); } -static OWNED_VTABLE: Vtable = Vtable { - clone: owned_clone, - into_vec: owned_to_vec, - into_mut: owned_to_mut, - is_unique: owned_is_unique, - drop: owned_drop, -}; - // ===== impl PromotableVtable ===== static PROMOTABLE_EVEN_VTABLE: Vtable = Vtable { From 60cbb776f22e4ef2268c026e88a24d6ed75b3776 Mon Sep 17 00:00:00 2001 From: discord9 <55937128+discord9@users.noreply.github.com> Date: Fri, 14 Nov 2025 17:52:50 +0800 Subject: [PATCH 198/203] fix: `BytesMut` only reuse if src has remaining (#803) Signed-off-by: discord9 --- src/bytes_mut.rs | 7 +++++-- tests/test_buf_mut.rs | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 333a0ae1e..565e91d9b 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1202,8 +1202,11 @@ unsafe impl BufMut for BytesMut { where Self: Sized, { - // When capacity is zero, try reusing allocation of `src`. - if self.capacity() == 0 { + if !src.has_remaining() { + // prevent calling `copy_to_bytes`->`put`->`copy_to_bytes` infintely when src is empty + return; + } else if self.capacity() == 0 { + // When capacity is zero, try reusing allocation of `src`. let src_copy = src.copy_to_bytes(src.remaining()); drop(src); match src_copy.try_into_mut() { diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs index 9eb0bffdc..f1bd3b0a0 100644 --- a/tests/test_buf_mut.rs +++ b/tests/test_buf_mut.rs @@ -273,3 +273,12 @@ fn copy_from_slice_panics_if_different_length_2() { let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) }; slice.copy_from_slice(b"abcd"); } + +/// Test if with zero capacity BytesMut does not infinitely recurse in put from Buf +#[test] +fn test_bytes_mut_reuse() { + let mut buf = BytesMut::new(); + buf.put(&[] as &[u8]); + let mut buf = BytesMut::new(); + buf.put(&[1u8, 2, 3] as &[u8]); +} From a7952fb4478f6dc226f623b217432fbc6f8dad24 Mon Sep 17 00:00:00 2001 From: Martin Grigorov Date: Fri, 14 Nov 2025 16:44:53 +0200 Subject: [PATCH 199/203] chore: prepare bytes v1.11.0 (#804) --- CHANGELOG.md | 19 +++++++++++++++++++ Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a98a5c981..91169d2f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +# 1.11.0 (November 14th, 2025) + +- Bump MSRV to 1.57 (#788) + +### Fixed + +- fix: `BytesMut` only reuse if src has remaining (#803) +- Specialize `BytesMut::put::` (#793) +- Reserve capacity in `BytesMut::put` (#794) +- Change `BytesMut::remaining_mut` to use `isize::MAX` instead of `usize::MAX` (#795) + +### Internal changes + +- Guarantee address in `slice()` for empty slices. (#780) +- Rename `Vtable::to_*` -> `Vtable::into_*` (#776) +- Fix latest clippy warnings (#787) +- Ignore `BytesMut::freeze` doctest on wasm (#790) +- Move `drop_fn` of `from_owner` into vtable (#801) + # 1.10.1 (March 5th, 2025) ### Fixed diff --git a/Cargo.toml b/Cargo.toml index e7c246649..62211136f 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.10.1" +version = "1.11.0" edition = "2021" rust-version = "1.57" license = "MIT" From acd1e0ffb8f076225759b8005d04f65ef77cccca Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 21 Nov 2025 11:14:40 +0100 Subject: [PATCH 200/203] Fix `get_int` if `nbytes` is zero (#806) --- src/buf/buf_impl.rs | 9 +++++++-- tests/test_buf.rs | 25 ++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/buf/buf_impl.rs b/src/buf/buf_impl.rs index 192034fbe..6508567b4 100644 --- a/src/buf/buf_impl.rs +++ b/src/buf/buf_impl.rs @@ -86,8 +86,13 @@ macro_rules! buf_get_impl { // https://en.wikipedia.org/wiki/Sign_extension fn sign_extend(val: u64, nbytes: usize) -> i64 { - let shift = (8 - nbytes) * 8; - (val << shift) as i64 >> shift + if nbytes == 0 { + // avoid `val << 64` panic + 0 + } else { + let shift = (8 - nbytes) * 8; + (val << shift) as i64 >> shift + } } /// Read bytes from a buffer. diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 099016e24..0d0c967c1 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -250,12 +250,12 @@ macro_rules! buf_tests { buf_tests!(number $make_input, get_f64_le, get_f64_le_overflow, f64, get_f64_le, f64::from_bits(0x7144726a727146ff)); buf_tests!(number $make_input, get_f64_ne, get_f64_ne_overflow, f64, get_f64_ne, f64::from_bits(e!(0xff4671726a724471, 0x7144726a727146ff))); - buf_tests!(var_number $make_input, get_uint_be, get_uint_be_overflow, u64, get_uint, 3, 0xff4671); - buf_tests!(var_number $make_input, get_uint_le, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff); - buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff)); - buf_tests!(var_number $make_input, get_int_be, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64); - buf_tests!(var_number $make_input, get_int_le, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff); - buf_tests!(var_number $make_input, get_int_ne, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff)); + buf_tests!(var_number $make_input, get_uint_be, get_uint_be_zero, get_uint_be_overflow, u64, get_uint, 3, 0xff4671); + buf_tests!(var_number $make_input, get_uint_le, get_uint_le_zero, get_uint_le_overflow, u64, get_uint_le, 3, 0x7146ff); + buf_tests!(var_number $make_input, get_uint_ne, get_uint_ne_zero, get_uint_ne_overflow, u64, get_uint_ne, 3, e!(0xff4671, 0x7146ff)); + buf_tests!(var_number $make_input, get_int_be, get_int_be_zero, get_int_be_overflow, i64, get_int, 3, 0xffffffffffff4671u64 as i64); + buf_tests!(var_number $make_input, get_int_le, get_int_le_zero, get_int_le_overflow, i64, get_int_le, 3, 0x7146ff); + buf_tests!(var_number $make_input, get_int_ne, get_int_ne_zero, get_int_ne_overflow, i64, get_int_ne, 3, e!(0xffffffffffff4671u64 as i64, 0x7146ff)); }; (number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $value:expr) => { #[test] @@ -276,7 +276,7 @@ macro_rules! buf_tests { let _ = buf.$method(); } }; - (var_number $make_input:ident, $ok_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => { + (var_number $make_input:ident, $ok_name:ident, $ok_zero_name:ident, $panic_name:ident, $number:ty, $method:ident, $len:expr, $value:expr) => { #[test] fn $ok_name() { let mut buf = $make_input(INPUT); @@ -287,6 +287,17 @@ macro_rules! buf_tests { assert_eq!(value, $value); } + // Regression test for https://github.com/tokio-rs/bytes/issues/798 + #[test] + fn $ok_zero_name() { + let mut buf = $make_input(INPUT); + + let value = buf.$method(0); + assert_eq!(buf.remaining(), 64); + assert!(buf.has_remaining()); + assert_eq!(value, 0); + } + #[test] #[should_panic] fn $panic_name() { From 94e42915a9a4bac7c2fbaa3d94f27bf0eb4dfb14 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Fri, 28 Nov 2025 10:02:34 +0100 Subject: [PATCH 201/203] Document that `BytesMut::{reserve,try_reserve}` doesn't preserve unused capacity (#808) --- src/bytes_mut.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 565e91d9b..fe6dbc733 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -551,6 +551,8 @@ impl BytesMut { /// and the original buffer is large enough to fit the requested additional /// capacity, then reallocations will never happen. /// + /// This method does not preserve data stored in the unused capacity. + /// /// # Examples /// /// In the following example, a new buffer is allocated. @@ -797,6 +799,8 @@ impl BytesMut { /// references through other `BytesMut`s or `Bytes` which point to the same underlying /// storage. /// + /// This method does not preserve data stored in the unused capacity. + /// /// # Examples /// /// ``` From b4ed70daee06013a35a13832807e8f244d5419aa Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 2 Dec 2025 13:08:19 +0100 Subject: [PATCH 202/203] Add test for copy_to_bytes() -> BytesMut avoiding clone (#809) --- tests/test_buf.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_buf.rs b/tests/test_buf.rs index 0d0c967c1..1520df897 100644 --- a/tests/test_buf.rs +++ b/tests/test_buf.rs @@ -448,3 +448,14 @@ fn test_deref_buf_forwards() { assert_eq!((Box::new(Special) as Box).get_u8(), b'x'); assert_eq!(Box::new(Special).get_u8(), b'x'); } + +#[test] +fn copy_to_bytes_mut() { + let mut bytes_mut = BytesMut::from(b"foobar".as_slice()); + let ptr = bytes_mut.as_ptr(); + let ret = bytes_mut.copy_to_bytes(bytes_mut.len()); + assert_eq!(ret.as_ptr(), ptr); + drop(bytes_mut); + let bytes_mut2 = BytesMut::from(ret); + assert_eq!(bytes_mut2.as_ptr(), ptr); +} From fd426ca0842cf9688def31d9ee4e027692ac6477 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 17 Dec 2025 13:51:19 +0000 Subject: [PATCH 203/203] Exclude development scripts from published package (#810) During a dependency review we noticed that the bytes crate includes various development scripts. These development scripts shouldn't be there as they might, at some point become problematic. As of now they prevent any downstream user from enabling the `[bans.build.interpreted]` option of cargo deny. I opted for using an explicit include list instead of an exclude list to prevent these files from being included in the published packages to make sure that everything that's included is an conscious choice. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 62211136f..f235bb365 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ repository = "https://github.com/tokio-rs/bytes" readme = "README.md" keywords = ["buffers", "zero-copy", "io"] categories = ["network-programming", "data-structures"] +include = ["CHANGELOG.md", "LICENSE", "README.md", "SECURITY.md", "Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "clippy.toml"] [features] default = ["std"]