1use glib::{prelude::*, translate::*};
4
5use std::{alloc, any::TypeId, mem, ptr};
6
7use crate::{ffi, Memory};
8
9#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
12pub enum MemoryIntoInnerError {
13 #[error("Memory does not use the Rust allocator (uses {actual_allocator:?})")]
14 WrongAllocator { actual_allocator: Option<String> },
15 #[error("Memory is not writable")]
16 NotWritable,
17 #[error("Cannot extract wrapped value from sub-memory (shared memory)")]
18 SubMemory,
19 #[error("Memory does not wrap anything")]
20 NothingWrapped,
21 #[error("Memory does not wrap the requested type (expected {expected:?}, found {actual:?})")]
22 TypeMismatch { expected: TypeId, actual: TypeId },
23 #[error("Buffer must contain exactly one memory block")]
24 MultipleMemoryBlocks,
25}
26
27#[repr(C)]
28struct WrappedMemory<T> {
29 mem: ffi::GstMemory,
30
31 data: *mut u8,
33
34 layout: alloc::Layout,
36
37 wrap_offset: usize,
39 wrap_drop_in_place: unsafe fn(*mut T),
41 wrap_type_id: TypeId,
43 wrap: T,
44}
45
46unsafe extern "C" fn free(_allocator: *mut ffi::GstAllocator, mem: *mut ffi::GstMemory) {
47 let mem = mem as *mut WrappedMemory<()>;
48
49 if (*mem).wrap_offset > 0 {
50 let wrap = (mem as *mut u8).add((*mem).wrap_offset) as *mut ();
51 ((*mem).wrap_drop_in_place)(wrap);
52 }
53
54 alloc::dealloc(mem as *mut u8, (*mem).layout);
55}
56
57unsafe extern "C" fn mem_map(
58 mem: *mut ffi::GstMemory,
59 _maxsize: usize,
60 _flags: ffi::GstMapFlags,
61) -> glib::ffi::gpointer {
62 let mem = mem as *mut WrappedMemory<()>;
63
64 (*mem).data as glib::ffi::gpointer
65}
66
67unsafe extern "C" fn mem_unmap(_mem: *mut ffi::GstMemory) {}
68
69unsafe extern "C" fn mem_share(
70 mem: *mut ffi::GstMemory,
71 offset: isize,
72 size: isize,
73) -> *mut ffi::GstMemory {
74 let mem = mem as *mut WrappedMemory<()>;
75
76 let parent = if (*mem).mem.parent.is_null() {
79 mem
80 } else {
81 (*mem).mem.parent as *mut WrappedMemory<()>
82 };
83
84 let offset = offset as usize;
87 let mut size = size as usize;
88
89 let new_offset = (*mem).mem.offset.wrapping_add(offset);
90 debug_assert!(new_offset < (*mem).mem.maxsize);
91
92 if size == usize::MAX {
93 size = (*mem).mem.size.wrapping_sub(offset);
94 }
95 debug_assert!(new_offset <= usize::MAX - size);
96 debug_assert!(new_offset + size <= (*mem).mem.maxsize);
97
98 let layout = alloc::Layout::new::<WrappedMemory<()>>();
99 let sub = alloc::alloc(layout) as *mut WrappedMemory<()>;
100
101 ffi::gst_memory_init(
102 sub as *mut ffi::GstMemory,
103 (*mem).mem.mini_object.flags | ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY,
104 (*mem).mem.allocator,
105 parent as *mut ffi::GstMemory,
106 (*mem).mem.maxsize,
107 (*mem).mem.align,
108 new_offset,
109 size,
110 );
111 ptr::write(ptr::addr_of_mut!((*sub).data), (*mem).data);
112 ptr::write(ptr::addr_of_mut!((*sub).layout), layout);
113 ptr::write(ptr::addr_of_mut!((*sub).wrap_offset), 0);
114 ptr::write(ptr::addr_of_mut!((*sub).wrap_drop_in_place), |_| ());
115
116 sub as *mut ffi::GstMemory
117}
118
119unsafe extern "C" fn mem_is_span(
120 mem1: *mut ffi::GstMemory,
121 mem2: *mut ffi::GstMemory,
122 offset: *mut usize,
123) -> glib::ffi::gboolean {
124 let mem1 = mem1 as *mut WrappedMemory<()>;
125 let mem2 = mem2 as *mut WrappedMemory<()>;
126
127 if !offset.is_null() {
129 let parent = (*mem1).mem.parent as *mut WrappedMemory<()>;
130 *offset = (*mem1).mem.offset - (*parent).mem.offset;
131 }
132
133 let is_span = (*mem1).data.add((*mem1).mem.offset).add((*mem1).mem.size)
134 == (*mem2).data.add((*mem2).mem.offset);
135
136 is_span.into_glib()
137}
138
139unsafe extern "C" fn class_init(class: glib::ffi::gpointer, _class_data: glib::ffi::gpointer) {
140 let class = class as *mut ffi::GstAllocatorClass;
141
142 (*class).free = Some(free);
143}
144
145unsafe extern "C" fn instance_init(
146 obj: *mut glib::gobject_ffi::GTypeInstance,
147 _class: glib::ffi::gpointer,
148) {
149 static ALLOCATOR_TYPE: &[u8] = b"RustGlobalAllocatorMemory\0";
150
151 let allocator = obj as *mut ffi::GstAllocator;
152
153 (*allocator).mem_type = ALLOCATOR_TYPE.as_ptr() as *const _;
154 (*allocator).mem_map = Some(mem_map);
155 (*allocator).mem_unmap = Some(mem_unmap);
156 (*allocator).mem_share = Some(mem_share);
158 (*allocator).mem_is_span = Some(mem_is_span);
159
160 (*allocator).object.flags |= ffi::GST_ALLOCATOR_FLAG_CUSTOM_ALLOC;
162 (*allocator).object.flags |= ffi::GST_OBJECT_FLAG_MAY_BE_LEAKED;
163}
164
165fn rust_allocator() -> &'static crate::Allocator {
166 static RUST_ALLOCATOR: std::sync::OnceLock<crate::Allocator> = std::sync::OnceLock::new();
167
168 RUST_ALLOCATOR.get_or_init(|| unsafe {
169 struct TypeInfoWrap(glib::gobject_ffi::GTypeInfo);
170 unsafe impl Send for TypeInfoWrap {}
171 unsafe impl Sync for TypeInfoWrap {}
172
173 static TYPE_INFO: TypeInfoWrap = TypeInfoWrap(glib::gobject_ffi::GTypeInfo {
174 class_size: mem::size_of::<ffi::GstAllocatorClass>() as u16,
175 base_init: None,
176 base_finalize: None,
177 class_init: Some(class_init),
178 class_finalize: None,
179 class_data: ptr::null_mut(),
180 instance_size: mem::size_of::<ffi::GstAllocator>() as u16,
181 n_preallocs: 0,
182 instance_init: Some(instance_init),
183 value_table: ptr::null(),
184 });
185
186 let type_name = {
187 let mut idx = 0;
188
189 loop {
190 let type_name = glib::gformat!("GstRsAllocator-{}", idx);
191 if glib::gobject_ffi::g_type_from_name(type_name.as_ptr())
192 == glib::gobject_ffi::G_TYPE_INVALID
193 {
194 break type_name;
195 }
196 idx += 1;
197 }
198 };
199
200 let t = glib::gobject_ffi::g_type_register_static(
201 crate::Allocator::static_type().into_glib(),
202 type_name.as_ptr(),
203 &TYPE_INFO.0,
204 0,
205 );
206
207 assert!(t != glib::gobject_ffi::G_TYPE_INVALID);
208
209 from_glib_none(
210 glib::gobject_ffi::g_object_newv(t, 0, ptr::null_mut()) as *mut ffi::GstAllocator
211 )
212 })
213}
214
215#[inline]
218pub(crate) unsafe fn try_into_from_memory_ptr<T: 'static>(
219 mem_ptr: *mut ffi::GstMemory,
220) -> Result<T, MemoryIntoInnerError> {
221 skip_assert_initialized!();
222
223 if (*mem_ptr).allocator.is_null() || (*mem_ptr).allocator != rust_allocator().as_ptr() {
225 let actual_allocator = if (*mem_ptr).allocator.is_null() {
226 None
227 } else {
228 Some(
229 std::ffi::CStr::from_ptr(glib::gobject_ffi::g_type_name_from_instance(
230 (*mem_ptr).allocator as *mut glib::gobject_ffi::GTypeInstance,
231 ))
232 .to_string_lossy()
233 .to_string(),
234 )
235 };
236 return Err(MemoryIntoInnerError::WrongAllocator { actual_allocator });
237 }
238
239 if ffi::gst_mini_object_is_writable(mem_ptr as *mut ffi::GstMiniObject) == glib::ffi::GFALSE {
240 return Err(MemoryIntoInnerError::NotWritable);
241 }
242
243 if !(*mem_ptr).parent.is_null() {
245 return Err(MemoryIntoInnerError::SubMemory);
246 }
247
248 let mem_wrapper = &*(mem_ptr as *mut WrappedMemory<T>);
253
254 if mem_wrapper.wrap_offset == 0 {
256 return Err(MemoryIntoInnerError::NothingWrapped);
257 }
258
259 if mem_wrapper.wrap_type_id != TypeId::of::<T>() {
262 return Err(MemoryIntoInnerError::TypeMismatch {
263 expected: std::any::TypeId::of::<T>(),
264 actual: mem_wrapper.wrap_type_id,
265 });
266 }
267
268 let mem_wrapper_mut = &mut *(mem_ptr as *mut WrappedMemory<T>);
270 let value = ptr::read(&mem_wrapper_mut.wrap);
271
272 mem_wrapper_mut.wrap_offset = 0;
274
275 Ok(value)
276}
277
278impl Memory {
279 #[doc(alias = "gst_memory_new_wrapped")]
280 #[doc(alias = "gst_memory_new_wrapped_full")]
281 #[inline]
282 pub fn from_slice<T: AsRef<[u8]> + Send + 'static>(slice: T) -> Self {
283 assert_initialized_main_thread!();
284
285 let len = slice.as_ref().len();
286 unsafe {
287 let layout = alloc::Layout::new::<WrappedMemory<T>>();
288 let mem = alloc::alloc(layout) as *mut WrappedMemory<T>;
289
290 ffi::gst_memory_init(
291 mem as *mut ffi::GstMemory,
292 ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY,
293 rust_allocator().to_glib_none().0,
294 ptr::null_mut(),
295 len,
296 0,
297 0,
298 len,
299 );
300
301 ptr::write(ptr::addr_of_mut!((*mem).wrap), slice);
302
303 assert_eq!(len, (*mem).wrap.as_ref().len());
304 let data = (*mem).wrap.as_ref().as_ptr();
305 ptr::write(ptr::addr_of_mut!((*mem).data), mut_override(data));
306
307 ptr::write(ptr::addr_of_mut!((*mem).layout), layout);
308
309 let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize;
310 ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset);
311
312 ptr::write(
313 ptr::addr_of_mut!((*mem).wrap_drop_in_place),
314 ptr::drop_in_place::<T>,
315 );
316
317 ptr::write(ptr::addr_of_mut!((*mem).wrap_type_id), TypeId::of::<T>());
318
319 from_glib_full(mem as *mut ffi::GstMemory)
320 }
321 }
322
323 #[doc(alias = "gst_memory_new_wrapped")]
324 #[doc(alias = "gst_memory_new_wrapped_full")]
325 #[inline]
326 pub fn from_mut_slice<T: AsMut<[u8]> + Send + 'static>(mut slice: T) -> Self {
327 assert_initialized_main_thread!();
328
329 let len = slice.as_mut().len();
330 unsafe {
331 let layout = alloc::Layout::new::<WrappedMemory<T>>();
332 let mem = alloc::alloc(layout) as *mut WrappedMemory<T>;
333
334 ffi::gst_memory_init(
335 mem as *mut ffi::GstMemory,
336 0,
337 rust_allocator().to_glib_none().0,
338 ptr::null_mut(),
339 len,
340 0,
341 0,
342 len,
343 );
344
345 ptr::write(ptr::addr_of_mut!((*mem).wrap), slice);
346
347 assert_eq!(len, (*mem).wrap.as_mut().len());
348 let data = (*mem).wrap.as_mut().as_mut_ptr();
349 ptr::write(ptr::addr_of_mut!((*mem).data), data);
350
351 ptr::write(ptr::addr_of_mut!((*mem).layout), layout);
352
353 let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize;
354 ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset);
355
356 ptr::write(
357 ptr::addr_of_mut!((*mem).wrap_drop_in_place),
358 ptr::drop_in_place::<T>,
359 );
360
361 ptr::write(ptr::addr_of_mut!((*mem).wrap_type_id), TypeId::of::<T>());
362
363 from_glib_full(mem as *mut ffi::GstMemory)
364 }
365 }
366
367 #[inline]
393 pub fn try_into_inner<T: 'static>(self) -> Result<T, (Self, MemoryIntoInnerError)> {
394 unsafe {
396 let mem_ptr = self.as_mut_ptr();
397
398 match try_into_from_memory_ptr(mem_ptr) {
400 Ok(value) => {
401 drop(self);
404 Ok(value)
405 }
406 Err(err) => {
407 Err((self, err))
409 }
410 }
411 }
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 #[test]
420 fn test_wrap_vec_u8() {
421 crate::init().unwrap();
422
423 let data = vec![1u8, 2, 3, 4, 5];
424 let expected = data.clone();
425
426 let mem = Memory::from_slice(data);
427 assert_eq!(mem.size(), 5);
428
429 let extracted: Vec<u8> = mem.try_into_inner().unwrap();
430 assert_eq!(extracted, expected);
431 }
432}