From 579219ed981ec5d2c8c7647f4657ed8178a81d1d Mon Sep 17 00:00:00 2001 From: Cyborus Date: Wed, 20 Dec 2023 11:51:08 -0500 Subject: [PATCH 1/5] add `Bytes::is_unique` --- src/bytes.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/bytes.rs b/src/bytes.rs index 9fed3d287..2f7ffc630 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -208,6 +208,42 @@ 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. + /// + /// # 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 { + if core::ptr::eq(self.vtable, &PROMOTABLE_EVEN_VTABLE) + || core::ptr::eq(self.vtable, &PROMOTABLE_ODD_VTABLE) + { + let shared = self.data.load(Ordering::Acquire); + let kind = shared as usize & KIND_MASK; + + if kind == KIND_ARC { + let ref_cnt = unsafe { (*shared.cast::()).ref_cnt.load(Ordering::Relaxed) }; + ref_cnt == 1 + } else { + true + } + } else if core::ptr::eq(self.vtable, &SHARED_VTABLE) { + let shared = self.data.load(Ordering::Acquire); + let ref_cnt = unsafe { (*shared.cast::()).ref_cnt.load(Ordering::Relaxed) }; + ref_cnt == 1 + } else { + false + } + } + /// Creates `Bytes` instance from slice, by copying it. pub fn copy_from_slice(data: &[u8]) -> Self { data.to_vec().into() From 0835c4548a0c4c492b8b133f8bbc699225ef9df6 Mon Sep 17 00:00:00 2001 From: Cyborus Date: Mon, 8 Jan 2024 17:25:48 -0500 Subject: [PATCH 2/5] change `Bytes::is_unique` to take `&mut self` --- src/bytes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 2f7ffc630..0f80f9806 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -217,12 +217,12 @@ impl Bytes { /// ``` /// use bytes::Bytes; /// - /// let a = Bytes::from(vec![1, 2, 3]); + /// let mut 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 { + pub fn is_unique(&mut self) -> bool { if core::ptr::eq(self.vtable, &PROMOTABLE_EVEN_VTABLE) || core::ptr::eq(self.vtable, &PROMOTABLE_ODD_VTABLE) { From b37e569560f6a0ebb76a22b5b615dcd7ef304e8e Mon Sep 17 00:00:00 2001 From: Cyborus Date: Mon, 8 Jan 2024 17:55:42 -0500 Subject: [PATCH 3/5] use vtable to implement `Bytes::is_unique` --- src/bytes.rs | 48 +++++++++++++++++++++++++++++------------------- src/bytes_mut.rs | 1 + 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 0f80f9806..2ec88b795 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), } @@ -223,25 +225,7 @@ impl Bytes { /// assert!(!a.is_unique()); /// ``` pub fn is_unique(&mut self) -> bool { - if core::ptr::eq(self.vtable, &PROMOTABLE_EVEN_VTABLE) - || core::ptr::eq(self.vtable, &PROMOTABLE_ODD_VTABLE) - { - let shared = self.data.load(Ordering::Acquire); - let kind = shared as usize & KIND_MASK; - - if kind == KIND_ARC { - let ref_cnt = unsafe { (*shared.cast::()).ref_cnt.load(Ordering::Relaxed) }; - ref_cnt == 1 - } else { - true - } - } else if core::ptr::eq(self.vtable, &SHARED_VTABLE) { - let shared = self.data.load(Ordering::Acquire); - let ref_cnt = unsafe { (*shared.cast::()).ref_cnt.load(Ordering::Relaxed) }; - ref_cnt == 1 - } else { - false - } + unsafe { (self.vtable.is_unique)(&self.data) } } /// Creates `Bytes` instance from slice, by copying it. @@ -934,6 +918,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, }; @@ -947,6 +932,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] } @@ -956,12 +945,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, }; @@ -1056,6 +1047,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()) @@ -1085,6 +1088,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, }; @@ -1130,6 +1134,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 57fd33e4b..cb9687d9d 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -1702,6 +1702,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, }; From e8365adee3a1a196d28d476701e478aaf920430c Mon Sep 17 00:00:00 2001 From: Cyborus Date: Tue, 9 Jan 2024 17:05:15 -0500 Subject: [PATCH 4/5] change `Bytes::is_unique` to take `&self` --- src/bytes.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 2ec88b795..9eda9f463 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -214,17 +214,21 @@ impl Bytes { /// /// 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 mut a = Bytes::from(vec![1, 2, 3]); + /// let a = Bytes::from(vec![1, 2, 3]); /// assert!(a.is_unique()); /// let b = a.clone(); /// assert!(!a.is_unique()); /// ``` - pub fn is_unique(&mut self) -> bool { + pub fn is_unique(&self) -> bool { unsafe { (self.vtable.is_unique)(&self.data) } } From a2f20f553878a913ff993bb7062f35a1aa1c488f Mon Sep 17 00:00:00 2001 From: Cyborus Date: Fri, 19 Jan 2024 14:29:47 -0500 Subject: [PATCH 5/5] add tests for `Bytes::is_unique` --- tests/test_bytes.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) 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()); +}