pyo3/impl_/freelist.rs
1//! Support for [free allocation lists][1].
2//!
3//! This can improve performance for types that are often created and deleted in quick succession.
4//!
5//! Rather than implementing this manually,
6//! implement it by annotating a struct with `#[pyclass(freelist = N)]`,
7//! where `N` is the size of the freelist.
8//!
9//! [1]: https://en.wikipedia.org/wiki/Free_list
10
11use crate::ffi;
12use std::mem;
13
14/// Represents a slot of a [`PyObjectFreeList`].
15enum PyObjectSlot {
16 /// A free slot.
17 Empty,
18 /// An allocated slot.
19 Filled(*mut ffi::PyObject),
20}
21
22// safety: access is guarded by a per-pyclass mutex
23unsafe impl Send for PyObjectSlot {}
24
25/// A free allocation list for PyObject ffi pointers.
26///
27/// See [the parent module](crate::impl_::freelist) for more details.
28pub struct PyObjectFreeList {
29 entries: Box<[PyObjectSlot]>,
30 split: usize,
31 capacity: usize,
32}
33
34impl PyObjectFreeList {
35 /// Creates a new `PyObjectFreeList` instance with specified capacity.
36 pub fn with_capacity(capacity: usize) -> PyObjectFreeList {
37 let entries = (0..capacity)
38 .map(|_| PyObjectSlot::Empty)
39 .collect::<Box<[_]>>();
40
41 PyObjectFreeList {
42 entries,
43 split: 0,
44 capacity,
45 }
46 }
47
48 /// Pops the first non empty item.
49 pub fn pop(&mut self) -> Option<*mut ffi::PyObject> {
50 let idx = self.split;
51 if idx == 0 {
52 None
53 } else {
54 match mem::replace(&mut self.entries[idx - 1], PyObjectSlot::Empty) {
55 PyObjectSlot::Filled(v) => {
56 self.split = idx - 1;
57 Some(v)
58 }
59 _ => panic!("PyObjectFreeList is corrupt"),
60 }
61 }
62 }
63
64 /// Inserts a value into the list. Returns `Some(val)` if the `PyObjectFreeList` is full.
65 pub fn insert(&mut self, val: *mut ffi::PyObject) -> Option<*mut ffi::PyObject> {
66 let next = self.split + 1;
67 if next < self.capacity {
68 self.entries[self.split] = PyObjectSlot::Filled(val);
69 self.split = next;
70 None
71 } else {
72 Some(val)
73 }
74 }
75}