Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 0 additions & 6 deletions src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/backend/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AtomicCount, PanicOnOverflow>;
Expand Down
49 changes: 41 additions & 8 deletions src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
}

Expand Down
114 changes: 85 additions & 29 deletions src/bytes/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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::<Borrowed>() - 1;
Expand Down Expand Up @@ -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<B: Backend + Sync> Sync for HipByt<'_, B> {}
unsafe impl<B: Backend + Send> 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<HipByt<_,_>>`
Expand All @@ -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> {
Expand Down Expand Up @@ -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.
Expand All @@ -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 }
Expand All @@ -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 }
Expand Down Expand Up @@ -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::<Pivot>() == size_of::<WordView>());

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::<Pivot>() - size_of::<NonZeroU8>());
assert!(offset_of!(WordView, tag) == size_of::<WordView>() - size_of::<usize>());
}
}

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() },
}
Expand Down Expand Up @@ -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<P>(vec: ThinVec<u8, P>) -> 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<u8>) -> Self {
let allocated = Allocated::new(vec);
let allocated = Allocated::from_vec(vec);
Self::from_allocated(allocated)
}

Expand Down Expand Up @@ -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,
)
}
}
};
Expand Down Expand Up @@ -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,
)
}
}
};
Expand All @@ -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.
Expand All @@ -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;
}
}
Expand All @@ -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)
}
Expand All @@ -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 };
Expand All @@ -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 };
Expand Down Expand Up @@ -567,8 +621,10 @@ impl<B: Backend> 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,
)
}
}
}
Expand Down
Loading