1use std::alloc::{GlobalAlloc, Layout, System};
2use std::ffi::c_char;
3
4use once_cell::race::OnceRef;
5use pyo3::ffi::{PyCapsule_Import, Py_IsInitialized};
6use pyo3::Python;
7
8unsafe extern "C" fn fallback_alloc(size: usize, align: usize) -> *mut u8 {
9 System.alloc(Layout::from_size_align_unchecked(size, align))
10}
11
12unsafe extern "C" fn fallback_dealloc(ptr: *mut u8, size: usize, align: usize) {
13 System.dealloc(ptr, Layout::from_size_align_unchecked(size, align))
14}
15
16unsafe extern "C" fn fallback_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
17 System.alloc_zeroed(Layout::from_size_align_unchecked(size, align))
18}
19
20unsafe extern "C" fn fallback_realloc(
21 ptr: *mut u8,
22 size: usize,
23 align: usize,
24 new_size: usize,
25) -> *mut u8 {
26 System.realloc(
27 ptr,
28 Layout::from_size_align_unchecked(size, align),
29 new_size,
30 )
31}
32
33#[repr(C)]
34struct AllocatorCapsule {
35 alloc: unsafe extern "C" fn(usize, usize) -> *mut u8,
36 dealloc: unsafe extern "C" fn(*mut u8, usize, usize),
37 alloc_zeroed: unsafe extern "C" fn(usize, usize) -> *mut u8,
38 realloc: unsafe extern "C" fn(*mut u8, usize, usize, usize) -> *mut u8,
39}
40
41static FALLBACK_ALLOCATOR_CAPSULE: AllocatorCapsule = AllocatorCapsule {
42 alloc: fallback_alloc,
43 alloc_zeroed: fallback_alloc_zeroed,
44 dealloc: fallback_dealloc,
45 realloc: fallback_realloc,
46};
47
48static ALLOCATOR_CAPSULE_NAME: &[u8] = b"polars.polars._allocator\0";
49
50pub struct PolarsAllocator(OnceRef<'static, AllocatorCapsule>);
64
65impl PolarsAllocator {
66 fn get_allocator(&self) -> &'static AllocatorCapsule {
67 self.0.get_or_init(|| {
70 let r = (unsafe { Py_IsInitialized() } != 0)
71 .then(|| {
72 Python::with_gil(|_| unsafe {
73 (PyCapsule_Import(ALLOCATOR_CAPSULE_NAME.as_ptr() as *const c_char, 0)
74 as *const AllocatorCapsule)
75 .as_ref()
76 })
77 })
78 .flatten();
79 #[cfg(debug_assertions)]
80 if r.is_none() {
81 let msg = b"failed to get allocator capsule\n";
83 let msg_len = msg.len().try_into().unwrap();
85 unsafe { libc::write(2, msg.as_ptr() as *const libc::c_void, msg_len) };
86 }
87 r.unwrap_or(&FALLBACK_ALLOCATOR_CAPSULE)
88 })
89 }
90
91 pub const fn new() -> Self {
93 PolarsAllocator(OnceRef::new())
94 }
95}
96
97impl Default for PolarsAllocator {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103unsafe impl GlobalAlloc for PolarsAllocator {
104 #[inline]
105 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
106 (self.get_allocator().alloc)(layout.size(), layout.align())
107 }
108
109 #[inline]
110 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
111 (self.get_allocator().dealloc)(ptr, layout.size(), layout.align());
112 }
113
114 #[inline]
115 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
116 (self.get_allocator().alloc_zeroed)(layout.size(), layout.align())
117 }
118
119 #[inline]
120 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
121 (self.get_allocator().realloc)(ptr, layout.size(), layout.align(), new_size)
122 }
123}