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 | |
11 | use crate::ffi; |
12 | use std::mem; |
13 | |
14 | /// Represents a slot of a [`PyObjectFreeList`]. |
15 | enum 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 |
23 | unsafe 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. |
28 | pub struct PyObjectFreeList { |
29 | entries: Box<[PyObjectSlot]>, |
30 | split: usize, |
31 | capacity: usize, |
32 | } |
33 | |
34 | impl 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 | } |
76 | |