From 79583507c1fb154d2f9782cf437123b25e5e2353 Mon Sep 17 00:00:00 2001 From: Jeremy Schoemaker Date: Mon, 21 Sep 2026 07:07:37 -0500 Subject: [PATCH] Release shared reference when Bytes is advanced to its end Bytes::advance only increments the start pointer, so advancing all the way to the end leaves an empty Bytes that still holds a reference to the parent buffer. Because the length of a Bytes can never grow again, that reference can only keep the allocation alive without ever being useful, preventing reuse or reclamation. truncate(0) and split_to(self.len()) already replace self with an empty Bytes so the shared reference is dropped. Do the same in advance when cnt equals the current length. --- src/bytes.rs | 9 +++++++++ tests/test_bytes.rs | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/bytes.rs b/src/bytes.rs index aed4d7cdb..7e2d6d115 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -700,6 +700,15 @@ impl Buf for Bytes { self.len(), ); + if cnt == self.len() { + // Advancing to the end leaves an empty `Bytes`, whose length can + // never grow again. Release the shared reference instead of + // retaining it, matching `truncate(0)` and `split_to(self.len())`. + let end_ptr = self.ptr.wrapping_add(cnt); + drop(mem::replace(self, Bytes::new_empty_with_ptr(end_ptr))); + return; + } + unsafe { self.inc_start(cnt); } diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 5d01b5b82..7ce66b4eb 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -364,6 +364,31 @@ fn truncate_to_zero_releases_shared_reference() { assert!(remaining.try_into_mut().is_err()); } +#[test] +fn advance_to_end_releases_shared_reference() { + let mut bytes = BytesMut::from(&b"hello"[..]); + drop(bytes.split_off(bytes.len())); + let mut advanced = bytes.freeze(); + let remaining = advanced.clone(); + + advanced.advance(5); + + assert!(advanced.is_empty()); + let mut remaining = remaining.try_into_mut().unwrap(); + remaining[0] = b'H'; + assert_eq!(remaining, b"Hello"[..]); + + let mut bytes = BytesMut::from(&b"hello"[..]); + drop(bytes.split_off(bytes.len())); + let mut nonempty = bytes.freeze(); + let remaining = nonempty.clone(); + + nonempty.advance(4); + + assert_eq!(nonempty, b"o"[..]); + assert!(remaining.try_into_mut().is_err()); +} + #[test] fn freeze_clone_shared() { let s = &b"abcdefgh"[..];