fixed_typed_arena/
manually_drop.rs

1/*
2 * Copyright (C) 2021-2022 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of fixed-typed-arena.
5 *
6 * fixed-typed-arena is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * fixed-typed-arena is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with fixed-typed-arena. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20//! An arena that returns references with arbitrary lifetimes.
21
22use super::chunk::ChunkRef;
23use super::options::{ChunkSizePriv, SupportsPositionsPriv};
24use super::ArenaOptions;
25use alloc::alloc::handle_alloc_error;
26use alloc::boxed::Box;
27use alloc::sync::Arc;
28use core::fmt::{Debug, Display};
29use core::hint::unreachable_unchecked;
30use core::marker::PhantomData;
31use core::mem;
32use core::ptr::{self, NonNull};
33use integral_constant::Bool;
34
35pub(crate) mod iter;
36use iter::{IntoIter, Iter, IterMut, IterPtr, Position};
37
38type Array<T, Options> =
39    <<Options as ArenaOptions<T>>::ChunkSize as ChunkSizePriv<T>>::Array;
40type SupportsPositions<T, Options> =
41    <Options as ArenaOptions<T>>::SupportsPositions;
42type ArenaRc<T, Options> =
43    <SupportsPositions<T, Options> as SupportsPositionsPriv>::Rc;
44type ArenaChunk<T, Options> = ChunkRef<T, Array<T, Options>>;
45
46/// Checks whether `old` and `new` point to the same allocation (see
47/// [`Arc::ptr_eq`]), but allows `old` to be [`None`], even if `new` is
48/// [`Some`].
49fn valid_rc_update<T>(old: &Option<Arc<T>>, new: &Option<Arc<T>>) -> bool {
50    match (old, new) {
51        (Some(old), Some(new)) => Arc::ptr_eq(old, new),
52        (Some(_old), None) => false,
53        (None, _new) => true,
54    }
55}
56
57// Invariants:
58//
59// * Every chunk except for `tail` must be full (all items initialized).
60// * If `tail_len` is less than `Self::CHUNK_SIZE`, `tail` is `Some`.
61// * If `tail` is `Some`, it contains at least one item (`tail_len > 0`).
62// * If `tail` is `Some`, the items in `tail` up to index `tail_len`
63//   (exclusive) are initialized.
64//
65/// Like [`Arena`], but returns references of any lifetime, including
66/// `'static`.
67///
68/// This lets the arena be used without being borrowed, but it comes with the
69/// tradeoff that the arena leaks memory unless the unsafe [`drop`](Self::drop)
70/// method is called.
71///
72/// [`Arena`]: super::arena::Arena
73pub struct ManuallyDropArena<T, Options: ArenaOptions<T> = super::Options> {
74    rc: Option<ArenaRc<T, Options>>,
75    head: Option<ArenaChunk<T, Options>>,
76    tail: Option<ArenaChunk<T, Options>>,
77    tail_len: usize,
78    len: usize,
79    /// Lets dropck know that `T` may be dropped.
80    phantom: PhantomData<Box<T>>,
81}
82
83impl<T, Options: ArenaOptions<T>> Default for ManuallyDropArena<T, Options> {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89impl<T, Options: ArenaOptions<T>> ManuallyDropArena<T, Options> {
90    const CHUNK_SIZE: usize = ArenaChunk::<T, Options>::CAPACITY;
91
92    /// Creates a new [`ManuallyDropArena`].
93    pub fn new() -> Self {
94        Self {
95            rc: None,
96            head: None,
97            tail: None,
98            tail_len: Self::CHUNK_SIZE,
99            len: 0,
100            phantom: PhantomData,
101        }
102    }
103
104    fn ensure_free_space(&mut self) -> Result<(), impl Debug + Display> {
105        assert!(
106            Self::CHUNK_SIZE > 0,
107            "cannot allocate items when chunk size is 0",
108        );
109        if self.tail_len < Self::CHUNK_SIZE {
110            // `self.tail` cannot be `None`. The only time `self.tail` is
111            // `None` is after calling `Self::new`, which also sets
112            // `self.tail_len` to `Self::CHUNK_SIZE`.
113            return Ok(());
114        }
115
116        let chunk = if let Some(chunk) = ChunkRef::new(self.tail.take()) {
117            chunk
118        } else {
119            return Err("could not allocate chunk");
120        };
121
122        self.head.get_or_insert_with(|| chunk.clone());
123        self.tail = Some(chunk);
124        self.tail_len = 0;
125        Ok(())
126    }
127
128    fn alloc_ptr(&mut self, value: T) -> NonNull<T> {
129        self.try_alloc_ptr(value).unwrap_or_else(|| {
130            handle_alloc_error(ArenaChunk::<T, Options>::LAYOUT);
131        })
132    }
133
134    fn try_alloc_ptr(&mut self, value: T) -> Option<NonNull<T>> {
135        self.ensure_free_space().ok()?;
136        SupportsPositions::<T, Options>::init_rc(&mut self.rc);
137
138        let chunk = self.tail.as_mut().unwrap_or_else(|| {
139            // SAFETY: `Self::ensure_free_space` ensures that `self.tail`
140            // is not `None`.
141            unsafe { unreachable_unchecked() }
142        });
143
144        // SAFETY: `Self::ensure_free_space` ensures that `self.tail_len` is
145        // less than the chunk size.
146        let item = unsafe { chunk.get(self.tail_len) };
147
148        // SAFETY: `ChunkRef::get` returns valid, properly aligned pointers.
149        unsafe {
150            item.as_ptr().write(value);
151        }
152
153        self.tail_len += 1;
154        self.len += 1;
155        Some(item)
156    }
157
158    /// Drops the contents of the arena. The arena will leak memory when
159    /// dropped unless this method is called.
160    ///
161    /// # Safety
162    ///
163    /// You must ensure that no references to items (or parts of items) in the
164    /// arena exist when calling this method, except possibly for references
165    /// within the items themselves.
166    ///
167    /// However, if there are references to other items (or parts of items)
168    /// within the items themselves, at least one of the following must be
169    /// true:
170    ///
171    /// * `T` does not have a custom [`Drop`] impl.
172    /// * `T`'s [`Drop`] impl does not directly or indirectly access any data
173    ///   via the references to other items or parts of items. (This is
174    ///   essentially the requirement imposed by [`#[may_dangle]`][dropck].)
175    ///
176    /// Additionally, there must be no instances of [`Iter`] or [`IterMut`]
177    /// for this arena.
178    ///
179    /// [dropck]: https://doc.rust-lang.org/nomicon/dropck.html
180    pub unsafe fn drop(&mut self) {
181        let mut head = if let Some(head) = self.head.take() {
182            head
183        } else {
184            return;
185        };
186
187        self.tail = None;
188        let tail_len = mem::replace(&mut self.tail_len, Self::CHUNK_SIZE);
189        self.len = 0;
190        self.rc = None;
191
192        // Drop the items in all chunks except the last.
193        while let Some(next) = head.next() {
194            // SAFETY: All chunks except for the tail are guaranteed to
195            // be full (all items initialized). We know this isn't the
196            // tail chunk because `head.next()` is not `None`.
197            unsafe {
198                head.drop_all();
199            }
200
201            // SAFETY: No clones of this `ChunkRef` exist. `self.head`
202            // and `self.tail` are both `None`, and the chunks form a
203            // singly linked list. Caller guarantees no iterators exist.
204            unsafe {
205                head.dealloc();
206            }
207            head = next;
208        }
209
210        // `head` is now the tail chunk; drop its items.
211        for i in 0..tail_len {
212            // SAFETY: The items in the tail chunk (when not `None`) at
213            // indices up to `self.tail_len` are always initialized.
214            unsafe {
215                head.drop_item(i);
216            }
217        }
218
219        // SAFETY: No clones of this `ChunkRef` exist for the same
220        // reasons as the other chunks above.
221        unsafe {
222            head.dealloc();
223        }
224    }
225
226    /// Alias of [`Self::drop`]. Can be used to prevent name collisions when
227    /// this arena is stored in a [`Deref`](core::ops::Deref) type:
228    ///
229    /// ```
230    /// # use fixed_typed_arena::ManuallyDropArena;
231    /// let mut arena = Box::new(ManuallyDropArena::<u8, 8>::new());
232    /// //unsafe { arena.drop() }; // Compile error: resolves to `Drop::drop`
233    /// unsafe { arena.manually_drop() }; // Works as expected
234    /// ```
235    ///
236    /// # Safety
237    ///
238    /// Same requirements as [`Self::drop`].
239    pub unsafe fn manually_drop(&mut self) {
240        // SAFETY: Checked by caller.
241        unsafe {
242            self.drop();
243        }
244    }
245
246    /// Returns the total number of items that have been allocated.
247    pub fn len(&self) -> usize {
248        self.len
249    }
250
251    /// Checks whether the arena is empty.
252    pub fn is_empty(&self) -> bool {
253        self.len == 0
254    }
255
256    /// Allocates a new item in the arena and initializes it with `value`.
257    /// Returns a reference to the allocated item. The reference can have any
258    /// lifetime, including `'static`, as long as `T` outlives that lifetime.
259    ///
260    /// This method calls [`handle_alloc_error`] if memory allocation fails;
261    /// for a version that returns [`None`], see [`Self::try_alloc`].
262    ///
263    /// [`handle_alloc_error`]: alloc::alloc::handle_alloc_error
264    pub fn alloc<'a>(&mut self, value: T) -> &'a mut T
265    where
266        Options: 'a + ArenaOptions<T, Mutable = Bool<true>>,
267    {
268        // SAFETY: `Self::alloc_ptr` returns initialized, properly aligned
269        // pointers, and we can return a reference with an arbitrary lifetime
270        // because the arena must be manually dropped.
271        unsafe { self.alloc_ptr(value).as_mut() }
272    }
273
274    /// Like [`Self::alloc`], but returns [`None`] if memory allocation fails.
275    pub fn try_alloc<'a>(&mut self, value: T) -> Option<&'a mut T>
276    where
277        Options: 'a + ArenaOptions<T, Mutable = Bool<true>>,
278    {
279        // SAFETY: See `Self::alloc`.
280        Some(unsafe { self.try_alloc_ptr(value)?.as_mut() })
281    }
282
283    /// Allocates a new item in the arena and initializes it with `value`.
284    /// Returns a shared/immutable reference to the allocated item. The
285    /// reference can have any lifetime, including `'static`, as long as `T`
286    /// outlives that lifetime.
287    ///
288    /// This method calls [`handle_alloc_error`] if memory allocation fails;
289    /// for a version that returns [`None`], see [`Self::try_alloc`].
290    ///
291    /// [`handle_alloc_error`]: alloc::alloc::handle_alloc_error
292    pub fn alloc_shared<'a>(&mut self, value: T) -> &'a T
293    where
294        Options: 'a,
295    {
296        // SAFETY: See `Self::alloc`.
297        unsafe { self.alloc_ptr(value).as_ref() }
298    }
299
300    /// Like [`Self::alloc_shared`], but returns [`None`] if memory allocation
301    /// fails.
302    pub fn try_alloc_shared<'a>(&mut self, value: T) -> Option<&'a T>
303    where
304        Options: 'a,
305    {
306        // SAFETY: See `Self::alloc`.
307        Some(unsafe { self.try_alloc_ptr(value)?.as_ref() })
308    }
309
310    fn end(&self) -> *const T {
311        self.tail.as_ref().map_or(ptr::null(), |c| {
312            // SAFETY: `self.tail_len` is necessarily less than or equal to
313            // the chunk capacity.
314            unsafe { c.get(self.tail_len) }.as_ptr()
315        })
316    }
317
318    fn iter_ptr<const DROP: bool>(&self) -> IterPtr<T, Options, DROP> {
319        IterPtr {
320            chunk: self.head.clone(),
321            index: 0,
322            end: self.end(),
323            rc: self.rc.clone(),
324            phantom: PhantomData,
325        }
326    }
327
328    /// Returns an iterator over the items in this arena.
329    pub fn iter<'a>(&self) -> Iter<'a, T, Options>
330    where
331        Options: ArenaOptions<T, Mutable = Bool<false>>,
332    {
333        unsafe { self.iter_unchecked() }
334    }
335
336    /// Returns an iterator over the items in this arena.
337    ///
338    /// # Safety
339    ///
340    /// There must be no mutable references (or references derived from mutable
341    /// references) to items (or parts of items) in this arena or instances of
342    /// [`IterMut`] for this arena.
343    pub unsafe fn iter_unchecked<'a>(&self) -> Iter<'a, T, Options> {
344        Iter {
345            inner: self.iter_ptr::<false>(),
346            phantom: PhantomData,
347        }
348    }
349
350    /// Returns a mutable iterator over the items in this arena.
351    ///
352    /// # Safety
353    ///
354    /// There must be no references to items (or parts of items) in this arena
355    /// or instances of [`Iter`] or [`IterMut`] for this arena.
356    pub unsafe fn iter_mut_unchecked<'a>(
357        &mut self,
358    ) -> IterMut<'a, T, Options> {
359        IterMut {
360            inner: self.iter_ptr::<false>(),
361            phantom: PhantomData,
362        }
363    }
364
365    /// Returns an owning iterator over the items in this arena.
366    ///
367    /// # Safety
368    ///
369    /// There must be no references to items (or parts of items) in this arena
370    /// or instances of [`Iter`] or [`IterMut`] for this arena.
371    pub unsafe fn into_iter_unchecked(self) -> IntoIter<T, Options> {
372        IntoIter(self.iter_ptr())
373    }
374}
375
376impl<T, Options> ManuallyDropArena<T, Options>
377where
378    Options: ArenaOptions<T, SupportsPositions = Bool<true>>,
379{
380    fn iter_ptr_at(&self, position: &Position) -> IterPtr<T, Options> {
381        assert!(
382            valid_rc_update(&position.rc, &self.rc),
383            "`position` is not part of this arena",
384        );
385
386        // SAFETY: Checking the pointer equality of `self.rc` and `position.rc`
387        // above ensures `position` belongs to this arena. Note that if
388        // `position.rc` is `None`, it may have come from a different arena,
389        // but this is okay, because in this case `position` does not contain a
390        // pointer that we dereference.
391        let chunk = position.chunk.map(|p| unsafe { ChunkRef::from_ptr(p) });
392
393        IterPtr {
394            chunk: chunk.or_else(|| self.head.clone()),
395            index: position.index,
396            end: self.end(),
397            rc: self.rc.clone(),
398            phantom: PhantomData,
399        }
400    }
401
402    /// Returns an iterator starting at the specified position.
403    ///
404    /// # Panics
405    ///
406    /// May panic if `position` does not refer to a position in this arena.
407    pub fn iter_at<'a>(&self, position: &Position) -> Iter<'a, T, Options>
408    where
409        Options: ArenaOptions<T, Mutable = Bool<false>>,
410    {
411        unsafe { self.iter_at_unchecked(position) }
412    }
413
414    /// Returns an iterator starting at the specified position.
415    ///
416    /// # Panics
417    ///
418    /// May panic if `position` does not refer to a position in this arena.
419    ///
420    /// # Safety
421    ///
422    /// Same requirements as [`Self::iter_unchecked`].
423    pub unsafe fn iter_at_unchecked<'a>(
424        &self,
425        position: &Position,
426    ) -> Iter<'a, T, Options> {
427        Iter {
428            inner: self.iter_ptr_at(position),
429            phantom: PhantomData,
430        }
431    }
432
433    /// Returns a mutable iterator starting at the specified position.
434    ///
435    /// # Panics
436    ///
437    /// May panic if `position` does not refer to a position in this arena.
438    ///
439    /// # Safety
440    ///
441    /// Same requirements as [`Self::iter_mut_unchecked`].
442    pub unsafe fn iter_mut_at_unchecked<'a>(
443        &mut self,
444        position: &Position,
445    ) -> IterMut<'a, T, Options> {
446        IterMut {
447            inner: self.iter_ptr_at(position),
448            phantom: PhantomData,
449        }
450    }
451}
452
453// SAFETY: `ManuallyDropArena` owns its items and provides access to them using
454// standard borrow rules, so it can be `Sync` as long as `T` is `Sync`.
455unsafe impl<T, Options> Sync for ManuallyDropArena<T, Options>
456where
457    T: Sync,
458    Options: ArenaOptions<T>,
459{
460}
461
462// SAFETY: `ManuallyDropArena` owns its items, so it can be `Send` as long
463// as `T` is both `Send` and `Sync`. `T` must be `Sync` because the life of
464// the iterators `ManuallyDropArena` provides (e.g., from `Self::iter`) is
465// unbounded, so a caller could obtain such an iterator and then move the arena
466// to another thread while still possessing the iterator.
467unsafe impl<T, Options> Send for ManuallyDropArena<T, Options>
468where
469    T: Send + Sync,
470    Options: ArenaOptions<T>,
471{
472}