rune_alloc/
raw_vec.rs

1use core::alloc::{Layout, LayoutError};
2use core::cmp;
3use core::mem::{self, ManuallyDrop, MaybeUninit};
4use core::slice;
5
6use crate::alloc::SizedTypeProperties;
7use crate::alloc::{AllocError, Allocator, Global};
8use crate::boxed::Box;
9use crate::error::Error;
10use crate::ptr::{self, NonNull, Unique};
11
12enum AllocInit {
13    /// The contents of the new memory are uninitialized.
14    Uninitialized,
15    /// The new memory is guaranteed to be zeroed.
16    Zeroed,
17}
18
19/// A low-level utility for more ergonomically allocating, reallocating, and deallocating
20/// a buffer of memory on the heap without having to worry about all the corner cases
21/// involved. This type is excellent for building your own data structures like Vec and VecDeque.
22/// In particular:
23///
24/// * Produces `Unique::dangling()` on zero-sized types.
25/// * Produces `Unique::dangling()` on zero-length allocations.
26/// * Avoids freeing `Unique::dangling()`.
27/// * Catches all overflows in capacity computations (promotes them to "capacity overflow" panics).
28/// * Guards against 32-bit systems allocating more than isize::MAX bytes.
29/// * Guards against overflowing your length.
30/// * Calls `handle_alloc_error` for fallible allocations.
31/// * Contains a `ptr::Unique` and thus endows the user with all related benefits.
32/// * Uses the excess returned from the allocator to use the largest available capacity.
33///
34/// This type does not in anyway inspect the memory that it manages. When dropped it *will*
35/// free its memory, but it *won't* try to drop its contents. It is up to the user of `RawVec`
36/// to handle the actual things *stored* inside of a `RawVec`.
37///
38/// Note that the excess of a zero-sized types is always infinite, so `capacity()` always returns
39/// `usize::MAX`. This means that you need to be careful when round-tripping this type with a
40/// `Box<[T]>`, since `capacity()` won't yield the length.
41#[allow(missing_debug_implementations)]
42pub(crate) struct RawVec<T, A: Allocator = Global> {
43    ptr: Unique<T>,
44    cap: usize,
45    alloc: A,
46}
47
48impl<T> RawVec<T, Global> {
49    /// HACK(Centril): This exists because stable `const fn` can only call
50    /// stable `const fn`, so they cannot call `Self::new()`.
51    ///
52    /// If you change `RawVec<T>::new` or dependencies, please take care to not
53    /// introduce anything that would truly const-call something unstable.
54    pub const NEW: Self = Self::new();
55
56    /// Creates the biggest possible `RawVec` (on the system heap)
57    /// without allocating. If `T` has positive size, then this makes a
58    /// `RawVec` with capacity `0`. If `T` is zero-sized, then it makes a
59    /// `RawVec` with capacity `usize::MAX`. Useful for implementing
60    /// delayed allocation.
61    #[must_use]
62    pub const fn new() -> Self {
63        Self::new_in(Global)
64    }
65}
66
67impl<T, A: Allocator> RawVec<T, A> {
68    // Tiny Vecs are dumb. Skip to:
69    // - 8 if the element size is 1, because any heap allocators is likely
70    //   to round up a request of less than 8 bytes to at least 8 bytes.
71    // - 4 if elements are moderate-sized (<= 1 KiB).
72    // - 1 otherwise, to avoid wasting too much space for very short Vecs.
73    pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
74        8
75    } else if mem::size_of::<T>() <= 1024 {
76        4
77    } else {
78        1
79    };
80
81    /// Like `new`, but parameterized over the choice of allocator for
82    /// the returned `RawVec`.
83    pub const fn new_in(alloc: A) -> Self {
84        // `cap: 0` means "unallocated". zero-sized types are ignored.
85        Self {
86            ptr: Unique::dangling(),
87            cap: 0,
88            alloc,
89        }
90    }
91
92    /// Like `with_capacity`, but parameterized over the choice of
93    /// allocator for the returned `RawVec`.
94    #[inline]
95    pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, Error> {
96        Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
97    }
98
99    /// Like `with_capacity_zeroed`, but parameterized over the choice
100    /// of allocator for the returned `RawVec`.
101    #[inline]
102    pub(crate) fn try_with_capacity_zeroed_in(capacity: usize, alloc: A) -> Result<Self, Error> {
103        Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc)
104    }
105
106    /// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
107    ///
108    /// Note that this will correctly reconstitute any `cap` changes
109    /// that may have been performed. (See description of type for details.)
110    ///
111    /// # Safety
112    ///
113    /// * `len` must be greater than or equal to the most recently requested capacity, and
114    /// * `len` must be less than or equal to `self.capacity()`.
115    ///
116    /// Note, that the requested capacity and `self.capacity()` could differ, as
117    /// an allocator could overallocate and return a greater memory block than requested.
118    pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit<T>], A> {
119        // Sanity-check one half of the safety requirement (we cannot check the other half).
120        debug_assert!(
121            len <= self.capacity(),
122            "`len` must be smaller than or equal to `self.capacity()`"
123        );
124
125        let me = ManuallyDrop::new(self);
126        unsafe {
127            let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit<T>, len);
128            Box::from_raw_in(slice, ptr::read(&me.alloc))
129        }
130    }
131
132    fn try_allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Result<Self, Error> {
133        // Don't allocate here because `Drop` will not deallocate when `capacity` is 0.
134        if T::IS_ZST || capacity == 0 {
135            Ok(Self::new_in(alloc))
136        } else {
137            // We avoid `unwrap_or_else` here because it bloats the amount of
138            // LLVM IR generated.
139            let layout = match Layout::array::<T>(capacity) {
140                Ok(layout) => layout,
141                Err(_) => return Err(Error::CapacityOverflow),
142            };
143            match alloc_guard(layout.size()) {
144                Ok(_) => {}
145                Err(_) => return Err(Error::CapacityOverflow),
146            }
147            let ptr = match init {
148                AllocInit::Uninitialized => alloc.allocate(layout)?,
149                AllocInit::Zeroed => alloc.allocate_zeroed(layout)?,
150            };
151
152            // Allocators currently return a `NonNull<[u8]>` whose length
153            // matches the size requested. If that ever changes, the capacity
154            // here should change to `ptr.len() / mem::size_of::<T>()`.
155            Ok(Self {
156                ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) },
157                cap: capacity,
158                alloc,
159            })
160        }
161    }
162
163    /// Reconstitutes a `RawVec` from a pointer, capacity, and allocator.
164    ///
165    /// # Safety
166    ///
167    /// The `ptr` must be allocated (via the given allocator `alloc`), and with the given
168    /// `capacity`.
169    /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit
170    /// systems). ZST vectors may have a capacity up to `usize::MAX`.
171    /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is
172    /// guaranteed.
173    #[inline]
174    pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self {
175        Self {
176            ptr: unsafe { Unique::new_unchecked(ptr) },
177            cap: capacity,
178            alloc,
179        }
180    }
181
182    /// Gets a raw pointer to the start of the allocation. Note that this is
183    /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must
184    /// be careful.
185    #[inline]
186    pub(crate) fn ptr(&self) -> *mut T {
187        self.ptr.as_ptr()
188    }
189
190    /// Gets the capacity of the allocation.
191    ///
192    /// This will always be `usize::MAX` if `T` is zero-sized.
193    #[inline(always)]
194    pub(crate) fn capacity(&self) -> usize {
195        if T::IS_ZST {
196            usize::MAX
197        } else {
198            self.cap
199        }
200    }
201
202    /// Returns a shared reference to the allocator backing this `RawVec`.
203    pub(crate) fn allocator(&self) -> &A {
204        &self.alloc
205    }
206
207    fn current_memory(&self) -> Option<(NonNull<u8>, Layout)> {
208        if T::IS_ZST || self.cap == 0 {
209            None
210        } else {
211            // We could use Layout::array here which ensures the absence of isize and usize overflows
212            // and could hypothetically handle differences between stride and size, but this memory
213            // has already been allocated so we know it can't overflow and currently rust does not
214            // support such types. So we can do better by skipping some checks and avoid an unwrap.
215            assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0);
216
217            unsafe {
218                let align = mem::align_of::<T>();
219                let size = mem::size_of::<T>().wrapping_mul(self.cap);
220                let layout = Layout::from_size_align_unchecked(size, align);
221                Some((self.ptr.cast().into(), layout))
222            }
223        }
224    }
225
226    /// Ensures that the buffer contains at least enough space to hold `len +
227    /// additional` elements. If it doesn't already have enough capacity, will
228    /// reallocate enough space plus comfortable slack space to get amortized
229    /// *O*(1) behavior. Will limit this behavior if it would needlessly cause
230    /// itself to panic.
231    ///
232    /// If `len` exceeds `self.capacity()`, this may fail to actually allocate
233    /// the requested space. This is not really unsafe, but the unsafe
234    /// code *you* write that relies on the behavior of this function may break.
235    ///
236    /// This is ideal for implementing a bulk-push operation like `extend`.
237    ///
238    /// # Panics
239    ///
240    /// Panics if the new capacity exceeds `isize::MAX` bytes.
241    ///
242    /// # Aborts
243    ///
244    /// Aborts on OOM.
245    pub(crate) fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), Error> {
246        if self.needs_to_grow(len, additional) {
247            self.grow_amortized(len, additional)?;
248        }
249
250        Ok(())
251    }
252
253    /// A specialized version of `reserve()` used only by the hot and
254    /// oft-instantiated `Vec::push()`, which does its own capacity check.
255    pub(crate) fn try_reserve_for_push(&mut self, len: usize) -> Result<(), Error> {
256        self.grow_amortized(len, 1)
257    }
258
259    /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
260    pub(crate) fn try_reserve_exact(&mut self, len: usize, additional: usize) -> Result<(), Error> {
261        if self.needs_to_grow(len, additional) {
262            self.grow_exact(len, additional)
263        } else {
264            Ok(())
265        }
266    }
267
268    /// Shrinks the buffer down to the specified capacity. If the given amount
269    /// is 0, actually completely deallocates.
270    ///
271    /// # Aborts
272    ///
273    /// Aborts on OOM.
274    pub(crate) fn try_shrink_to_fit(&mut self, cap: usize) -> Result<(), Error> {
275        self.shrink(cap)
276    }
277}
278
279impl<T, A: Allocator> RawVec<T, A> {
280    /// Returns if the buffer needs to grow to fulfill the needed extra capacity.
281    /// Mainly used to make inlining reserve-calls possible without inlining `grow`.
282    fn needs_to_grow(&self, len: usize, additional: usize) -> bool {
283        additional > self.capacity().wrapping_sub(len)
284    }
285
286    fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) {
287        // Allocators currently return a `NonNull<[u8]>` whose length matches
288        // the size requested. If that ever changes, the capacity here should
289        // change to `ptr.len() / mem::size_of::<T>()`.
290        self.ptr = unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) };
291        self.cap = cap;
292    }
293
294    // This method is usually instantiated many times. So we want it to be as
295    // small as possible, to improve compile times. But we also want as much of
296    // its contents to be statically computable as possible, to make the
297    // generated code run faster. Therefore, this method is carefully written
298    // so that all of the code that depends on `T` is within it, while as much
299    // of the code that doesn't depend on `T` as possible is in functions that
300    // are non-generic over `T`.
301    fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), Error> {
302        // This is ensured by the calling contexts.
303        debug_assert!(additional > 0);
304
305        if T::IS_ZST {
306            // Since we return a capacity of `usize::MAX` when `elem_size` is
307            // 0, getting to here necessarily means the `RawVec` is overfull.
308            return Err(Error::CapacityOverflow);
309        }
310
311        // Nothing we can really do about these checks, sadly.
312        let required_cap = len.checked_add(additional).ok_or(Error::CapacityOverflow)?;
313
314        // This guarantees exponential growth. The doubling cannot overflow
315        // because `cap <= isize::MAX` and the type of `cap` is `usize`.
316        let cap = cmp::max(self.cap * 2, required_cap);
317        let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
318
319        let new_layout = Layout::array::<T>(cap);
320
321        // `finish_grow` is non-generic over `T`.
322        let ptr = finish_grow(new_layout, self.current_memory(), &self.alloc)?;
323        self.set_ptr_and_cap(ptr, cap);
324        Ok(())
325    }
326
327    // The constraints on this method are much the same as those on
328    // `grow_amortized`, but this method is usually instantiated less often so
329    // it's less critical.
330    fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), Error> {
331        if T::IS_ZST {
332            // Since we return a capacity of `usize::MAX` when the type size is
333            // 0, getting to here necessarily means the `RawVec` is overfull.
334            return Err(Error::CapacityOverflow);
335        }
336
337        let cap = len.checked_add(additional).ok_or(Error::CapacityOverflow)?;
338        let new_layout = Layout::array::<T>(cap);
339
340        // `finish_grow` is non-generic over `T`.
341        let ptr = finish_grow(new_layout, self.current_memory(), &self.alloc)?;
342        self.set_ptr_and_cap(ptr, cap);
343        Ok(())
344    }
345
346    fn shrink(&mut self, cap: usize) -> Result<(), Error> {
347        // See current_memory() why this assert is here
348        assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0);
349        assert!(
350            cap <= self.capacity(),
351            "Tried to shrink to a larger capacity"
352        );
353
354        let (ptr, layout) = if let Some(mem) = self.current_memory() {
355            mem
356        } else {
357            return Ok(());
358        };
359
360        // If shrinking to 0, deallocate the buffer. We don't reach this point
361        // for the T::IS_ZST case since current_memory() will have returned
362        // None.
363        if cap == 0 {
364            unsafe { self.alloc.deallocate(ptr, layout) };
365            self.ptr = Unique::dangling();
366            self.cap = 0;
367        } else {
368            let ptr = unsafe {
369                // `Layout::array` cannot overflow here because it would have
370                // overflowed earlier when capacity was larger.
371                let new_size = mem::size_of::<T>().wrapping_mul(cap);
372                let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
373                self.alloc
374                    .shrink(ptr, layout, new_layout)
375                    .map_err(|_| AllocError { layout: new_layout })?
376            };
377            self.set_ptr_and_cap(ptr, cap);
378        }
379        Ok(())
380    }
381}
382
383// This function is outside `RawVec` to minimize compile times. See the comment
384// above `RawVec::grow_amortized` for details. (The `A` parameter isn't
385// significant, because the number of different `A` types seen in practice is
386// much smaller than the number of `T` types.)
387#[inline(never)]
388fn finish_grow<A>(
389    new_layout: Result<Layout, LayoutError>,
390    current_memory: Option<(NonNull<u8>, Layout)>,
391    alloc: &A,
392) -> Result<NonNull<[u8]>, Error>
393where
394    A: Allocator,
395{
396    // Check for the error here to minimize the size of `RawVec::grow_*`.
397    let new_layout = new_layout.map_err(|_| Error::CapacityOverflow)?;
398
399    alloc_guard(new_layout.size())?;
400
401    let memory = if let Some((ptr, old_layout)) = current_memory {
402        debug_assert_eq!(old_layout.align(), new_layout.align());
403        unsafe {
404            // The allocator checks for alignment equality
405            debug_assert!(old_layout.align() == new_layout.align());
406            alloc.grow(ptr, old_layout, new_layout)
407        }
408    } else {
409        alloc.allocate(new_layout)
410    };
411
412    memory.map_err(|_| AllocError { layout: new_layout }.into())
413}
414
415#[cfg(not(rune_nightly))]
416impl<T, A: Allocator> Drop for RawVec<T, A> {
417    /// Frees the memory owned by the `RawVec` *without* trying to drop its contents.
418    fn drop(&mut self) {
419        if let Some((ptr, layout)) = self.current_memory() {
420            unsafe { self.alloc.deallocate(ptr, layout) }
421        }
422    }
423}
424
425#[cfg(rune_nightly)]
426unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
427    /// Frees the memory owned by the `RawVec` *without* trying to drop its contents.
428    fn drop(&mut self) {
429        if let Some((ptr, layout)) = self.current_memory() {
430            unsafe { self.alloc.deallocate(ptr, layout) }
431        }
432    }
433}
434
435// We need to guarantee the following:
436// * We don't ever allocate `> isize::MAX` byte-size objects.
437// * We don't overflow `usize::MAX` and actually allocate too little.
438//
439// On 64-bit we just need to check for overflow since trying to allocate
440// `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add
441// an extra guard for this in case we're running on a platform which can use
442// all 4GB in user-space, e.g., PAE or x32.
443
444#[inline]
445fn alloc_guard(alloc_size: usize) -> Result<(), Error> {
446    if usize::BITS < 64 && alloc_size > isize::MAX as usize {
447        Err(Error::CapacityOverflow)
448    } else {
449        Ok(())
450    }
451}