Skip to content
Merged
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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.#*
Cargo.lock
target/
# IDE files
.idea
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ of panicking drop implementations.
- Added `from_bytes_truncating_at_nul` to `CString`
- Added `CString::{into_bytes, into_bytes_with_nul, into_string}`
- Added `pop_front_if` and `pop_back_if` to `Deque`
- Made `Vec::from_array` const.

## [v0.9.2] 2025-11-12

Expand Down
6 changes: 3 additions & 3 deletions src/deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ impl<T, S: VecStorage<T> + ?Sized> DequeInner<T, S> {
// as the two slices combined should be more than `len`.
if len > front.len() {
let begin = len - front.len();
let drop_back = back.get_unchecked_mut(begin..) as *mut _;
let drop_back = core::ptr::from_mut(back.get_unchecked_mut(begin..));

// Self::to_physical_index returns the index `len` units _after_ the front cursor,
// meaning we can use it to find the decremented index for `back` for non-contiguous
Expand All @@ -939,8 +939,8 @@ impl<T, S: VecStorage<T> + ?Sized> DequeInner<T, S> {
} else {
// Otherwise, we know back's entire contents need to be dropped,
// since the desired length never reaches into it.
let drop_back = back as *mut _;
let drop_front = front.get_unchecked_mut(len..) as *mut _;
let drop_back = core::ptr::from_mut(back);
let drop_front = core::ptr::from_mut(front.get_unchecked_mut(len..));

self.back = self.to_physical_index(len);
self.full = false;
Expand Down
80 changes: 79 additions & 1 deletion src/len_type.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use core::{
fmt::{Debug, Display},
mem,
ops::{Add, AddAssign, Sub, SubAssign},
};

#[allow(non_camel_case_types)]
pub enum TypeEnum {
u8,
u16,
u32,
usize,
}

#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

Expand All @@ -27,6 +36,8 @@ pub trait Sealed:
const MAX: Self;
/// The maximum value of this type, as a `usize`.
const MAX_USIZE: usize;
/// This type as an enum.
const TYPE: TypeEnum;

/// The one value of the integer type.
///
Expand Down Expand Up @@ -58,12 +69,13 @@ pub trait Sealed:
}

macro_rules! impl_lentype {
($($(#[$meta:meta])* $LenT:ty),*) => {$(
($($(#[$meta:meta])* $LenT:ident),*) => {$(
$(#[$meta])*
impl Sealed for $LenT {
const ZERO: Self = 0;
const MAX: Self = Self::MAX;
const MAX_USIZE: usize = Self::MAX as _;
const TYPE: TypeEnum = TypeEnum::$LenT;

fn one() -> Self {
1
Expand Down Expand Up @@ -100,3 +112,69 @@ impl_lentype!(
pub const fn check_capacity_fits<LenT: LenType, const N: usize>() {
assert!(LenT::MAX_USIZE >= N, "The capacity is larger than `LenT` can hold, increase the size of `LenT` or reduce the capacity");
}

/// Const cast from [`usize`] to [`LenType`] with `as`.
#[inline]
pub const fn as_len_type<L: LenType>(n: usize) -> L {
// SAFETY: transmute is safe since after cast we cast to the same type.
unsafe {
// ALWAYS compiletime switch.
match L::TYPE {
// transmute_copy, instead of transmute - because `L`
// is a "dependent type".
TypeEnum::u8 => mem::transmute_copy(&(n as u8)),
TypeEnum::u16 => mem::transmute_copy(&(n as u16)),
TypeEnum::u32 => mem::transmute_copy(&(n as u32)),
TypeEnum::usize => mem::transmute_copy(&n),
}
}
}

/// Checked cast to [`LenType`].
///
/// # Panic
///
/// Panics if `n` is outside of `L` range.
#[inline]
pub const fn to_len_type<L: LenType>(n: usize) -> L {
try_to_len_type(n).unwrap()
}

/// Checked cast to [`LenType`].
///
/// Returns `None` if `n` is outside of `L` range.
#[inline]
pub const fn try_to_len_type<L: LenType>(n: usize) -> Option<L> {
if n > L::MAX_USIZE {
return None;
}
Some(as_len_type(n))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_len_cast() {
// 1. Check constness
const {
assert!(to_len_type::<u8>(150) == 150);
assert!(to_len_type::<u16>(15_000) == 15_000);
assert!(to_len_type::<u32>(1_500_000) == 1_500_000);
assert!(to_len_type::<usize>(usize::MAX) == usize::MAX);
}
// 2. Check correctness
fn check<T: LenType>() {
const COUNT: usize = 100;
for i in 0..COUNT {
let n = i * (T::MAX_USIZE / COUNT);
assert_eq!(to_len_type::<T>(n).into_usize(), n);
}
}
check::<u8>();
check::<u16>();
check::<u32>();
check::<usize>();
}
}
2 changes: 1 addition & 1 deletion src/pool/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ mod tests {

let arc = MyArcPool.alloc(Zst4096).ok().unwrap();

let raw = &*arc as *const Zst4096;
let raw = std::ptr::from_ref::<Zst4096>(&*arc);
assert_eq!(0, raw as usize % 4096);
}
}
2 changes: 1 addition & 1 deletion src/pool/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ mod tests {

let boxed = MyBoxPool.alloc(Zst4096).ok().unwrap();

let raw = &*boxed as *const Zst4096;
let raw = std::ptr::from_ref::<Zst4096>(&*boxed);
assert_eq!(0, raw as usize % 4096);
}

Expand Down
2 changes: 1 addition & 1 deletion src/pool/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ mod tests {

let object = MyObjectPool.request().unwrap();

let raw = &*object as *const Zst4096;
let raw = std::ptr::from_ref::<Zst4096>(&*object);
assert_eq!(0, raw as usize % 4096);
}
}
2 changes: 1 addition & 1 deletion src/string/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ impl<LenT: LenType, S: StringStorage + ?Sized> StringInner<LenT, S> {

// Take out two simultaneous borrows. The &mut String won't be accessed
// until iteration is over, in Drop.
let self_ptr = self.as_mut_view() as *mut _;
let self_ptr = core::ptr::from_mut(self.as_mut_view());
// SAFETY: `slice::range` and `is_char_boundary` do the appropriate bounds checks.
let chars_iter = unsafe { self.get_unchecked(start..end) }.chars();

Expand Down
42 changes: 21 additions & 21 deletions src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::{
fmt, hash,
iter::FusedIterator,
marker::PhantomData,
mem::{self, ManuallyDrop, MaybeUninit},
mem::{ManuallyDrop, MaybeUninit},
ops::{self, Range, RangeBounds},
ptr::{self, NonNull},
slice,
Expand All @@ -16,7 +16,7 @@ use core::{
use zeroize::Zeroize;

use crate::{
len_type::{check_capacity_fits, LenType},
len_type::{check_capacity_fits, to_len_type, LenType},
CapacityError,
};

Expand Down Expand Up @@ -355,7 +355,7 @@ impl<T, LenT: LenType, const N: usize> Vec<T, N, LenT> {
///
/// If the length of the provided array is greater than the capacity of the
/// vector a compile-time error will be produced.
pub fn from_array<const M: usize>(src: [T; M]) -> Self {
pub const fn from_array<const M: usize>(src: [T; M]) -> Self {
const {
assert!(N >= M);
}
Expand All @@ -364,26 +364,24 @@ impl<T, LenT: LenType, const N: usize> Vec<T, N, LenT> {
// any Drop code for T.
let src = ManuallyDrop::new(src);

if N == M {
Self {
phantom: PhantomData,
len: LenT::from_usize(N),
// NOTE(unsafe) ManuallyDrop<[T; M]> and [MaybeUninit<T>; N]
// have the same layout when N == M.
buffer: unsafe { mem::transmute_copy(&src) },
}
} else {
let mut v = Self::new();
let len: LenT = to_len_type(M);

for (src_elem, dst_elem) in src.iter().zip(v.buffer.buffer.iter_mut()) {
// NOTE(unsafe) src element is not going to drop as src itself
// is wrapped in a ManuallyDrop.
dst_elem.write(unsafe { ptr::read(src_elem) });
}
let mut v = Self::new();

unsafe { v.set_len(M) };
v
}
// MaybeUninit::deref is non-const, so we just cast to it's internal value.
let src_ptr: *const T = ptr::from_ref(&src).cast();

// Cast from buffer's [MaybeUninit<T>] to [T].
let dst_ptr: *mut T = v.buffer.buffer.as_mut_ptr().cast();

// SAFETY: Move/copy data from src to v's internal buffer.
// * Using src_ptr as `*const T` is safe since src's ManuallyDrop<[T; M]> is transparent.
// * Using dst_ptr as `*mut T` is safe since v's [MaybeUninit<T>; N] is an array of
// transparent types.
unsafe { ptr::copy_nonoverlapping(src_ptr, dst_ptr, M) };
v.len = len;

v
}

/// Returns the contents of the vector as an array of length `M` if the length
Expand Down Expand Up @@ -466,6 +464,8 @@ impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> VecInner<T, LenT, S> {
/// [`mem::forget`], for example), the vector may have lost and leaked
/// elements arbitrarily, including elements outside the range.
///
/// [`mem::forget`]: core::mem::forget
///
/// # Examples
///
/// ```
Expand Down
Loading