1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use glib::{prelude::*, translate::*}; |
4 | |
5 | use std::{alloc, mem, ptr}; |
6 | |
7 | use crate::{ffi, Memory}; |
8 | |
9 | #[repr (C)] |
10 | struct 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 | |
26 | unsafe extern "C" fn free(_allocator: *mut ffi::GstAllocator, mem: *mut ffi::GstMemory) { |
27 | let mem: *mut WrappedMemory<()> = mem as *mut WrappedMemory<()>; |
28 | |
29 | if (*mem).wrap_offset > 0 { |
30 | let wrap: *mut () = (mem as *mut u8).add((*mem).wrap_offset) as *mut (); |
31 | ((*mem).wrap_drop_in_place)(wrap); |
32 | } |
33 | |
34 | alloc::dealloc(ptr:mem as *mut u8, (*mem).layout); |
35 | } |
36 | |
37 | unsafe extern "C" fn mem_map( |
38 | mem: *mut ffi::GstMemory, |
39 | _maxsize: usize, |
40 | _flags: ffi::GstMapFlags, |
41 | ) -> glib::ffi::gpointer { |
42 | let mem: *mut WrappedMemory<()> = mem as *mut WrappedMemory<()>; |
43 | |
44 | (*mem).data as glib::ffi::gpointer |
45 | } |
46 | |
47 | unsafe extern "C" fn mem_unmap(_mem: *mut ffi::GstMemory) {} |
48 | |
49 | unsafe 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 | |
99 | unsafe 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: *mut WrappedMemory<()> = mem1 as *mut WrappedMemory<()>; |
105 | let mem2: *mut WrappedMemory<()> = mem2 as *mut WrappedMemory<()>; |
106 | |
107 | // Basically a re-implementation of _sysmem_is_span() |
108 | if !offset.is_null() { |
109 | let parent: *mut WrappedMemory<()> = (*mem1).mem.parent as *mut WrappedMemory<()>; |
110 | *offset = (*mem1).mem.offset - (*parent).mem.offset; |
111 | } |
112 | |
113 | let is_span: bool = (*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 | |
119 | unsafe extern "C" fn class_init(class: glib::ffi::gpointer, _class_data: glib::ffi::gpointer) { |
120 | let class: *mut GstAllocatorClass = class as *mut ffi::GstAllocatorClass; |
121 | |
122 | (*class).free = Some(free); |
123 | } |
124 | |
125 | unsafe 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: *mut GstAllocator = 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 | |
144 | fn 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 | |
194 | impl 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 | } |
279 | |