use std::{
alloc::Layout,
cell::Cell,
hash::{BuildHasher, Hash},
mem::MaybeUninit,
ptr::NonNull,
slice,
};
use crate::{Allocator, Box, HashMap, Vec};
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum CloneInSemanticIds {
With = 0,
Without = u32::MAX,
}
pub trait CloneIn<'new_alloc>: Sized {
type Cloned;
#[expect(clippy::inline_always)]
#[inline(always)]
fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
self.clone_in_impl(CloneInSemanticIds::Without, allocator)
}
#[expect(clippy::inline_always)]
#[inline(always)]
fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
self.clone_in_impl(CloneInSemanticIds::With, allocator)
}
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned;
}
impl<'alloc, T, C> CloneIn<'alloc> for Option<T>
where
T: CloneIn<'alloc, Cloned = C>,
{
type Cloned = Option<C>;
#[inline]
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'alloc Allocator,
) -> Self::Cloned {
self.as_ref().map(|it| it.clone_in_impl(with_semantic_ids, allocator))
}
}
impl<'new_alloc, T, C> CloneIn<'new_alloc> for Box<'_, T>
where
T: CloneIn<'new_alloc, Cloned = C>,
{
type Cloned = Box<'new_alloc, C>;
#[inline]
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned {
Box::new_in(self.as_ref().clone_in_impl(with_semantic_ids, allocator), &allocator)
}
}
impl<'new_alloc, T, C> CloneIn<'new_alloc> for Box<'_, [T]>
where
T: CloneIn<'new_alloc, Cloned = C>,
{
type Cloned = Box<'new_alloc, [C]>;
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned {
let ptr = clone_slice_in(self.as_ref(), with_semantic_ids, allocator);
unsafe { Box::from_non_null(ptr) }
}
}
impl<'new_alloc, T, C> CloneIn<'new_alloc> for Vec<'_, T>
where
T: CloneIn<'new_alloc, Cloned = C>,
C: 'new_alloc,
{
type Cloned = Vec<'new_alloc, C>;
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned {
let slice = self.as_slice();
if slice.is_empty() {
return Vec::new_in(&allocator);
}
let ptr = clone_slice_in(slice, with_semantic_ids, allocator);
let len = slice.len();
unsafe { Vec::from_raw_parts_in(ptr.cast::<C>(), len, len, &allocator) }
}
}
#[inline]
fn clone_slice_in<'new_alloc, T, C>(
slice: &[T],
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> NonNull<[C]>
where
T: CloneIn<'new_alloc, Cloned = C>,
{
const {
assert!(
size_of::<C>() == size_of::<T>() && align_of::<C>() == align_of::<T>(),
"Size and alignment of `T` and `<T as CloneIn>::Cloned` must be the same"
);
}
let layout = Layout::for_value(slice);
let dst_ptr = allocator.alloc_layout(layout).cast::<MaybeUninit<C>>().as_ptr();
let dst = unsafe { slice::from_raw_parts_mut(dst_ptr, slice.len()) };
clone_between_slices(slice, dst, with_semantic_ids, allocator);
let new_slice = unsafe { dst.assume_init_mut() };
NonNull::from(new_slice)
}
#[expect(clippy::inline_always)]
#[inline(always)] fn clone_between_slices<'new_alloc, T, C>(
src: &[T],
dst: &mut [MaybeUninit<C>],
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) where
T: CloneIn<'new_alloc, Cloned = C>,
{
for (src_item, dst_item) in src.iter().zip(dst.iter_mut()) {
dst_item.write(src_item.clone_in_impl(with_semantic_ids, allocator));
}
}
impl<'new_alloc, K, V, CK, CV, S> CloneIn<'new_alloc> for HashMap<'_, K, V, S>
where
K: CloneIn<'new_alloc, Cloned = CK>,
V: CloneIn<'new_alloc, Cloned = CV>,
CK: Hash + Eq,
S: Default + BuildHasher,
{
type Cloned = HashMap<'new_alloc, CK, CV, S>;
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned {
let mut cloned = HashMap::with_capacity_in(self.len(), allocator);
for (key, value) in self {
cloned.insert(
key.clone_in_impl(with_semantic_ids, allocator),
value.clone_in_impl(with_semantic_ids, allocator),
);
}
cloned
}
}
impl<'alloc, T, C> CloneIn<'alloc> for Cell<T>
where
T: Copy + CloneIn<'alloc, Cloned = C>,
{
type Cloned = Cell<C>;
#[inline]
fn clone_in_impl(
&self,
with_semantic_ids: CloneInSemanticIds,
allocator: &'alloc Allocator,
) -> Self::Cloned {
Cell::new(self.get().clone_in_impl(with_semantic_ids, allocator))
}
}
impl<'new_alloc> CloneIn<'new_alloc> for &str {
type Cloned = &'new_alloc str;
fn clone_in_impl(
&self,
_with_semantic_ids: CloneInSemanticIds,
allocator: &'new_alloc Allocator,
) -> Self::Cloned {
allocator.alloc_str(self)
}
}
macro_rules! impl_clone_in {
($($t:ty)*) => {
$(
impl<'alloc> CloneIn<'alloc> for $t {
type Cloned = Self;
#[inline(always)]
fn clone_in_impl(&self, _with_semantic_ids: CloneInSemanticIds, _: &'alloc Allocator) -> Self {
*self
}
}
)*
}
}
impl_clone_in! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f32 f64
bool char
}
#[cfg(test)]
mod test {
use super::{Allocator, CloneIn, HashMap, Vec};
#[test]
fn clone_in_boxed_slice() {
let allocator = Allocator::default();
let allocator = &allocator;
let mut original = Vec::from_iter_in([1, 2, 3], &allocator).into_boxed_slice();
let cloned = original.clone_in(allocator);
let cloned2 = original.clone_in_with_semantic_ids(allocator);
original[1] = 4;
assert_eq!(original.as_ref(), &[1, 4, 3]);
assert_eq!(cloned.as_ref(), &[1, 2, 3]);
assert_eq!(cloned2.as_ref(), &[1, 2, 3]);
}
#[test]
fn clone_in_empty_boxed_slice() {
let allocator = Allocator::default();
let allocator = &allocator;
let original = Vec::<u32>::new_in(&allocator).into_boxed_slice();
let cloned = original.clone_in(allocator);
let cloned2 = original.clone_in_with_semantic_ids(allocator);
assert_eq!(cloned.as_ref(), &[] as &[u32]);
assert_eq!(cloned2.as_ref(), &[] as &[u32]);
}
#[test]
fn clone_in_vec() {
let allocator = Allocator::default();
let allocator = &allocator;
let mut original = Vec::with_capacity_in(8, &allocator);
original.extend_from_slice(&[1, 2, 3]);
let cloned = original.clone_in(allocator);
let cloned2 = original.clone_in_with_semantic_ids(allocator);
original[1] = 4;
assert_eq!(original.as_slice(), &[1, 4, 3]);
assert_eq!(cloned.as_slice(), &[1, 2, 3]);
assert_eq!(cloned.capacity(), 3);
assert_eq!(cloned2.as_slice(), &[1, 2, 3]);
assert_eq!(cloned2.capacity(), 3);
}
#[test]
fn clone_in_empty_vec() {
let allocator = Allocator::default();
let allocator = &allocator;
let original = Vec::<u32>::new_in(&allocator);
let cloned = original.clone_in(allocator);
let cloned2 = original.clone_in_with_semantic_ids(allocator);
assert_eq!(cloned.as_slice(), &[] as &[u32]);
assert_eq!(cloned.capacity(), 0);
assert_eq!(cloned2.as_slice(), &[] as &[u32]);
assert_eq!(cloned2.capacity(), 0);
}
#[test]
fn clone_in_hash_map() {
let allocator = Allocator::default();
let mut original: HashMap<'_, &str, &str> = HashMap::with_capacity_in(8, &allocator);
original.extend(&[("x", "xx"), ("y", "yy"), ("z", "zz")]);
let cloned = original.clone_in(&allocator);
let cloned2 = original.clone_in_with_semantic_ids(&allocator);
*original.get_mut("y").unwrap() = "changed";
let mut original_as_vec = original.iter().collect::<std::vec::Vec<_>>();
original_as_vec.sort_unstable();
assert_eq!(original_as_vec, &[(&"x", &"xx"), (&"y", &"changed"), (&"z", &"zz")]);
assert_eq!(cloned.capacity(), 3);
let mut cloned_as_vec = cloned.iter().collect::<std::vec::Vec<_>>();
cloned_as_vec.sort_unstable();
assert_eq!(cloned_as_vec, &[(&"x", &"xx"), (&"y", &"yy"), (&"z", &"zz")]);
assert_eq!(cloned2.capacity(), 3);
let mut cloned2_as_vec = cloned2.iter().collect::<std::vec::Vec<_>>();
cloned2_as_vec.sort_unstable();
assert_eq!(cloned2_as_vec, &[(&"x", &"xx"), (&"y", &"yy"), (&"z", &"zz")]);
}
}