1use std::{
3 borrow::Borrow,
4 ffi::c_void,
5 marker::PhantomData,
6 ops::{Deref, DerefMut},
7 ptr::NonNull,
8};
9
10#[cfg(feature = "allocator_api")]
11use allocator_api2::alloc;
12
13use crate::{
14 error::{Error, Result},
15 ffi::{self, GhosttyAllocator, GhosttyAllocatorVtable},
16};
17
18#[derive(Debug)]
28pub struct Allocator<'ctx, Ctx: 'ctx = ()> {
29 pub(crate) inner: GhosttyAllocator,
30 _phan: PhantomData<&'ctx Ctx>,
31}
32
33impl<Ctx> Allocator<'_, Ctx> {
34 pub(crate) fn to_raw(&self) -> *const GhosttyAllocator {
35 std::ptr::from_ref(&self.inner)
36 }
37}
38
39#[derive(Debug)]
42pub(crate) struct Object<'alloc, T> {
43 pub(crate) ptr: NonNull<T>,
44 _phan: PhantomData<&'alloc GhosttyAllocator>,
45}
46
47impl<T> Object<'_, T> {
48 pub(crate) fn new(raw: *mut T) -> Result<Self> {
49 let ptr = NonNull::new(raw).ok_or(Error::OutOfMemory)?;
50 Ok(Self {
51 ptr,
52 _phan: PhantomData,
53 })
54 }
55 pub(crate) fn as_raw(&self) -> *mut T {
56 self.ptr.as_ptr()
57 }
58}
59
60#[derive(Debug)]
62pub struct Bytes<'alloc> {
63 ptr: NonNull<u8>,
64 len: usize,
65 alloc: *const GhosttyAllocator,
66 _phan: PhantomData<&'alloc GhosttyAllocator>,
67}
68impl<'alloc> Bytes<'alloc> {
69 pub fn new(len: usize) -> Result<Self> {
73 unsafe { Self::new_inner(std::ptr::null(), len) }
75 }
76
77 pub fn new_with_alloc<'ctx: 'alloc, Ctx>(
81 alloc: &'alloc Allocator<'ctx, Ctx>,
82 len: usize,
83 ) -> Result<Self> {
84 unsafe { Self::new_inner(alloc.to_raw(), len) }
86 }
87
88 unsafe fn new_inner(alloc: *const ffi::GhosttyAllocator, len: usize) -> Result<Self> {
89 let raw = unsafe { ffi::ghostty_alloc(alloc, len) };
90 let ptr = NonNull::new(raw).ok_or(Error::OutOfMemory)?;
91 Ok(unsafe { Self::from_raw_parts(ptr, len, alloc) })
92 }
93
94 pub(crate) unsafe fn from_raw_parts(
95 ptr: NonNull<u8>,
96 len: usize,
97 alloc: *const GhosttyAllocator,
98 ) -> Self {
99 Self {
100 ptr,
101 len,
102 alloc,
103 _phan: PhantomData,
104 }
105 }
106}
107impl Drop for Bytes<'_> {
108 fn drop(&mut self) {
109 unsafe { ffi::ghostty_free(self.alloc, self.ptr.as_ptr(), self.len) };
113 }
114}
115impl Deref for Bytes<'_> {
116 type Target = [u8];
117
118 #[inline]
119 fn deref(&self) -> &Self::Target {
120 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
122 }
123}
124impl DerefMut for Bytes<'_> {
125 #[inline]
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
129 }
130}
131impl AsRef<[u8]> for Bytes<'_> {
132 fn as_ref(&self) -> &[u8] {
133 self
134 }
135}
136impl AsMut<[u8]> for Bytes<'_> {
137 fn as_mut(&mut self) -> &mut [u8] {
138 self
139 }
140}
141impl Borrow<[u8]> for Bytes<'_> {
142 fn borrow(&self) -> &[u8] {
143 self
144 }
145}
146impl<'a> IntoIterator for &'a Bytes<'_> {
147 type Item = &'a u8;
148 type IntoIter = std::slice::Iter<'a, u8>;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.deref().iter()
152 }
153}
154
155impl Allocator<'static> {
160 pub const GLOBAL: Self = Self {
163 inner: GhosttyAllocator {
164 ctx: std::ptr::null_mut(),
165 vtable: &GhosttyAllocatorVtable {
166 alloc: Some(_global_alloc),
167 free: Some(_global_free),
168 resize: Some(_global_resize),
169 remap: Some(_global_remap),
170 },
171 },
172 _phan: PhantomData,
173 };
174}
175
176unsafe extern "C" fn _global_alloc(
177 _allocator: *mut c_void,
178 len: usize,
179 alignment: u8,
180 _ret_addr: usize,
181) -> *mut c_void {
182 let Ok(layout) = std::alloc::Layout::from_size_align(len, 1 << alignment) else {
183 return std::ptr::null_mut();
184 };
185 unsafe { std::alloc::alloc(layout).cast::<c_void>() }
186}
187
188unsafe extern "C" fn _global_free(
189 _allocator: *mut c_void,
190 mem: *mut c_void,
191 len: usize,
192 alignment: u8,
193 _ret_addr: usize,
194) {
195 let Ok(layout) = std::alloc::Layout::from_size_align(len, 1 << alignment) else {
196 return;
197 };
198 unsafe { std::alloc::dealloc(mem.cast::<u8>(), layout) }
199}
200unsafe extern "C" fn _global_resize(
201 _allocator: *mut c_void,
202 _mem: *mut c_void,
203 _old_len: usize,
204 _alignment: u8,
205 _new_len: usize,
206 _ret_addr: usize,
207) -> bool {
208 false
209}
210unsafe extern "C" fn _global_remap(
211 _allocator: *mut c_void,
212 mem: *mut c_void,
213 old_len: usize,
214 alignment: u8,
215 new_len: usize,
216 _ret_addr: usize,
217) -> *mut c_void {
218 let Ok(layout) = std::alloc::Layout::from_size_align(old_len, 1 << alignment) else {
219 return std::ptr::null_mut();
220 };
221 unsafe { std::alloc::realloc(mem.cast::<u8>(), layout, new_len).cast::<c_void>() }
222}
223
224#[cfg(feature = "allocator_api")]
230impl<'ctx, A: alloc::Allocator + 'ctx> From<A> for Allocator<'ctx, A> {
231 fn from(value: A) -> Self {
232 Self {
233 inner: GhosttyAllocator {
234 ctx: std::ptr::from_ref(value.by_ref()) as *mut std::ffi::c_void,
235 vtable: &GhosttyAllocatorVtable {
236 alloc: Some(_alloc::<A>),
237 free: Some(_free::<A>),
238 resize: Some(_resize),
239 remap: Some(_remap::<A>),
240 },
241 },
242 _phan: PhantomData,
243 }
244 }
245}
246
247#[cfg(feature = "allocator_api")]
248unsafe extern "C" fn _alloc<A: alloc::Allocator>(
249 allocator: *mut c_void,
250 len: usize,
251 alignment: u8,
252 _ret_addr: usize,
253) -> *mut c_void {
254 let layout = alloc::Layout::from_size_align(len, 1 << alignment).ok();
255
256 unsafe { get_allocator::<A>(allocator) }
257 .and_then(|alloc| alloc.allocate(layout?).ok())
258 .map(|p| p.as_ptr().cast::<c_void>())
259 .unwrap_or(std::ptr::null_mut())
260}
261
262#[cfg(feature = "allocator_api")]
263unsafe extern "C" fn _free<A: alloc::Allocator>(
264 allocator: *mut c_void,
265 mem: *mut c_void,
266 len: usize,
267 alignment: u8,
268 _ret_addr: usize,
269) {
270 let Some(mem) = NonNull::new(mem.cast::<u8>()) else {
271 return;
272 };
273 let Some(layout) = alloc::Layout::from_size_align(len, 1 << alignment).ok() else {
274 return;
275 };
276 if let Some(alloc) = unsafe { get_allocator::<A>(allocator) } {
277 unsafe { alloc.deallocate(mem, layout) };
278 }
279}
280
281#[cfg(feature = "allocator_api")]
289unsafe extern "C" fn _resize(
290 _allocator: *mut c_void,
291 _mem: *mut c_void,
292 _old_len: usize,
293 _alignment: u8,
294 _new_len: usize,
295 _ret_addr: usize,
296) -> bool {
297 false
298}
299
300#[cfg(feature = "allocator_api")]
303unsafe extern "C" fn _remap<A: alloc::Allocator>(
304 allocator: *mut c_void,
305 mem: *mut c_void,
306 old_len: usize,
307 alignment: u8,
308 new_len: usize,
309 _ret_addr: usize,
310) -> *mut c_void {
311 let mem = NonNull::new(mem.cast::<u8>());
312 let old_layout = alloc::Layout::from_size_align(old_len, 1 << alignment).ok();
313 let new_layout = alloc::Layout::from_size_align(new_len, 1 << alignment).ok();
314
315 unsafe { get_allocator::<A>(allocator) }
316 .and_then(|alloc| {
317 if new_len < old_len {
318 unsafe { alloc.shrink(mem?, old_layout?, new_layout?) }.ok()
319 } else {
320 unsafe { alloc.grow(mem?, old_layout?, new_layout?) }.ok()
321 }
322 })
323 .map(|p| p.as_ptr().cast::<c_void>())
324 .unwrap_or(std::ptr::null_mut())
325}
326
327#[inline(always)]
339#[cfg(feature = "allocator_api")]
340unsafe fn get_allocator<'a, A: alloc::Allocator>(ptr: *mut c_void) -> Option<&'a A> {
341 unsafe { ptr.cast::<A>().as_ref() }
342}