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 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..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)) } } @@ -515,6 +516,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] @@ -870,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. @@ -911,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 @@ -929,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 b8eb3d35..48455f1d 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; @@ -14,6 +16,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; @@ -31,10 +34,7 @@ const MASK: u8 = (1 << TAG_BITS) - 1; const TAG_INLINE: u8 = 1; /// Tag for the borrowed repr -const TAG_BORROWED: u8 = 2; - -/// Tag for the allocated repr -const TAG_ALLOCATED: u8 = 3; +const TAG_OWNED: u8 = 2; /// Maximal byte capacity of an inline [`HipByt`]. pub(crate) const INLINE_CAPACITY: usize = size_of::() - 1; @@ -123,9 +123,31 @@ pub(super) struct Pivot { tag_byte: NonZeroU8, } +#[derive(Clone, Copy)] +#[repr(C)] +pub(super) struct WordView { + #[cfg(target_endian = "little")] + tag: usize, + + _others: [MaybeUninit<*mut ()>; 2], + + #[cfg(target_endian = "big")] + tag: usize, +} + 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 +164,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 +195,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. @@ -199,7 +224,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 } @@ -208,7 +233,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 } @@ -249,10 +274,28 @@ 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 { - 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() }, } @@ -300,9 +343,15 @@ 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::new(vec); + let allocated = Allocated::from_vec(vec); Self::from_allocated(allocated) } @@ -380,10 +429,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); - Self::from_allocated(allocated) - } + unsafe { allocated.slice_unchecked(range.clone()) }.map_or_else( + || Self::from_slice(&allocated.as_slice()[range]), + Self::from_allocated, + ) } } }; @@ -432,10 +481,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); - Self::from_allocated(allocated) - } + unsafe { allocated.slice_unchecked(range.clone()) }.map_or_else( + || Self::from_slice(&allocated.as_slice()[range]), + Self::from_allocated, + ) } } }; @@ -444,6 +493,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. @@ -455,7 +509,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; } } @@ -476,9 +530,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) } @@ -493,7 +547,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 }; @@ -506,7 +560,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 }; @@ -567,8 +621,10 @@ 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) + 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 4b674136..9b7f6515 100644 --- a/src/bytes/raw/allocated.rs +++ b/src/bytes/raw/allocated.rs @@ -2,16 +2,26 @@ 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 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), + Thin(T), +} /// Tagged smart pointer (with forced exposed provenance). /// @@ -32,55 +42,144 @@ impl Clone for TaggedSmart { impl Copy for TaggedSmart {} impl TaggedSmart { + /// Gets the owner. + const 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) }; + + ptr + } + /// Constructed a tagged smart pointer from a [`Smart`]. #[inline] - fn from(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) & 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) + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_fat()); + this } - /// Converts back into the [`Smart`]. #[inline] - fn into(self) -> Smart, B> { - let this: Smart, B>; + fn from_thin(raw: SmartThinVec) -> Self { + let ptr = SmartThinVec::into_raw(raw); - debug_assert!(self.0 & MASK == TAG); + 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); - // 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()); + let this = Self(addr, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_thin()); + this + } - #[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, ptr::from_ref(counter)); + } + Variant::Thin(thin) => { + debug_assert_eq!(&raw const thin.0.as_ref().prefix, ptr::from_ref(counter)); + } + } + } } - 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 + } + + 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()) } } - /// Explicitly clones this tagged smart pointer. - fn explicit_clone(self) -> Self { - let r = ManuallyDrop::new(self.into()); - Self::from((*r).force_clone()) + fn update_thin(&mut self, thin: &mut ThinVec) { + debug_assert!(self.is_thin()); + let ptr = unsafe { thin.handle().0 }; + *self = Self::from_thin_raw(ptr); } } @@ -127,47 +226,35 @@ impl UnwindSafe for Allocated {} impl RefUnwindSafe for Allocated {} impl Allocated { - /// Converts the allocated representation into its owner. - fn into_owner(self) -> Smart, B> { - self.owner.into() + #[inline] + pub const fn is_thin(&self) -> bool { + self.owner.is_thin() } - /// 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()) + #[inline] + pub const fn is_fat(&self) -> bool { + self.owner.is_fat() } - /// 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()) + /// Converts the allocated representation into its owner. + const fn into_owner(self) -> Variant, B>, SmartThinVec> { + self.owner.get() } /// 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_fat(owner), }; debug_assert!(this.is_unique()); @@ -175,9 +262,27 @@ impl Allocated { this } + #[inline] + pub fn from_thin_vec

(v: ThinVec) -> Self { + let stv = SmartThinVec::from_thin_vec(v); + let ptr = stv.as_ptr(); + let len = stv.len(); + + let this = Self { + ptr, + len, + owner: TaggedSmart::from_thin(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_thin_vec(ThinVec::<_, B>::from_slice_copy(slice)) } /// Returns the length of this allocated string. @@ -269,35 +374,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 } } @@ -316,13 +423,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 } } @@ -331,7 +439,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. @@ -339,31 +451,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() }); + + 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 len = self.len(); + 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. @@ -373,23 +483,42 @@ 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 + shift); + 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 + shift); + thin.extend_from_slice_copy(addition); + self.owner.update_thin(thin); + 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 @@ -406,21 +535,30 @@ 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() }; + #[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); + vec.spare_capacity_mut() + } + 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); + let spare = vec.spare_capacity_mut(); + + // extend lifetime of the slicce + // SAFETY: the slice is valid while self is owned + unsafe { transmute::<&mut [MaybeUninit], &mut [MaybeUninit]>(spare) } + } + } } /// Forces the length of the vector to `new_len`. @@ -446,16 +584,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; } @@ -472,9 +627,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(); } } @@ -484,31 +639,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/borrowed.rs b/src/bytes/raw/borrowed.rs index b8cea5aa..b2c03456 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 = 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/bytes/raw/tests.rs b/src/bytes/raw/tests.rs index 39bc196f..b70f4971 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(); @@ -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(); } diff --git a/src/bytes/tests.rs b/src/bytes/tests.rs index 25228679..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]; @@ -95,10 +96,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); } @@ -215,31 +218,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(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 +630,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 +660,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 +677,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()); } @@ -981,17 +1005,21 @@ 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); + a.push_slice(ABCDEF); assert_eq!(&a[0..42], MEDIUM); - assert_eq!(&a[42..], ABC); + assert_eq!(&a[42..], ABCDEF); + assert!(a.is_thin()); +} - // 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 +1028,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]); @@ -1266,7 +1300,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] @@ -1438,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()); +} diff --git a/src/common.rs b/src/common.rs index 9a8eaa9c..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}; @@ -157,8 +158,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); } } } @@ -189,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/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..ce84f845 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() } @@ -125,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/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/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>) diff --git a/src/smart.rs b/src/smart.rs index d171a6d2..b45eb24e 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, @@ -19,11 +22,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, } @@ -34,14 +38,15 @@ where { fn clone(&self) -> Self { Self { - count: C::one(), + count: C::default(), value: self.value.clone(), } } } /// Basic smart pointer, with generic counter. -pub struct Smart(NonNull>) +#[repr(transparent)] +pub struct Smart(pub(crate) NonNull>) where C: Backend; @@ -66,7 +71,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) }) @@ -120,15 +125,15 @@ where /// let q = unsafe { Smart::from_raw(ptr) }; /// ``` #[inline] - pub fn from_raw(ptr: NonNull>) -> Self { - debug_assert!(ptr.is_aligned()); + #[must_use] + pub const unsafe fn from_raw(ptr: NonNull>) -> Self { unsafe { Self(ptr) } } /// 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 } @@ -188,18 +193,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) } } } @@ -208,20 +217,119 @@ where self.inner().count.incr() } - pub(crate) fn force_clone(&self) -> Self + 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 + } + + /// 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().unwrap();; + /// 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)) + } else { + None + } + } + + /// 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) }; 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(Self::get(self).clone()); + } + + pub(crate) fn detach_copy(&mut self) + where + T: Copy, + { + *self = Self::new(*Self::get(self)); + } + + pub(crate) const fn count(&self) -> &C { + &self.inner().count + } } impl Clone for Smart> { @@ -271,7 +379,7 @@ where #[inline] fn deref(&self) -> &Self::Target { - Smart::as_ref(self) + Self::get(self) } } @@ -281,7 +389,7 @@ where { #[inline] fn as_ref(&self) -> &T { - Smart::as_ref(self) + Self::get(self) } } @@ -298,3 +406,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 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, + 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 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/smart/tests.rs b/src/smart/tests.rs index 9d5ae871..de066b85 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"))] @@ -108,8 +109,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 +147,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 +189,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 +384,130 @@ 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()); } + +#[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] +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() { + 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); +} + +#[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); +} 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.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/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/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/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.rs b/src/vecs/smart.rs new file mode 100644 index 00000000..67b021d2 --- /dev/null +++ b/src/vecs/smart.rs @@ -0,0 +1,629 @@ +//! Smart vector type (fat or thin). + +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 super::smart_thin::SmartThinVec; +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; + +#[cfg(test)] +mod tests; + +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), +} + +/// Tagged smart pointer. +#[repr(transparent)] +struct TaggedSmart(NonNull<()>, 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()) }) + } + } + + fn addr(self) -> usize { + self.0.as_ptr() as usize + } + + #[inline] + fn is_thin(self) -> bool { + debug_assert!(self.check_tag()); + self.addr() & THIN_BIT != 0 + } + + #[inline] + fn is_fat(self) -> bool { + debug_assert!(self.check_tag()); + self.addr() & THIN_BIT == 0 + } + + fn ptr(self) -> NonNull<()> { + debug_assert!(self.check_tag()); + + 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); + debug_assert!(ptr.is_aligned()); + 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 tagged = ptr.map_addr(|addr| addr | TAG_FAT).cast(); + + let this = Self(tagged, 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); + 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); + + // 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 tagged = ptr.map_addr(|addr| addr | TAG_THIN).cast(); + + let this = Self(tagged, PhantomData); + debug_assert!(this.check_tag()); + debug_assert!(this.is_thin()); + this + } + + /// Returns a reference to the counter. + #[inline] + fn count(&self) -> &B { + const { + assert!(offset_of!(Header, prefix) == 0); + assert!(offset_of!(Inner, B>, count) == 0); + } + let counter: &B = 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] + fn check_tag(self) -> bool { + let addr = self.0.addr().get(); + (addr & !TAG_MASK) != 0 && (addr & TAG_OWNED_MASK) != 0 + } +} + +/// 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 { + /// Creates a new empty smart vector. + #[must_use] + 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. + #[must_use] + pub fn with_capacity(capacity: usize) -> Self { + let thin = SmartThinVec::with_capacity(capacity); + let tagged_ptr = TaggedSmart::from_thin(thin); + Self(tagged_ptr) + } + + #[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 { + Variant::Fat(fat) => fat.as_ptr(), + Variant::Thin(thin) => thin.as_ptr(), + } + } + + /// 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); + /// ``` + #[must_use] + pub fn len(&self) -> usize { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.len(), + Variant::Thin(thin) => thin.len(), + } + } + + #[must_use] + pub fn capacity(&self) -> usize { + let v = ManuallyDrop::new(self.0.get()); + match &*v { + Variant::Fat(fat) => fat.capacity(), + Variant::Thin(thin) => thin.capacity(), + } + } + + #[must_use] + 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(), + } + } + + /// 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()); + /// ``` + #[must_use] + pub fn is_unique(&self) -> bool { + self.count().is_unique() + } + + 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()); + 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) + } + } + Variant::Thin(thin) => { + let _ = thin.mutate(); + let handle = unsafe { thin.handle().extend_lifetime() }; + Variant::Thin(handle) + } + }; + RefMut(r, self) + } + + 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, self) + } + + #[must_use] + 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, self)) + } else { + None + } + } + + #[must_use] + pub fn try_clone(&self) -> Option { + if self.count().incr() == UpdateResult::Done { + Some(Self(self.0)) + } else { + None + } + } + + fn count(&self) -> &B { + self.0.count() + } + + #[must_use] + 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) + }) + } + + #[must_use] + 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() + } +} + +impl Drop for SmartVec { + fn drop(&mut self) { + let _ = self.0.get(); + } +} + +#[must_use] +pub struct RefMut<'a, T, B: Backend>( + Variant<&'a mut Vec, Handle<'a, ThinVec>>, + &'a mut SmartVec, +); + +impl Drop for RefMut<'_, T, B> { + fn drop(&mut self) { + match &self.0 { + Variant::Fat(_) => {} + Variant::Thin(thin) => { + // update the thin vector pointer in the smart vector + self.1 .0 = TaggedSmart::from_thin_ptr(thin.0); + } + } + } +} + +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_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().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 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(), + 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), + } + } + + #[must_use] + pub fn split_off(&mut self, at: usize) -> SmartVec + where + B: 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)) + } + } + } + + #[must_use] + 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) { + match &mut self.0 { + Variant::Fat(fat) => fat.extend(iter), + Variant::Thin(thin) => thin.extend_iter(iter), + } + } +} + +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/smart/tests.rs b/src/vecs/smart/tests.rs new file mode 100644 index 00000000..6cc3fb5c --- /dev/null +++ b/src/vecs/smart/tests.rs @@ -0,0 +1,249 @@ +use alloc::vec; +use alloc::vec::Vec; + +use super::SmartVec; +use crate::backend::PanickyUnique; +use crate::smart::Smart; +use crate::{smart_thin_vec, Arc, Unique}; + +#[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 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]); + 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); +} + +#[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()); +} + +#[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!(w.is_thin()); + 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!(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] +#[should_panic(expected = "count overflow")] +fn clone_panic() { + let v = SmartVec::::new(); + let _w = v.clone(); +} + +#[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()); +} + +#[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 84ee106b..2064ca59 100644 --- a/src/vecs/smart_thin.rs +++ b/src/vecs/smart_thin.rs @@ -48,6 +48,7 @@ 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)] @@ -70,19 +71,31 @@ mod tests; #[macro_export] macro_rules! smart_thin_vec { - [ $t:ty : $($rest:tt)* ] => { + [ $($rest:expr),* $(,)? ] => { + 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; $crate::vecs::smart_thin::SmartThinVec::<_, $t>::from( - thin_vec![ $( $rest )* ] + thin_vec![ $( $rest ),* ] ) } }; - [ $($rest:tt)* ] => { - smart_thin_vec![$crate::Arc : $($rest)*] - } - + [ $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 @@ -102,7 +115,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; @@ -113,6 +126,14 @@ impl Deref for SmartThinVec { } impl SmartThinVec { + pub(crate) const 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 @@ -149,7 +170,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() } @@ -245,6 +266,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, @@ -253,6 +310,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 @@ -309,6 +374,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`. @@ -341,6 +416,59 @@ 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, + { + self.try_clone().unwrap_or_else(|| { + let thin_vec: ThinVec<_, _> = self.as_thin_vec().fresh_clone(); + unsafe { Self::from_thin_vec_unchecked(thin_vec) } + }) + } + + /// 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.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()); + /// ``` + #[must_use] + pub fn force_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 @@ -364,6 +492,10 @@ impl SmartThinVec { Err(self) } } + + pub(crate) unsafe fn handle(&mut self) -> Handle> { + unsafe { self.as_mut_unchecked().handle() } + } } impl Clone for SmartThinVec> { diff --git a/src/vecs/smart_thin/tests.rs b/src/vecs/smart_thin/tests.rs index 651dba3f..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); @@ -158,6 +200,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]; @@ -271,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); +} diff --git a/src/vecs/thin.rs b/src/vecs/thin.rs index 115c599c..068930de 100644 --- a/src/vecs/thin.rs +++ b/src/vecs/thin.rs @@ -8,14 +8,14 @@ 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}; 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, Handle, RangeError, }; use crate::{common, macros}; @@ -74,8 +74,8 @@ macro_rules! thin_vec { /// A shared thin vector's header. #[derive(Clone, Copy, Debug)] #[repr(C)] -pub(super) struct Header { - prefix: P, +pub(crate) struct Header { + pub(crate) prefix: P, cap: usize, len: usize, phantom: PhantomData, @@ -93,7 +93,7 @@ pub(super) struct Header { /// /// [`Vec`]: alloc::vec::Vec #[repr(transparent)] -pub struct ThinVec(pub(super) NonNull>); +pub struct ThinVec(pub(crate) NonNull>); impl ThinVec where @@ -309,6 +309,10 @@ where other } + + pub(crate) const unsafe fn handle(&self) -> Handle { + unsafe { Handle::new(ManuallyDrop::new(Self(self.0))) } + } } impl ThinVec { @@ -725,7 +729,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; @@ -959,7 +963,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(); @@ -1157,7 +1161,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; @@ -1307,6 +1311,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::() {