Skip to main content

oxc_allocator/
clone_in.rs

1use std::{
2    alloc::Layout,
3    cell::Cell,
4    hash::{BuildHasher, Hash},
5    ptr::NonNull,
6    slice,
7};
8
9use crate::{Allocator, Box, HashMap, Vec};
10
11/// A trait to explicitly clone an object into an arena allocator.
12///
13/// As a convention `Cloned` associated type should always be the same as `Self`,
14/// It'd only differ in the lifetime, Here's an example:
15///
16/// ```
17/// # use oxc_allocator::{Allocator, CloneIn, Vec};
18/// # struct Struct<'a> {a: Vec<'a, u8>, b: u8}
19///
20/// impl<'old_alloc, 'new_alloc> CloneIn<'new_alloc> for Struct<'old_alloc> {
21///     type Cloned = Struct<'new_alloc>;
22///     fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
23///         Struct { a: self.a.clone_in(allocator), b: self.b.clone_in(allocator) }
24///     }
25/// }
26/// ```
27///
28/// Implementations of this trait on non-allocated items usually short-circuit to `Clone::clone`;
29/// However, it **isn't** guaranteed.
30///
31pub trait CloneIn<'new_alloc>: Sized {
32    /// The type of the cloned object.
33    ///
34    /// This should always be `Self` with a different lifetime.
35    type Cloned;
36
37    /// Clone `self` into the given `allocator`. `allocator` may be the same one
38    /// that `self` is already in.
39    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned;
40
41    /// Almost identical as `clone_in`, but for some special type, it will also clone the semantic ids.
42    /// Please use this method only if you make sure semantic info is synced with the ast node.
43    #[inline]
44    fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
45        self.clone_in(allocator)
46    }
47}
48
49impl<'alloc, T, C> CloneIn<'alloc> for Option<T>
50where
51    T: CloneIn<'alloc, Cloned = C>,
52{
53    type Cloned = Option<C>;
54
55    fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned {
56        self.as_ref().map(|it| it.clone_in(allocator))
57    }
58
59    fn clone_in_with_semantic_ids(&self, allocator: &'alloc Allocator) -> Self::Cloned {
60        self.as_ref().map(|it| it.clone_in_with_semantic_ids(allocator))
61    }
62}
63
64impl<'new_alloc, T, C> CloneIn<'new_alloc> for Box<'_, T>
65where
66    T: CloneIn<'new_alloc, Cloned = C>,
67{
68    type Cloned = Box<'new_alloc, C>;
69
70    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
71        Box::new_in(self.as_ref().clone_in(allocator), allocator)
72    }
73
74    fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
75        Box::new_in(self.as_ref().clone_in_with_semantic_ids(allocator), allocator)
76    }
77}
78
79impl<'new_alloc, T, C> CloneIn<'new_alloc> for Box<'_, [T]>
80where
81    T: CloneIn<'new_alloc, Cloned = C>,
82{
83    type Cloned = Box<'new_alloc, [C]>;
84
85    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
86        let slice = self.as_ref();
87
88        // Compile-time check that `T` and `C` have identical size and alignment - which they always will
89        // with intended usage that `T` and `C` are same types, just with different lifetimes.
90        // This guarantees that layout of clone is same as layout of `slice`,
91        // so we can create `Layout` with `for_value`, which has no runtime checks.
92        const {
93            assert!(
94                size_of::<C>() == size_of::<T>() && align_of::<C>() == align_of::<T>(),
95                "Size and alignment of `T` and `<T as CloneIn>::Cloned` must be the same"
96            );
97        }
98        let layout = Layout::for_value(slice);
99
100        let dst_ptr = allocator.alloc_layout(layout).cast::<C>();
101
102        // SAFETY: We allocated space for `slice.len()` items of type `C`, starting at `dst_ptr`.
103        // Therefore, writing `slice.len()` elements to that memory region is safe.
104        // `C` isn't `Drop`, and allocation is in the arena, so we don't need to worry about a panic
105        // in the loop - can't lead to a memory leak.
106        unsafe {
107            let mut ptr = dst_ptr;
108            for item in slice {
109                ptr.write(item.clone_in(allocator));
110                ptr = ptr.add(1);
111            }
112        }
113
114        // SAFETY: We just initialized `slice.len()` x `C`s, starting at `dst_ptr`
115        let new_slice = unsafe { slice::from_raw_parts_mut(dst_ptr.as_ptr(), slice.len()) };
116        // SAFETY: `NonNull::from(new_slice)` produces a valid pointer. The data is in the arena.
117        // Lifetime of returned `Box` matches the `Allocator` the data was allocated in.
118        unsafe { Box::from_non_null(NonNull::from(new_slice)) }
119    }
120
121    fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
122        let slice = self.as_ref();
123
124        // Compile-time check that `T` and `C` have identical size and alignment - which they always will
125        // with intended usage that `T` and `C` are same types, just with different lifetimes.
126        // This guarantees that layout of clone is same as layout of `slice`,
127        // so we can create `Layout` with `for_value`, which has no runtime checks.
128        const {
129            assert!(
130                size_of::<C>() == size_of::<T>() && align_of::<C>() == align_of::<T>(),
131                "Size and alignment of `T` and `<T as CloneIn>::Cloned` must be the same"
132            );
133        }
134        let layout = Layout::for_value(slice);
135
136        let dst_ptr = allocator.alloc_layout(layout).cast::<C>();
137
138        // SAFETY: We allocated space for `slice.len()` items of type `C`, starting at `dst_ptr`.
139        // Therefore, writing `slice.len()` elements to that memory region is safe.
140        // `C` isn't `Drop`, and allocation is in the arena, so we don't need to worry about a panic
141        // in the loop - can't lead to a memory leak.
142        unsafe {
143            let mut ptr = dst_ptr;
144            for item in slice {
145                ptr.write(item.clone_in_with_semantic_ids(allocator));
146                ptr = ptr.add(1);
147            }
148        }
149
150        // SAFETY: We just initialized `slice.len()` x `C`s, starting at `dst_ptr`
151        let new_slice = unsafe { slice::from_raw_parts_mut(dst_ptr.as_ptr(), slice.len()) };
152        // SAFETY: `NonNull::from(new_slice)` produces a valid pointer. The data is in the arena.
153        // Lifetime of returned `Box` matches the `Allocator` the data was allocated in.
154        unsafe { Box::from_non_null(NonNull::from(new_slice)) }
155    }
156}
157
158impl<'new_alloc, T, C> CloneIn<'new_alloc> for Vec<'_, T>
159where
160    T: CloneIn<'new_alloc, Cloned = C>,
161    // TODO: This lifetime bound possibly shouldn't be required.
162    // https://github.com/oxc-project/oxc/pull/9656#issuecomment-2719762898
163    C: 'new_alloc,
164{
165    type Cloned = Vec<'new_alloc, C>;
166
167    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
168        // The implementation below is equivalent to:
169        // `Vec::from_iter_in(self.iter().map(|it| it.clone_in(allocator)), allocator)`
170        // But `Vec::from_iter_in` is inefficient because it performs a bounds check for each item.
171        // This is unnecessary in this case as we know the length of the slice with certainty.
172        // This implementation takes advantage of that invariant, and skips those checks.
173
174        let slice = self.as_slice();
175
176        let mut vec = Vec::<C>::with_capacity_in(slice.len(), allocator);
177
178        // SAFETY: We allocated capacity for `slice.len()` elements in `vec`.
179        // Therefore, writing `slice.len()` elements to that memory region is safe.
180        // `C` and `Vec` aren't `Drop`, and allocation is in the arena, so we don't need to worry about
181        // a panic in this loop - can't lead to a memory leak. We just set length at the end.
182        unsafe {
183            let mut ptr = vec.as_mut_ptr();
184            for item in slice {
185                ptr.write(item.clone_in(allocator));
186                ptr = ptr.add(1);
187            }
188            vec.set_len(slice.len());
189        }
190
191        vec
192    }
193
194    fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
195        let slice = self.as_slice();
196
197        let mut vec = Vec::<C>::with_capacity_in(slice.len(), allocator);
198
199        // SAFETY: We allocated capacity for `slice.len()` elements in `vec`.
200        // Therefore, writing `slice.len()` elements to that memory region is safe.
201        // `C` and `Vec` aren't `Drop`, and allocation is in the arena, so we don't need to worry about
202        // a panic in this loop - can't lead to a memory leak. We just set length at the end.
203        unsafe {
204            let mut ptr = vec.as_mut_ptr();
205            for item in slice {
206                ptr.write(item.clone_in_with_semantic_ids(allocator));
207                ptr = ptr.add(1);
208            }
209            vec.set_len(slice.len());
210        }
211
212        vec
213    }
214}
215
216impl<'new_alloc, K, V, CK, CV, S> CloneIn<'new_alloc> for HashMap<'_, K, V, S>
217where
218    K: CloneIn<'new_alloc, Cloned = CK>,
219    V: CloneIn<'new_alloc, Cloned = CV>,
220    CK: Hash + Eq,
221    S: Default + BuildHasher,
222{
223    type Cloned = HashMap<'new_alloc, CK, CV, S>;
224
225    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
226        // Keys in original hash map are guaranteed to be unique.
227        // Unfortunately, we have no static guarantee that `CloneIn` maintains that uniqueness
228        // - original keys (`K`) are guaranteed unique, but cloned keys (`CK`) might not be.
229        // If we did have that guarantee, we could use the faster `insert_unique_unchecked` here.
230        // `hashbrown::HashMap` also has a faster cloning method in its `Clone` implementation,
231        // but those APIs are not exposed, and `Clone` doesn't support custom allocators.
232        // So sadly this is a lot slower than it could be, especially for `Copy` types.
233        let mut cloned = HashMap::with_capacity_in(self.len(), allocator);
234        for (key, value) in self {
235            cloned.insert(key.clone_in(allocator), value.clone_in(allocator));
236        }
237        cloned
238    }
239
240    fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
241        let mut cloned = HashMap::with_capacity_in(self.len(), allocator);
242        for (key, value) in self {
243            cloned.insert(
244                key.clone_in_with_semantic_ids(allocator),
245                value.clone_in_with_semantic_ids(allocator),
246            );
247        }
248        cloned
249    }
250}
251
252impl<'alloc, T: Copy> CloneIn<'alloc> for Cell<T> {
253    type Cloned = Cell<T>;
254
255    fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned {
256        Cell::new(self.get())
257    }
258}
259
260impl<'new_alloc> CloneIn<'new_alloc> for &str {
261    type Cloned = &'new_alloc str;
262
263    fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
264        allocator.alloc_str(self)
265    }
266}
267
268macro_rules! impl_clone_in {
269    ($($t:ty)*) => {
270        $(
271            impl<'alloc> CloneIn<'alloc> for $t {
272                type Cloned = Self;
273                #[inline(always)]
274                fn clone_in(&self, _: &'alloc Allocator) -> Self {
275                    *self
276                }
277            }
278        )*
279    }
280}
281
282impl_clone_in! {
283    usize u8 u16 u32 u64 u128
284    isize i8 i16 i32 i64 i128
285    f32 f64
286    bool char
287}
288
289#[cfg(test)]
290mod test {
291    use super::{Allocator, CloneIn, HashMap, Vec};
292
293    #[test]
294    fn clone_in_boxed_slice() {
295        let allocator = Allocator::default();
296
297        let mut original = Vec::from_iter_in([1, 2, 3], &allocator).into_boxed_slice();
298
299        let cloned = original.clone_in(&allocator);
300        let cloned2 = original.clone_in_with_semantic_ids(&allocator);
301        original[1] = 4;
302
303        assert_eq!(original.as_ref(), &[1, 4, 3]);
304        assert_eq!(cloned.as_ref(), &[1, 2, 3]);
305        assert_eq!(cloned2.as_ref(), &[1, 2, 3]);
306    }
307
308    #[test]
309    fn clone_in_vec() {
310        let allocator = Allocator::default();
311
312        let mut original = Vec::with_capacity_in(8, &allocator);
313        original.extend_from_slice(&[1, 2, 3]);
314
315        let cloned = original.clone_in(&allocator);
316        let cloned2 = original.clone_in_with_semantic_ids(&allocator);
317        original[1] = 4;
318
319        assert_eq!(original.as_slice(), &[1, 4, 3]);
320        assert_eq!(cloned.as_slice(), &[1, 2, 3]);
321        assert_eq!(cloned.capacity(), 3);
322        assert_eq!(cloned2.as_slice(), &[1, 2, 3]);
323        assert_eq!(cloned2.capacity(), 3);
324    }
325
326    #[test]
327    fn clone_in_hash_map() {
328        let allocator = Allocator::default();
329
330        let mut original: HashMap<'_, &str, &str> = HashMap::with_capacity_in(8, &allocator);
331        original.extend(&[("x", "xx"), ("y", "yy"), ("z", "zz")]);
332
333        let cloned = original.clone_in(&allocator);
334        let cloned2 = original.clone_in_with_semantic_ids(&allocator);
335        *original.get_mut("y").unwrap() = "changed";
336
337        let mut original_as_vec = original.iter().collect::<std::vec::Vec<_>>();
338        original_as_vec.sort_unstable();
339        assert_eq!(original_as_vec, &[(&"x", &"xx"), (&"y", &"changed"), (&"z", &"zz")]);
340
341        assert_eq!(cloned.capacity(), 3);
342        let mut cloned_as_vec = cloned.iter().collect::<std::vec::Vec<_>>();
343        cloned_as_vec.sort_unstable();
344        assert_eq!(cloned_as_vec, &[(&"x", &"xx"), (&"y", &"yy"), (&"z", &"zz")]);
345
346        assert_eq!(cloned2.capacity(), 3);
347        let mut cloned2_as_vec = cloned2.iter().collect::<std::vec::Vec<_>>();
348        cloned2_as_vec.sort_unstable();
349        assert_eq!(cloned2_as_vec, &[(&"x", &"xx"), (&"y", &"yy"), (&"z", &"zz")]);
350    }
351}