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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
.#*
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 @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- 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
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>();
}
}
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