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}