use crate::err::PyResult;
use crate::ffi;
use crate::python::Python;
use std::mem;
use std::os::raw::c_void;
use crate::typeob::{pytype_drop, PyObjectAlloc, PyTypeInfo};
pub trait PyObjectWithFreeList: PyTypeInfo {
fn get_free_list() -> &'static mut FreeList<*mut ffi::PyObject>;
}
pub enum Slot<T> {
Empty,
Filled(T),
}
pub struct FreeList<T> {
entries: Vec<Slot<T>>,
split: usize,
capacity: usize,
}
impl<T> FreeList<T> {
pub fn with_capacity(capacity: usize) -> FreeList<T> {
let entries = (0..capacity).map(|_| Slot::Empty).collect::<Vec<_>>();
FreeList {
entries,
split: 0,
capacity,
}
}
pub fn pop(&mut self) -> Option<T> {
let idx = self.split;
if idx == 0 {
None
} else {
match mem::replace(&mut self.entries[idx - 1], Slot::Empty) {
Slot::Filled(v) => {
self.split = idx - 1;
Some(v)
}
_ => panic!("FreeList is corrupt"),
}
}
}
pub fn insert(&mut self, val: T) -> Option<T> {
let next = self.split + 1;
if next < self.capacity {
self.entries[self.split] = Slot::Filled(val);
self.split = next;
None
} else {
Some(val)
}
}
}
impl<T> PyObjectAlloc<T> for T
where
T: PyObjectWithFreeList,
{
unsafe fn alloc(_py: Python) -> PyResult<*mut ffi::PyObject> {
let obj = if let Some(obj) = <T as PyObjectWithFreeList>::get_free_list().pop() {
ffi::PyObject_Init(obj, <T as PyTypeInfo>::type_object());
obj
} else {
ffi::PyType_GenericAlloc(<T as PyTypeInfo>::type_object(), 0)
};
Ok(obj)
}
#[cfg(Py_3)]
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
pytype_drop::<T>(py, obj);
if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 {
return;
}
if let Some(obj) = <T as PyObjectWithFreeList>::get_free_list().insert(obj) {
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut c_void);
}
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
}
}
}
}
#[cfg(not(Py_3))]
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
pytype_drop::<T>(py, obj);
if let Some(obj) = <T as PyObjectWithFreeList>::get_free_list().insert(obj) {
match (*T::type_object()).tp_free {
Some(free) => free(obj as *mut c_void),
None => {
let ty = ffi::Py_TYPE(obj);
if ffi::PyType_IS_GC(ty) != 0 {
ffi::PyObject_GC_Del(obj as *mut c_void);
} else {
ffi::PyObject_Free(obj as *mut c_void);
}
if ffi::PyType_HasFeature(ty, ffi::Py_TPFLAGS_HEAPTYPE) != 0 {
ffi::Py_DECREF(ty as *mut ffi::PyObject);
}
}
}
}
}
}