use crate::ffi;
use crate::marker::Python;
use crate::platform::prelude::*;
use crate::platform::sync::nonpoison::Mutex;
use crate::sync::{MutexExt, PyOnceLock};
use core::ops::DerefMut;
use core::ptr::NonNull;
pub struct FreeList(PyOnceLock<Mutex<PyObjectFreeList>>);
impl FreeList {
#[expect(clippy::new_without_default, reason = "always called in const context")]
pub const fn new() -> Self {
Self(PyOnceLock::new())
}
pub fn get(
&self,
py: Python<'_>,
capacity: usize,
) -> impl DerefMut<Target = PyObjectFreeList> + use<'_> {
self.0
.get_or_init(py, || Mutex::new(PyObjectFreeList::with_capacity(capacity)))
.lock_py_attached(py)
}
}
pub struct PyObjectFreeList {
entries: Box<[Option<NonNull<ffi::PyObject>>]>,
split: usize,
}
unsafe impl Send for PyObjectFreeList {}
impl PyObjectFreeList {
pub fn with_capacity(capacity: usize) -> PyObjectFreeList {
let entries = vec![None; capacity].into_boxed_slice();
PyObjectFreeList { entries, split: 0 }
}
pub fn pop(&mut self) -> Option<NonNull<ffi::PyObject>> {
let idx = self.split;
if idx == 0 {
None
} else {
let val = self.entries[idx - 1]
.take()
.expect("PyObjectFreeList is corrupt");
self.split = idx - 1;
Some(val)
}
}
pub fn insert(&mut self, val: NonNull<ffi::PyObject>) -> Option<NonNull<ffi::PyObject>> {
let next = self.split + 1;
if next < self.entries.len() {
self.entries[self.split] = Some(val);
self.split = next;
None
} else {
Some(val)
}
}
}