From b794dd68bacb66846902b58b2a56771edc77d560 Mon Sep 17 00:00:00 2001 From: polazarus Date: Thu, 1 May 2025 22:21:14 +0200 Subject: [PATCH 01/31] wip --- src/bytes/raw.rs | 42 ++++++++++++++++++++++++++++++-------- src/bytes/raw/allocated.rs | 21 +++++++++++++++++-- src/bytes/raw/borrowed.rs | 41 +++++++++++++------------------------ src/vecs/smart_thin.rs | 8 ++++++++ src/vecs/thin.rs | 2 +- 5 files changed, 76 insertions(+), 38 deletions(-) diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index b8eb3d35..ad0d9cc0 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -31,10 +31,10 @@ const MASK: u8 = (1 << TAG_BITS) - 1; const TAG_INLINE: u8 = 1; /// Tag for the borrowed repr -const TAG_BORROWED: u8 = 2; +const TAG_OWNED: u8 = 2; /// Tag for the allocated repr -const TAG_ALLOCATED: u8 = 3; +const TAG_ALLOCATED: u8 = 2; /// Maximal byte capacity of an inline [`HipByt`]. pub(crate) const INLINE_CAPACITY: usize = size_of::() - 1; @@ -123,9 +123,26 @@ pub(super) struct Pivot { tag_byte: NonZeroU8, } +#[derive(Clone, Copy)] +#[repr(C)] +pub(super) union WordView { + tag: usize, + _remainder: [MaybeUninit<*mut ()>; 2], +} + unsafe impl Sync for HipByt<'_, B> {} unsafe impl Send for HipByt<'_, B> {} +// U -> maybe uninit +// +// Big endian last word (reversed for little endian) +// +// UUUU_UUUU*3 0000_0000: None +// UUUU_UUUU*3 LLLL_LL01: Inline (L -> length) +// PPPP_PPPP*3 PPPP_PP10: Fat vec \__ allocated +// PPPP_PPPP*3 PPPP_PP11: Thin vec / +// 0000_0000*3 0000_001X: Borrowed + /// Equivalent union representation. /// /// NOTE: Cannot be used directly to keep the niche for `Option>` @@ -142,6 +159,9 @@ pub union Union<'borrow, B: Backend> { /// Pivot representation with niche pivot: Pivot, + + /// View to access the tagged word + words: WordView, } impl<'borrow, B: Backend> Union<'borrow, B> { @@ -170,9 +190,9 @@ impl<'borrow, B: Backend> Union<'borrow, B> { #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Tag { - Inline = TAG_INLINE, - Borrowed = TAG_BORROWED, - Allocated = TAG_ALLOCATED, + Inline, + Borrowed, + Allocated, } /// Helper enum to split this raw byte string into its possible representation. @@ -250,9 +270,15 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { /// Retrieves the tag. pub(super) const fn tag(&self) -> Tag { match self.pivot.tag_byte.get() & MASK { - TAG_INLINE => Tag::Inline, - TAG_BORROWED => Tag::Borrowed, - TAG_ALLOCATED => Tag::Allocated, + 0b01 => Tag::Inline, + 0b11 | 0b10 => { + let first = unsafe { self.union().words.tag }; + if first == borrowed::TAG { + Tag::Borrowed + } else { + Tag::Allocated + } + } // SAFETY: type invariant _ => unsafe { unreachable_unchecked() }, } diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index 4b674136..b3d69e47 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -9,10 +9,16 @@ use core::ptr::NonNull; use crate::backend::{Backend, UpdateResult}; use crate::smart::{Inner, Smart}; +use crate::vecs::SmartThinVec; const MASK: usize = super::MASK as usize; const TAG: usize = super::TAG_ALLOCATED as usize; +enum Variant { + Fat(F), + Thin(T), +} + /// Tagged smart pointer (with forced exposed provenance). /// /// The exposed provenance is required to cast [`Allocated`] from and to the @@ -32,6 +38,17 @@ impl Clone for TaggedSmart { impl Copy for TaggedSmart {} impl TaggedSmart { + /// Gets the owner. + fn get(&self) -> Variant, B>, SmartThinVec> { + if (self.0 & 1) != 0 { + Variant::Fat(unsafe { Smart::from_raw(NonNull::new_unchecked(self.0 as *mut _)) }) + } else { + Variant::Thin(unsafe { + SmartThinVec::from_raw(NonNull::new_unchecked(self.0 as *mut _)) + }) + } + } + /// Constructed a tagged smart pointer from a [`Smart`]. #[inline] fn from(raw: Smart, B>) -> Self { @@ -128,8 +145,8 @@ impl RefUnwindSafe for Allocated {} impl Allocated { /// Converts the allocated representation into its owner. - fn into_owner(self) -> Smart, B> { - self.owner.into() + fn into_owner(self) -> Variant, B>, SmartThinVec> { + self.owner.get() } /// Returns a reference to the owner. diff --git a/src/bytes/raw/borrowed.rs b/src/bytes/raw/borrowed.rs index b8cea5aa..54ab56f9 100644 --- a/src/bytes/raw/borrowed.rs +++ b/src/bytes/raw/borrowed.rs @@ -1,14 +1,13 @@ //! Representation for borrowed slice. -use core::mem::{offset_of, size_of, MaybeUninit}; -use core::num::NonZeroU8; +use core::mem::{offset_of, size_of}; -use super::TAG_BORROWED; +use super::TAG_OWNED; #[cfg(test)] mod tests; -const TAG_NZ: NonZeroU8 = NonZeroU8::new(TAG_BORROWED).unwrap(); +pub const TAG: usize = unsafe { TAG_OWNED as usize }; // a null pointer tagged with `TAG_OWNED` /// Borrowed slice representation. /// @@ -20,39 +19,27 @@ const TAG_NZ: NonZeroU8 = NonZeroU8::new(TAG_BORROWED).unwrap(); #[repr(C)] pub struct Borrowed<'borrow> { #[cfg(target_endian = "little")] - tag: NonZeroU8, - - #[cfg(target_endian = "little")] - reserved: MaybeUninit<[u8; size_of::() - 1]>, + tag: usize, slice: &'borrow [u8], #[cfg(target_endian = "big")] - reserved: MaybeUninit<[u8; size_of::() - 1]>, - - #[cfg(target_endian = "big")] - tag: NonZeroU8, + tag: usize, } impl<'borrow> Borrowed<'borrow> { - const ASSERTS: () = { - if cfg!(target_endian = "little") { - assert!(offset_of!(Self, tag) == 0); - } else { - assert!(offset_of!(Self, tag) == size_of::() - 1); - } - }; - /// Creates a new borrowed representation. #[inline] pub const fn new(slice: &'borrow [u8]) -> Self { - let () = Self::ASSERTS; // HACK to actually do the check + const { + if cfg!(target_endian = "little") { + assert!(offset_of!(Self, tag) == 0); + } else { + assert!(offset_of!(Self, tag) == size_of::() - size_of::()); + } + } - let this = Self { - slice, - tag: TAG_NZ, - reserved: MaybeUninit::uninit(), - }; + let this = Self { slice, tag: TAG }; debug_assert!(this.is_valid()); this } @@ -81,7 +68,7 @@ impl<'borrow> Borrowed<'borrow> { /// Return `true` iff this representation is valid. #[inline] pub const fn is_valid(&self) -> bool { - self.tag.get() == TAG_BORROWED + self.tag == TAG } /// Sets the length diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 84ee106b..4d9c8ae1 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -113,6 +113,14 @@ impl Deref for SmartThinVec { } impl SmartThinVec { + pub(crate) unsafe fn from_raw(ptr: NonNull>) -> Self { + Self(ptr) + } + pub(crate) fn into_raw(self) -> NonNull> { + let this = ManuallyDrop::new(self); + this.0 + } + /// Creates a new empty vector. /// /// # Examples diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 115c599c..f5aa8ae9 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -74,7 +74,7 @@ macro_rules! thin_vec { /// A shared thin vector's header. #[derive(Clone, Copy, Debug)] #[repr(C)] -pub(super) struct Header { +pub(crate) struct Header { prefix: P, cap: usize, len: usize, From ea48e45eeb2e5571464130c674fb1414f06fd3c2 Mon Sep 17 00:00:00 2001 From: polazarus Date: Fri, 2 May 2025 16:13:57 +0200 Subject: [PATCH 02/31] wip --- src/bytes/raw.rs | 19 +- src/bytes/raw/allocated.rs | 373 ++++++++++++++++++++++++------------- src/bytes/raw/tests.rs | 2 +- src/smart.rs | 25 ++- src/vecs/hip.rs | 60 ++++++ src/vecs/smart_thin.rs | 12 +- src/vecs/thin.rs | 2 +- 7 files changed, 351 insertions(+), 142 deletions(-) create mode 100644 src/vecs/hip.rs diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index ad0d9cc0..3a9499f7 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -328,7 +328,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { /// Creates a new `HipByt` from a vector. pub(super) fn from_vec(vec: Vec) -> Self { - let allocated = Allocated::new(vec); + let allocated = Allocated::from_vec(vec); Self::from_allocated(allocated) } @@ -406,9 +406,10 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { unsafe { Self::inline_unchecked(&allocated.as_slice()[range]) } } else { // SAFETY: length is checked above - unsafe { - let allocated = allocated.slice_unchecked(range); + if let Some(allocated) = unsafe { allocated.slice_unchecked(range.clone()) } { Self::from_allocated(allocated) + } else { + Self::from_slice(&allocated.as_slice()[range]) } } } @@ -458,9 +459,10 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { // SAFETY: by the function precondition let range = unsafe { range_of_unchecked(self.as_slice(), slice) }; // SAFETY: length checked above - unsafe { - let allocated = allocated.slice_unchecked(range); + if let Some(allocated) = unsafe { allocated.slice_unchecked(range.clone()) } { Self::from_allocated(allocated) + } else { + Self::from_slice(&allocated.as_slice()[range]) } } } @@ -593,8 +595,11 @@ impl Clone for HipByt<'_, B> { Split::Borrowed(&borrowed) => Self::from_borrowed(borrowed), Split::Allocated(allocated) => { // increase the ref count or clone if overflow - let clone = allocated.explicit_clone(); - Self::from_allocated(clone) + if let Some(clone) = allocated.try_clone() { + Self::from_allocated(clone) + } else { + Self::from_slice(allocated.as_slice()) + } } } } diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index b3d69e47..9f8d5f51 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -2,17 +2,22 @@ use alloc::vec::Vec; use core::marker::PhantomData; -use core::mem::{forget, ManuallyDrop, MaybeUninit}; -use core::ops::{Deref, DerefMut, Range}; +use core::mem::{offset_of, transmute, ManuallyDrop, MaybeUninit}; +use core::ops::Range; use core::panic::{RefUnwindSafe, UnwindSafe}; -use core::ptr::NonNull; +use core::ptr::{self, NonNull}; +use super::TAG_OWNED; use crate::backend::{Backend, UpdateResult}; use crate::smart::{Inner, Smart}; +use crate::vecs::thin::{Header, ThinVec}; use crate::vecs::SmartThinVec; -const MASK: usize = super::MASK as usize; -const TAG: usize = super::TAG_ALLOCATED as usize; +const TAG_MASK: usize = super::MASK as usize; +const TAG: usize = super::TAG_OWNED as usize; +const THIN_BIT: usize = 0b1; +const TAG_THIN: usize = super::TAG_OWNED as usize | THIN_BIT; +const TAG_FAT: usize = super::TAG_OWNED as usize; enum Variant { Fat(F), @@ -40,64 +45,125 @@ impl Copy for TaggedSmart {} impl TaggedSmart { /// Gets the owner. fn get(&self) -> Variant, B>, SmartThinVec> { - if (self.0 & 1) != 0 { - Variant::Fat(unsafe { Smart::from_raw(NonNull::new_unchecked(self.0 as *mut _)) }) + let ptr = self.ptr(); + + if self.is_fat() { + Variant::Fat(unsafe { Smart::from_raw(ptr.cast()) }) } else { - Variant::Thin(unsafe { - SmartThinVec::from_raw(NonNull::new_unchecked(self.0 as *mut _)) - }) + Variant::Thin(unsafe { SmartThinVec::from_raw(ptr.cast()) }) } } + const fn is_fat(&self) -> bool { + self.0 & THIN_BIT == 0 + } + + const fn ptr(&self) -> NonNull<()> { + // expose provenance semantics + let ptr = (self.0 & !TAG_MASK) as *mut (); + debug_assert!(!ptr.is_null()); + // debug only + + // SAFETY: type invariant + let ptr: NonNull<()> = unsafe { NonNull::new_unchecked(ptr) }; + + #[cfg(miri)] + let _ = &*ptr; // check provenance early + ptr + } + /// Constructed a tagged smart pointer from a [`Smart`]. #[inline] - fn from(raw: Smart, B>) -> Self { + fn from_smart_vec(raw: Smart, B>) -> Self { let ptr = Smart::into_raw(raw).as_ptr(); debug_assert!(ptr.is_aligned()); - debug_assert!((ptr as usize) & MASK == 0); + debug_assert!((ptr as usize) & TAG_MASK == 0); // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment // requirement as usize (typically 4 bytes on 32-bit architectures, and // more on 64-bit architectures) - let addr = ptr.map_addr(|addr| addr | TAG).expose_provenance(); + let addr = ptr.map_addr(|addr| addr | TAG_FAT).expose_provenance(); Self(addr, PhantomData) } - /// Converts back into the [`Smart`]. #[inline] - fn into(self) -> Smart, B> { - let this: Smart, B>; - - debug_assert!(self.0 & MASK == TAG); + fn from_smart_thin_vec(raw: SmartThinVec) -> Self { + let ptr = SmartThinVec::into_raw(raw).as_ptr(); + debug_assert!(ptr.is_aligned()); + debug_assert!((ptr as usize) & TAG_MASK == 0); - // SAFETY: remove a 2-bit tag to a non-null pointer with the same - // alignment as usize (typically 4 on 32-bit architectures, and + // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment + // requirement as usize (typically 4 bytes on 32-bit architectures, and // more on 64-bit architectures) - unsafe { - let new_ptr = core::ptr::with_exposed_provenance_mut::, B>>(self.0 ^ TAG); + let addr = ptr.map_addr(|addr| addr | TAG_THIN).expose_provenance(); - debug_assert!(!new_ptr.is_null()); + Self(addr, PhantomData) + } - #[cfg(miri)] - let _ = &*new_ptr; // check provenance early + #[inline] + fn is_unique(self) -> bool { + const { + assert!(offset_of!(Inner, count) == 0); + assert!(offset_of!(Header, prefix) == 0); + } + self.count().is_unique() + } + + #[inline] + fn count(&self) -> &B { + let counter = unsafe { self.ptr().cast().as_ref() }; - this = Smart::from_raw(NonNull::new_unchecked(new_ptr)); + #[cfg(debug_assertions)] + { + let m = ManuallyDrop::new(self.get()); + unsafe { + match &*m { + Variant::Fat(fat) => { + debug_assert_eq!(&raw const fat.0.as_ref().count, counter as *const _); + } + Variant::Thin(thin) => { + debug_assert_eq!(&raw const thin.0.as_ref().prefix, counter as *const _); + } + } + } } - this + counter + } + + #[inline] + fn as_slice(&self) -> &[u8] { + let v = ManuallyDrop::new(self.get()); + let short_lived = match &*v { + Variant::Fat(fat) => fat.as_slice(), + Variant::Thin(thin) => thin.as_slice(), + }; + // SAFETY: the owner is valid. + unsafe { transmute(short_lived) } } /// Checks if the tag is valid. #[inline] const fn check_tag(self) -> bool { - self.0 & MASK == TAG + (self.0 & !TAG_MASK) != 0 && (self.0 & TAG_OWNED as usize) != 0 } /// Explicitly clones this tagged smart pointer. - fn explicit_clone(self) -> Self { - let r = ManuallyDrop::new(self.into()); - Self::from((*r).force_clone()) + fn try_clone(self) -> Option { + let variant = ManuallyDrop::new(self.get()); + match &*variant { + Variant::Fat(fat) => fat.try_clone().map(Self::from_smart_vec), + Variant::Thin(thin) => thin.try_clone().map(Self::from_smart_thin_vec), + } + } + + unsafe fn as_fat_unchecked(&self) -> Smart, B> { + debug_assert!(self.is_fat()); + debug_assert!(self.check_tag()); + + // SAFETY: type invariant + unsafe { Smart::from_raw(self.ptr().cast()) } } } @@ -149,42 +215,20 @@ impl Allocated { self.owner.get() } - /// Returns a reference to the owner. - /// - /// This function abuses the [`Copy`]-ness of [`Allocated`] to get a copy - /// (and not a clone) of the [`Smart`] reference wrapped in [`ManuallyDrop`] - /// to ensure it's not dropped. - fn owner(&self) -> impl Deref, B>> { - ManuallyDrop::new(self.into_owner()) - } - - /// Returns a mutable reference to the owner. - /// - /// This function abuses the [`Copy`]-ness of [`Allocated`] to get a copy - /// (and not a clone) of the [`Smart`] reference wrapped in [`ManuallyDrop`] - /// to ensure it's not dropped. - /// - /// # Safety - /// - /// The owner must not be shared, cf. [`Self::is_unique`]. - unsafe fn owner_mut(&mut self) -> impl DerefMut, B>> { - debug_assert!(self.is_unique()); - ManuallyDrop::new(self.into_owner()) - } - /// Creates an allocated from a vector. /// /// Takes ownership of the vector without copying the data. #[inline] - pub fn new(v: Vec) -> Self { + pub fn from_vec(v: Vec) -> Self { let ptr = v.as_ptr(); let len = v.len(); let owner = Smart::new(v); + assert!(owner.is_unique()); let this = Self { ptr, len, - owner: TaggedSmart::from(owner), + owner: TaggedSmart::from_smart_vec(owner), }; debug_assert!(this.is_unique()); @@ -192,9 +236,27 @@ impl Allocated { this } + #[inline] + pub fn from_thin_vec

(v: ThinVec) -> Self { + let ptr = v.as_ptr(); + let len = v.len(); + let stv = SmartThinVec::from(v); + + let this = Self { + ptr, + len, + owner: TaggedSmart::from_smart_thin_vec(stv), + }; + + debug_assert!(this.is_unique()); + debug_assert!(this.is_valid()); + + this + } + /// Creates an allocated vector from a slice. pub fn from_slice(slice: &[u8]) -> Self { - Self::new(slice.to_vec()) + Self::from_vec(slice.to_vec()) } /// Returns the length of this allocated string. @@ -286,35 +348,37 @@ impl Allocated { /// /// Range must be a range `a..b` such that `a <= b <= len`. #[inline] - pub unsafe fn slice_unchecked(&self, range: Range) -> Self { + pub unsafe fn slice_unchecked(&self, range: Range) -> Option { debug_assert!(self.is_valid()); debug_assert!(range.start <= range.end); debug_assert!(range.start <= self.len); debug_assert!(range.end <= self.len); - let owner = self.owner.explicit_clone(); + let owner = self.owner; + if owner.count().incr() == UpdateResult::Overflow { + return None; + } // SAFETY: type invariant -> self.ptr..self.ptr+self.len is valid // also Rust like C specify you can move to the last + 1 let ptr = unsafe { self.ptr.add(range.start) }; - Self { + Some(Self { ptr, len: range.len(), owner, - } + }) } /// Clones this vector. #[inline] - pub fn explicit_clone(&self) -> Self { + pub fn try_clone(&self) -> Option { debug_assert!(self.is_valid()); - let owner = self.owner(); - if owner.incr() == UpdateResult::Overflow { - Self::from_slice(self.as_slice()) + if self.owner.count().incr() == UpdateResult::Done { + Some(*self) } else { - *self + None } } @@ -333,13 +397,14 @@ impl Allocated { return false; } - let owner = self.owner(); - let owner_ptr = owner.as_ptr(); + let owner_slice: &[u8] = self.owner.as_slice(); + let owner_ptr = owner_slice.as_ptr(); + let owner_len = owner_slice.len(); let shift = unsafe { self.ptr.offset_from(owner_ptr) }; shift >= 0 && { #[allow(clippy::cast_sign_loss)] let shift = shift as usize; - shift <= owner.len() && shift + self.len <= owner.len() + shift <= owner_len && shift + self.len <= owner_len } } @@ -348,7 +413,11 @@ impl Allocated { pub fn capacity(&self) -> usize { debug_assert!(self.is_valid()); - self.owner().capacity() + let owner = ManuallyDrop::new(self.owner.get()); + match &*owner { + Variant::Fat(fat) => fat.capacity(), + Variant::Thin(thin) => thin.capacity(), + } } /// Unwraps the inner vector if it makes any sense. @@ -356,31 +425,29 @@ impl Allocated { pub fn try_into_vec(self) -> Result, Self> { debug_assert!(self.is_valid()); - let owner = self.owner(); - if self.ptr != owner.as_ptr() { - // the starts differ, cannot truncate + if !self.owner.is_fat() { return Err(self); } + let owner = ManuallyDrop::new(unsafe { self.owner.as_fat_unchecked() }); - let len = self.len(); + if !owner.is_unique() || self.ptr != owner.as_ptr() { + // the owner is shared or the slice is not at the start of the vector + return Err(self); + } + + let owner = ManuallyDrop::into_inner(owner); + let mut vec = unsafe { Smart::into_inner_unchecked(owner) }; + vec.truncate(self.len); + + // forget(self); - self.into_owner().try_unwrap().map_or_else( - |owner| { - forget(owner); // do not drop - Err(self) - }, - |mut owner| { - owner.truncate(len); - Ok(owner) - }, - ) + Ok(vec) } /// Returns `true` if there is only one reference to the underlying vector. pub fn is_unique(&self) -> bool { debug_assert!(self.is_valid()); - - self.owner().is_unique() + self.owner.is_unique() } /// Pushes a slice at the end of the underlying vector. @@ -390,23 +457,41 @@ impl Allocated { /// The reference must be unique, cf. [`Self::is_unique`]. pub unsafe fn push_slice_unchecked(&mut self, addition: &[u8]) { debug_assert!(self.is_valid()); + debug_assert!(self.is_unique()); - // SAFETY: unique by precondition - let mut owner = unsafe { self.owner_mut() }; + // SAFETY: uniqueness is a precondition, owner can be mutable + let mut owner = ManuallyDrop::new(self.owner.get()); - // SAFETY: unique by precondition - let v = unsafe { owner.as_mut_unchecked() }; + let shift; // start of the slice in the vector + let ptr; // pointer to the start of the vector, may change when growing - // SAFETY: compute the shift from within the vector range (type invariant) #[allow(clippy::cast_sign_loss)] - let shift = unsafe { self.ptr.offset_from(v.as_ptr()) as usize }; - v.truncate(shift + self.len); - v.extend_from_slice(addition); + match &mut *owner { + Variant::Fat(fat) => { + let fat = unsafe { fat.as_mut_unchecked() }; + + // SAFETY: compute the shift from within the vector range (type invariant) + shift = unsafe { self.ptr.offset_from(fat.as_ptr()) as usize }; + fat.truncate(self.len); + fat.extend_from_slice(addition); + ptr = fat.as_ptr(); + } + Variant::Thin(thin) => { + let thin = unsafe { thin.as_mut_unchecked() }; + + // SAFETY: compute the shift from within the vector range (type invariant) + shift = unsafe { self.ptr.offset_from(thin.as_ptr()) as usize }; + thin.truncate(self.len); + thin.extend_from_slice_copy(addition); + ptr = thin.as_ptr(); + } + } + self.len += addition.len(); // SAFETY: shift to within the vector range (the shift was already // in the old range). - self.ptr = unsafe { v.as_ptr().add(shift) }; + self.ptr = unsafe { ptr.add(shift) }; } /// Returns the remaining spare capacity of the unique vector as a slice of @@ -423,21 +508,28 @@ impl Allocated { return &mut []; } - // SAFETY: unique checked above - let mut owner = unsafe { self.owner_mut() }; - - // SAFETY: lifetime is extended to `'_` only - let v = unsafe { owner.as_mut_unchecked_extended() }; - - // SAFETY: computes the shift from within the vector range (type - // invariant) - #[allow(clippy::cast_sign_loss)] - let start = unsafe { self.ptr.offset_from(v.as_ptr()) as usize }; - - // truncates to the actual used length without shrinking - v.truncate(start + self.len); - - v.spare_capacity_mut() + let mut owner = ManuallyDrop::new(self.owner.get()); + + match &mut *owner { + Variant::Fat(fat) => { + let vec = unsafe { fat.as_mut_unchecked_extended() }; + let start = unsafe { self.ptr.offset_from(vec.as_ptr()) as usize }; + let end = start + self.len; + vec.truncate(end); + vec.spare_capacity_mut() + } + Variant::Thin(thin) => { + let vec = unsafe { thin.as_mut_unchecked() }; + let start = unsafe { self.ptr.offset_from(vec.as_ptr()) as usize }; + let end = start + self.len; + vec.truncate(end); + let spare = vec.spare_capacity_mut(); + + // extend lifetime of the slicce + // SAFETY: the slice is valid while self is owned + unsafe { transmute(spare) } + } + } } /// Forces the length of the vector to `new_len`. @@ -463,16 +555,33 @@ impl Allocated { debug_assert!(self.is_unique()); debug_assert!(new_len <= self.capacity()); - // SAFETY: uniqueness is a precondition - let mut owner = unsafe { self.owner_mut() }; - - // SAFETY: uniqueness is a precondition - let v = unsafe { owner.as_mut_unchecked() }; + let mut owner = ManuallyDrop::new(self.owner.get()); + match &mut *owner { + Variant::Fat(fat) => { + // SAFETY: uniqueness is a precondition + let vec = unsafe { fat.as_mut_unchecked() }; + + // SAFETY: compute the shift from within the vector range (type invariant) + #[allow(clippy::cast_sign_loss)] + let start = unsafe { self.ptr.offset_from(vec.as_ptr()).abs_diff(0) }; + + unsafe { + vec.set_len(start + new_len); + } + } + + Variant::Thin(thin) => { + // SAFETY: uniqueness is a precondition + let vec = unsafe { thin.as_mut_unchecked() }; + // SAFETY: compute the shift from within the vector range (type invariant) + #[allow(clippy::cast_sign_loss)] + let start = unsafe { self.ptr.offset_from(vec.as_ptr()).abs_diff(0) }; + unsafe { + vec.set_len(start + new_len); + } + } + } - // SAFETY: compute the shift from within the vector range (type invariant) - #[allow(clippy::cast_sign_loss)] - let start = unsafe { self.ptr.offset_from(v.as_ptr()).abs_diff(0) }; - unsafe { v.set_len(start + new_len) }; self.len = new_len; } @@ -489,9 +598,9 @@ impl Allocated { return; } - let mut new_vec = Vec::with_capacity(min_capacity); - new_vec.extend_from_slice(self.as_slice()); - let old = core::mem::replace(self, Self::new(new_vec)); + let mut new_vec: ThinVec = ThinVec::with_capacity(min_capacity); + new_vec.extend_from_slice_copy(self.as_slice()); + let old = core::mem::replace(self, Self::from_thin_vec(new_vec)); old.explicit_drop(); } } @@ -501,31 +610,37 @@ mod tests { use alloc::vec; use super::Allocated; - use crate::Rc; + use crate::backend::Counter; + use crate::{thin_vec, Rc}; #[test] fn test_alloc() { - let allocated = Allocated::::new(vec![]); + let allocated = Allocated::::from_vec(vec![]); + let _ = allocated.explicit_drop(); + + let allocated = Allocated::::from_thin_vec(thin_vec![0_u8]); + let allocated2 = allocated.try_clone().unwrap(); let _ = allocated.explicit_drop(); + let _ = allocated2.explicit_drop(); } #[test] #[cfg_attr(coverage_nightly, coverage(off))] fn test_try_into_vec() { - let allocated = Allocated::::new(vec![0, 1, 2]); - assert_eq!(allocated.owner().ref_count(), 1); + let allocated = Allocated::::from_vec(vec![0, 1, 2]); + assert_eq!(allocated.owner.count().get(), 1); { - let slice = unsafe { allocated.slice_unchecked(1..2) }; + let slice = unsafe { allocated.slice_unchecked(1..2) }.unwrap(); - assert_eq!(allocated.owner().ref_count(), 2); + assert_eq!(allocated.owner.count().get(), 2); let Err(allocated) = slice.try_into_vec() else { panic!("shared reference cannot be converted to vec") }; allocated.explicit_drop(); } - assert_eq!(allocated.owner().ref_count(), 1); + assert_eq!(allocated.owner.count().get(), 1); assert!(allocated.try_into_vec().is_ok()); } diff --git a/src/bytes/raw/tests.rs b/src/bytes/raw/tests.rs index 39bc196f..a6d931d8 100644 --- a/src/bytes/raw/tests.rs +++ b/src/bytes/raw/tests.rs @@ -17,7 +17,7 @@ fn test_union() { let _: R = union.into_raw(); let union = Union { - allocated: Allocated::new([42].repeat(42)), + allocated: Allocated::from_vec([42].repeat(42)), }; let _: R = union.into_raw(); diff --git a/src/smart.rs b/src/smart.rs index d171a6d2..e7e31480 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -19,11 +19,12 @@ use crate::backend::{ mod tests; /// Smart pointer inner cell. +#[repr(C)] pub struct Inner where C: Backend, { - count: C, + pub(crate) count: C, value: T, } @@ -41,7 +42,8 @@ where } /// Basic smart pointer, with generic counter. -pub struct Smart(NonNull>) +#[repr(transparent)] +pub struct Smart(pub(crate) NonNull>) where C: Backend; @@ -120,7 +122,7 @@ where /// let q = unsafe { Smart::from_raw(ptr) }; /// ``` #[inline] - pub fn from_raw(ptr: NonNull>) -> Self { + pub unsafe fn from_raw(ptr: NonNull>) -> Self { debug_assert!(ptr.is_aligned()); unsafe { Self(ptr) } } @@ -208,6 +210,23 @@ where self.inner().count.incr() } + pub(crate) unsafe fn into_inner_unchecked(self) -> T { + debug_assert!(self.is_unique()); + + let this = ManuallyDrop::new(self); + // SAFETY: type invariant, pointer must be valid + let inner = unsafe { Box::from_raw(this.0.as_ptr()) }; + inner.value + } + + pub fn try_clone(&self) -> Option { + if self.incr() == UpdateResult::Done { + Some(Self(self.0)) + } else { + None + } + } + pub(crate) fn force_clone(&self) -> Self where T: Clone, diff --git a/src/vecs/hip.rs b/src/vecs/hip.rs new file mode 100644 index 00000000..122b6283 --- /dev/null +++ b/src/vecs/hip.rs @@ -0,0 +1,60 @@ +use core::marker::PhantomData; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::num::NonZeroU8; +use core::ptr::NonNull; + +use typenum::{U}; +use super::InlineVec; + +type Align = [T; 0]; + +const POINTER_SIZE: usize = core::mem::size_of::<*mut ()>(); +const POINTER_SIZE_M1: usize = POINTER_SIZE - 1; + +pub struct HipVec<'borrow, T, B>(Pivot, PhantomData<(T, &'borrow B)>); + +struct Pivot { + #[cfg(target_endian = "little")] + tag_byte: NonZeroU8, + + #[cfg(target_endian = "little")] + _word_remainder: MaybeUninit<[u8; POINTER_SIZE_M1]>, + + _words: [MaybeUninit<*mut ()>; 2], + + #[cfg(target_endian = "big")] + _word_remainder: MaybeUninit<[u8; POINTER_SIZE_M1]>, + + #[cfg(target_endian = "big")] + _tag: NonZeroU8, +} + +trait SizeOf { + const SIZE: usize; + type Size; +} +impl SizeOf for T { + const SIZE: usize = size_of::(); + type Size = U<{size_of::()}>; +} + +pub struct Inline { + _size: NonZeroU8, + _inline: [MaybeUninit; N], +} + + +impl<'borrow, T, B> HipVec<'borrow, T, B> { + const N: usize = 2; + fn as_inline(&self) -> Option<&InlineVec> { + if self.is_inline() { + let + } + + } +} + +enum H { + Inline(InlineSize, [MaybeUninit; N]), + Heap(NonNull, NonNull, usize), +} diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 4d9c8ae1..7f0c2917 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -102,7 +102,7 @@ macro_rules! smart_thin_vec { /// assert_eq!(v.as_ptr(), v2.as_ptr()); /// ``` #[repr(transparent)] -pub struct SmartThinVec(pub(super) NonNull>); +pub struct SmartThinVec(pub(crate) NonNull>); impl Deref for SmartThinVec { type Target = ThinVec; @@ -349,6 +349,16 @@ impl SmartThinVec { } } + pub fn force_clone(&self) -> Self + where + T: Clone, + { + self.try_clone().unwrap_or_else(|| { + let thin_vec: ThinVec<_, _> = self.as_thin_vec().fresh_clone(); + unsafe { Self::from_thin_vec_unchecked(thin_vec) } + }) + } + /// Converts into a [`ThinVec`] if the reference is unique. /// /// # Errors diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index f5aa8ae9..30b0e8d7 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -75,7 +75,7 @@ macro_rules! thin_vec { #[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Header { - prefix: P, + pub(crate) prefix: P, cap: usize, len: usize, phantom: PhantomData, From 46e400bb806358a7f26c63ea4b7ce334947c8053 Mon Sep 17 00:00:00 2001 From: polazarus Date: Sun, 4 May 2025 22:32:23 +0200 Subject: [PATCH 03/31] wip --- src/backend.rs | 6 --- src/backend/atomic.rs | 2 +- src/bytes.rs | 26 +++++++++++ src/bytes/raw.rs | 30 ++++++------- src/bytes/raw/allocated.rs | 70 +++++++++++++++++++++--------- src/bytes/raw/borrowed.rs | 2 +- src/bytes/tests.rs | 19 +++++--- src/common/drain.rs | 2 +- src/common/traits.rs | 88 ++++++++++++++++++++++++++++++-------- src/common/traits/tests.rs | 2 +- src/macros.rs | 16 +++---- src/smart.rs | 74 ++++++++++++++++++++++++++------ src/smart/tests.rs | 16 +++---- src/vecs/inline/tests.rs | 2 +- src/vecs/smart_thin.rs | 45 ++++++++++++++++++- src/vecs/thin.rs | 11 +++++ 16 files changed, 305 insertions(+), 106 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 139155f0..877cc706 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -113,12 +113,6 @@ pub enum UpdateResult { /// /// This trait is sealed and cannot be implemented outside this crate. pub trait Counter: Sealed + Default + 'static { - /// Creates a new counter that starts at one. - #[inline] - fn one() -> Self { - Self::default() - } - /// Tries to increment the counter. /// /// In case of atomics, the [`Ordering::Release`] semantics is expected on the write. diff --git a/src/backend/atomic.rs b/src/backend/atomic.rs index a311fd34..8f6dc2c8 100644 --- a/src/backend/atomic.rs +++ b/src/backend/atomic.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{fence, AtomicUsize, Ordering}; #[cfg(loom)] use loom::sync::atomic::{fence, AtomicUsize, Ordering}; -use super::*; +use super::{BackendImpl, Counter, PanicOnOverflow, Sealed, UpdateResult}; /// Atomic counter backend. pub type Arc = BackendImpl; diff --git a/src/bytes.rs b/src/bytes.rs index ebbf0a03..cca4852e 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -515,6 +515,32 @@ where matches!(self.tag(), Tag::Allocated) } + #[inline] + #[must_use] + pub const fn is_thin(&self) -> bool { + matches!(self.split(), Split::Allocated(allocated) if allocated.is_thin()) + } + + #[inline] + #[must_use] + pub const fn is_fat(&self) -> bool { + matches!(self.split(), Split::Allocated(allocated) if allocated.is_fat()) + } + + /// Returns `true` if this `HipByt` is not shared. + /// + /// Note that for the purpose of this function, a borrowed slice is + /// considered shared. + #[inline] + #[must_use] + pub fn is_unique(&self) -> bool { + match self.split() { + Split::Inline(_) => true, + Split::Allocated(allocated) if allocated.is_unique() => true, + _ => false, + } + } + /// Returns `true` if the representation is normalized. #[inline] #[must_use] diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index 3a9499f7..cef9b1f9 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -33,9 +33,6 @@ const TAG_INLINE: u8 = 1; /// Tag for the borrowed repr const TAG_OWNED: u8 = 2; -/// Tag for the allocated repr -const TAG_ALLOCATED: u8 = 2; - /// Maximal byte capacity of an inline [`HipByt`]. pub(crate) const INLINE_CAPACITY: usize = size_of::() - 1; @@ -406,11 +403,10 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { unsafe { Self::inline_unchecked(&allocated.as_slice()[range]) } } else { // SAFETY: length is checked above - if let Some(allocated) = unsafe { allocated.slice_unchecked(range.clone()) } { - Self::from_allocated(allocated) - } else { - Self::from_slice(&allocated.as_slice()[range]) - } + unsafe { allocated.slice_unchecked(range.clone()) }.map_or_else( + || Self::from_slice(&allocated.as_slice()[range]), + Self::from_allocated, + ) } } }; @@ -459,11 +455,10 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { // SAFETY: by the function precondition let range = unsafe { range_of_unchecked(self.as_slice(), slice) }; // SAFETY: length checked above - if let Some(allocated) = unsafe { allocated.slice_unchecked(range.clone()) } { - Self::from_allocated(allocated) - } else { - Self::from_slice(&allocated.as_slice()[range]) - } + unsafe { allocated.slice_unchecked(range.clone()) }.map_or_else( + || Self::from_slice(&allocated.as_slice()[range]), + Self::from_allocated, + ) } } }; @@ -595,11 +590,10 @@ impl Clone for HipByt<'_, B> { Split::Borrowed(&borrowed) => Self::from_borrowed(borrowed), Split::Allocated(allocated) => { // increase the ref count or clone if overflow - if let Some(clone) = allocated.try_clone() { - Self::from_allocated(clone) - } else { - Self::from_slice(allocated.as_slice()) - } + allocated.try_clone().map_or_else( + || Self::from_slice(allocated.as_slice()), + Self::from_allocated, + ) } } } diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index 9f8d5f51..234f2596 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -44,7 +44,7 @@ impl Copy for TaggedSmart {} impl TaggedSmart { /// Gets the owner. - fn get(&self) -> Variant, B>, SmartThinVec> { + fn get(self) -> Variant, B>, SmartThinVec> { let ptr = self.ptr(); if self.is_fat() { @@ -54,11 +54,21 @@ impl TaggedSmart { } } - const fn is_fat(&self) -> bool { + #[inline] + const fn is_thin(self) -> bool { + debug_assert!(self.check_tag()); + self.0 & THIN_BIT != 0 + } + + #[inline] + const fn is_fat(self) -> bool { + debug_assert!(self.check_tag()); self.0 & THIN_BIT == 0 } - const fn ptr(&self) -> NonNull<()> { + const fn ptr(self) -> NonNull<()> { + debug_assert!(self.check_tag()); + // expose provenance semantics let ptr = (self.0 & !TAG_MASK) as *mut (); debug_assert!(!ptr.is_null()); @@ -74,7 +84,7 @@ impl TaggedSmart { /// Constructed a tagged smart pointer from a [`Smart`]. #[inline] - fn from_smart_vec(raw: Smart, B>) -> Self { + fn from_fat(raw: Smart, B>) -> Self { let ptr = Smart::into_raw(raw).as_ptr(); debug_assert!(ptr.is_aligned()); debug_assert!((ptr as usize) & TAG_MASK == 0); @@ -84,11 +94,14 @@ impl TaggedSmart { // more on 64-bit architectures) let addr = ptr.map_addr(|addr| addr | TAG_FAT).expose_provenance(); - Self(addr, PhantomData) + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_fat()); + this } #[inline] - fn from_smart_thin_vec(raw: SmartThinVec) -> Self { + fn from_thin(raw: SmartThinVec) -> Self { let ptr = SmartThinVec::into_raw(raw).as_ptr(); debug_assert!(ptr.is_aligned()); debug_assert!((ptr as usize) & TAG_MASK == 0); @@ -98,7 +111,10 @@ impl TaggedSmart { // more on 64-bit architectures) let addr = ptr.map_addr(|addr| addr | TAG_THIN).expose_provenance(); - Self(addr, PhantomData) + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_thin()); + this } #[inline] @@ -120,10 +136,10 @@ impl TaggedSmart { unsafe { match &*m { Variant::Fat(fat) => { - debug_assert_eq!(&raw const fat.0.as_ref().count, counter as *const _); + debug_assert_eq!(&raw const fat.0.as_ref().count, ptr::from_ref(counter)); } Variant::Thin(thin) => { - debug_assert_eq!(&raw const thin.0.as_ref().prefix, counter as *const _); + debug_assert_eq!(&raw const thin.0.as_ref().prefix, ptr::from_ref(counter)); } } } @@ -153,12 +169,12 @@ impl TaggedSmart { fn try_clone(self) -> Option { let variant = ManuallyDrop::new(self.get()); match &*variant { - Variant::Fat(fat) => fat.try_clone().map(Self::from_smart_vec), - Variant::Thin(thin) => thin.try_clone().map(Self::from_smart_thin_vec), + Variant::Fat(fat) => fat.try_clone().map(Self::from_fat), + Variant::Thin(thin) => thin.try_clone().map(Self::from_thin), } } - unsafe fn as_fat_unchecked(&self) -> Smart, B> { + unsafe fn as_fat_unchecked(self) -> Smart, B> { debug_assert!(self.is_fat()); debug_assert!(self.check_tag()); @@ -210,6 +226,16 @@ impl UnwindSafe for Allocated {} impl RefUnwindSafe for Allocated {} impl Allocated { + #[inline] + pub const fn is_thin(&self) -> bool { + self.owner.is_thin() + } + + #[inline] + pub const fn is_fat(&self) -> bool { + self.owner.is_fat() + } + /// Converts the allocated representation into its owner. fn into_owner(self) -> Variant, B>, SmartThinVec> { self.owner.get() @@ -228,7 +254,7 @@ impl Allocated { let this = Self { ptr, len, - owner: TaggedSmart::from_smart_vec(owner), + owner: TaggedSmart::from_fat(owner), }; debug_assert!(this.is_unique()); @@ -238,14 +264,14 @@ impl Allocated { #[inline] pub fn from_thin_vec

(v: ThinVec) -> Self { - let ptr = v.as_ptr(); - let len = v.len(); - let stv = SmartThinVec::from(v); + let stv = SmartThinVec::from_thin_vec(v); + let ptr = stv.as_ptr(); + let len = stv.len(); let this = Self { ptr, len, - owner: TaggedSmart::from_smart_thin_vec(stv), + owner: TaggedSmart::from_thin(stv), }; debug_assert!(this.is_unique()); @@ -256,7 +282,7 @@ impl Allocated { /// Creates an allocated vector from a slice. pub fn from_slice(slice: &[u8]) -> Self { - Self::from_vec(slice.to_vec()) + Self::from_thin_vec(ThinVec::<_, B>::from_slice_copy(slice)) } /// Returns the length of this allocated string. @@ -472,7 +498,7 @@ impl Allocated { // SAFETY: compute the shift from within the vector range (type invariant) shift = unsafe { self.ptr.offset_from(fat.as_ptr()) as usize }; - fat.truncate(self.len); + fat.truncate(self.len + shift); fat.extend_from_slice(addition); ptr = fat.as_ptr(); } @@ -481,7 +507,7 @@ impl Allocated { // SAFETY: compute the shift from within the vector range (type invariant) shift = unsafe { self.ptr.offset_from(thin.as_ptr()) as usize }; - thin.truncate(self.len); + thin.truncate(self.len + shift); thin.extend_from_slice_copy(addition); ptr = thin.as_ptr(); } @@ -513,6 +539,7 @@ impl Allocated { match &mut *owner { Variant::Fat(fat) => { let vec = unsafe { fat.as_mut_unchecked_extended() }; + #[expect(clippy::cast_sign_loss)] let start = unsafe { self.ptr.offset_from(vec.as_ptr()) as usize }; let end = start + self.len; vec.truncate(end); @@ -520,6 +547,7 @@ impl Allocated { } Variant::Thin(thin) => { let vec = unsafe { thin.as_mut_unchecked() }; + #[expect(clippy::cast_sign_loss)] let start = unsafe { self.ptr.offset_from(vec.as_ptr()) as usize }; let end = start + self.len; vec.truncate(end); @@ -527,7 +555,7 @@ impl Allocated { // extend lifetime of the slicce // SAFETY: the slice is valid while self is owned - unsafe { transmute(spare) } + unsafe { transmute::<&mut [MaybeUninit], &mut [MaybeUninit]>(spare) } } } } diff --git a/src/bytes/raw/borrowed.rs b/src/bytes/raw/borrowed.rs index 54ab56f9..b2c03456 100644 --- a/src/bytes/raw/borrowed.rs +++ b/src/bytes/raw/borrowed.rs @@ -7,7 +7,7 @@ use super::TAG_OWNED; #[cfg(test)] mod tests; -pub const TAG: usize = unsafe { TAG_OWNED as usize }; // a null pointer tagged with `TAG_OWNED` +pub const TAG: usize = TAG_OWNED as usize; // a null pointer tagged with `TAG_OWNED` /// Borrowed slice representation. /// diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 25228679..4284dcd2 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -981,17 +981,20 @@ fn test_push_slice_inline() { } #[test] -fn test_push_slice_allocated() { - // allocated, unique +fn push_slice_allocated_unique() { let mut a = H::from(MEDIUM); + assert!(a.is_thin()); assert!(a.is_allocated()); a.push_slice(ABC); assert_eq!(&a[0..42], MEDIUM); assert_eq!(&a[42..], ABC); +} - // allocated, not unique +#[test] +fn push_slice_allocated_shared() { + // allocated, shared let mut a = H::from(MEDIUM); - assert!(a.is_allocated()); + assert!(a.is_thin()); let pa = a.as_ptr(); let b = a.clone(); assert_eq!(pa, b.as_ptr()); @@ -1000,13 +1003,19 @@ fn test_push_slice_allocated() { assert_eq!(&a[0..42], MEDIUM); assert_eq!(&a[42..], ABC); assert_eq!(b, MEDIUM); +} - // allocated, unique but sliced +#[test] +fn push_slice_allocated_unique_sliced() { let mut a = { let x = H::from(MEDIUM); + assert!(x.is_allocated()); + assert!(x.is_thin()); x.slice(1..39) }; assert!(a.is_allocated()); + assert!(a.is_thin()); + assert!(a.is_unique()); let p = a.as_ptr(); a.push_slice(ABC); assert_eq!(&a[..38], &MEDIUM[1..39]); diff --git a/src/common/drain.rs b/src/common/drain.rs index b5941a10..4b34b87b 100644 --- a/src/common/drain.rs +++ b/src/common/drain.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; use core::ops::{Range, RangeBounds}; use core::{fmt, mem, ptr, slice}; -use super::traits::MutVector; +use super::traits::{MutVector, MutVectorExt}; use super::RangeError; /// A draining iterator for vectors. diff --git a/src/common/traits.rs b/src/common/traits.rs index d0c89656..74550960 100644 --- a/src/common/traits.rs +++ b/src/common/traits.rs @@ -1,3 +1,6 @@ +#![allow(clippy::len_without_is_empty)] + +use core::mem::MaybeUninit; use core::ptr::NonNull; use sealed::Sealed; @@ -9,25 +12,86 @@ pub(crate) mod sealed { pub trait Sealed {} } +/// A vector trait that allows for low-level operations on the vector's memory. pub trait Vector: Sealed { type Item; + /// Returns the length of the vector. fn len(&self) -> usize; + + /// Returns the capacity of the vector. fn capacity(&self) -> usize; - fn as_slice(&self) -> &[Self::Item]; + + /// Returns a pointer to the first element of the vector. fn as_ptr(&self) -> *const Self::Item; } -pub trait MutVector: Vector { - unsafe fn set_len(&mut self, len: usize); +/// A vector trait that extends the [`Vector`] trait with additional methods. +pub trait VectorExt: Vector { + /// Returns `true` if the vector is empty. + #[inline] + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns a slice of the vector. + #[inline] + fn as_slice(&self) -> &[Self::Item] { + unsafe { core::slice::from_raw_parts(self.as_ptr(), self.len()) } + } +} - fn as_mut_ptr(&mut self) -> *mut Self::Item; +impl VectorExt for T {} + +/// A mutable vector trait that allows for low-level operations on the vector's memory. +/// +/// # Safety +/// +/// This trait is unsafe because it allows for low-level operations that can +/// lead to undefined behavior if not used correctly. +/// +/// In particular, the intricate semantics of the length and capacity of the vector +/// can lead to memory safety issues if not handled properly. +pub unsafe trait MutVector: Vector { + /// Sets the length of the vector. + /// + /// # Safety + /// + /// The caller must ensure that the new length does not exceed the capacity of the vector. + /// This is a low-level operation and should be used with caution. + /// The caller must ensure that the memory is valid for the new length. + unsafe fn set_len(&mut self, len: usize); + /// Returns a non-null pointer to the first element of the vector. fn as_non_null(&mut self) -> NonNull; +} + +/// A mutable vector trait that extends the [`MutVector`] trait with additional methods. +pub trait MutVectorExt: MutVector { + /// Returns a mutable slice of the vector. + fn as_mut_slice(&mut self) -> &mut [Self::Item] { + unsafe { core::slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) } + } - fn as_mut_slice(&mut self) -> &mut [Self::Item]; + /// Returns a mutable pointer to the first element of the vector. + fn as_mut_ptr(&mut self) -> *mut Self::Item { + self.as_non_null().as_ptr() + } + + /// Returns a mutable slice to the spare capacity of the vector. + fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + let len = self.len(); + let capacity = self.capacity(); + let spare_len = capacity - len; + unsafe { + let start = self.as_mut_ptr().add(len).cast(); + core::slice::from_raw_parts_mut(start, spare_len) + } + } } +impl MutVectorExt for T {} + impl Sealed for alloc::vec::Vec {} impl Vector for alloc::vec::Vec { @@ -41,28 +105,16 @@ impl Vector for alloc::vec::Vec { self.capacity() } - fn as_slice(&self) -> &[Self::Item] { - self.as_slice() - } - fn as_ptr(&self) -> *const Self::Item { self.as_ptr() } } -impl MutVector for alloc::vec::Vec { +unsafe impl MutVector for alloc::vec::Vec { unsafe fn set_len(&mut self, len: usize) { unsafe { self.set_len(len) } } - fn as_mut_ptr(&mut self) -> *mut Self::Item { - self.as_mut_ptr() - } - - fn as_mut_slice(&mut self) -> &mut [Self::Item] { - self.as_mut_slice() - } - fn as_non_null(&mut self) -> NonNull { unsafe { NonNull::new_unchecked(self.as_mut_ptr()) } } diff --git a/src/common/traits/tests.rs b/src/common/traits/tests.rs index 0fa26c71..97cda9b6 100644 --- a/src/common/traits/tests.rs +++ b/src/common/traits/tests.rs @@ -1,6 +1,6 @@ use alloc::vec; -use super::MutVector; +use super::*; #[test] fn vector_vec() { diff --git a/src/macros.rs b/src/macros.rs index 98d07d8b..26b00d4b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -88,15 +88,15 @@ macro_rules! trait_impls { impl $(< $($gen)* >)? crate::common::traits::sealed::Sealed for $t $(where $($wh)*)? {} impl $(< $($gen)* >)? crate::common::traits::Vector for $t $(where $($wh)*)? { type Item = $item; + #[inline] fn len(&self) -> usize { self.len() } + #[inline] fn capacity(&self) -> usize { self.capacity() } - fn as_slice(&self) -> &[Self::Item] { - self.as_slice() - } + #[inline] fn as_ptr(&self) -> *const Self::Item { self.as_ptr() } @@ -106,16 +106,12 @@ macro_rules! trait_impls { (@MutVector $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { }) => {}; (@MutVector $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { $t:ty ; $($rest:tt)* }) => { - impl $(< $($gen)* >)? crate::common::traits::MutVector for $t $(where $($wh)*)? { + unsafe impl $(< $($gen)* >)? crate::common::traits::MutVector for $t $(where $($wh)*)? { + #[inline] unsafe fn set_len(&mut self, len: usize) { unsafe { self.set_len(len) } } - fn as_mut_ptr(&mut self) -> *mut Self::Item { - self.as_mut_ptr() - } - fn as_mut_slice(&mut self) -> &mut [Self::Item] { - self.as_mut_slice() - } + #[inline] fn as_non_null(&mut self) -> core::ptr::NonNull { self.as_non_null() } diff --git a/src/smart.rs b/src/smart.rs index e7e31480..b9425f5b 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -35,7 +35,7 @@ where { fn clone(&self) -> Self { Self { - count: C::one(), + count: C::default(), value: self.value.clone(), } } @@ -68,7 +68,7 @@ where #[must_use] pub fn new(value: T) -> Self { let ptr = Box::into_raw(Box::new(Inner { - count: C::one(), + count: C::default(), value, })); Self(unsafe { NonNull::new_unchecked(ptr) }) @@ -122,6 +122,7 @@ where /// let q = unsafe { Smart::from_raw(ptr) }; /// ``` #[inline] + #[must_use] pub unsafe fn from_raw(ptr: NonNull>) -> Self { debug_assert!(ptr.is_aligned()); unsafe { Self(ptr) } @@ -130,7 +131,7 @@ where /// Gets a reference to the value. #[inline] #[must_use] - pub const fn as_ref(this: &Self) -> &T { + pub const fn get(this: &Self) -> &T { &this.inner().value } @@ -190,18 +191,22 @@ where inner.count.get() } - /// Try to unwrap to its inner value. + /// Tries to unwrap to its inner value. + /// + /// # Errors + /// + /// Returns `Err(this)` if the reference is shared. #[inline] - pub fn try_unwrap(self) -> Result { + pub fn try_unwrap(this: Self) -> Result { unsafe { - if self.is_unique() { + if this.is_unique() { // do not drop `self`! - let this = ManuallyDrop::new(self); + let this = ManuallyDrop::new(this); // SAFETY: type invariant, pointer must be valid let inner = unsafe { Box::from_raw(this.0.as_ptr()) }; Ok(inner.value) } else { - Err(self) + Err(this) } } } @@ -219,6 +224,23 @@ where inner.value } + /// Tries to clone the smart pointer by increasing the reference count. + /// + /// # Examples + /// + /// ``` + /// # use hipstr::smart::Smart; + /// # use hipstr::{Arc, Unique}; + /// let p = Smart::<_, Arc>::new(42); + /// let q = p.try_clone(); + /// assert!(q.is_some()); + /// assert_eq!(p.as_ref(), q.as_ref()); + /// + /// let u = Smart::<_, Unique>::new(42); + /// let q = u.try_clone(); + /// assert!(q.is_none()); + /// ``` + #[must_use] pub fn try_clone(&self) -> Option { if self.incr() == UpdateResult::Done { Some(Self(self.0)) @@ -227,14 +249,38 @@ where } } - pub(crate) fn force_clone(&self) -> Self + /// Clones the smart pointer even if the referenced count overflows, in + /// which case the underlying data is cloned. + /// + /// This is equivalent to calling [`Self::try_clone`] and cloning the inner + /// value if the reference count overflows. + /// + /// This is the default behavior of [`Clone::clone`] for `Unique`. + /// + /// # Examples + /// + /// ``` + /// # use hipstr::smart::Smart; + /// # use hipstr::{Arc, Unique}; + /// let p = Smart::<_, Arc>::new(42); + /// let q = Smart::force_clone(&p); + /// assert_eq!(p.as_ref(), q.as_ref()); + /// assert_eq!(p.as_ref() as *const i32, q.as_ref() as *const i32); + /// + /// let u = Smart::<_, Unique>::new(42); + /// let q = Smart::force_clone(&u); + /// assert_eq!(u.as_ref(), q.as_ref()); + /// assert_ne!(u.as_ref() as *const i32, q.as_ref() as *const i32); + /// ``` + #[must_use] + pub fn force_clone(this: &Self) -> Self where T: Clone, { - if unsafe { &(*self.0.as_ptr()).count }.incr() == UpdateResult::Done { - Self(self.0) + if unsafe { &(*this.0.as_ptr()).count }.incr() == UpdateResult::Done { + Self(this.0) } else { - let inner = self.inner().clone(); + let inner = this.inner().clone(); let ptr = Box::into_raw(Box::new(inner)); // SAFETY: duh let nonnull = unsafe { NonNull::new_unchecked(ptr) }; @@ -290,7 +336,7 @@ where #[inline] fn deref(&self) -> &Self::Target { - Smart::as_ref(self) + Self::get(self) } } @@ -300,7 +346,7 @@ where { #[inline] fn as_ref(&self) -> &T { - Smart::as_ref(self) + Self::get(self) } } diff --git a/src/smart/tests.rs b/src/smart/tests.rs index 9d5ae871..0a1b2e7a 100644 --- a/src/smart/tests.rs +++ b/src/smart/tests.rs @@ -108,8 +108,8 @@ fn test_unique_misc() { assert!(b.as_mut().is_some()); assert_eq!(b.as_ref(), &1); - assert_eq!(a.try_unwrap().unwrap_or(0), 1); - assert_eq!(b.try_unwrap().unwrap_or(0), 1); + assert_eq!(Smart::try_unwrap(a).unwrap_or(0), 1); + assert_eq!(Smart::try_unwrap(b).unwrap_or(0), 1); } #[test] @@ -146,10 +146,10 @@ fn test_local_misc() { assert_eq!(b.as_ref(), &1); // will drop b - assert!(b.try_unwrap().is_err()); + assert!(Smart::try_unwrap(b).is_err()); assert!(a.as_mut().is_some()); - assert_eq!(a.try_unwrap().unwrap_or(0), 1); + assert_eq!(Smart::try_unwrap(a).unwrap_or(0), 1); } #[test] @@ -188,11 +188,11 @@ fn test_atomic_misc() { assert!(b.as_mut().is_none()); // will drop a - assert!(a.try_unwrap().is_err()); + assert!(Smart::try_unwrap(a).is_err()); assert_eq!(b.ref_count(), 1); assert!(b.as_mut().is_some()); - assert_eq!(b.try_unwrap().unwrap_or(0), 1); + assert_eq!(Smart::try_unwrap(b).unwrap_or(0), 1); } #[test] @@ -383,12 +383,12 @@ fn loom_atomic_mt_clone_overflow() { #[test] fn force_clone() { let v = Smart::<_, PanickyUnique>::new(vec![1, 2, 3]); - let w = v.force_clone(); + let w = Smart::force_clone(&v); assert_eq!(w.as_slice(), [1, 2, 3]); assert_ne!(v.as_ptr(), w.as_ptr()); let v = Smart::<_, Arc>::new(vec![1, 2, 3]); - let w = v.force_clone(); + let w = Smart::force_clone(&v); assert_eq!(w.as_slice(), [1, 2, 3]); assert_eq!(v.as_ptr(), w.as_ptr()); } diff --git a/src/vecs/inline/tests.rs b/src/vecs/inline/tests.rs index 02ffbe26..4edaf50d 100644 --- a/src/vecs/inline/tests.rs +++ b/src/vecs/inline/tests.rs @@ -7,7 +7,7 @@ use core::mem::size_of; use core::ptr; use super::*; -use crate::common::traits::MutVector; +use crate::common::traits::{MutVector, MutVectorExt, VectorExt}; use crate::{inline_vec, thin_vec}; const SMALL_CAP: usize = 7; diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 7f0c2917..858f07ca 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -113,7 +113,7 @@ impl Deref for SmartThinVec { } impl SmartThinVec { - pub(crate) unsafe fn from_raw(ptr: NonNull>) -> Self { + pub(crate) const unsafe fn from_raw(ptr: NonNull>) -> Self { Self(ptr) } pub(crate) fn into_raw(self) -> NonNull> { @@ -349,6 +349,21 @@ impl SmartThinVec { } } + /// Clones the vector even if the count overflows, in which case it clones + /// the vector's data. + /// + /// # Examples + /// + /// ``` + /// # use hipstr::smart_thin_vec; + /// # use hipstr::Unique; + /// let v = smart_thin_vec![Unique : 1, 2, 3]; + /// assert_eq!(v.as_slice(), &[1, 2, 3]); + /// let v2 = v.force_clone(); // exactly the same as `v.clone()` in the case of `Unique` + /// assert_eq!(v.as_slice(), v2.as_slice()); + /// assert_ne!(v.as_ptr(), v2.as_ptr()); + /// ``` + #[must_use] pub fn force_clone(&self) -> Self where T: Clone, @@ -359,6 +374,34 @@ impl SmartThinVec { }) } + /// Clones the vector even if the count overflows, in which case it copies + /// the vector's data. + /// + /// This function may be more efficient than + /// [`force_clone`](Self::force_clone) for `Copy` types. + /// + /// # Examples + /// + /// ``` + /// # use hipstr::smart_thin_vec; + /// # use hipstr::Unique; + /// let v = smart_thin_vec![Unique : 1, 2, 3]; + /// assert_eq!(v.as_slice(), &[1, 2, 3]); + /// let v2 = v.clone_or_copy(); // maybe a bit more efficient than `v.clone()` for `Copy` types + /// assert_eq!(v.as_slice(), v2.as_slice()); + /// assert_ne!(v.as_ptr(), v2.as_ptr()); + /// ``` + #[must_use] + pub fn clone_or_copy(&self) -> Self + where + T: Copy, + { + self.try_clone().unwrap_or_else(|| { + let thin_vec: ThinVec<_, _> = self.as_thin_vec().fresh_copy(); + unsafe { Self::from_thin_vec_unchecked(thin_vec) } + }) + } + /// Converts into a [`ThinVec`] if the reference is unique. /// /// # Errors diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 30b0e8d7..2a391c31 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -1307,6 +1307,17 @@ impl ThinVec { this } + /// Clones with a fresh prefix. + pub(crate) fn fresh_copy(&self) -> ThinVec + where + T: Copy, + { + let len = self.len(); + let mut this = ThinVec::with_capacity(len); + this.extend_from_slice_copy(self.as_slice()); + this + } + /// Moves the items to a new vector with a fresh prefix. pub(crate) fn fresh_move(mut self) -> ThinVec { if can_reuse::() { From b259a81ff310abc53e1fa6c4c0e8bea5a8a66bc4 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 13:47:15 +0200 Subject: [PATCH 04/31] more tests --- src/bytes/tests.rs | 79 +++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 4284dcd2..8a4cd7c0 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -215,31 +215,43 @@ fn test_from_slice() { } #[test] -fn test_as_slice() { - // static - { - let a = H::borrowed(ABC); - assert!(a.is_borrowed()); - assert!(!a.is_inline()); - assert!(!a.is_allocated()); - assert_eq!(a.as_slice(), ABC); - } - // inline - { - let a = H::from(ABC); - assert!(!a.is_borrowed()); - assert!(a.is_inline()); - assert!(!a.is_allocated()); - assert_eq!(a.as_slice(), ABC); - } - // allocated - { - let a = H::from(Vec::from(MEDIUM)); - assert!(!a.is_borrowed()); - assert!(!a.is_inline()); - assert!(a.is_allocated()); - assert_eq!(a.as_slice(), MEDIUM); - } +fn borrowed() { + let a = H::borrowed(ABC); + assert!(a.is_borrowed()); + assert!(!a.is_inline()); + assert!(!a.is_allocated()); + assert_eq!(a.as_slice(), ABC); +} + +#[test] +fn inline() { + let a = H::from(ABC); + assert!(!a.is_borrowed()); + assert!(a.is_inline()); + assert!(!a.is_allocated()); + assert_eq!(a.as_slice(), ABC); +} + +#[test] +fn allocated_thin() { + let a = H::from(Vec::from(MEDIUM)); + assert!(a.is_thin()); + assert!(!a.is_fat()); + assert!(!a.is_borrowed()); + assert!(!a.is_inline()); + assert!(a.is_allocated()); + assert_eq!(a.as_slice(), MEDIUM); +} + +#[test] +fn allocated_fat() { + let a = H::from(Vec::from(MEDIUM)); + assert!(a.is_fat()); + assert!(!a.is_thin()); + assert!(!a.is_borrowed()); + assert!(!a.is_inline()); + assert!(a.is_allocated()); + assert_eq!(a.as_slice(), MEDIUM); } #[test] @@ -615,18 +627,27 @@ fn test_into_vec() { { // static let a = H::borrowed(ABC); + assert!(a.is_borrowed()); assert!(a.into_vec().is_err()); } { // inline let a = H::from(ABC); + assert!(a.is_inline()); + assert!(a.into_vec().is_err()); + } + + { + // allocated, thin + let a = H::from(MEDIUM); + assert!(a.is_thin()); assert!(a.into_vec().is_err()); } let v = vec![42; INLINE_CAPACITY + 2]; { - // allocated, unique + // allocated, fat, unique let v = v.clone(); let p = v.as_ptr(); let a = H::from(v); @@ -636,14 +657,14 @@ fn test_into_vec() { } { - // allocated, shared + // allocated, fat, shared let a = H::from(v.clone()); let _b = a.clone(); assert!(a.into_vec().is_err()); } { - // allocated, unique, sliced at start + // allocated, fat, unique, sliced at start let v = v.clone(); let p = v.as_ptr(); let a = H::from(v).slice(0..=INLINE_CAPACITY); @@ -653,7 +674,7 @@ fn test_into_vec() { } { - // allocated, unique, sliced at start + // allocated, fat, unique, sliced at start let a = H::from(v).slice(1..5); assert!(a.into_vec().is_err()); } From c6e4be2ca1fb6a97de0df29031fc0c0f985a87ba Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 13:57:30 +0200 Subject: [PATCH 05/31] more thin vecs --- src/bytes.rs | 23 +++++++++++++++-------- src/bytes/raw.rs | 7 +++++++ src/bytes/raw/allocated.rs | 10 ---------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index cca4852e..f96e4afe 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -19,6 +19,7 @@ use raw::{Split, SplitMut, Tag, Union, INLINE_CAPACITY}; use self::raw::try_range_of; pub use self::raw::HipByt; use crate::backend::Backend; +use crate::vecs::thin::{Reserved, ThinVec}; use crate::vecs::InlineVec; mod cmp; @@ -167,7 +168,7 @@ where if capacity <= Self::inline_capacity() { Self::inline_empty() } else { - Self::from_vec(Vec::with_capacity(capacity)) + Self::from_thin_vec(ThinVec::<_, Reserved>::with_capacity(capacity)) } } @@ -896,12 +897,12 @@ where } // requires a new vector - let mut vec = Vec::with_capacity(new_len); - vec.extend_from_slice(self.as_slice()); - vec.extend_from_slice(addition); + let mut vec = ThinVec::<_, Reserved>::with_capacity(new_len); + vec.extend_from_slice_copy(self.as_slice()); + vec.extend_from_slice_copy(addition); // SAFETY: vec's len (new_len) is checked above to be > INLINE_CAPACITY - *self = Self::from_vec(vec); + *self = Self::from_thin_vec(vec); } /// Creates a new `HipByt` by copying this one `n` times. @@ -937,10 +938,11 @@ where let src_len = self.len(); let new_len = src_len.checked_mul(n).expect("capacity overflow"); + if new_len <= Self::inline_capacity() { let mut inline = unsafe { InlineVec::zeroed(new_len) }; - let src = self.as_slice().as_ptr(); let mut dst = inline.as_mut_slice().as_mut_ptr(); + let src = self.as_slice().as_ptr(); // SAFETY: copy only `new_len` bytes with an // upper bound of `INLINE_CAPACITY` checked above @@ -955,8 +957,13 @@ where Self::from_inline(inline) } else { - let vec = self.as_slice().repeat(n); - Self::from_vec(vec) + let mut thin = ThinVec::<_, Reserved>::with_capacity(new_len); + + for _ in 0..n { + thin.extend_from_slice_copy(self.as_slice()); + } + + Self::from_thin_vec(thin) } } diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index cef9b1f9..42af44e4 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -14,6 +14,7 @@ use borrowed::Borrowed; use crate::backend::Backend; use crate::common::{manually_drop_as_mut, manually_drop_as_ref}; +use crate::vecs::thin::ThinVec; use crate::vecs::InlineVec; pub mod allocated; @@ -323,6 +324,12 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { } } + /// Creates a new `HipByt` from a thin vector. + pub(crate) fn from_thin_vec

(vec: ThinVec) -> Self { + let allocated = Allocated::from_thin_vec(vec); + Self::from_allocated(allocated) + } + /// Creates a new `HipByt` from a vector. pub(super) fn from_vec(vec: Vec) -> Self { let allocated = Allocated::from_vec(vec); diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index 234f2596..c19a40e2 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -14,7 +14,6 @@ use crate::vecs::thin::{Header, ThinVec}; use crate::vecs::SmartThinVec; const TAG_MASK: usize = super::MASK as usize; -const TAG: usize = super::TAG_OWNED as usize; const THIN_BIT: usize = 0b1; const TAG_THIN: usize = super::TAG_OWNED as usize | THIN_BIT; const TAG_FAT: usize = super::TAG_OWNED as usize; @@ -165,15 +164,6 @@ impl TaggedSmart { (self.0 & !TAG_MASK) != 0 && (self.0 & TAG_OWNED as usize) != 0 } - /// Explicitly clones this tagged smart pointer. - fn try_clone(self) -> Option { - let variant = ManuallyDrop::new(self.get()); - match &*variant { - Variant::Fat(fat) => fat.try_clone().map(Self::from_fat), - Variant::Thin(thin) => thin.try_clone().map(Self::from_thin), - } - } - unsafe fn as_fat_unchecked(self) -> Smart, B> { debug_assert!(self.is_fat()); debug_assert!(self.check_tag()); From 46a2f89915497409573d517b8904cff0a6d2af34 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 16:44:46 +0200 Subject: [PATCH 06/31] add mutate_copy for smart_thin --- src/vecs/smart_thin.rs | 44 ++++++++++++++++++++++++++++++++++++ src/vecs/smart_thin/tests.rs | 26 +++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 858f07ca..cc15f761 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -253,6 +253,42 @@ impl SmartThinVec { unsafe { self.as_mut_unchecked() } } + /// Returns a mutable reference to the vector, possibly copying the data if + /// shared. + /// + /// If the vector is not unique, the data will be copied. + /// + /// This function may be more efficient for `Copy` types than + /// [`mutate`](Self::mutate). + /// + /// # Examples + /// + /// ``` + /// # use hipstr::smart_thin_vec; + /// let mut v = smart_thin_vec![1, 2, 3]; + /// assert_eq!(v.as_slice(), &[1, 2, 3]); + /// + /// let mut v2 = v.clone(); + /// assert!(!v.is_unique()); + /// { + /// let v_mut = v.mutate_copy(); + /// assert_eq!(v_mut.as_slice(), &[1, 2, 3]); + /// v_mut.push(4); + /// } + /// assert!(v.is_unique()); + /// assert_eq!(v.as_slice(), &[1, 2, 3, 4]); + /// ``` + #[doc(alias = "make_mut_copy")] + pub fn mutate_copy(&mut self) -> &mut ThinVec + where + T: Copy, + { + if !self.count().is_unique() { + self.detach_copy(); + } + unsafe { self.as_mut_unchecked() } + } + fn detach(&mut self) where T: Clone, @@ -261,6 +297,14 @@ impl SmartThinVec { *self = unsafe { Self::from_thin_vec_unchecked(thin_vec) }; } + fn detach_copy(&mut self) + where + T: Copy, + { + let thin_vec: ThinVec<_, _> = self.as_thin_vec().fresh_copy(); + *self = unsafe { Self::from_thin_vec_unchecked(thin_vec) }; + } + /// Creates a new `SmartThinVec` from a `ThinVec` without additional checks. /// /// # Safety diff --git a/src/vecs/smart_thin/tests.rs b/src/vecs/smart_thin/tests.rs index 651dba3f..b4e599a0 100644 --- a/src/vecs/smart_thin/tests.rs +++ b/src/vecs/smart_thin/tests.rs @@ -158,6 +158,32 @@ fn mutate() { assert_ne!(v.as_ptr(), p); } +#[test] +fn mutate_copy() { + let mut v = SmartThinVec::::with_capacity(3); + let p = v.as_ptr(); + assert!(v.is_unique()); + { + let m = v.mutate_copy(); + m.push(1); + m.push(2); + m.push(3); + } + assert_eq!(v.as_slice(), [1, 2, 3]); + assert_eq!(v.as_ptr(), p); + + let _v2 = v.clone(); + assert!(!v.is_unique()); + { + let m = v.mutate_copy(); + m.push(4); + m.push(5); + } + assert!(v.is_unique()); + assert_eq!(v.as_slice(), [1, 2, 3, 4, 5]); + assert_ne!(v.as_ptr(), p); +} + #[test] fn try_clone() { let v = smart_thin_vec![1, 2, 3]; From a447d147faf53698d41245b8550583731f7e6c8b Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 17:03:24 +0200 Subject: [PATCH 07/31] add smartvec --- src/smart.rs | 38 +++++ src/vecs.rs | 12 +- src/vecs/smart.rs | 420 ++++++++++++++++++++++++++++++++++++++++++++++ src/vecs/thin.rs | 2 +- 4 files changed, 469 insertions(+), 3 deletions(-) create mode 100644 src/vecs/smart.rs diff --git a/src/smart.rs b/src/smart.rs index b9425f5b..886405e8 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -287,6 +287,44 @@ where Self(nonnull) } } + + #[inline] + pub fn mutate(this: &mut Self) -> &mut T + where + T: Clone, + { + if !this.is_unique() { + this.detach(); + } + // SAFETY: uniqueness enforced + unsafe { this.as_mut_unchecked() } + } + + #[inline] + pub fn mutate_copy(this: &mut Self) -> &mut T + where + T: Copy, + { + if !this.is_unique() { + this.detach_copy(); + } + // SAFETY: uniqueness enforced + unsafe { this.as_mut_unchecked() } + } + + pub(crate) fn detach(&mut self) + where + T: Clone, + { + *self = Self::new(Smart::get(self).clone()); + } + + pub(crate) fn detach_copy(&mut self) + where + T: Copy, + { + *self = Self::new(*Smart::get(self)); + } } impl Clone for Smart> { diff --git a/src/vecs.rs b/src/vecs.rs index 1e878f4f..503979ce 100644 --- a/src/vecs.rs +++ b/src/vecs.rs @@ -1,13 +1,21 @@ //! Vector types. +use alloc::vec::Vec; + +use crate::smart::Smart; + pub mod inline; +pub mod smart; pub mod smart_thin; pub mod thin; #[doc(inline)] pub use inline::InlineVec; +#[doc(inline)] +pub use smart::SmartVec; +#[doc(inline)] +pub use smart_thin::SmartThinVec; pub type ThinVec = thin::ThinVec; -#[doc(inline)] -pub use smart_thin::SmartThinVec; +pub type SmartFatVec = Smart, C>; diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs new file mode 100644 index 00000000..415851be --- /dev/null +++ b/src/vecs/smart.rs @@ -0,0 +1,420 @@ +//! Smart vector type (fat or thin). + +use alloc::vec::Vec; +use core::marker::PhantomData; +use core::mem::{offset_of, transmute, ManuallyDrop, MaybeUninit}; +use core::ptr::NonNull; +use core::{ptr, slice}; + +use super::smart_thin::SmartThinVec; +use super::thin::ThinVec; +use crate::backend::Backend; +use crate::smart::{Inner, Smart}; +use crate::vecs::thin::Header; + +pub struct SmartVecInner { + ptr: NonNull<()>, + phantom: PhantomData<(*mut T, C)>, +} + +const TAG_MASK: usize = 0b11 as usize; +const THIN_BIT: usize = 0b01; +const TAG_THIN: usize = 0b11; +const TAG_FAT: usize = 0b10; +const TAG_OWNED_MASK: usize = 0b10; + +enum Variant { + Fat(F), + Thin(T), +} + +/// Tagged smart pointer (with forced exposed provenance). +/// +/// The exposed provenance is required to cast [`Allocated`] from and to the +/// [`Pivot`](super::Pivot) representation. +struct TaggedSmart(usize, PhantomData<(Vec, B)>); + +// Manual implementation of Clone and Copy traits to avoid requiring additional +// trait bounds on the generic parameter B + +impl Clone for TaggedSmart { + #[cfg_attr(coverage_nightly, coverage(off))] + fn clone(&self) -> Self { + *self + } +} + +impl Copy for TaggedSmart {} + +impl TaggedSmart { + /// Gets the owner. + fn get(self) -> Variant, B>, SmartThinVec> { + let ptr = self.ptr(); + + if self.is_fat() { + Variant::Fat(unsafe { Smart::from_raw(ptr.cast()) }) + } else { + Variant::Thin(unsafe { SmartThinVec::from_raw(ptr.cast()) }) + } + } + + #[inline] + const fn is_thin(self) -> bool { + debug_assert!(self.check_tag()); + self.0 & THIN_BIT != 0 + } + + #[inline] + const fn is_fat(self) -> bool { + debug_assert!(self.check_tag()); + self.0 & THIN_BIT == 0 + } + + const fn ptr(self) -> NonNull<()> { + debug_assert!(self.check_tag()); + + // expose provenance semantics + let ptr = (self.0 & !TAG_MASK) as *mut (); + debug_assert!(!ptr.is_null()); + // debug only + + // SAFETY: type invariant + let ptr: NonNull<()> = unsafe { NonNull::new_unchecked(ptr) }; + + #[cfg(miri)] + let _ = &*ptr; // check provenance early + ptr + } + + /// Constructed a tagged smart pointer from a [`Smart`]. + #[inline] + fn from_fat(raw: Smart, B>) -> Self { + let ptr = Smart::into_raw(raw).as_ptr(); + debug_assert!(ptr.is_aligned()); + debug_assert!((ptr as usize) & TAG_MASK == 0); + + // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment + // requirement as usize (typically 4 bytes on 32-bit architectures, and + // more on 64-bit architectures) + let addr = ptr.map_addr(|addr| addr | TAG_FAT).expose_provenance(); + + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_fat()); + this + } + + #[inline] + fn from_thin(raw: SmartThinVec) -> Self { + let ptr = SmartThinVec::into_raw(raw).as_ptr(); + debug_assert!(ptr.is_aligned()); + debug_assert!((ptr as usize) & TAG_MASK == 0); + + // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment + // requirement as usize (typically 4 bytes on 32-bit architectures, and + // more on 64-bit architectures) + let addr = ptr.map_addr(|addr| addr | TAG_THIN).expose_provenance(); + + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_thin()); + this + } + + #[inline] + fn is_unique(self) -> bool { + const { + assert!(offset_of!(Inner, count) == 0); + assert!(offset_of!(Header, prefix) == 0); + } + self.count().is_unique() + } + + #[inline] + fn count(&self) -> &B { + let counter = unsafe { self.ptr().cast().as_ref() }; + + #[cfg(debug_assertions)] + { + let m = ManuallyDrop::new(self.get()); + unsafe { + match &*m { + Variant::Fat(fat) => { + debug_assert_eq!(&raw const fat.0.as_ref().count, ptr::from_ref(counter)); + } + Variant::Thin(thin) => { + debug_assert_eq!(&raw const thin.0.as_ref().prefix, ptr::from_ref(counter)); + } + } + } + } + + counter + } + + #[inline] + fn as_slice(&self) -> &[T] { + let v = ManuallyDrop::new(self.get()); + let short_lived = match &*v { + Variant::Fat(fat) => fat.as_slice(), + Variant::Thin(thin) => thin.as_slice(), + }; + // SAFETY: the owner is valid. + unsafe { transmute(short_lived) } + } + + /// Checks if the tag is valid. + #[inline] + const fn check_tag(self) -> bool { + (self.0 & !TAG_MASK) != 0 && (self.0 & TAG_OWNED_MASK as usize) != 0 + } + + unsafe fn as_fat_unchecked(self) -> Smart, B> { + debug_assert!(self.is_fat()); + debug_assert!(self.check_tag()); + + // SAFETY: type invariant + unsafe { Smart::from_raw(self.ptr().cast()) } + } +} + +pub struct SmartVec(TaggedSmart); + +impl SmartVec { + /// Creates a new smart vector. + pub fn new() -> Self { + let thin = SmartThinVec::new(); + let tagged_ptr = TaggedSmart::from_thin(thin); + Self(tagged_ptr) + } + + /// Creates a new smart vector with the given capacity. + pub fn with_capacity(capacity: usize) -> Self { + let thin = SmartThinVec::with_capacity(capacity); + let tagged_ptr = TaggedSmart::from_thin(thin); + Self(tagged_ptr) + } + + fn from_fat(raw: Smart, B>) -> Self { + let tagged_ptr = TaggedSmart::from_fat(raw); + Self(tagged_ptr) + } + + fn from_thin(raw: SmartThinVec) -> Self { + let tagged_ptr = TaggedSmart::from_thin(raw); + Self(tagged_ptr) + } + + pub fn as_slice(&self) -> &[T] { + self.0.as_slice() + } + + pub fn as_ptr(&self) -> *const T { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.as_ptr(), + Variant::Thin(thin) => thin.as_ptr(), + } + } + + pub fn len(&self) -> usize { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.len(), + Variant::Thin(thin) => thin.len(), + } + } + + pub fn is_empty(&self) -> bool { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.is_empty(), + Variant::Thin(thin) => thin.is_empty(), + } + } + + pub fn mutate(&mut self) -> RefMut + where + T: Clone, + { + let mut v = ManuallyDrop::new(self.0.get()); + let r = match &mut *v { + Variant::Fat(fat) => { + if let Some(mut_vec) = fat.as_mut() { + Variant::Fat(unsafe { transmute::<&mut Vec, &mut Vec>(mut_vec) }) + } else { + let mut thin = SmartThinVec::::from_slice_clone(fat.as_slice()); + let mut_ref = unsafe { ptr::from_mut(thin.as_mut_unchecked()) }; + self.0 = TaggedSmart::from_thin(thin); + Variant::Thin(unsafe { &mut *mut_ref }) + } + } + Variant::Thin(thin) => Variant::Thin(unsafe { transmute(thin.mutate()) }), + }; + RefMut(r) + } +} + +pub struct RefMut<'a, T, P>(Variant<&'a mut Vec, &'a mut ThinVec>); + +impl<'a, T, P> RefMut<'a, T, P> { + pub fn as_slice(&self) -> &[T] { + match &self.0 { + Variant::Fat(fat) => fat.as_slice(), + Variant::Thin(thin) => thin.as_slice(), + } + } + + pub fn as_mut_slice(&mut self) -> &mut [T] { + match &mut self.0 { + Variant::Fat(fat) => fat.as_mut_slice(), + Variant::Thin(thin) => thin.as_mut_slice(), + } + } + + pub fn push(&mut self, value: T) { + match &mut self.0 { + Variant::Fat(fat) => fat.push(value), + Variant::Thin(thin) => thin.push(value), + } + } + + pub fn pop(&mut self) -> Option { + match &mut self.0 { + Variant::Fat(fat) => fat.pop(), + Variant::Thin(thin) => thin.pop(), + } + } + + pub fn insert(&mut self, index: usize, value: T) { + match &mut self.0 { + Variant::Fat(fat) => fat.insert(index, value), + Variant::Thin(thin) => thin.insert(index, value), + } + } + + pub fn remove(&mut self, index: usize) -> T { + match &mut self.0 { + Variant::Fat(fat) => fat.remove(index), + Variant::Thin(thin) => thin.remove(index), + } + } + + pub fn clear(&mut self) { + match &mut self.0 { + Variant::Fat(fat) => fat.clear(), + Variant::Thin(thin) => thin.clear(), + } + } + + pub fn reserve(&mut self, additional: usize) { + match &mut self.0 { + Variant::Fat(fat) => fat.reserve(additional), + Variant::Thin(thin) => thin.reserve(additional), + } + } + + pub fn reserve_exact(&mut self, additional: usize) { + match &mut self.0 { + Variant::Fat(fat) => fat.reserve_exact(additional), + Variant::Thin(thin) => thin.reserve_exact(additional), + } + } + + pub fn shrink_to_fit(&mut self) { + match &mut self.0 { + Variant::Fat(fat) => fat.shrink_to_fit(), + Variant::Thin(thin) => thin.shrink_to_fit(), + } + } + + pub fn shrink_to(&mut self, min_capacity: usize) { + match &mut self.0 { + Variant::Fat(fat) => fat.shrink_to(min_capacity), + Variant::Thin(thin) => thin.shrink_to(min_capacity), + } + } + + pub fn truncate(&mut self, len: usize) { + match &mut self.0 { + Variant::Fat(fat) => fat.truncate(len), + Variant::Thin(thin) => thin.truncate(len), + } + } + + pub fn split_off(&mut self, at: usize) -> SmartVec + where + P: Backend, + { + match &mut self.0 { + Variant::Fat(fat) => { + let new = fat.split_off(at); + SmartVec::from_fat(Smart::new(new)) + } + Variant::Thin(thin) => { + let new = thin.split_off(at); + SmartVec::from_thin(SmartThinVec::from_thin_vec(new)) + } + } + } + + pub fn as_ptr(&self) -> *const T { + match &self.0 { + Variant::Fat(fat) => fat.as_ptr(), + Variant::Thin(thin) => thin.as_ptr(), + } + } + + pub fn as_mut_ptr(&mut self) -> *mut T { + match &mut self.0 { + Variant::Fat(fat) => fat.as_mut_ptr(), + Variant::Thin(thin) => thin.as_mut_ptr(), + } + } + + pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + match &mut self.0 { + Variant::Fat(fat) => fat.spare_capacity_mut(), + Variant::Thin(thin) => thin.spare_capacity_mut(), + } + } + + pub unsafe fn set_len(&mut self, len: usize) { + unsafe { + match &mut self.0 { + Variant::Fat(fat) => fat.set_len(len), + Variant::Thin(thin) => thin.set_len(len), + } + } + } + + pub fn extend_from_slice(&mut self, other: &[T]) + where + T: Clone, + { + match &mut self.0 { + Variant::Fat(fat) => fat.extend_from_slice(other), + Variant::Thin(thin) => thin.extend_from_slice(other), + } + } + + pub fn extend_from_slice_copy(&mut self, other: &[T]) + where + T: Copy, + { + match &mut self.0 { + Variant::Fat(fat) => fat.extend_from_slice(other), + Variant::Thin(thin) => thin.extend_from_slice_copy(other), + } + } + + fn extend_iter(&mut self, iter: impl IntoIterator) + where + T: Clone, + { + match &mut self.0 { + Variant::Fat(fat) => fat.extend(iter), + Variant::Thin(thin) => thin.extend_iter(iter), + } + } +} diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 2a391c31..b8098ec4 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -1157,7 +1157,7 @@ impl ThinVec { } } - fn extend_iter(&mut self, iterable: impl IntoIterator) { + pub(crate) fn extend_iter(&mut self, iterable: impl IntoIterator) { let mut iter = iterable.into_iter(); let len = self.len(); let min = iter.size_hint().0; From 731d169a6c976b1bbb599c3d48b1d0e52fa43931 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 21:52:43 +0200 Subject: [PATCH 08/31] more smart and fix test --- src/bytes/tests.rs | 8 +-- src/os_string/tests.rs | 5 +- src/smart.rs | 112 ++++++++++++++++++++++++++++++++++++++++- src/string/tests.rs | 2 +- src/vecs/smart.rs | 14 ++++-- 5 files changed, 128 insertions(+), 13 deletions(-) diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 8a4cd7c0..8a457e23 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -95,10 +95,12 @@ fn test_with_capacity() { assert_eq!(h.capacity(), INLINE_CAPACITY); let mut h = H::with_capacity(42); + assert!(h.is_allocated()); + assert!(h.is_thin()); let p = h.as_ptr(); assert_eq!(h, EMPTY_SLICE); assert!(h.is_empty()); - assert_eq!(h.capacity(), 42); + assert!(h.capacity() >= 42); for _ in 0..42 { h.push_slice(A); } @@ -234,7 +236,7 @@ fn inline() { #[test] fn allocated_thin() { - let a = H::from(Vec::from(MEDIUM)); + let a = H::from(MEDIUM); assert!(a.is_thin()); assert!(!a.is_fat()); assert!(!a.is_borrowed()); @@ -1296,7 +1298,7 @@ fn test_spare_capacity_mut() { assert_eq!(h.spare_capacity_mut().len(), INLINE_CAPACITY - ABC.len()); let mut h = H::with_capacity(42); - assert_eq!(h.spare_capacity_mut().len(), 42); + assert!(h.spare_capacity_mut().len() >= 42); } #[test] diff --git a/src/os_string/tests.rs b/src/os_string/tests.rs index e7cd4ff0..bf6a3a00 100644 --- a/src/os_string/tests.rs +++ b/src/os_string/tests.rs @@ -31,17 +31,18 @@ fn test_new_default() { } #[test] -fn test_with_capacity() { +fn with_capacity() { let h = H::with_capacity(0); assert_eq!(h, *EMPTY_SLICE); assert!(h.is_empty()); assert_eq!(h.capacity(), INLINE_CAPACITY); let mut h = H::with_capacity(42); + assert!(h.is_allocated()); let p = h.as_ptr(); assert_eq!(h, *EMPTY_SLICE); assert!(h.is_empty()); - assert_eq!(h.capacity(), 42); + assert!(h.capacity() >= 42); for _ in 0..42 { h.push(A); } diff --git a/src/smart.rs b/src/smart.rs index 886405e8..f28f24e5 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -7,9 +7,12 @@ //! - atomically reference counted. use alloc::boxed::Box; +use core::borrow::Borrow; +use core::hash::{Hash, Hasher}; use core::mem::ManuallyDrop; use core::ops::Deref; use core::ptr::NonNull; +use core::{fmt, ptr}; use crate::backend::{ Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, @@ -232,8 +235,7 @@ where /// # use hipstr::smart::Smart; /// # use hipstr::{Arc, Unique}; /// let p = Smart::<_, Arc>::new(42); - /// let q = p.try_clone(); - /// assert!(q.is_some()); + /// let q = p.try_clone().unwrap();; /// assert_eq!(p.as_ref(), q.as_ref()); /// /// let u = Smart::<_, Unique>::new(42); @@ -401,3 +403,109 @@ where C: Sync + Backend, { } + +impl fmt::Debug for Smart +where + T: fmt::Debug, + C: Backend, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(self, f) + } +} + +impl fmt::Display for Smart +where + T: fmt::Display, + C: Backend, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(self, f) + } +} + +impl PartialEq> for Smart +where + T: PartialEq, + C1: Backend, + C2: Backend, +{ + #[inline] + fn eq(&self, other: &Smart) -> bool { + T::eq(self, other) + } +} + +impl Eq for Smart +where + T: Eq, + C: Backend, +{ +} + +impl PartialOrd> for Smart +where + T: PartialOrd, + C1: Backend, + C2: Backend, +{ + #[inline] + fn partial_cmp(&self, other: &Smart) -> Option { + T::partial_cmp(self, other) + } +} + +impl Ord for Smart +where + T: Ord, + C: Backend, +{ + #[inline] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + T::cmp(self, other) + } +} + +impl fmt::Pointer for Smart +where + C: Backend, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ptr::from_ref(Self::get(self)).fmt(f) + } +} + +impl Default for Smart +where + T: Default, + C: Backend, +{ + #[inline] + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Borrow for Smart +where + C: Backend, +{ + #[inline] + fn borrow(&self) -> &T { + Self::get(self) + } +} + +impl Hash for Smart +where + T: Hash, + C: Backend, +{ + #[inline] + fn hash(&self, state: &mut H) { + T::hash(self, state) + } +} diff --git a/src/string/tests.rs b/src/string/tests.rs index 1d9e9ea8..8c265bd5 100644 --- a/src/string/tests.rs +++ b/src/string/tests.rs @@ -59,7 +59,7 @@ fn test_with_capacity() { let p = h.as_ptr(); assert_eq!(h, EMPTY_SLICE); assert!(h.is_empty()); - assert_eq!(h.capacity(), 42); + assert!(h.capacity() >= 42); for _ in 0..42 { h.push_str(A); } diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 415851be..cd9b9fe6 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -12,11 +12,6 @@ use crate::backend::Backend; use crate::smart::{Inner, Smart}; use crate::vecs::thin::Header; -pub struct SmartVecInner { - ptr: NonNull<()>, - phantom: PhantomData<(*mut T, C)>, -} - const TAG_MASK: usize = 0b11 as usize; const THIN_BIT: usize = 0b01; const TAG_THIN: usize = 0b11; @@ -32,6 +27,7 @@ enum Variant { /// /// The exposed provenance is required to cast [`Allocated`] from and to the /// [`Pivot`](super::Pivot) representation. +#[repr(transparent)] struct TaggedSmart(usize, PhantomData<(Vec, B)>); // Manual implementation of Clone and Copy traits to avoid requiring additional @@ -178,6 +174,14 @@ impl TaggedSmart { } } +/// A smart vector. +/// +/// This type has two internal representations: +/// +/// - *fat* representation: [`Smart, B>`]. +/// A double indirection is needed to access the vector's data. +/// - *thin* representation: [`SmartThinVec`]. +#[repr(transparent)] pub struct SmartVec(TaggedSmart); impl SmartVec { From 9d1f4fd0dab2b4f8f59bf8b954d301c86b695a38 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 22:22:44 +0200 Subject: [PATCH 09/31] more tests --- src/macros.rs | 4 +- src/smart.rs | 20 +++++----- src/smart/tests.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++ src/vecs/smart.rs | 12 ++++-- 4 files changed, 114 insertions(+), 16 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 26b00d4b..ce84f845 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -106,7 +106,7 @@ macro_rules! trait_impls { (@MutVector $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { }) => {}; (@MutVector $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { $t:ty ; $($rest:tt)* }) => { - unsafe impl $(< $($gen)* >)? crate::common::traits::MutVector for $t $(where $($wh)*)? { + unsafe impl $(< $($gen)* >)? crate::common::traits::MutVector for $t $($(where $($wh)*)?)? { #[inline] unsafe fn set_len(&mut self, len: usize) { unsafe { self.set_len(len) } @@ -121,7 +121,7 @@ macro_rules! trait_impls { (@Extend $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { }) => {}; (@Extend $([ $($gen:tt)* ] $( where [ $($wh:tt)* ])? )? { $el:ty => $t:ty ; $($rest:tt)* }) => { - impl $(< $($gen)* >)? Extend<$el> for $t $(where $($wh)*)? { + impl $(< $($gen)* >)? Extend<$el> for $t $($(where $($wh)*)?)? { fn extend>(&mut self, iter: T001) { self.extend_iter(iter) } diff --git a/src/smart.rs b/src/smart.rs index f28f24e5..a1257fd7 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -426,6 +426,16 @@ where } } +impl fmt::Pointer for Smart +where + C: Backend, +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ptr::from_ref(Self::get(self)).fmt(f) + } +} + impl PartialEq> for Smart where T: PartialEq, @@ -468,16 +478,6 @@ where } } -impl fmt::Pointer for Smart -where - C: Backend, -{ - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ptr::from_ref(Self::get(self)).fmt(f) - } -} - impl Default for Smart where T: Default, diff --git a/src/smart/tests.rs b/src/smart/tests.rs index 0a1b2e7a..66be868b 100644 --- a/src/smart/tests.rs +++ b/src/smart/tests.rs @@ -1,5 +1,6 @@ use alloc::vec; use alloc::vec::Vec; +use core::cmp::Ordering; #[cfg(all(not(loom), feature = "std"))] use std::sync; #[cfg(all(not(loom), feature = "std"))] @@ -392,3 +393,96 @@ fn force_clone() { assert_eq!(w.as_slice(), [1, 2, 3]); assert_eq!(v.as_ptr(), w.as_ptr()); } + +#[test] +fn mutate() { + let mut v = Smart::<_, Arc>::new(vec![1, 2, 3]); + assert_eq!(v.as_slice(), [1, 2, 3]); + + Smart::mutate(&mut v).push(4); + assert_eq!(v.as_slice(), [1, 2, 3, 4]); + + let w = v.clone(); + + Smart::mutate(&mut v).pop(); + assert_eq!(v.as_slice(), [1, 2, 3]); + assert_eq!(w.as_slice(), [1, 2, 3, 4]); +} + +#[test] +#[cfg(feature = "std")] +fn hash() { + use std::hash::{BuildHasher, BuildHasherDefault, DefaultHasher}; + let hasher = BuildHasherDefault::::new(); + + let mut v = Smart::<_, Arc>::new(vec![1, 2, 3]); + + assert_eq!(v.as_slice(), [1, 2, 3]); + let slice_hash = hasher.hash_one(v.as_slice()); + + let hash = hasher.hash_one(&v); + assert_eq!(hash, slice_hash); + + let w = v.clone(); + let clone_hash = hasher.hash_one(&w); + assert_eq!(hash, clone_hash); + + Smart::mutate(&mut v).push(4); + let new_hash = hasher.hash_one(&v); + assert_ne!(hash, new_hash); +} + +#[test] +fn borrow() { + let v = Smart::<_, Arc>::new(1); + assert_eq!(v.as_ref(), &1); + assert_eq!(<_ as Borrow>::borrow(&v), &1_i32); + assert!(ptr::eq(v.as_ref(), v.borrow())); +} + +#[test] +fn default() { + let v = Smart::::default(); + assert_eq!(*v, 0); + + let v = Smart::, Arc>::default(); + let empty: &[i32] = &[]; + assert_eq!(v.as_slice(), empty); +} + +#[test] +#[cfg(feature = "std")] +fn fmt() { + use std::format; + let v = Smart::<_, Arc>::new(1); + assert_eq!(format!("{:?}", v), "1"); + assert_eq!(format!("{}", v), "1"); + assert_eq!(format!("{:p}", v), format!("{:p}", ptr::from_ref(&*v))); +} + +#[test] +fn eq() { + let v = Smart::<_, Arc>::new(1); + let w = Smart::<_, Arc>::new(1); + let x = Smart::<_, Arc>::new(2); + assert_eq!(v, w); + assert_ne!(v, x); + assert_eq!(v.as_ref(), w.as_ref()); + assert_ne!(v.as_ref(), x.as_ref()); + + let v = Smart::<_, Arc>::new(vec![1, 2, 3]); + let w = Smart::<_, Arc>::new(vec![1, 2, 3]); + assert_eq!(v, w); + assert_eq!(v.as_slice(), w.as_slice()); +} + +#[test] +fn cmp() { + let v = Smart::<_, Arc>::new(1); + let w = Smart::<_, Arc>::new(1); + let x = Smart::<_, Arc>::new(2); + assert_eq!(v.cmp(&w), Ordering::Equal); + assert_eq!(v.cmp(&x), Ordering::Less); + assert_eq!(v.as_ref().cmp(w.as_ref()), Ordering::Equal); + assert_eq!(v.as_ref().cmp(x.as_ref()), Ordering::Less); +} diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index cd9b9fe6..3ed00614 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -9,6 +9,7 @@ use core::{ptr, slice}; use super::smart_thin::SmartThinVec; use super::thin::ThinVec; use crate::backend::Backend; +use crate::macros::trait_impls; use crate::smart::{Inner, Smart}; use crate::vecs::thin::Header; @@ -412,13 +413,16 @@ impl<'a, T, P> RefMut<'a, T, P> { } } - fn extend_iter(&mut self, iter: impl IntoIterator) - where - T: Clone, - { + fn extend_iter(&mut self, iter: impl IntoIterator) { match &mut self.0 { Variant::Fat(fat) => fat.extend(iter), Variant::Thin(thin) => thin.extend_iter(iter), } } } + +trait_impls! { + [T, B] where [B: Backend] { + Extend { T => RefMut<'_, T, B>; } + } +} From 0dbb11fdfce24396c299c3911c0b24f8986698c3 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 22:24:41 +0200 Subject: [PATCH 10/31] more test --- src/smart/tests.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/smart/tests.rs b/src/smart/tests.rs index 66be868b..8e19a02a 100644 --- a/src/smart/tests.rs +++ b/src/smart/tests.rs @@ -409,6 +409,21 @@ fn mutate() { assert_eq!(w.as_slice(), [1, 2, 3, 4]); } +#[test] +fn mutate_copy() { + let mut v = Smart::<_, Arc>::new(1); + assert_eq!(*v, 1); + + *Smart::mutate_copy(&mut v) += 1; + assert_eq!(*v, 2); + + let w = v.clone(); + + *Smart::mutate_copy(&mut v) += 1; + assert_eq!(*v, 3); + assert_eq!(*w, 2); +} + #[test] #[cfg(feature = "std")] fn hash() { From a7701651a76a530cc03fb86c46b2ba1c6ee41730 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 6 May 2025 22:26:30 +0200 Subject: [PATCH 11/31] more test --- src/smart/tests.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/smart/tests.rs b/src/smart/tests.rs index 8e19a02a..de066b85 100644 --- a/src/smart/tests.rs +++ b/src/smart/tests.rs @@ -498,6 +498,16 @@ fn cmp() { let x = Smart::<_, Arc>::new(2); assert_eq!(v.cmp(&w), Ordering::Equal); assert_eq!(v.cmp(&x), Ordering::Less); - assert_eq!(v.as_ref().cmp(w.as_ref()), Ordering::Equal); - assert_eq!(v.as_ref().cmp(x.as_ref()), Ordering::Less); +} + +#[test] +fn partial_cmp() { + let v = Smart::<_, Arc>::new(1.); + let w = Smart::<_, Arc>::new(1.); + let x = Smart::<_, Arc>::new(2.); + let nan = Smart::<_, Arc>::new(f64::NAN); + assert_eq!(v.partial_cmp(&w), Some(Ordering::Equal)); + assert_eq!(v.partial_cmp(&x), Some(Ordering::Less)); + assert_eq!(v.partial_cmp(&nan), None); + assert_eq!(nan.partial_cmp(&v), None); } From 8fdaf7af178514c4cbd7380b8195206cc27a5415 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 10:29:42 +0200 Subject: [PATCH 12/31] more tests and fix macro --- src/vecs/smart.rs | 187 ++++++++++++++++++++++++++--------- src/vecs/smart/tests.rs | 81 +++++++++++++++ src/vecs/smart_thin.rs | 24 +++-- src/vecs/smart_thin/tests.rs | 52 ++++++++++ 4 files changed, 288 insertions(+), 56 deletions(-) create mode 100644 src/vecs/smart/tests.rs diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 3ed00614..1486bd7e 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -3,16 +3,22 @@ use alloc::vec::Vec; use core::marker::PhantomData; use core::mem::{offset_of, transmute, ManuallyDrop, MaybeUninit}; +use core::num::NonZeroUsize; use core::ptr::NonNull; -use core::{ptr, slice}; +use core::{ops, ptr, slice}; use super::smart_thin::SmartThinVec; use super::thin::ThinVec; -use crate::backend::Backend; +use crate::backend::{ + Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, +}; use crate::macros::trait_impls; use crate::smart::{Inner, Smart}; use crate::vecs::thin::Header; +#[cfg(test)] +mod tests; + const TAG_MASK: usize = 0b11 as usize; const THIN_BIT: usize = 0b01; const TAG_THIN: usize = 0b11; @@ -24,12 +30,9 @@ enum Variant { Thin(T), } -/// Tagged smart pointer (with forced exposed provenance). -/// -/// The exposed provenance is required to cast [`Allocated`] from and to the -/// [`Pivot`](super::Pivot) representation. +/// Tagged smart pointer. #[repr(transparent)] -struct TaggedSmart(usize, PhantomData<(Vec, B)>); +struct TaggedSmart(NonNull<()>, PhantomData<(Vec, B)>); // Manual implementation of Clone and Copy traits to avoid requiring additional // trait bounds on the generic parameter B @@ -55,47 +58,44 @@ impl TaggedSmart { } } + fn addr(self) -> usize { + self.0.as_ptr() as usize + } + #[inline] - const fn is_thin(self) -> bool { + fn is_thin(self) -> bool { debug_assert!(self.check_tag()); - self.0 & THIN_BIT != 0 + self.addr() & THIN_BIT != 0 } #[inline] - const fn is_fat(self) -> bool { + fn is_fat(self) -> bool { debug_assert!(self.check_tag()); - self.0 & THIN_BIT == 0 + self.addr() & THIN_BIT == 0 } - const fn ptr(self) -> NonNull<()> { + fn ptr(self) -> NonNull<()> { debug_assert!(self.check_tag()); - // expose provenance semantics - let ptr = (self.0 & !TAG_MASK) as *mut (); - debug_assert!(!ptr.is_null()); - // debug only - - // SAFETY: type invariant - let ptr: NonNull<()> = unsafe { NonNull::new_unchecked(ptr) }; - - #[cfg(miri)] - let _ = &*ptr; // check provenance early - ptr + self.0.map_addr(|addr| { + // SAFETY: the pointer is non-null and aligned + unsafe { NonZeroUsize::new_unchecked(addr.get() & !TAG_MASK) } + }) } /// Constructed a tagged smart pointer from a [`Smart`]. #[inline] fn from_fat(raw: Smart, B>) -> Self { - let ptr = Smart::into_raw(raw).as_ptr(); + let ptr = Smart::into_raw(raw); debug_assert!(ptr.is_aligned()); - debug_assert!((ptr as usize) & TAG_MASK == 0); + debug_assert!(ptr.addr().get() & TAG_MASK == 0); // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment // requirement as usize (typically 4 bytes on 32-bit architectures, and // more on 64-bit architectures) - let addr = ptr.map_addr(|addr| addr | TAG_FAT).expose_provenance(); + let tagged = ptr.map_addr(|addr| addr | TAG_FAT).cast(); - let this = Self(addr, PhantomData); + let this = Self(tagged, PhantomData); debug_assert!(this.check_tag()); debug_assert!(this.is_fat()); this @@ -103,33 +103,29 @@ impl TaggedSmart { #[inline] fn from_thin(raw: SmartThinVec) -> Self { - let ptr = SmartThinVec::into_raw(raw).as_ptr(); + let ptr = SmartThinVec::into_raw(raw); debug_assert!(ptr.is_aligned()); - debug_assert!((ptr as usize) & TAG_MASK == 0); + debug_assert!(ptr.addr().get() & TAG_MASK == 0); // SAFETY: add a 2-bit tag to a non-null pointer with the same alignment // requirement as usize (typically 4 bytes on 32-bit architectures, and // more on 64-bit architectures) - let addr = ptr.map_addr(|addr| addr | TAG_THIN).expose_provenance(); + let tagged = ptr.map_addr(|addr| addr | TAG_THIN).cast(); - let this = Self(addr, PhantomData); + let this = Self(tagged, PhantomData); debug_assert!(this.check_tag()); debug_assert!(this.is_thin()); this } + /// Returns a reference to the counter. #[inline] - fn is_unique(self) -> bool { + fn count(&self) -> &B { const { - assert!(offset_of!(Inner, count) == 0); assert!(offset_of!(Header, prefix) == 0); + assert!(offset_of!(Inner, B>, count) == 0); } - self.count().is_unique() - } - - #[inline] - fn count(&self) -> &B { - let counter = unsafe { self.ptr().cast().as_ref() }; + let counter: &B = unsafe { self.ptr().cast().as_ref() }; #[cfg(debug_assertions)] { @@ -162,16 +158,9 @@ impl TaggedSmart { /// Checks if the tag is valid. #[inline] - const fn check_tag(self) -> bool { - (self.0 & !TAG_MASK) != 0 && (self.0 & TAG_OWNED_MASK as usize) != 0 - } - - unsafe fn as_fat_unchecked(self) -> Smart, B> { - debug_assert!(self.is_fat()); - debug_assert!(self.check_tag()); - - // SAFETY: type invariant - unsafe { Smart::from_raw(self.ptr().cast()) } + fn check_tag(self) -> bool { + let addr = self.0.addr().get(); + (addr & !TAG_MASK) != 0 && (addr & TAG_OWNED_MASK as usize) != 0 } } @@ -200,6 +189,16 @@ impl SmartVec { Self(tagged_ptr) } + #[inline] + pub fn is_thin(&self) -> bool { + self.0.is_thin() + } + + #[inline] + pub fn is_fat(&self) -> bool { + self.0.is_fat() + } + fn from_fat(raw: Smart, B>) -> Self { let tagged_ptr = TaggedSmart::from_fat(raw); Self(tagged_ptr) @@ -222,6 +221,19 @@ impl SmartVec { } } + /// Returns the length of the vector. + /// + /// # Examples + /// + /// ```rust + /// use hipstr::vecs::SmartVec; + /// use hipstr::Arc; + /// + /// let mut v = SmartVec::::new(); + /// assert_eq!(v.len(), 0); + /// v.mutate().push(1); + /// assert_eq!(v.len(), 1); + /// ``` pub fn len(&self) -> usize { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -230,6 +242,14 @@ impl SmartVec { } } + pub fn capacity(&self) -> usize { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.capacity(), + Variant::Thin(thin) => thin.capacity(), + } + } + pub fn is_empty(&self) -> bool { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -238,6 +258,23 @@ impl SmartVec { } } + /// Returns true if this vector reference is unique, that is, not shared. + /// + /// # Examples + /// + /// ```rust + /// use hipstr::vecs::SmartVec; + /// use hipstr::Arc; + /// + /// let v = SmartVec::::new(); + /// assert!(v.is_unique()); + /// let w = v.clone(); + /// assert!(!v.is_unique()); + /// ``` + pub fn is_unique(&self) -> bool { + self.count().is_unique() + } + pub fn mutate(&mut self) -> RefMut where T: Clone, @@ -258,6 +295,58 @@ impl SmartVec { }; RefMut(r) } + + pub fn try_clone(&self) -> Option { + if self.count().incr() == UpdateResult::Overflow { + Some(Self(self.0)) + } else { + None + } + } + + fn count(&self) -> &B { + self.0.count() + } + + pub fn force_clone(&self) -> Self + where + T: Clone, + { + self.try_clone().unwrap_or_else(|| { + let v = ManuallyDrop::new(self.0.get()); + let t = match &*v { + Variant::Fat(fat) => SmartThinVec::from_slice_clone(fat.as_slice()), + Variant::Thin(thin) => thin.force_clone(), + }; + Self::from_thin(t) + }) + } + + pub fn force_clone_or_copy(&self) -> Self + where + T: Copy, + { + self.try_clone().unwrap_or_else(|| { + let v = ManuallyDrop::new(self.0.get()); + let t = match &*v { + Variant::Fat(fat) => SmartThinVec::from_slice_copy(fat.as_slice()), + Variant::Thin(thin) => thin.force_clone_or_copy(), + }; + Self::from_thin(t) + }) + } +} + +impl Clone for SmartVec> { + fn clone(&self) -> Self { + self.try_clone().unwrap_or_else(|| panic!("count overflow")) + } +} + +impl Clone for SmartVec> { + fn clone(&self) -> Self { + self.force_clone() + } } pub struct RefMut<'a, T, P>(Variant<&'a mut Vec, &'a mut ThinVec>); diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs new file mode 100644 index 00000000..d9fb6f5f --- /dev/null +++ b/src/vecs/smart/tests.rs @@ -0,0 +1,81 @@ +use alloc::vec; + +use super::SmartVec; +use crate::smart::Smart; +use crate::{smart_thin_vec, Arc}; + +#[test] +fn new() { + let v = SmartVec::::new(); + assert!(v.is_empty()); + assert_eq!(v.len(), 0); + assert!(v.is_thin()); + assert!(!v.is_fat()); +} + +#[test] +fn with_capacity() { + let v = SmartVec::::with_capacity(10); + assert!(v.is_empty()); + assert!(v.is_thin()); + assert!(!v.is_fat()); + assert!(v.capacity() >= 10); +} + +#[test] +fn from_fat() { + let fat = Smart::new(vec![1, 2, 3]); + let p = fat.as_ptr(); + let v = SmartVec::::from_fat(fat); + assert_eq!(v.as_slice(), &[1, 2, 3]); + assert_eq!(v.len(), 3); + assert!(v.is_fat()); + assert!(!v.is_thin()); + assert_eq!(v.as_ptr(), p); +} + +#[test] +fn from_thin() { + let thin = smart_thin_vec![Arc : 1, 2, 3]; + let p = thin.as_ptr(); + let v = SmartVec::::from_thin(thin); + assert_eq!(v.as_slice(), &[1, 2, 3]); + assert_eq!(v.len(), 3); + assert!(v.is_thin()); + assert!(!v.is_fat()); + assert_eq!(v.as_ptr(), p); +} + +#[test] +fn len_is_empty() { + let v = SmartVec::::new(); + assert!(v.is_empty()); + assert_eq!(v.len(), 0); + assert!(v.is_thin()); + assert!(!v.is_fat()); + + let v = SmartVec::::from_fat(Smart::new(vec![1, 2, 3])); + assert!(!v.is_empty()); + assert_eq!(v.len(), 3); + assert!(v.is_fat()); + assert!(!v.is_thin()); + + let v = SmartVec::::from_thin(smart_thin_vec![Arc : 1, 2, 3]); + assert!(!v.is_empty()); + assert_eq!(v.len(), 3); + assert!(v.is_thin()); + assert!(!v.is_fat()); +} + +#[test] +fn clone() { + let thin = smart_thin_vec![Arc : 1, 2, 3]; + let p = thin.as_ptr(); + let v = SmartVec::::from_thin(thin); + let v2 = v.clone(); + assert_eq!(v2.as_slice(), &[1, 2, 3]); + assert_eq!(v2.len(), 3); + assert!(v2.is_thin()); + assert!(!v2.is_fat()); + assert_eq!(v2.as_ptr(), p); +} diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index cc15f761..7ab9857f 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -70,19 +70,19 @@ mod tests; #[macro_export] macro_rules! smart_thin_vec { - [ $t:ty : $($rest:tt)* ] => { + [ $($rest:expr),* $(,)? ] => { + smart_thin_vec![$crate::Arc : $($rest),*] + }; + + [ $t:ty : $($rest:expr),* $(,)?] => { { use $crate::thin_vec; $crate::vecs::smart_thin::SmartThinVec::<_, $t>::from( - thin_vec![ $( $rest )* ] + thin_vec![ $( $rest ),* ] ) } }; - [ $($rest:tt)* ] => { - smart_thin_vec![$crate::Arc : $($rest)*] - } - } /// A smart thin vector that can be either unique, reference counted or @@ -361,6 +361,16 @@ impl SmartThinVec { unsafe { Self::from_thin_vec_unchecked(thin_vec) } } + #[inline] + #[must_use] + pub(crate) fn from_slice_copy(slice: &[T]) -> Self + where + T: Copy, + { + let thin_vec = ThinVec::from_slice_copy(slice); + unsafe { Self::from_thin_vec_unchecked(thin_vec) } + } + /// Tries to clone the reference without cloning the data. /// /// If the reference count overflows ([`Unique`] always does), it returns `None`. @@ -436,7 +446,7 @@ impl SmartThinVec { /// assert_ne!(v.as_ptr(), v2.as_ptr()); /// ``` #[must_use] - pub fn clone_or_copy(&self) -> Self + pub fn force_clone_or_copy(&self) -> Self where T: Copy, { diff --git a/src/vecs/smart_thin/tests.rs b/src/vecs/smart_thin/tests.rs index b4e599a0..350068d6 100644 --- a/src/vecs/smart_thin/tests.rs +++ b/src/vecs/smart_thin/tests.rs @@ -55,6 +55,48 @@ fn clone_panic() { let _v2 = v1.clone(); } +#[test] +fn force_clone() { + let v1 = smart_thin_vec![Box::new(1), Box::new(2), Box::new(3)]; + assert_eq!(v1.len(), 3); + assert!(v1.is_unique()); + + let v2 = v1.force_clone(); + assert_eq!(v2.len(), 3); + assert!(!v2.is_unique()); + assert!(!v1.is_unique()); + + let v1 = smart_thin_vec![PanickyUnique : Box::new(1), Box::new(2), Box::new(3)]; + assert_eq!(v1.len(), 3); + assert!(v1.is_unique()); + + let v2 = v1.force_clone(); + assert_eq!(v2.len(), 3); + assert!(v1.is_unique()); + assert!(v2.is_unique()); +} + +#[test] +fn clone_or_copy() { + let v1 = smart_thin_vec![1, 2, 3]; + assert_eq!(v1.len(), 3); + assert!(v1.is_unique()); + + let v2 = v1.force_clone_or_copy(); + assert_eq!(v2.len(), 3); + assert!(!v2.is_unique()); + assert!(!v1.is_unique()); + + let v1 = smart_thin_vec![PanickyUnique : 1, 2, 3]; + assert_eq!(v1.len(), 3); + assert!(v1.is_unique()); + + let v2 = v1.force_clone_or_copy(); + assert_eq!(v2.len(), 3); + assert!(v1.is_unique()); + assert!(v2.is_unique()); +} + #[test] fn with_capacity() { let v = SmartThinVec::::with_capacity(10); @@ -297,3 +339,13 @@ fn cmp() { assert_eq!([1, 2].partial_cmp(&v).unwrap(), Ordering::Less); assert_eq!([1, 2, 3].partial_cmp(&v).unwrap(), Ordering::Equal); } + +#[test] +fn raw() { + let v = smart_thin_vec![1, 2, 3]; + let p = v.as_ptr(); + let r = v.into_raw(); + let v = unsafe { SmartThinVec::from_raw(r) }; + assert_eq!(v.as_slice(), [1, 2, 3]); + assert_eq!(v.as_ptr(), p); +} From 2cde7e496cdf522167646abcd5fc9cf1b6334618 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 10:32:28 +0200 Subject: [PATCH 13/31] fix clone --- src/vecs/smart.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 1486bd7e..4e4a0403 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -297,7 +297,7 @@ impl SmartVec { } pub fn try_clone(&self) -> Option { - if self.count().incr() == UpdateResult::Overflow { + if self.count().incr() == UpdateResult::Done { Some(Self(self.0)) } else { None From 5310e6f1386e41eca674d961ab2c1b3da302bab8 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 10:38:57 +0200 Subject: [PATCH 14/31] more tests --- src/vecs/smart/tests.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index d9fb6f5f..93ac572c 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -79,3 +79,29 @@ fn clone() { assert!(!v2.is_fat()); assert_eq!(v2.as_ptr(), p); } + +#[test] +fn is_unique_thin() { + let thin = smart_thin_vec![Arc : 1, 2, 3]; + let v = SmartVec::::from_thin(thin); + assert!(v.is_unique()); + assert_eq!(v.len(), 3); + assert!(v.is_thin()); + assert!(!v.is_fat()); + + let _w = v.clone(); + assert!(!v.is_unique()); +} + +#[test] +fn is_unique_fat() { + let fat = Smart::new(vec![1, 2, 3]); + let v = SmartVec::::from_fat(fat); + assert!(v.is_unique()); + assert_eq!(v.len(), 3); + assert!(v.is_fat()); + assert!(!v.is_thin()); + + let _w = v.clone(); + assert!(!v.is_unique()); +} From 00885bda5eb46578282025a8cdba49d0f5c3c8f9 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 11:05:23 +0200 Subject: [PATCH 15/31] more tests --- src/vecs/smart.rs | 2 +- src/vecs/smart/tests.rs | 46 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 4e4a0403..4405e0c7 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -4,8 +4,8 @@ use alloc::vec::Vec; use core::marker::PhantomData; use core::mem::{offset_of, transmute, ManuallyDrop, MaybeUninit}; use core::num::NonZeroUsize; +use core::ptr; use core::ptr::NonNull; -use core::{ops, ptr, slice}; use super::smart_thin::SmartThinVec; use super::thin::ThinVec; diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index 93ac572c..1967a599 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -2,7 +2,7 @@ use alloc::vec; use super::SmartVec; use crate::smart::Smart; -use crate::{smart_thin_vec, Arc}; +use crate::{smart_thin_vec, Arc, Unique}; #[test] fn new() { @@ -22,6 +22,15 @@ fn with_capacity() { assert!(v.capacity() >= 10); } +#[test] +fn capacity_fat() { + let fat = Smart::new(vec![1, 2, 3]); + let v = SmartVec::::from_fat(fat); + assert!(v.is_fat()); + assert!(!v.is_thin()); + assert!(v.capacity() >= 3); +} + #[test] fn from_fat() { let fat = Smart::new(vec![1, 2, 3]); @@ -105,3 +114,38 @@ fn is_unique_fat() { let _w = v.clone(); assert!(!v.is_unique()); } + +#[test] +fn try_clone() { + let v = SmartVec::::new(); + let w = v.try_clone().unwrap(); + assert_eq!(v.as_ptr(), w.as_ptr()); + + let v = SmartVec::::new(); + assert!(v.try_clone().is_none()); +} + +#[test] +fn force_clone_arc() { + let v = SmartVec::::new(); + let w = v.force_clone(); + assert_eq!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn force_clone_unique_thin() { + let v = SmartVec::::new(); + assert!(v.is_thin()); + let w = v.force_clone(); + assert_ne!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn force_clone_unique_fat() { + let fat = Smart::new(vec![1, 2, 3]); + let v = SmartVec::::from_fat(fat); + assert!(v.is_fat()); + + let w = v.force_clone(); + assert_ne!(v.as_ptr(), w.as_ptr()); +} From 0d6549c870138fa09dade1cad094eec6101010da Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 11:08:26 +0200 Subject: [PATCH 16/31] more tests --- src/vecs/smart/tests.rs | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index 1967a599..dbc86af2 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -136,7 +136,9 @@ fn force_clone_arc() { fn force_clone_unique_thin() { let v = SmartVec::::new(); assert!(v.is_thin()); + let w = v.force_clone(); + assert!(w.is_thin()); assert_ne!(v.as_ptr(), w.as_ptr()); } @@ -147,5 +149,51 @@ fn force_clone_unique_fat() { assert!(v.is_fat()); let w = v.force_clone(); + assert!(w.is_thin()); + assert_ne!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn force_clone_or_copy_arc() { + let v = SmartVec::::new(); + let w = v.force_clone_or_copy(); + assert_eq!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn force_clone_or_copy_unique_thin() { + let v = SmartVec::::new(); + assert!(v.is_thin()); + + let w = v.force_clone_or_copy(); + assert!(w.is_thin()); + assert_ne!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn force_clone_or_copy_unique_fat() { + let fat = Smart::new(vec![1, 2, 3]); + let v = SmartVec::::from_fat(fat); + assert!(v.is_fat()); + + let w = v.force_clone_or_copy(); + assert!(w.is_thin()); + assert_ne!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn clone_arc() { + let v = SmartVec::::new(); + let w = v.clone(); + assert_eq!(v.as_ptr(), w.as_ptr()); +} + +#[test] +fn clone_unique() { + let v = SmartVec::::new(); + assert!(v.is_thin()); + + let w = v.clone(); + assert!(w.is_thin()); assert_ne!(v.as_ptr(), w.as_ptr()); } From 12d93cb9f4619ecc069873aa6261a2783e9c1132 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 11:10:19 +0200 Subject: [PATCH 17/31] more tests --- src/vecs/smart/tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index dbc86af2..49bc1730 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -1,6 +1,7 @@ use alloc::vec; use super::SmartVec; +use crate::backend::PanickyUnique; use crate::smart::Smart; use crate::{smart_thin_vec, Arc, Unique}; @@ -188,6 +189,13 @@ fn clone_arc() { assert_eq!(v.as_ptr(), w.as_ptr()); } +#[test] +#[should_panic(expected = "count overflow")] +fn clone_panic() { + let v = SmartVec::::new(); + let _w = v.clone(); +} + #[test] fn clone_unique() { let v = SmartVec::::new(); From 1e802650bd95698465f6490fd69dd68f61018350 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 7 May 2025 13:50:59 +0200 Subject: [PATCH 18/31] wip --- src/bytes/raw/allocated.rs | 2 -- src/vecs/smart.rs | 64 +++++++++++++++++++++++++++++++++++--- src/vecs/smart/tests.rs | 42 +++++++++++++++++++++++++ src/vecs/smart_thin.rs | 6 +++- src/vecs/thin.rs | 30 +++++++++++++++++- 5 files changed, 135 insertions(+), 9 deletions(-) diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index c19a40e2..8aab6803 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -76,8 +76,6 @@ impl TaggedSmart { // SAFETY: type invariant let ptr: NonNull<()> = unsafe { NonNull::new_unchecked(ptr) }; - #[cfg(miri)] - let _ = &*ptr; // check provenance early ptr } diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 4405e0c7..a7af087e 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -8,7 +8,7 @@ use core::ptr; use core::ptr::NonNull; use super::smart_thin::SmartThinVec; -use super::thin::ThinVec; +use super::thin::ThinHandle; use crate::backend::{ Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, }; @@ -286,16 +286,64 @@ impl SmartVec { Variant::Fat(unsafe { transmute::<&mut Vec, &mut Vec>(mut_vec) }) } else { let mut thin = SmartThinVec::::from_slice_clone(fat.as_slice()); - let mut_ref = unsafe { ptr::from_mut(thin.as_mut_unchecked()) }; + let handle = unsafe { thin.handle().extend_lifetime() }; self.0 = TaggedSmart::from_thin(thin); - Variant::Thin(unsafe { &mut *mut_ref }) + Variant::Thin(handle) } } - Variant::Thin(thin) => Variant::Thin(unsafe { transmute(thin.mutate()) }), + Variant::Thin(thin) => { + let _ = thin.mutate(); + let handle = unsafe { thin.handle().extend_lifetime() }; + Variant::Thin(handle) + } }; RefMut(r) } + pub fn mutate_copy(&mut self) -> RefMut + where + T: Copy, + { + let mut v = ManuallyDrop::new(self.0.get()); + let r = match &mut *v { + Variant::Fat(fat) => { + if let Some(mut_vec) = fat.as_mut() { + Variant::Fat(unsafe { transmute::<&mut Vec, &mut Vec>(mut_vec) }) + } else { + let mut thin = SmartThinVec::::from_slice_copy(fat.as_slice()); + let handle = unsafe { thin.handle().extend_lifetime() }; + self.0 = TaggedSmart::from_thin(thin); + Variant::Thin(handle) + } + } + Variant::Thin(thin) => { + let _ = thin.mutate_copy(); + let handle = unsafe { thin.handle().extend_lifetime() }; + Variant::Thin(handle) + } + }; + RefMut(r) + } + + pub fn as_mut(&mut self) -> Option> { + if self.count().is_unique() { + let r = { + let mut v = ManuallyDrop::new(self.0.get()); + + // SAFETY: uniqueness checked above + match &mut *v { + Variant::Fat(fat) => Variant::Fat(unsafe { fat.as_mut_unchecked_extended() }), + Variant::Thin(thin) => { + Variant::Thin(unsafe { thin.handle().extend_lifetime() }) + } + } + }; + Some(RefMut(r)) + } else { + None + } + } + pub fn try_clone(&self) -> Option { if self.count().incr() == UpdateResult::Done { Some(Self(self.0)) @@ -349,7 +397,13 @@ impl Clone for SmartVec } } -pub struct RefMut<'a, T, P>(Variant<&'a mut Vec, &'a mut ThinVec>); +impl Drop for SmartVec { + fn drop(&mut self) { + let _ = self.0.get(); + } +} + +pub struct RefMut<'a, T, P>(Variant<&'a mut Vec, ThinHandle<'a, T, P>>); impl<'a, T, P> RefMut<'a, T, P> { pub fn as_slice(&self) -> &[T] { diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index 49bc1730..74a780c2 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -1,4 +1,5 @@ use alloc::vec; +use std::vec::Vec; use super::SmartVec; use crate::backend::PanickyUnique; @@ -205,3 +206,44 @@ fn clone_unique() { assert!(w.is_thin()); assert_ne!(v.as_ptr(), w.as_ptr()); } + +#[test] +fn as_mut() { + let mut v = SmartVec::::new(); + { + let mut m = v.as_mut().unwrap(); + m.push(1); + } + assert_eq!(v.as_slice(), &[1]); +} + +#[test] +fn as_mut_shared() { + let mut v = SmartVec::::new(); + let _w = v.clone(); + assert!(v.as_mut().is_none()); +} + +#[test] +fn mutate() { + let mut v = SmartVec::::new(); + v.mutate().push(1); + assert_eq!(v.as_slice(), &[1]); + v.mutate().push(2); + assert_eq!(v.as_slice(), &[1, 2]); + let _w = v.clone(); + v.mutate().push(3); + assert_eq!(v.as_slice(), &[1, 2, 3]); +} + +#[test] +fn mutate_fat() { + let mut v = SmartVec::::from_fat(Smart::new(Vec::with_capacity(10))); + v.mutate().push(1); + assert_eq!(v.as_slice(), &[1]); + v.mutate().push(2); + assert_eq!(v.as_slice(), &[1, 2]); + let _w = v.clone(); + v.mutate().push(3); + assert_eq!(v.as_slice(), &[1, 2, 3]); +} diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 7ab9857f..af42989a 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -43,7 +43,7 @@ use core::ops::Deref; use core::ptr; use core::ptr::NonNull; -use super::thin::{Header, Reserved, ThinVec}; +use super::thin::{Header, Reserved, ThinHandle, ThinVec}; use crate::backend::{ Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, }; @@ -479,6 +479,10 @@ impl SmartThinVec { Err(self) } } + + pub(crate) unsafe fn handle(&mut self) -> ThinHandle { + unsafe { self.as_mut_unchecked().handle() } + } } impl Clone for SmartThinVec> { diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index b8098ec4..0966063b 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -8,7 +8,7 @@ use alloc::vec::Vec; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem::{offset_of, ManuallyDrop, MaybeUninit}; -use core::ops::{Bound, Range, RangeBounds}; +use core::ops::{Bound, Deref, DerefMut, Range, RangeBounds}; use core::ptr::NonNull; use core::{cmp, fmt, mem, ops, panic, ptr, slice}; @@ -309,6 +309,34 @@ where other } + + pub(crate) unsafe fn handle(&self) -> ThinHandle { + ThinHandle(ManuallyDrop::new(Self(self.0)), PhantomData) + } +} + +pub(crate) struct ThinHandle<'a, T, P>(ManuallyDrop>, PhantomData<&'a ()>); + +impl ThinHandle<'_, T, P> { + pub(crate) unsafe fn extend_lifetime<'a>(self) -> ThinHandle<'a, T, P> { + ThinHandle(self.0, PhantomData) + } +} + +impl Deref for ThinHandle<'_, T, P> { + type Target = ThinVec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ThinHandle<'_, T, P> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } impl ThinVec { From bae8f230ba2870820d80d9b1d805ff9f6ee3f765 Mon Sep 17 00:00:00 2001 From: polazarus Date: Mon, 12 May 2025 07:22:54 +0200 Subject: [PATCH 19/31] wip --- src/vecs/smart.rs | 31 ++++++++++++++++++++++++------- src/vecs/smart_thin.rs | 6 +++++- src/vecs/thin.rs | 5 +++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index a7af087e..2280e1be 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -104,6 +104,11 @@ impl TaggedSmart { #[inline] fn from_thin(raw: SmartThinVec) -> Self { let ptr = SmartThinVec::into_raw(raw); + Self::from_thin_ptr(ptr) + } + + #[inline] + fn from_thin_ptr(ptr: NonNull>) -> Self { debug_assert!(ptr.is_aligned()); debug_assert!(ptr.addr().get() & TAG_MASK == 0); @@ -297,7 +302,7 @@ impl SmartVec { Variant::Thin(handle) } }; - RefMut(r) + RefMut(r, self) } pub fn mutate_copy(&mut self) -> RefMut @@ -322,7 +327,7 @@ impl SmartVec { Variant::Thin(handle) } }; - RefMut(r) + RefMut(r, self) } pub fn as_mut(&mut self) -> Option> { @@ -338,7 +343,7 @@ impl SmartVec { } } }; - Some(RefMut(r)) + Some(RefMut(r, self)) } else { None } @@ -403,9 +408,21 @@ impl Drop for SmartVec { } } -pub struct RefMut<'a, T, P>(Variant<&'a mut Vec, ThinHandle<'a, T, P>>); +pub struct RefMut<'a, T, B: Backend>( + Variant<&'a mut Vec, ThinHandle<'a, T, B>>, + &'a mut SmartVec, +); + +impl<'a, T, B: Backend> Drop for RefMut<'a, T, B> { + fn drop(&mut self) { + match &self.0 { + Variant::Fat(_) => {} + Variant::Thin(thin) => self.1 .0 = TaggedSmart::from_thin_ptr(thin.get_raw()), + } + } +} -impl<'a, T, P> RefMut<'a, T, P> { +impl<'a, T, B: Backend> RefMut<'a, T, B> { pub fn as_slice(&self) -> &[T] { match &self.0 { Variant::Fat(fat) => fat.as_slice(), @@ -490,9 +507,9 @@ impl<'a, T, P> RefMut<'a, T, P> { } } - pub fn split_off(&mut self, at: usize) -> SmartVec + pub fn split_off(&mut self, at: usize) -> SmartVec where - P: Backend, + B: Backend, { match &mut self.0 { Variant::Fat(fat) => { diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index af42989a..60d61aa5 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -74,6 +74,10 @@ macro_rules! smart_thin_vec { smart_thin_vec![$crate::Arc : $($rest),*] }; + [ $value:expr ; $len:expr ] => { + smart_thin_vec![$crate::Arc : $value; $len] + }; + [ $t:ty : $($rest:expr),* $(,)?] => { { use $crate::thin_vec; @@ -441,7 +445,7 @@ impl SmartThinVec { /// # use hipstr::Unique; /// let v = smart_thin_vec![Unique : 1, 2, 3]; /// assert_eq!(v.as_slice(), &[1, 2, 3]); - /// let v2 = v.clone_or_copy(); // maybe a bit more efficient than `v.clone()` for `Copy` types + /// let v2 = v.force_clone_or_copy(); // maybe a bit more efficient than `v.clone()` for `Copy` types /// assert_eq!(v.as_slice(), v2.as_slice()); /// assert_ne!(v.as_ptr(), v2.as_ptr()); /// ``` diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 0966063b..e34d3a6c 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -318,6 +318,11 @@ where pub(crate) struct ThinHandle<'a, T, P>(ManuallyDrop>, PhantomData<&'a ()>); impl ThinHandle<'_, T, P> { + pub(crate) fn get_raw(&self) -> NonNull> { + let ptr = self.0 .0; + ptr + } + pub(crate) unsafe fn extend_lifetime<'a>(self) -> ThinHandle<'a, T, P> { ThinHandle(self.0, PhantomData) } From 9f8ebd4b24312f2ff3246ca771fd72136e588eef Mon Sep 17 00:00:00 2001 From: polazarus Date: Mon, 12 May 2025 10:11:35 +0200 Subject: [PATCH 20/31] fix macro --- src/vecs/smart_thin.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 60d61aa5..d518f19b 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -87,6 +87,14 @@ macro_rules! smart_thin_vec { } }; + [ $t:ty : $value:expr ; $len:expr ] => { + { + use $crate::thin_vec; + $crate::vecs::smart_thin::SmartThinVec::<_, $t>::from( + thin_vec![ $value ; $len ] + ) + } + }; } /// A smart thin vector that can be either unique, reference counted or From bffc96d39e071b31872d557490dc11d826bec11a Mon Sep 17 00:00:00 2001 From: polazarus Date: Sat, 24 May 2025 13:49:33 +0200 Subject: [PATCH 21/31] fix mutate_fat test --- src/smart.rs | 4 ++++ src/vecs/smart.rs | 1 + src/vecs/smart_thin.rs | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/smart.rs b/src/smart.rs index a1257fd7..e7c2236a 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -327,6 +327,10 @@ where { *self = Self::new(*Smart::get(self)); } + + pub(crate) fn count(&self) -> &C { + &self.inner().count + } } impl Clone for Smart> { diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 2280e1be..f8a338f9 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -291,6 +291,7 @@ impl SmartVec { Variant::Fat(unsafe { transmute::<&mut Vec, &mut Vec>(mut_vec) }) } else { let mut thin = SmartThinVec::::from_slice_clone(fat.as_slice()); + fat.count().decr(); // decrement the count to avoid double counting let handle = unsafe { thin.handle().extend_lifetime() }; self.0 = TaggedSmart::from_thin(thin); Variant::Thin(handle) diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index d518f19b..20818194 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -169,7 +169,7 @@ impl SmartThinVec { unsafe { Self::from_thin_vec_unchecked(tv) } } - const fn count(&self) -> &C { + pub(crate) const fn count(&self) -> &C { self.as_thin_vec().prefix() } From 5b055f3dd1fe66e6b02697f411ff37236e9508cc Mon Sep 17 00:00:00 2001 From: polazarus Date: Sun, 25 May 2025 15:32:45 +0200 Subject: [PATCH 22/31] wip --- src/bytes/raw.rs | 19 ++++++++----- src/bytes/raw/allocated.rs | 4 +-- src/common.rs | 5 ++-- src/smart.rs | 11 ++++---- src/vecs/inline.rs | 4 +-- src/vecs/smart.rs | 57 ++++++++++++++++++++++++++++++++------ src/vecs/thin.rs | 25 +++++++++++------ 7 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index 42af44e4..96c20aa2 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -217,7 +217,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { /// Retrieves a reference on the union. #[inline] pub(super) const fn union(&self) -> &Union<'borrow, B> { - let raw_ptr: *const _ = &self.pivot; + let raw_ptr = &raw const self.pivot; let union_ptr: *const Union<'borrow, B> = raw_ptr.cast(); // SAFETY: same layout and same niche hopefully, same immutability unsafe { &*union_ptr } @@ -226,7 +226,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { /// Retrieves a mutable reference on the union. #[inline] pub(super) const fn union_mut(&mut self) -> &mut Union<'borrow, B> { - let raw_ptr: *mut _ = &mut self.pivot; + let raw_ptr = &raw mut self.pivot; let union_ptr: *mut Union<'borrow, B> = raw_ptr.cast(); // SAFETY: same layout and same niche hopefully, same mutability unsafe { &mut *union_ptr } @@ -474,6 +474,11 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { result } + #[allow(clippy::mem_replace_with_default)] + const fn take(&mut self) -> Self { + replace(self, Self::new()) + } + /// Takes a vector representation of this raw byte string. /// /// Will only allocate if needed. @@ -485,7 +490,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { if let Ok(owned) = allocated.try_into_vec() { // SAFETY: ownership is taken, replace with empty // and forget old value (otherwise double drop!!) - forget(replace(self, Self::new())); + forget(self.take()); return owned; } } @@ -506,9 +511,9 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { Split::Allocated(&allocated) => { // Takes a copy of allocated - // replace `self` one by an empty raw + // replace `self` one by an empty one // forget the old value, we have `allocated` as a valid handle - forget(replace(self, Self::new())); + forget(self.take()); Some(allocated) } @@ -523,7 +528,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { match tag { Tag::Inline => {} Tag::Borrowed => { - let old = replace(self, Self::new()).union_move(); + let old = self.take().union_move(); // SAFETY: representation is checked above let borrowed = unsafe { old.borrowed }; @@ -536,7 +541,7 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { return; } - let old = replace(self, Self::new()); + let old = self.take(); // SAFETY: representation checked above let allocated = unsafe { old.union_move().allocated }; diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index 8aab6803..e8fa4067 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -43,7 +43,7 @@ impl Copy for TaggedSmart {} impl TaggedSmart { /// Gets the owner. - fn get(self) -> Variant, B>, SmartThinVec> { + const fn get(self) -> Variant, B>, SmartThinVec> { let ptr = self.ptr(); if self.is_fat() { @@ -225,7 +225,7 @@ impl Allocated { } /// Converts the allocated representation into its owner. - fn into_owner(self) -> Variant, B>, SmartThinVec> { + const fn into_owner(self) -> Variant, B>, SmartThinVec> { self.owner.get() } diff --git a/src/common.rs b/src/common.rs index 9a8eaa9c..eb81bf06 100644 --- a/src/common.rs +++ b/src/common.rs @@ -157,8 +157,9 @@ impl Drop for SliceGuard<'_, T> { fn drop(&mut self) { if mem::needs_drop::() { unsafe { - let slice: *mut [MaybeUninit] = &mut self.slice[..self.initialized]; - ptr::drop_in_place(slice as *mut [T]); + let slice_ptr = ptr::from_mut(&mut self.slice[..self.initialized]); + let slice_ptr = slice_ptr as *mut [T]; + ptr::drop_in_place(slice_ptr); } } } diff --git a/src/smart.rs b/src/smart.rs index e7c2236a..b45eb24e 100644 --- a/src/smart.rs +++ b/src/smart.rs @@ -126,8 +126,7 @@ where /// ``` #[inline] #[must_use] - pub unsafe fn from_raw(ptr: NonNull>) -> Self { - debug_assert!(ptr.is_aligned()); + pub const unsafe fn from_raw(ptr: NonNull>) -> Self { unsafe { Self(ptr) } } @@ -318,17 +317,17 @@ where where T: Clone, { - *self = Self::new(Smart::get(self).clone()); + *self = Self::new(Self::get(self).clone()); } pub(crate) fn detach_copy(&mut self) where T: Copy, { - *self = Self::new(*Smart::get(self)); + *self = Self::new(*Self::get(self)); } - pub(crate) fn count(&self) -> &C { + pub(crate) const fn count(&self) -> &C { &self.inner().count } } @@ -510,6 +509,6 @@ where { #[inline] fn hash(&self, state: &mut H) { - T::hash(self, state) + T::hash(self, state); } } diff --git a/src/vecs/inline.rs b/src/vecs/inline.rs index 74e48cfe..8eee3d1c 100644 --- a/src/vecs/inline.rs +++ b/src/vecs/inline.rs @@ -443,7 +443,7 @@ impl InlineVec &mut [MaybeUninit] { + pub const fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { let len = self.len(); unsafe { slice::from_raw_parts_mut(self.data.as_mut_ptr().add(len), CAP - len) } } @@ -460,7 +460,7 @@ impl InlineVec Option { + pub const fn pop(&mut self) -> Option { let len = self.len.get(); if len == 0 { None diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index f8a338f9..1d4fa9e0 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -19,12 +19,13 @@ use crate::vecs::thin::Header; #[cfg(test)] mod tests; -const TAG_MASK: usize = 0b11 as usize; +const TAG_MASK: usize = 0b11; const THIN_BIT: usize = 0b01; const TAG_THIN: usize = 0b11; const TAG_FAT: usize = 0b10; const TAG_OWNED_MASK: usize = 0b10; +#[derive(Clone, Copy)] enum Variant { Fat(F), Thin(T), @@ -165,7 +166,7 @@ impl TaggedSmart { #[inline] fn check_tag(self) -> bool { let addr = self.0.addr().get(); - (addr & !TAG_MASK) != 0 && (addr & TAG_OWNED_MASK as usize) != 0 + (addr & !TAG_MASK) != 0 && (addr & TAG_OWNED_MASK) != 0 } } @@ -180,7 +181,8 @@ impl TaggedSmart { pub struct SmartVec(TaggedSmart); impl SmartVec { - /// Creates a new smart vector. + /// Creates a new empty smart vector. + #[must_use] pub fn new() -> Self { let thin = SmartThinVec::new(); let tagged_ptr = TaggedSmart::from_thin(thin); @@ -188,6 +190,7 @@ impl SmartVec { } /// Creates a new smart vector with the given capacity. + #[must_use] pub fn with_capacity(capacity: usize) -> Self { let thin = SmartThinVec::with_capacity(capacity); let tagged_ptr = TaggedSmart::from_thin(thin); @@ -195,29 +198,35 @@ impl SmartVec { } #[inline] + #[must_use] pub fn is_thin(&self) -> bool { self.0.is_thin() } #[inline] + #[must_use] pub fn is_fat(&self) -> bool { self.0.is_fat() } + #[must_use] fn from_fat(raw: Smart, B>) -> Self { let tagged_ptr = TaggedSmart::from_fat(raw); Self(tagged_ptr) } + #[must_use] fn from_thin(raw: SmartThinVec) -> Self { let tagged_ptr = TaggedSmart::from_thin(raw); Self(tagged_ptr) } + #[must_use] pub fn as_slice(&self) -> &[T] { self.0.as_slice() } + #[must_use] pub fn as_ptr(&self) -> *const T { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -239,6 +248,7 @@ impl SmartVec { /// v.mutate().push(1); /// assert_eq!(v.len(), 1); /// ``` + #[must_use] pub fn len(&self) -> usize { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -247,6 +257,7 @@ impl SmartVec { } } + #[must_use] pub fn capacity(&self) -> usize { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -255,6 +266,7 @@ impl SmartVec { } } + #[must_use] pub fn is_empty(&self) -> bool { let v = ManuallyDrop::new(self.0.get()); match &*v { @@ -276,6 +288,7 @@ impl SmartVec { /// let w = v.clone(); /// assert!(!v.is_unique()); /// ``` + #[must_use] pub fn is_unique(&self) -> bool { self.count().is_unique() } @@ -331,6 +344,7 @@ impl SmartVec { RefMut(r, self) } + #[must_use] pub fn as_mut(&mut self) -> Option> { if self.count().is_unique() { let r = { @@ -350,6 +364,7 @@ impl SmartVec { } } + #[must_use] pub fn try_clone(&self) -> Option { if self.count().incr() == UpdateResult::Done { Some(Self(self.0)) @@ -362,6 +377,7 @@ impl SmartVec { self.0.count() } + #[must_use] pub fn force_clone(&self) -> Self where T: Clone, @@ -376,6 +392,7 @@ impl SmartVec { }) } + #[must_use] pub fn force_clone_or_copy(&self) -> Self where T: Copy, @@ -409,32 +426,39 @@ impl Drop for SmartVec { } } +#[must_use] pub struct RefMut<'a, T, B: Backend>( Variant<&'a mut Vec, ThinHandle<'a, T, B>>, &'a mut SmartVec, ); -impl<'a, T, B: Backend> Drop for RefMut<'a, T, B> { +impl Drop for RefMut<'_, T, B> { fn drop(&mut self) { match &self.0 { Variant::Fat(_) => {} - Variant::Thin(thin) => self.1 .0 = TaggedSmart::from_thin_ptr(thin.get_raw()), + Variant::Thin(thin) => { + // update the thin vector pointer in the smart vector + self.1 .0 = TaggedSmart::from_thin_ptr(thin.raw()); + } } } } -impl<'a, T, B: Backend> RefMut<'a, T, B> { - pub fn as_slice(&self) -> &[T] { +impl RefMut<'_, T, B> { + /// Returns the underlying slice. + #[must_use] + pub const fn as_slice(&self) -> &[T] { match &self.0 { Variant::Fat(fat) => fat.as_slice(), - Variant::Thin(thin) => thin.as_slice(), + Variant::Thin(thin) => thin.as_ref().as_slice(), } } + #[must_use] pub fn as_mut_slice(&mut self) -> &mut [T] { match &mut self.0 { Variant::Fat(fat) => fat.as_mut_slice(), - Variant::Thin(thin) => thin.as_mut_slice(), + Variant::Thin(thin) => thin.as_mut().as_mut_slice(), } } @@ -466,6 +490,13 @@ impl<'a, T, B: Backend> RefMut<'a, T, B> { } } + pub fn swap_remove(&mut self, index: usize) -> T { + match &mut self.0 { + Variant::Fat(fat) => fat.swap_remove(index), + Variant::Thin(thin) => thin.swap_remove(index), + } + } + pub fn clear(&mut self) { match &mut self.0 { Variant::Fat(fat) => fat.clear(), @@ -508,6 +539,7 @@ impl<'a, T, B: Backend> RefMut<'a, T, B> { } } + #[must_use] pub fn split_off(&mut self, at: usize) -> SmartVec where B: Backend, @@ -524,6 +556,7 @@ impl<'a, T, B: Backend> RefMut<'a, T, B> { } } + #[must_use] pub fn as_ptr(&self) -> *const T { match &self.0 { Variant::Fat(fat) => fat.as_ptr(), @@ -582,6 +615,12 @@ impl<'a, T, B: Backend> RefMut<'a, T, B> { } } +impl Default for SmartVec { + fn default() -> Self { + Self::new() + } +} + trait_impls! { [T, B] where [B: Backend] { Extend { T => RefMut<'_, T, B>; } diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index e34d3a6c..15bce19c 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -14,8 +14,8 @@ use core::{cmp, fmt, mem, ops, panic, ptr, slice}; use crate::common::drain::Drain; use crate::common::{ - check_alloc, guarded_slice_clone, maybe_uninit_write_copy_of_slice, panic_display, traits, - RangeError, + check_alloc, guarded_slice_clone, manually_drop_as_mut, manually_drop_as_ref, + maybe_uninit_write_copy_of_slice, panic_display, traits, RangeError, }; use crate::{common, macros}; @@ -310,7 +310,7 @@ where other } - pub(crate) unsafe fn handle(&self) -> ThinHandle { + pub(crate) const unsafe fn handle(&self) -> ThinHandle { ThinHandle(ManuallyDrop::new(Self(self.0)), PhantomData) } } @@ -318,12 +318,19 @@ where pub(crate) struct ThinHandle<'a, T, P>(ManuallyDrop>, PhantomData<&'a ()>); impl ThinHandle<'_, T, P> { - pub(crate) fn get_raw(&self) -> NonNull> { - let ptr = self.0 .0; - ptr + pub(crate) const fn as_ref(&self) -> &ThinVec { + manually_drop_as_ref(&self.0) } - pub(crate) unsafe fn extend_lifetime<'a>(self) -> ThinHandle<'a, T, P> { + pub(crate) const fn as_mut(&mut self) -> &mut ThinVec { + manually_drop_as_mut(&mut self.0) + } + + pub(crate) const fn raw(&self) -> NonNull> { + manually_drop_as_ref(&self.0).0 + } + + pub(crate) const unsafe fn extend_lifetime<'a>(self) -> ThinHandle<'a, T, P> { ThinHandle(self.0, PhantomData) } } @@ -758,7 +765,7 @@ impl ThinVec { /// assert_eq!(vec.pop(), Some(1)); /// assert_eq!(vec.pop(), None); /// ``` - pub fn pop(&mut self) -> Option { + pub const fn pop(&mut self) -> Option { let len = self.len(); if len == 0 { return None; @@ -992,7 +999,7 @@ impl ThinVec { /// /// assert_eq!(&v, &[0, 1, 2]); /// ``` - pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { + pub const fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit] { let len = self.len(); let cap = self.capacity(); From d69fe90733c0da8f28cf1da6ea6fe1bc0f9857ad Mon Sep 17 00:00:00 2001 From: polazarus Date: Mon, 26 May 2025 09:42:06 +0200 Subject: [PATCH 23/31] fix std ref --- src/vecs/smart/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vecs/smart/tests.rs b/src/vecs/smart/tests.rs index 74a780c2..6cc3fb5c 100644 --- a/src/vecs/smart/tests.rs +++ b/src/vecs/smart/tests.rs @@ -1,5 +1,5 @@ use alloc::vec; -use std::vec::Vec; +use alloc::vec::Vec; use super::SmartVec; use crate::backend::PanickyUnique; From ebb3d8841310744e92636a1e9f481b2302486ae0 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 27 May 2025 22:45:05 +0200 Subject: [PATCH 24/31] fix test --- src/bytes/raw/allocated.rs | 15 ++++++++++++++- src/bytes/tests.rs | 5 +++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index e8fa4067..a90b4a26 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -99,7 +99,13 @@ impl TaggedSmart { #[inline] fn from_thin(raw: SmartThinVec) -> Self { - let ptr = SmartThinVec::into_raw(raw).as_ptr(); + let ptr = SmartThinVec::into_raw(raw); + + Self::from_thin_raw(ptr) + } + + fn from_thin_raw(ptr: NonNull>) -> Self { + let ptr = ptr.as_ptr(); debug_assert!(ptr.is_aligned()); debug_assert!((ptr as usize) & TAG_MASK == 0); @@ -169,6 +175,12 @@ impl TaggedSmart { // SAFETY: type invariant unsafe { Smart::from_raw(self.ptr().cast()) } } + + fn update_thin(&mut self, thin: &mut ThinVec) { + debug_assert!(self.is_thin()); + let ptr = unsafe { thin.handle().raw() }; + *self = Self::from_thin_raw(ptr); + } } /// Allocated representation. @@ -497,6 +509,7 @@ impl Allocated { shift = unsafe { self.ptr.offset_from(thin.as_ptr()) as usize }; thin.truncate(self.len + shift); thin.extend_from_slice_copy(addition); + self.owner.update_thin(thin); ptr = thin.as_ptr(); } } diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 8a457e23..98e9bf00 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -1008,9 +1008,10 @@ fn push_slice_allocated_unique() { let mut a = H::from(MEDIUM); assert!(a.is_thin()); assert!(a.is_allocated()); - a.push_slice(ABC); + a.push_slice(ABCDEF); assert_eq!(&a[0..42], MEDIUM); - assert_eq!(&a[42..], ABC); + assert_eq!(&a[42..], ABCDEF); + assert!(a.is_thin()); } #[test] From f5a40875ef19c1452ffa4ddf0516b86d0edebc60 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 27 May 2025 23:03:33 +0200 Subject: [PATCH 25/31] fix be --- src/bytes/raw.rs | 3 +++ src/bytes/raw/tests.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index 96c20aa2..a064efbe 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -124,8 +124,11 @@ pub(super) struct Pivot { #[derive(Clone, Copy)] #[repr(C)] pub(super) union WordView { + #[cfg(target_endian = "little")] tag: usize, _remainder: [MaybeUninit<*mut ()>; 2], + #[cfg(target_endian = "big")] + tag: usize, } unsafe impl Sync for HipByt<'_, B> {} diff --git a/src/bytes/raw/tests.rs b/src/bytes/raw/tests.rs index a6d931d8..b70f4971 100644 --- a/src/bytes/raw/tests.rs +++ b/src/bytes/raw/tests.rs @@ -28,10 +28,12 @@ fn test_union() { } #[cfg(debug_assertions)] -#[should_panic] +#[should_panic(expected = "mutable slice of borrowed string")] #[test] fn test_to_mut_slice_unchecked_panic() { let mut r = R::borrowed(b"abc"); + assert!(r.is_borrowed()); + assert!(!r.is_allocated()); unsafe { let _sl = r.as_mut_slice_unchecked(); } From ef2c8db27afc77566c5026c19e8f87688cdf5f95 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 27 May 2025 23:14:56 +0200 Subject: [PATCH 26/31] fix wordview --- src/bytes/raw.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index a064efbe..00f6ea80 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -5,7 +5,9 @@ use alloc::vec::Vec; use core::hint::unreachable_unchecked; use core::marker::PhantomData; -use core::mem::{align_of, forget, replace, size_of, transmute, ManuallyDrop, MaybeUninit}; +use core::mem::{ + align_of, forget, offset_of, replace, size_of, transmute, ManuallyDrop, MaybeUninit, +}; use core::num::NonZeroU8; use core::ops::Range; @@ -123,10 +125,12 @@ pub(super) struct Pivot { #[derive(Clone, Copy)] #[repr(C)] -pub(super) union WordView { +pub(super) struct WordView { #[cfg(target_endian = "little")] tag: usize, - _remainder: [MaybeUninit<*mut ()>; 2], + + _others: [*mut (); 2], + #[cfg(target_endian = "big")] tag: usize, } @@ -270,6 +274,18 @@ impl<'borrow, B: Backend> HipByt<'borrow, B> { /// Retrieves the tag. pub(super) const fn tag(&self) -> Tag { + const { + assert!(size_of::() == size_of::()); + + if cfg!(target_endian = "little") { + assert!(offset_of!(Pivot, tag_byte) == 0); + assert!(offset_of!(WordView, tag) == 0); + } else { + assert!(offset_of!(Pivot, tag_byte) == size_of::() - size_of::()); + assert!(offset_of!(WordView, tag) == size_of::() - size_of::()); + } + } + match self.pivot.tag_byte.get() & MASK { 0b01 => Tag::Inline, 0b11 | 0b10 => { From 26da4668d9175e84e06cc07d3a88b084cecd0476 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 27 May 2025 23:15:28 +0200 Subject: [PATCH 27/31] improve previous fix --- src/bytes/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bytes/raw.rs b/src/bytes/raw.rs index 00f6ea80..48455f1d 100644 --- a/src/bytes/raw.rs +++ b/src/bytes/raw.rs @@ -129,7 +129,7 @@ pub(super) struct WordView { #[cfg(target_endian = "little")] tag: usize, - _others: [*mut (); 2], + _others: [MaybeUninit<*mut ()>; 2], #[cfg(target_endian = "big")] tag: usize, From 7e9a66a4d81ce9c127e0bd021adf527a47986608 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 4 Jun 2025 10:51:20 +0200 Subject: [PATCH 28/31] fix doc issue --- src/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path.rs b/src/path.rs index eff8a9c4..a7403162 100644 --- a/src/path.rs +++ b/src/path.rs @@ -73,7 +73,7 @@ mod tests; /// [`String`]: std::string::String /// [Box]: std::boxed::Box /// [`HipStr`]: crate::string::HipStr -/// [`HipOsStr``]: crate::string::HipOsStr +/// [`HipOsStr``]: crate::os_string::HipOsStr #[repr(transparent)] #[allow(clippy::module_name_repetitions)] pub struct HipPath<'borrow, B>(HipOsStr<'borrow, B>) From c1946c582cc3ec9594a3f4baac11b3910ef4367c Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 4 Jun 2025 10:52:55 +0200 Subject: [PATCH 29/31] fix msrv --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b5a272ea..dabd416b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ categories = ["memory-management", "data-structures"] license = "MIT OR Apache-2.0" edition = "2021" readme = "README.md" -rust-version = "1.85.0" +rust-version = "1.87.0" [package.metadata.docs.rs] all-features = true From b2e2afe3cc90630b98627396737e5b2a6151ba23 Mon Sep 17 00:00:00 2001 From: polazarus Date: Wed, 4 Jun 2025 11:40:21 +0200 Subject: [PATCH 30/31] chore: add is_unique test for bytes --- src/bytes/tests.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 98e9bf00..126ca321 100644 --- a/src/bytes/tests.rs +++ b/src/bytes/tests.rs @@ -13,6 +13,7 @@ use std::collections::HashSet; use fastrand::Rng; use super::{simplify_range, SliceErrorKind}; +use crate::vecs::ThinVec; use crate::HipByt as H; type S<'a> = &'a [u8]; @@ -1471,3 +1472,27 @@ fn test_join_bad_iter2() { b",", ); } + +#[test] +fn is_unique() { + let a = H::inline(ABC); + assert!(a.is_unique()); + + let _a2 = a.clone(); + assert!(a.is_unique()); + + let b = H::from(MEDIUM.to_vec()); + assert!(b.is_unique()); + + let _b2 = b.clone(); + assert!(!b.is_unique()); + + // TODO missing from_thin_vec + let c = H::from_thin_vec(ThinVec::from(MEDIUM)); + assert!(c.is_unique()); + let _c2 = c.clone(); + assert!(!c.is_unique()); + + let d = H::borrowed(MEDIUM); + assert!(!d.is_unique()); +} From 1238367adc97b866f5fbbb5ba705173ccd93ab79 Mon Sep 17 00:00:00 2001 From: polazarus Date: Tue, 8 Jul 2025 10:24:41 +0200 Subject: [PATCH 31/31] chore: cleanup handle --- src/bytes/raw/allocated.rs | 2 +- src/common.rs | 44 +++++++++++++++++++++++++++++++++++++- src/vecs/smart.rs | 7 +++--- src/vecs/smart_thin.rs | 5 +++-- src/vecs/thin.rs | 44 ++++---------------------------------- 5 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/bytes/raw/allocated.rs b/src/bytes/raw/allocated.rs index a90b4a26..9b7f6515 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -178,7 +178,7 @@ impl TaggedSmart { fn update_thin(&mut self, thin: &mut ThinVec) { debug_assert!(self.is_thin()); - let ptr = unsafe { thin.handle().raw() }; + let ptr = unsafe { thin.handle().0 }; *self = Self::from_thin_raw(ptr); } } diff --git a/src/common.rs b/src/common.rs index eb81bf06..34d51ad9 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,8 +2,9 @@ use alloc::alloc::handle_alloc_error; use core::alloc::Layout; +use core::marker::PhantomData; use core::mem::{self, ManuallyDrop, MaybeUninit}; -use core::ops::{Bound, Range, RangeBounds}; +use core::ops::{Bound, Deref, DerefMut, Range, RangeBounds}; use core::ptr::NonNull; use core::{error, fmt, ptr}; @@ -190,3 +191,44 @@ pub(crate) fn check_alloc(ptr: *mut u8, layout: Layout) -> NonNull { }; ptr } + +/// A handle to some smart pointer-like type `T` that can be constructed from a +/// `ManuallyDrop`. +/// +/// This handle allows for safe access to the underlying type `T` without +/// really owning `T`. +pub(crate) struct Handle<'a, T>(ManuallyDrop, PhantomData<&'a mut T>); + +impl Handle<'_, T> { + pub const unsafe fn new(m: ManuallyDrop) -> Self { + Handle(m, PhantomData) + } + + pub const fn as_ref(&self) -> &T { + manually_drop_as_ref(&self.0) + } + + pub const fn as_mut(&mut self) -> &mut T { + manually_drop_as_mut(&mut self.0) + } + + pub const unsafe fn extend_lifetime<'a>(self) -> Handle<'a, T> { + Handle(self.0, PhantomData) + } +} + +impl Deref for Handle<'_, T> { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Handle<'_, T> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/src/vecs/smart.rs b/src/vecs/smart.rs index 1d4fa9e0..67b021d2 100644 --- a/src/vecs/smart.rs +++ b/src/vecs/smart.rs @@ -8,10 +8,11 @@ use core::ptr; use core::ptr::NonNull; use super::smart_thin::SmartThinVec; -use super::thin::ThinHandle; +use super::thin::ThinVec; use crate::backend::{ Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, }; +use crate::common::Handle; use crate::macros::trait_impls; use crate::smart::{Inner, Smart}; use crate::vecs::thin::Header; @@ -428,7 +429,7 @@ impl Drop for SmartVec { #[must_use] pub struct RefMut<'a, T, B: Backend>( - Variant<&'a mut Vec, ThinHandle<'a, T, B>>, + Variant<&'a mut Vec, Handle<'a, ThinVec>>, &'a mut SmartVec, ); @@ -438,7 +439,7 @@ impl Drop for RefMut<'_, T, B> { Variant::Fat(_) => {} Variant::Thin(thin) => { // update the thin vector pointer in the smart vector - self.1 .0 = TaggedSmart::from_thin_ptr(thin.raw()); + self.1 .0 = TaggedSmart::from_thin_ptr(thin.0); } } } diff --git a/src/vecs/smart_thin.rs b/src/vecs/smart_thin.rs index 20818194..2064ca59 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -43,11 +43,12 @@ use core::ops::Deref; use core::ptr; use core::ptr::NonNull; -use super::thin::{Header, Reserved, ThinHandle, ThinVec}; +use super::thin::{Header, Reserved, ThinVec}; use crate::backend::{ Backend, BackendImpl, CloneOnOverflow, Counter, PanicOnOverflow, UpdateResult, }; use crate::common::traits::MutVector; +use crate::common::Handle; use crate::macros::trait_impls; #[cfg(test)] @@ -492,7 +493,7 @@ impl SmartThinVec { } } - pub(crate) unsafe fn handle(&mut self) -> ThinHandle { + pub(crate) unsafe fn handle(&mut self) -> Handle> { unsafe { self.as_mut_unchecked().handle() } } } diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 15bce19c..068930de 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -15,7 +15,7 @@ use core::{cmp, fmt, mem, ops, panic, ptr, slice}; use crate::common::drain::Drain; use crate::common::{ check_alloc, guarded_slice_clone, manually_drop_as_mut, manually_drop_as_ref, - maybe_uninit_write_copy_of_slice, panic_display, traits, RangeError, + maybe_uninit_write_copy_of_slice, panic_display, traits, Handle, RangeError, }; use crate::{common, macros}; @@ -93,7 +93,7 @@ pub(crate) struct Header { /// /// [`Vec`]: alloc::vec::Vec #[repr(transparent)] -pub struct ThinVec(pub(super) NonNull>); +pub struct ThinVec(pub(crate) NonNull>); impl ThinVec where @@ -310,44 +310,8 @@ where other } - pub(crate) const unsafe fn handle(&self) -> ThinHandle { - ThinHandle(ManuallyDrop::new(Self(self.0)), PhantomData) - } -} - -pub(crate) struct ThinHandle<'a, T, P>(ManuallyDrop>, PhantomData<&'a ()>); - -impl ThinHandle<'_, T, P> { - pub(crate) const fn as_ref(&self) -> &ThinVec { - manually_drop_as_ref(&self.0) - } - - pub(crate) const fn as_mut(&mut self) -> &mut ThinVec { - manually_drop_as_mut(&mut self.0) - } - - pub(crate) const fn raw(&self) -> NonNull> { - manually_drop_as_ref(&self.0).0 - } - - pub(crate) const unsafe fn extend_lifetime<'a>(self) -> ThinHandle<'a, T, P> { - ThinHandle(self.0, PhantomData) - } -} - -impl Deref for ThinHandle<'_, T, P> { - type Target = ThinVec; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for ThinHandle<'_, T, P> { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + pub(crate) const unsafe fn handle(&self) -> Handle { + unsafe { Handle::new(ManuallyDrop::new(Self(self.0))) } } }