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}