gstreamer/
memory_wrapped.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::{prelude::*, translate::*};
4
5use std::{alloc, mem, ptr};
6
7use crate::{ffi, Memory};
8
9#[repr(C)]
10struct WrappedMemory<T> {
11    mem: ffi::GstMemory,
12
13    // AsRef / AsMut values
14    data: *mut u8,
15
16    // Layout used for allocating this struct, literally `Layout::new<Self>`
17    layout: alloc::Layout,
18
19    // Offset from the beginning of the struct until `wrap`
20    wrap_offset: usize,
21    // `ptr::drop_in_place()` for `T`
22    wrap_drop_in_place: unsafe fn(*mut T),
23    wrap: T,
24}
25
26unsafe extern "C" fn free(_allocator: *mut ffi::GstAllocator, mem: *mut ffi::GstMemory) {
27    let mem = mem as *mut WrappedMemory<()>;
28
29    if (*mem).wrap_offset > 0 {
30        let wrap = (mem as *mut u8).add((*mem).wrap_offset) as *mut ();
31        ((*mem).wrap_drop_in_place)(wrap);
32    }
33
34    alloc::dealloc(mem as *mut u8, (*mem).layout);
35}
36
37unsafe extern "C" fn mem_map(
38    mem: *mut ffi::GstMemory,
39    _maxsize: usize,
40    _flags: ffi::GstMapFlags,
41) -> glib::ffi::gpointer {
42    let mem = mem as *mut WrappedMemory<()>;
43
44    (*mem).data as glib::ffi::gpointer
45}
46
47unsafe extern "C" fn mem_unmap(_mem: *mut ffi::GstMemory) {}
48
49unsafe extern "C" fn mem_share(
50    mem: *mut ffi::GstMemory,
51    offset: isize,
52    size: isize,
53) -> *mut ffi::GstMemory {
54    let mem = mem as *mut WrappedMemory<()>;
55
56    // Basically a re-implementation of _sysmem_share()
57
58    let parent = if (*mem).mem.parent.is_null() {
59        mem
60    } else {
61        (*mem).mem.parent as *mut WrappedMemory<()>
62    };
63
64    // Offset and size are actually usizes and the API assumes that negative values simply wrap
65    // around, so let's cast to usizes here and do wrapping arithmetic.
66    let offset = offset as usize;
67    let mut size = size as usize;
68
69    let new_offset = (*mem).mem.offset.wrapping_add(offset);
70    debug_assert!(new_offset < (*mem).mem.maxsize);
71
72    if size == usize::MAX {
73        size = (*mem).mem.size.wrapping_sub(offset);
74    }
75    debug_assert!(new_offset <= usize::MAX - size);
76    debug_assert!(new_offset + size <= (*mem).mem.maxsize);
77
78    let layout = alloc::Layout::new::<WrappedMemory<()>>();
79    let sub = alloc::alloc(layout) as *mut WrappedMemory<()>;
80
81    ffi::gst_memory_init(
82        sub as *mut ffi::GstMemory,
83        (*mem).mem.mini_object.flags | ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY,
84        (*mem).mem.allocator,
85        parent as *mut ffi::GstMemory,
86        (*mem).mem.maxsize,
87        (*mem).mem.align,
88        new_offset,
89        size,
90    );
91    ptr::write(ptr::addr_of_mut!((*sub).data), (*mem).data);
92    ptr::write(ptr::addr_of_mut!((*sub).layout), layout);
93    ptr::write(ptr::addr_of_mut!((*sub).wrap_offset), 0);
94    ptr::write(ptr::addr_of_mut!((*sub).wrap_drop_in_place), |_| ());
95
96    sub as *mut ffi::GstMemory
97}
98
99unsafe extern "C" fn mem_is_span(
100    mem1: *mut ffi::GstMemory,
101    mem2: *mut ffi::GstMemory,
102    offset: *mut usize,
103) -> glib::ffi::gboolean {
104    let mem1 = mem1 as *mut WrappedMemory<()>;
105    let mem2 = mem2 as *mut WrappedMemory<()>;
106
107    // Basically a re-implementation of _sysmem_is_span()
108    if !offset.is_null() {
109        let parent = (*mem1).mem.parent as *mut WrappedMemory<()>;
110        *offset = (*mem1).mem.offset - (*parent).mem.offset;
111    }
112
113    let is_span = (*mem1).data.add((*mem1).mem.offset).add((*mem1).mem.size)
114        == (*mem2).data.add((*mem2).mem.offset);
115
116    is_span.into_glib()
117}
118
119unsafe extern "C" fn class_init(class: glib::ffi::gpointer, _class_data: glib::ffi::gpointer) {
120    let class = class as *mut ffi::GstAllocatorClass;
121
122    (*class).free = Some(free);
123}
124
125unsafe extern "C" fn instance_init(
126    obj: *mut glib::gobject_ffi::GTypeInstance,
127    _class: glib::ffi::gpointer,
128) {
129    static ALLOCATOR_TYPE: &[u8] = b"RustGlobalAllocatorMemory\0";
130
131    let allocator = obj as *mut ffi::GstAllocator;
132
133    (*allocator).mem_type = ALLOCATOR_TYPE.as_ptr() as *const _;
134    (*allocator).mem_map = Some(mem_map);
135    (*allocator).mem_unmap = Some(mem_unmap);
136    // mem_copy not set because the fallback already does the right thing
137    (*allocator).mem_share = Some(mem_share);
138    (*allocator).mem_is_span = Some(mem_is_span);
139
140    // TODO: Could also implement alloc()
141    (*allocator).object.flags |= ffi::GST_ALLOCATOR_FLAG_CUSTOM_ALLOC;
142}
143
144fn rust_allocator() -> &'static crate::Allocator {
145    static RUST_ALLOCATOR: std::sync::OnceLock<crate::Allocator> = std::sync::OnceLock::new();
146
147    RUST_ALLOCATOR.get_or_init(|| unsafe {
148        struct TypeInfoWrap(glib::gobject_ffi::GTypeInfo);
149        unsafe impl Send for TypeInfoWrap {}
150        unsafe impl Sync for TypeInfoWrap {}
151
152        static TYPE_INFO: TypeInfoWrap = TypeInfoWrap(glib::gobject_ffi::GTypeInfo {
153            class_size: mem::size_of::<ffi::GstAllocatorClass>() as u16,
154            base_init: None,
155            base_finalize: None,
156            class_init: Some(class_init),
157            class_finalize: None,
158            class_data: ptr::null_mut(),
159            instance_size: mem::size_of::<ffi::GstAllocator>() as u16,
160            n_preallocs: 0,
161            instance_init: Some(instance_init),
162            value_table: ptr::null(),
163        });
164
165        let type_name = {
166            let mut idx = 0;
167
168            loop {
169                let type_name = glib::gformat!("GstRsAllocator-{}", idx);
170                if glib::gobject_ffi::g_type_from_name(type_name.as_ptr())
171                    == glib::gobject_ffi::G_TYPE_INVALID
172                {
173                    break type_name;
174                }
175                idx += 1;
176            }
177        };
178
179        let t = glib::gobject_ffi::g_type_register_static(
180            crate::Allocator::static_type().into_glib(),
181            type_name.as_ptr(),
182            &TYPE_INFO.0,
183            0,
184        );
185
186        assert!(t != glib::gobject_ffi::G_TYPE_INVALID);
187
188        from_glib_none(
189            glib::gobject_ffi::g_object_newv(t, 0, ptr::null_mut()) as *mut ffi::GstAllocator
190        )
191    })
192}
193
194impl Memory {
195    #[doc(alias = "gst_memory_new_wrapped")]
196    #[doc(alias = "gst_memory_new_wrapped_full")]
197    #[inline]
198    pub fn from_slice<T: AsRef<[u8]> + Send + 'static>(slice: T) -> Self {
199        assert_initialized_main_thread!();
200
201        let len = slice.as_ref().len();
202        unsafe {
203            let layout = alloc::Layout::new::<WrappedMemory<T>>();
204            let mem = alloc::alloc(layout) as *mut WrappedMemory<T>;
205
206            ffi::gst_memory_init(
207                mem as *mut ffi::GstMemory,
208                ffi::GST_MINI_OBJECT_FLAG_LOCK_READONLY,
209                rust_allocator().to_glib_none().0,
210                ptr::null_mut(),
211                len,
212                0,
213                0,
214                len,
215            );
216
217            ptr::write(ptr::addr_of_mut!((*mem).wrap), slice);
218
219            assert_eq!(len, (*mem).wrap.as_ref().len());
220            let data = (*mem).wrap.as_ref().as_ptr();
221            ptr::write(ptr::addr_of_mut!((*mem).data), mut_override(data));
222
223            ptr::write(ptr::addr_of_mut!((*mem).layout), layout);
224
225            let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize;
226            ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset);
227
228            ptr::write(
229                ptr::addr_of_mut!((*mem).wrap_drop_in_place),
230                ptr::drop_in_place::<T>,
231            );
232
233            from_glib_full(mem as *mut ffi::GstMemory)
234        }
235    }
236
237    #[doc(alias = "gst_memory_new_wrapped")]
238    #[doc(alias = "gst_memory_new_wrapped_full")]
239    #[inline]
240    pub fn from_mut_slice<T: AsMut<[u8]> + Send + 'static>(mut slice: T) -> Self {
241        assert_initialized_main_thread!();
242
243        let len = slice.as_mut().len();
244        unsafe {
245            let layout = alloc::Layout::new::<WrappedMemory<T>>();
246            let mem = alloc::alloc(layout) as *mut WrappedMemory<T>;
247
248            ffi::gst_memory_init(
249                mem as *mut ffi::GstMemory,
250                0,
251                rust_allocator().to_glib_none().0,
252                ptr::null_mut(),
253                len,
254                0,
255                0,
256                len,
257            );
258
259            ptr::write(ptr::addr_of_mut!((*mem).wrap), slice);
260
261            assert_eq!(len, (*mem).wrap.as_mut().len());
262            let data = (*mem).wrap.as_mut().as_mut_ptr();
263            ptr::write(ptr::addr_of_mut!((*mem).data), data);
264
265            ptr::write(ptr::addr_of_mut!((*mem).layout), layout);
266
267            let wrap_offset = ptr::addr_of!((*mem).wrap) as usize - mem as usize;
268            ptr::write(ptr::addr_of_mut!((*mem).wrap_offset), wrap_offset);
269
270            ptr::write(
271                ptr::addr_of_mut!((*mem).wrap_drop_in_place),
272                ptr::drop_in_place::<T>,
273            );
274
275            from_glib_full(mem as *mut ffi::GstMemory)
276        }
277    }
278}