1use std::ptr;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6
7use super::prelude::*;
8use crate::{AllocationParams, Allocator, ffi};
9
10pub unsafe trait AllocatorImpl:
11 GstObjectImpl + ObjectSubclass<Type: IsA<Allocator>>
12{
13 unsafe fn alloc(&self, size: usize, params: &AllocationParams) -> *mut ffi::GstMemory {
37 unsafe { self.parent_alloc(size, params) }
38 }
39
40 unsafe fn free(&self, memory: *mut ffi::GstMemory) {
41 unsafe { self.parent_free(memory) }
42 }
43}
44
45pub trait AllocatorImplExt: AllocatorImpl {
46 unsafe fn parent_alloc(&self, size: usize, params: &AllocationParams) -> *mut ffi::GstMemory {
47 unsafe {
48 let data = Self::type_data();
49 let parent_class = data.as_ref().parent_class() as *mut ffi::GstAllocatorClass;
50
51 if let Some(f) = (*parent_class).alloc {
52 f(
53 self.obj().unsafe_cast_ref::<Allocator>().to_glib_none().0,
54 size,
55 mut_override(params.to_glib_none().0),
56 )
57 } else {
58 ptr::null_mut()
59 }
60 }
61 }
62
63 unsafe fn parent_free(&self, memory: *mut ffi::GstMemory) {
64 unsafe {
65 let data = Self::type_data();
66 let parent_class = data.as_ref().parent_class() as *mut ffi::GstAllocatorClass;
67
68 if let Some(f) = (*parent_class).free {
69 f(
70 self.obj().unsafe_cast_ref::<Allocator>().to_glib_none().0,
71 memory,
72 )
73 }
74 }
75 }
76}
77
78impl<T: AllocatorImpl> AllocatorImplExt for T {}
79
80unsafe impl<T: AllocatorImpl> IsSubclassable<T> for Allocator {
81 fn class_init(klass: &mut glib::Class<Self>) {
82 Self::parent_class_init::<T>(klass);
83 let klass = klass.as_mut();
84 klass.alloc = Some(alloc::<T>);
85 klass.free = Some(free::<T>);
86 }
87}
88
89unsafe extern "C" fn alloc<T: AllocatorImpl>(
90 ptr: *mut ffi::GstAllocator,
91 size: usize,
92 params: *mut ffi::GstAllocationParams,
93) -> *mut ffi::GstMemory {
94 unsafe {
95 let instance = &*(ptr as *mut T::Instance);
96 let imp = instance.imp();
97
98 let params = &*(params as *mut AllocationParams);
99
100 imp.alloc(size, params)
101 }
102}
103
104unsafe extern "C" fn free<T: AllocatorImpl>(
105 ptr: *mut ffi::GstAllocator,
106 memory: *mut ffi::GstMemory,
107) {
108 unsafe {
109 debug_assert_eq!((*memory).mini_object.refcount, 0);
110
111 let instance = &*(ptr as *mut T::Instance);
112 let imp = instance.imp();
113
114 imp.free(memory);
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::prelude::*;
122
123 pub mod imp {
128 use std::alloc;
129
130 use super::*;
131
132 #[repr(C)]
133 struct Memory {
134 mem: ffi::GstMemory,
135 layout: alloc::Layout,
136 data: *mut u8,
137 }
138
139 const LAYOUT: alloc::Layout = alloc::Layout::new::<Memory>();
140
141 #[derive(Default)]
142 pub struct TestAllocator;
143
144 impl ObjectImpl for TestAllocator {}
145 impl GstObjectImpl for TestAllocator {}
146 unsafe impl AllocatorImpl for TestAllocator {
147 unsafe fn alloc(&self, size: usize, params: &AllocationParams) -> *mut ffi::GstMemory {
148 unsafe {
149 let Some(maxsize) = size
150 .checked_add(params.prefix())
151 .and_then(|s| s.checked_add(params.padding()))
152 else {
153 return ptr::null_mut();
154 };
155
156 let align = params.align() | crate::Memory::default_alignment();
157 let Ok(layout) = alloc::Layout::from_size_align(maxsize, align + 1) else {
158 return ptr::null_mut();
159 };
160
161 let mem = alloc::alloc(LAYOUT) as *mut Memory;
162
163 let data = alloc::alloc(layout);
164
165 if params.prefix() > 0
166 && params.flags().contains(crate::MemoryFlags::ZERO_PREFIXED)
167 {
168 ptr::write_bytes(data, 0, params.prefix());
169 }
170
171 if params.flags().contains(crate::MemoryFlags::ZERO_PADDED) {
172 ptr::write_bytes(data.add(params.prefix()).add(size), 0, params.padding());
173 }
174
175 ffi::gst_memory_init(
176 ptr::addr_of_mut!((*mem).mem),
177 params.flags().into_glib(),
178 self.obj().as_ptr() as *mut ffi::GstAllocator,
179 ptr::null_mut(),
180 maxsize,
181 params.align(),
182 params.prefix(),
183 size,
184 );
185 ptr::write(ptr::addr_of_mut!((*mem).layout), layout);
186 ptr::write(ptr::addr_of_mut!((*mem).data), data);
187
188 mem as *mut ffi::GstMemory
189 }
190 }
191
192 unsafe fn free(&self, mem: *mut ffi::GstMemory) {
193 unsafe {
194 let mem = mem as *mut Memory;
195
196 if (*mem).mem.parent.is_null() {
197 alloc::dealloc((*mem).data, (*mem).layout);
198 ptr::drop_in_place(ptr::addr_of_mut!((*mem).layout));
199 }
200 alloc::dealloc(mem as *mut u8, LAYOUT);
201 }
202 }
203 }
204
205 #[glib::object_subclass]
206 impl ObjectSubclass for TestAllocator {
207 const NAME: &'static str = "TestAllocator";
208 type Type = super::TestAllocator;
209 type ParentType = Allocator;
210
211 fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
212 static ALLOCATOR_TYPE: &[u8] = b"TestAllocatorMemory\0";
213
214 unsafe {
215 let allocator = obj.as_ptr() as *mut ffi::GstAllocator;
216
217 (*allocator).mem_type = ALLOCATOR_TYPE.as_ptr() as *const _;
219 (*allocator).mem_map = Some(TestAllocator::mem_map);
220 (*allocator).mem_unmap = Some(TestAllocator::mem_unmap);
221 (*allocator).mem_share = Some(TestAllocator::mem_share);
223 (*allocator).mem_is_span = Some(TestAllocator::mem_is_span);
224 }
225 }
226 }
227
228 impl TestAllocator {
229 unsafe extern "C" fn mem_map(
230 mem: *mut ffi::GstMemory,
231 _maxsize: usize,
232 _flags: ffi::GstMapFlags,
233 ) -> glib::ffi::gpointer {
234 unsafe {
235 let mem = mem as *mut Memory;
236
237 let parent = if (*mem).mem.parent.is_null() {
238 mem
239 } else {
240 (*mem).mem.parent as *mut Memory
241 };
242
243 (*parent).data as *mut _
245 }
246 }
247
248 unsafe extern "C" fn mem_unmap(_mem: *mut ffi::GstMemory) {}
249
250 unsafe extern "C" fn mem_share(
251 mem: *mut ffi::GstMemory,
252 offset: isize,
253 size: isize,
254 ) -> *mut ffi::GstMemory {
255 unsafe {
256 let mem = mem as *mut Memory;
257
258 let parent = if (*mem).mem.parent.is_null() {
261 mem
262 } else {
263 (*mem).mem.parent as *mut Memory
264 };
265
266 let offset = offset as usize;
269 let mut size = size as usize;
270
271 let new_offset = (*mem).mem.offset.wrapping_add(offset);
272 debug_assert!(new_offset < (*mem).mem.maxsize);
273
274 if size == usize::MAX {
275 size = (*mem).mem.size.wrapping_sub(offset);
276 }
277 debug_assert!(new_offset <= usize::MAX - size);
278 debug_assert!(new_offset + size <= (*mem).mem.maxsize);
279
280 let sub = alloc::alloc(LAYOUT) as *mut Memory;
281
282 ffi::gst_memory_init(
283 sub as *mut ffi::GstMemory,
284 (*mem).mem.mini_object.flags | ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY,
285 (*mem).mem.allocator,
286 parent as *mut ffi::GstMemory,
287 (*mem).mem.maxsize,
288 (*mem).mem.align,
289 new_offset,
290 size,
291 );
292 ptr::write(ptr::addr_of_mut!((*sub).data), ptr::null_mut());
294
295 sub as *mut ffi::GstMemory
296 }
297 }
298
299 unsafe extern "C" fn mem_is_span(
300 mem1: *mut ffi::GstMemory,
301 mem2: *mut ffi::GstMemory,
302 offset: *mut usize,
303 ) -> glib::ffi::gboolean {
304 unsafe {
305 let mem1 = mem1 as *mut Memory;
306 let mem2 = mem2 as *mut Memory;
307
308 let parent1 = (*mem1).mem.parent as *mut Memory;
310 let parent2 = (*mem2).mem.parent as *mut Memory;
311 debug_assert_eq!(parent1, parent2);
312
313 if !offset.is_null() {
314 *offset = (*mem1).mem.offset.wrapping_sub((*parent1).mem.offset);
319 }
320
321 let is_span = ((*mem1).mem.offset + ((*mem1).mem.size)) == (*mem2).mem.offset;
323
324 is_span.into_glib()
325 }
326 }
327 }
328 }
329
330 glib::wrapper! {
331 pub struct TestAllocator(ObjectSubclass<imp::TestAllocator>) @extends Allocator, crate::Object;
332 }
333
334 impl Default for TestAllocator {
335 fn default() -> Self {
336 glib::Object::new()
337 }
338 }
339
340 #[test]
341 fn test_allocator_registration() {
342 crate::init().unwrap();
343
344 const TEST_ALLOCATOR_NAME: &str = "TestAllocator";
345
346 let allocator = TestAllocator::default();
347 Allocator::register(TEST_ALLOCATOR_NAME, allocator);
348
349 let allocator = Allocator::find(Some(TEST_ALLOCATOR_NAME));
350
351 assert!(allocator.is_some());
352 }
353
354 #[test]
355 fn test_allocator_alloc() {
356 crate::init().unwrap();
357
358 const SIZE: usize = 1024;
359
360 let allocator = TestAllocator::default();
361
362 let memory = allocator.alloc(SIZE, None).unwrap();
363
364 assert_eq!(memory.size(), SIZE);
365 }
366
367 #[test]
368 fn test_allocator_mem_ops() {
369 crate::init().unwrap();
370
371 let data = [0, 1, 2, 3, 4, 5, 6, 7];
372
373 let allocator = TestAllocator::default();
374
375 let mut memory = allocator.alloc(data.len(), None).unwrap();
376 assert_eq!(memory.size(), data.len());
377
378 {
379 let memory = memory.get_mut().unwrap();
380 let mut map = memory.map_writable().unwrap();
381 map.copy_from_slice(&data);
382 }
383
384 let copy = memory.copy();
385 assert!(copy.parent().is_none());
386
387 {
388 let map1 = memory.map_readable().unwrap();
389 let map2 = copy.map_readable().unwrap();
390 assert_eq!(map1.as_slice(), map2.as_slice());
391 }
392
393 let share = memory.share(..);
394 assert_eq!(share.parent().unwrap().as_ptr(), memory.as_ptr());
395
396 {
397 let map1 = memory.map_readable().unwrap();
398 let map2 = share.map_readable().unwrap();
399 assert_eq!(map1.as_slice(), map2.as_slice());
400 }
401
402 let sub1 = memory.share(..2);
403 assert_eq!(sub1.size(), 2);
404 assert_eq!(sub1.parent().unwrap().as_ptr(), memory.as_ptr());
405
406 {
407 let map = sub1.map_readable().unwrap();
408 assert_eq!(map.as_slice(), &data[..2]);
409 }
410
411 let sub2 = memory.share(2..);
412 assert_eq!(sub2.size(), 6);
413 assert_eq!(sub2.parent().unwrap().as_ptr(), memory.as_ptr());
414
415 {
416 let map = sub2.map_readable().unwrap();
417 assert_eq!(map.as_slice(), &data[2..]);
418 }
419
420 let offset = sub1.is_span(&sub2).unwrap();
421 assert_eq!(offset, 0);
422
423 let sub3 = sub2.share(2..);
424 assert_eq!(sub3.size(), 4);
425 assert_eq!(sub3.parent().unwrap().as_ptr(), memory.as_ptr());
426
427 {
428 let map = sub3.map_readable().unwrap();
429 assert_eq!(map.as_slice(), &data[4..]);
430 }
431 }
432}