Skip to main content

modelio/
mesh_buffer.rs

1use std::panic::AssertUnwindSafe;
2use std::ptr;
3
4use crate::error::{ModelIoError, Result};
5use crate::ffi;
6use crate::handle::ObjectHandle;
7use crate::mesh::MeshBuffer;
8use crate::types::MeshBufferType;
9use crate::util::required_handle;
10
11type MeshBufferAllocatorCallbackFn =
12    dyn Fn(MeshBufferAllocatorEvent) -> MeshBufferAllocatorResponse + Send + Sync + 'static;
13
14struct MeshBufferAllocatorCallback {
15    callback: Box<MeshBufferAllocatorCallbackFn>,
16}
17
18#[derive(Debug, Clone)]
19/// Describes one `MDLMeshBufferAllocator` protocol request routed into Rust.
20pub enum MeshBufferAllocatorEvent {
21    /// Allocates a new zone with the requested capacity.
22    NewZone { capacity: usize },
23    /// Allocates a zone sized for the requested buffer sizes and types.
24    NewZoneForBuffers {
25        sizes: Vec<usize>,
26        types: Vec<MeshBufferType>,
27    },
28    /// Allocates a buffer with the requested length and type.
29    NewBuffer {
30        length: usize,
31        buffer_type: MeshBufferType,
32    },
33    /// Allocates a buffer initialized from the provided bytes.
34    NewBufferWithData {
35        data: Vec<u8>,
36        buffer_type: MeshBufferType,
37    },
38    /// Allocates a buffer from the provided zone.
39    NewBufferFromZone {
40        zone: Option<MeshBufferZone>,
41        length: usize,
42        buffer_type: MeshBufferType,
43    },
44    /// Allocates a buffer from the provided zone and initial bytes.
45    NewBufferFromZoneWithData {
46        zone: Option<MeshBufferZone>,
47        data: Vec<u8>,
48        buffer_type: MeshBufferType,
49    },
50}
51
52#[derive(Debug, Clone)]
53/// Returns the result of one `MDLMeshBufferAllocator` protocol request.
54pub enum MeshBufferAllocatorResponse {
55    /// Returns an optional zone result.
56    Zone(Option<MeshBufferZone>),
57    /// Returns an optional buffer result.
58    Buffer(Option<MeshBuffer>),
59    /// Indicates that the callback did not provide a custom result.
60    None,
61}
62
63fn callback_response(
64    context: *mut core::ffi::c_void,
65    event: MeshBufferAllocatorEvent,
66) -> Option<MeshBufferAllocatorResponse> {
67    let context = (!context.is_null()).then_some(context.cast::<MeshBufferAllocatorCallback>())?;
68    std::panic::catch_unwind(AssertUnwindSafe(|| {
69        // SAFETY: The unsafe operation is valid in this context.
70        (unsafe { &*context }.callback)(event)
71    }))
72    .ok()
73}
74
75fn zone_from_retained_ptr(ptr: *mut core::ffi::c_void) -> Option<MeshBufferZone> {
76    // SAFETY: The unsafe operation is valid in this context.
77    unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferZone::from_handle)
78}
79
80fn zone_ptr_from_response(response: Option<MeshBufferAllocatorResponse>) -> *mut core::ffi::c_void {
81    match response {
82        Some(MeshBufferAllocatorResponse::Zone(Some(zone))) =>
83        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
84        unsafe { ffi::mdl_object_retain(zone.as_ptr()) },
85        _ => ptr::null_mut(),
86    }
87}
88
89fn buffer_ptr_from_response(
90    response: Option<MeshBufferAllocatorResponse>,
91) -> *mut core::ffi::c_void {
92    match response {
93        Some(MeshBufferAllocatorResponse::Buffer(Some(buffer))) =>
94        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
95        unsafe { ffi::mdl_object_retain(buffer.as_ptr()) },
96        _ => ptr::null_mut(),
97    }
98}
99
100fn mesh_buffer_type(raw: u32) -> Option<MeshBufferType> {
101    MeshBufferType::from_raw(raw)
102}
103
104#[no_mangle]
105pub extern "C" fn mdlx_mesh_buffer_allocator_new_zone(
106    context: *mut core::ffi::c_void,
107    capacity: u64,
108) -> *mut core::ffi::c_void {
109    zone_ptr_from_response(callback_response(
110        context,
111        MeshBufferAllocatorEvent::NewZone {
112            capacity: capacity as usize,
113        },
114    ))
115}
116
117#[no_mangle]
118pub extern "C" fn mdlx_mesh_buffer_allocator_new_zone_for_buffers_with_size(
119    context: *mut core::ffi::c_void,
120    sizes: *const u64,
121    types: *const u32,
122    count: u64,
123) -> *mut core::ffi::c_void {
124    let sizes = if count == 0 {
125        Vec::new()
126    } else if sizes.is_null() {
127        return ptr::null_mut();
128    } else {
129        // SAFETY: The unsafe operation is valid in this context.
130        unsafe { std::slice::from_raw_parts(sizes, count as usize) }
131            .iter()
132            .map(|size| *size as usize)
133            .collect::<Vec<_>>()
134    };
135    let Some(types) = (if count == 0 {
136        Some(Vec::new())
137    } else if types.is_null() {
138        None
139    } else {
140        // SAFETY: The unsafe operation is valid in this context.
141        unsafe { std::slice::from_raw_parts(types, count as usize) }
142            .iter()
143            .copied()
144            .map(mesh_buffer_type)
145            .collect::<Option<Vec<_>>>()
146    }) else {
147        return ptr::null_mut();
148    };
149    zone_ptr_from_response(callback_response(
150        context,
151        MeshBufferAllocatorEvent::NewZoneForBuffers { sizes, types },
152    ))
153}
154
155#[no_mangle]
156pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer(
157    context: *mut core::ffi::c_void,
158    length: u64,
159    buffer_type: u32,
160) -> *mut core::ffi::c_void {
161    let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
162        return ptr::null_mut();
163    };
164    buffer_ptr_from_response(callback_response(
165        context,
166        MeshBufferAllocatorEvent::NewBuffer {
167            length: length as usize,
168            buffer_type,
169        },
170    ))
171}
172
173#[no_mangle]
174pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_with_data(
175    context: *mut core::ffi::c_void,
176    bytes: *const u8,
177    count: u64,
178    buffer_type: u32,
179) -> *mut core::ffi::c_void {
180    let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
181        return ptr::null_mut();
182    };
183    let data = if count == 0 || bytes.is_null() {
184        Vec::new()
185    } else {
186        // SAFETY: The unsafe operation is valid in this context.
187        unsafe { std::slice::from_raw_parts(bytes, count as usize) }.to_vec()
188    };
189    buffer_ptr_from_response(callback_response(
190        context,
191        MeshBufferAllocatorEvent::NewBufferWithData { data, buffer_type },
192    ))
193}
194
195#[no_mangle]
196pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_from_zone_length(
197    context: *mut core::ffi::c_void,
198    zone: *mut core::ffi::c_void,
199    length: u64,
200    buffer_type: u32,
201) -> *mut core::ffi::c_void {
202    let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
203        return ptr::null_mut();
204    };
205    buffer_ptr_from_response(callback_response(
206        context,
207        MeshBufferAllocatorEvent::NewBufferFromZone {
208            zone: zone_from_retained_ptr(zone),
209            length: length as usize,
210            buffer_type,
211        },
212    ))
213}
214
215#[no_mangle]
216pub extern "C" fn mdlx_mesh_buffer_allocator_new_buffer_from_zone_data(
217    context: *mut core::ffi::c_void,
218    zone: *mut core::ffi::c_void,
219    bytes: *const u8,
220    count: u64,
221    buffer_type: u32,
222) -> *mut core::ffi::c_void {
223    let Some(buffer_type) = mesh_buffer_type(buffer_type) else {
224        return ptr::null_mut();
225    };
226    let data = if count == 0 || bytes.is_null() {
227        Vec::new()
228    } else {
229        // SAFETY: The unsafe operation is valid in this context.
230        unsafe { std::slice::from_raw_parts(bytes, count as usize) }.to_vec()
231    };
232    buffer_ptr_from_response(callback_response(
233        context,
234        MeshBufferAllocatorEvent::NewBufferFromZoneWithData {
235            zone: zone_from_retained_ptr(zone),
236            data,
237            buffer_type,
238        },
239    ))
240}
241
242#[no_mangle]
243pub extern "C" fn mdlx_mesh_buffer_allocator_release(context: *mut core::ffi::c_void) {
244    if context.is_null() {
245        return;
246    }
247    // SAFETY: The unsafe operation is valid in this context.
248    unsafe { drop(Box::from_raw(context.cast::<MeshBufferAllocatorCallback>())) };
249}
250
251fn release_callback_context(context: *mut core::ffi::c_void) {
252    mdlx_mesh_buffer_allocator_release(context);
253}
254
255#[derive(Debug, Clone)]
256/// Wraps the corresponding Model I/O mesh buffer map counterpart.
257pub struct MeshBufferMap {
258    handle: ObjectHandle,
259    length: usize,
260}
261
262impl MeshBufferMap {
263    fn from_handle(handle: ObjectHandle, length: usize) -> Self {
264        Self { handle, length }
265    }
266
267    /// Returns the opaque pointer used to call the wrapped Model I/O mesh buffer map counterpart.
268    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
269        self.handle.as_ptr()
270    }
271
272    #[must_use]
273    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer map counterpart.
274    pub fn length(&self) -> usize {
275        self.length
276    }
277
278    #[must_use]
279    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer map counterpart.
280    pub fn bytes(&self) -> Vec<u8> {
281        let mut bytes = vec![0_u8; self.length];
282        if bytes.is_empty() {
283            return bytes;
284        }
285        // SAFETY: The unsafe operation is valid in this context.
286        let written = unsafe {
287            ffi::mdl_mesh_buffer_map_copy_bytes(
288                self.as_ptr(),
289                self.length as u64,
290                bytes.as_mut_ptr(),
291                bytes.len() as u64,
292            )
293        } as usize;
294        bytes.truncate(written);
295        bytes
296    }
297
298    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer map counterpart.
299    pub fn write(&self, offset: usize, bytes: &[u8]) -> usize {
300        // SAFETY: The unsafe operation is valid in this context.
301        unsafe {
302            ffi::mdl_mesh_buffer_map_write_bytes(
303                self.as_ptr(),
304                self.length as u64,
305                bytes.as_ptr(),
306                bytes.len() as u64,
307                offset as u64,
308            ) as usize
309        }
310    }
311}
312
313#[derive(Debug, Clone)]
314/// Wraps the corresponding Model I/O mesh buffer allocator counterpart.
315pub struct MeshBufferAllocator {
316    handle: ObjectHandle,
317}
318
319impl MeshBufferAllocator {
320    /// Wraps a Rust callback as the corresponding Model I/O mesh buffer allocator protocol counterpart.
321    pub fn new<F>(callback: F) -> Result<Self>
322    where
323        F: Fn(MeshBufferAllocatorEvent) -> MeshBufferAllocatorResponse + Send + Sync + 'static,
324    {
325        let callback = Box::new(MeshBufferAllocatorCallback {
326            callback: Box::new(callback),
327        });
328        let callback_ptr = Box::into_raw(callback).cast::<core::ffi::c_void>();
329        let mut out_allocator = ptr::null_mut();
330        let mut out_error = ptr::null_mut();
331        // SAFETY: The unsafe operation is valid in this context.
332        let status = unsafe {
333            ffi::mdl_mesh_buffer_allocator_new_with_callback(
334                callback_ptr,
335                &mut out_allocator,
336                &mut out_error,
337            )
338        };
339        if let Err(error) = crate::util::status_result(status, out_error) {
340            release_callback_context(callback_ptr);
341            return Err(error);
342        }
343        match required_handle(out_allocator, "MDLMeshBufferAllocator") {
344            Ok(handle) => Ok(Self::from_handle(handle)),
345            Err(error) => {
346                release_callback_context(callback_ptr);
347                Err(error)
348            }
349        }
350    }
351
352    /// Builds this wrapper from the retained handle of the wrapped Model I/O mesh buffer allocator counterpart.
353    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
354        Self { handle }
355    }
356
357    /// Returns the opaque pointer used to call the wrapped Model I/O mesh buffer allocator counterpart.
358    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
359        self.handle.as_ptr()
360    }
361
362    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
363    pub fn new_zone(&self, capacity: usize) -> Result<MeshBufferZone> {
364        let mut out_zone = ptr::null_mut();
365        let mut out_error = ptr::null_mut();
366        // SAFETY: The unsafe operation is valid in this context.
367        let status = unsafe {
368            ffi::mdl_mesh_buffer_allocator_new_zone(
369                self.as_ptr(),
370                capacity as u64,
371                &mut out_zone,
372                &mut out_error,
373            )
374        };
375        crate::util::status_result(status, out_error)?;
376        Ok(MeshBufferZone::from_handle(required_handle(
377            out_zone,
378            "MDLMeshBufferZone",
379        )?))
380    }
381
382    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
383    pub fn new_zone_for_buffers(
384        &self,
385        sizes: &[usize],
386        types: &[MeshBufferType],
387    ) -> Result<MeshBufferZone> {
388        if sizes.len() != types.len() {
389            return Err(ModelIoError::new(
390                ffi::status::INVALID_ARGUMENT,
391                "mesh buffer sizes and types must have the same length",
392            ));
393        }
394        let raw_sizes = sizes.iter().map(|size| *size as u64).collect::<Vec<_>>();
395        let raw_types = types
396            .iter()
397            .map(|buffer_type| buffer_type.as_raw())
398            .collect::<Vec<_>>();
399        let mut out_zone = ptr::null_mut();
400        let mut out_error = ptr::null_mut();
401        // SAFETY: The unsafe operation is valid in this context.
402        let status = unsafe {
403            ffi::mdl_mesh_buffer_allocator_new_zone_for_buffers_with_size(
404                self.as_ptr(),
405                raw_sizes.as_ptr(),
406                raw_types.as_ptr(),
407                raw_sizes.len() as u64,
408                &mut out_zone,
409                &mut out_error,
410            )
411        };
412        crate::util::status_result(status, out_error)?;
413        Ok(MeshBufferZone::from_handle(required_handle(
414            out_zone,
415            "MDLMeshBufferZone",
416        )?))
417    }
418
419    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
420    pub fn new_buffer(&self, length: usize, buffer_type: MeshBufferType) -> Result<MeshBuffer> {
421        let mut out_buffer = ptr::null_mut();
422        let mut out_error = ptr::null_mut();
423        // SAFETY: The unsafe operation is valid in this context.
424        let status = unsafe {
425            ffi::mdl_mesh_buffer_allocator_new_buffer(
426                self.as_ptr(),
427                length as u64,
428                buffer_type.as_raw(),
429                &mut out_buffer,
430                &mut out_error,
431            )
432        };
433        crate::util::status_result(status, out_error)?;
434        Ok(MeshBuffer::from_handle(required_handle(
435            out_buffer,
436            "MDLMeshBuffer",
437        )?))
438    }
439
440    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
441    pub fn new_buffer_with_data(
442        &self,
443        data: &[u8],
444        buffer_type: MeshBufferType,
445    ) -> Result<MeshBuffer> {
446        let mut out_buffer = ptr::null_mut();
447        let mut out_error = ptr::null_mut();
448        // SAFETY: The unsafe operation is valid in this context.
449        let status = unsafe {
450            ffi::mdl_mesh_buffer_allocator_new_buffer_with_data(
451                self.as_ptr(),
452                data.as_ptr(),
453                data.len() as u64,
454                buffer_type.as_raw(),
455                &mut out_buffer,
456                &mut out_error,
457            )
458        };
459        crate::util::status_result(status, out_error)?;
460        Ok(MeshBuffer::from_handle(required_handle(
461            out_buffer,
462            "MDLMeshBuffer",
463        )?))
464    }
465
466    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
467    pub fn new_buffer_from_zone(
468        &self,
469        zone: Option<&MeshBufferZone>,
470        length: usize,
471        buffer_type: MeshBufferType,
472    ) -> Result<Option<MeshBuffer>> {
473        let mut out_buffer = ptr::null_mut();
474        let mut out_error = ptr::null_mut();
475        // SAFETY: The unsafe operation is valid in this context.
476        let status = unsafe {
477            ffi::mdl_mesh_buffer_allocator_new_buffer_from_zone_length(
478                self.as_ptr(),
479                zone.map_or(ptr::null_mut(), MeshBufferZone::as_ptr),
480                length as u64,
481                buffer_type.as_raw(),
482                &mut out_buffer,
483                &mut out_error,
484            )
485        };
486        crate::util::status_result(status, out_error)?;
487        // SAFETY: The unsafe operation is valid in this context.
488        Ok(unsafe { ObjectHandle::from_retained_ptr(out_buffer) }.map(MeshBuffer::from_handle))
489    }
490
491    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer allocator counterpart.
492    pub fn new_buffer_from_zone_with_data(
493        &self,
494        zone: Option<&MeshBufferZone>,
495        data: &[u8],
496        buffer_type: MeshBufferType,
497    ) -> Result<Option<MeshBuffer>> {
498        let mut out_buffer = ptr::null_mut();
499        let mut out_error = ptr::null_mut();
500        // SAFETY: The unsafe operation is valid in this context.
501        let status = unsafe {
502            ffi::mdl_mesh_buffer_allocator_new_buffer_from_zone_data(
503                self.as_ptr(),
504                zone.map_or(ptr::null_mut(), MeshBufferZone::as_ptr),
505                data.as_ptr(),
506                data.len() as u64,
507                buffer_type.as_raw(),
508                &mut out_buffer,
509                &mut out_error,
510            )
511        };
512        crate::util::status_result(status, out_error)?;
513        // SAFETY: The unsafe operation is valid in this context.
514        Ok(unsafe { ObjectHandle::from_retained_ptr(out_buffer) }.map(MeshBuffer::from_handle))
515    }
516}
517
518#[derive(Debug, Clone)]
519/// Wraps the corresponding Model I/O mesh buffer zone counterpart.
520pub struct MeshBufferZone {
521    handle: ObjectHandle,
522}
523
524impl MeshBufferZone {
525    /// Builds this wrapper from the retained handle of the wrapped Model I/O mesh buffer zone counterpart.
526    pub(crate) fn from_handle(handle: ObjectHandle) -> Self {
527        Self { handle }
528    }
529
530    /// Returns the opaque pointer used to call the wrapped Model I/O mesh buffer zone counterpart.
531    pub(crate) fn as_ptr(&self) -> *mut core::ffi::c_void {
532        self.handle.as_ptr()
533    }
534
535    #[must_use]
536    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone counterpart.
537    pub fn capacity(&self) -> usize {
538        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
539        unsafe { ffi::mdl_mesh_buffer_zone_capacity(self.as_ptr()) as usize }
540    }
541
542    #[must_use]
543    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone counterpart.
544    pub fn allocator(&self) -> Option<MeshBufferAllocator> {
545        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
546        let ptr = unsafe { ffi::mdl_mesh_buffer_zone_allocator(self.as_ptr()) };
547        // SAFETY: The unsafe operation is valid in this context.
548        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
549    }
550
551    #[must_use]
552    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone counterpart.
553    pub fn as_default(&self) -> Option<MeshBufferZoneDefault> {
554        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
555        (unsafe { ffi::mdl_mesh_buffer_zone_is_default(self.as_ptr()) != 0 })
556            .then(|| MeshBufferZoneDefault::from_handle(self.handle.clone()))
557    }
558}
559
560#[derive(Debug, Clone)]
561/// Wraps the corresponding Model I/O mesh buffer zone default counterpart.
562pub struct MeshBufferZoneDefault {
563    handle: ObjectHandle,
564}
565
566impl MeshBufferZoneDefault {
567    fn from_handle(handle: ObjectHandle) -> Self {
568        Self { handle }
569    }
570
571    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer zone default counterpart.
572    pub fn new() -> Result<Self> {
573        let mut out_zone = ptr::null_mut();
574        let mut out_error = ptr::null_mut();
575        let status =
576            // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
577            unsafe { ffi::mdl_mesh_buffer_zone_default_new(&mut out_zone, &mut out_error) };
578        crate::util::status_result(status, out_error)?;
579        Ok(Self::from_handle(required_handle(
580            out_zone,
581            "MDLMeshBufferZoneDefault",
582        )?))
583    }
584
585    #[must_use]
586    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone default counterpart.
587    pub fn capacity(&self) -> usize {
588        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
589        unsafe { ffi::mdl_mesh_buffer_zone_capacity(self.handle.as_ptr()) as usize }
590    }
591
592    #[must_use]
593    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone default counterpart.
594    pub fn allocator(&self) -> Option<MeshBufferAllocator> {
595        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
596        let ptr = unsafe { ffi::mdl_mesh_buffer_zone_allocator(self.handle.as_ptr()) };
597        // SAFETY: The unsafe operation is valid in this context.
598        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
599    }
600
601    #[must_use]
602    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer zone default counterpart.
603    pub fn as_mesh_buffer_zone(&self) -> MeshBufferZone {
604        MeshBufferZone::from_handle(self.handle.clone())
605    }
606}
607
608#[derive(Debug, Clone)]
609/// Wraps the corresponding Model I/O mesh buffer data counterpart.
610pub struct MeshBufferData {
611    handle: ObjectHandle,
612}
613
614impl MeshBufferData {
615    fn from_handle(handle: ObjectHandle) -> Self {
616        Self { handle }
617    }
618
619    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer data counterpart.
620    pub fn new(length: usize, buffer_type: MeshBufferType) -> Result<Self> {
621        let mut out_buffer = ptr::null_mut();
622        let mut out_error = ptr::null_mut();
623        // SAFETY: The unsafe operation is valid in this context.
624        let status = unsafe {
625            ffi::mdl_mesh_buffer_data_new(
626                length as u64,
627                buffer_type.as_raw(),
628                &mut out_buffer,
629                &mut out_error,
630            )
631        };
632        crate::util::status_result(status, out_error)?;
633        Ok(Self::from_handle(required_handle(
634            out_buffer,
635            "MDLMeshBufferData",
636        )?))
637    }
638
639    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer data counterpart.
640    pub fn from_bytes(data: &[u8], buffer_type: MeshBufferType) -> Result<Self> {
641        let mut out_buffer = ptr::null_mut();
642        let mut out_error = ptr::null_mut();
643        // SAFETY: The unsafe operation is valid in this context.
644        let status = unsafe {
645            ffi::mdl_mesh_buffer_data_new_with_bytes(
646                data.as_ptr(),
647                data.len() as u64,
648                buffer_type.as_raw(),
649                &mut out_buffer,
650                &mut out_error,
651            )
652        };
653        crate::util::status_result(status, out_error)?;
654        Ok(Self::from_handle(required_handle(
655            out_buffer,
656            "MDLMeshBufferData",
657        )?))
658    }
659
660    #[must_use]
661    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer data counterpart.
662    pub fn data(&self) -> Vec<u8> {
663        let info = self.as_mesh_buffer().info().ok();
664        let mut bytes = vec![0_u8; info.map_or(0, |buffer| buffer.length)];
665        if bytes.is_empty() {
666            return bytes;
667        }
668        // SAFETY: The unsafe operation is valid in this context.
669        let written = unsafe {
670            ffi::mdl_mesh_buffer_data_copy_data(
671                self.handle.as_ptr(),
672                bytes.as_mut_ptr(),
673                bytes.len() as u64,
674            )
675        } as usize;
676        bytes.truncate(written);
677        bytes
678    }
679
680    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer data counterpart.
681    pub fn map(&self) -> Result<MeshBufferMap> {
682        self.as_mesh_buffer().map()
683    }
684
685    #[must_use]
686    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer data counterpart.
687    pub fn as_mesh_buffer(&self) -> MeshBuffer {
688        MeshBuffer::from_handle(self.handle.clone())
689    }
690}
691
692#[derive(Debug, Clone)]
693/// Wraps the corresponding Model I/O mesh buffer data allocator counterpart.
694pub struct MeshBufferDataAllocator {
695    handle: ObjectHandle,
696}
697
698impl MeshBufferDataAllocator {
699    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer data allocator counterpart.
700    pub fn new() -> Result<Self> {
701        let mut out_allocator = ptr::null_mut();
702        let mut out_error = ptr::null_mut();
703        let status =
704            // SAFETY: Output pointers are initialized and managed; FFI function is called safely.
705            unsafe { ffi::mdl_mesh_buffer_data_allocator_new(&mut out_allocator, &mut out_error) };
706        crate::util::status_result(status, out_error)?;
707        Ok(Self {
708            handle: required_handle(out_allocator, "MDLMeshBufferDataAllocator")?,
709        })
710    }
711
712    #[must_use]
713    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer data allocator counterpart.
714    pub fn as_mesh_buffer_allocator(&self) -> MeshBufferAllocator {
715        MeshBufferAllocator::from_handle(self.handle.clone())
716    }
717
718    /// Wraps the corresponding Model I/O initializer for the wrapped Model I/O mesh buffer data allocator counterpart.
719    pub fn new_default_zone(&self, capacity: usize) -> Result<MeshBufferZoneDefault> {
720        self.as_mesh_buffer_allocator()
721            .new_zone(capacity)?
722            .as_default()
723            .ok_or_else(|| {
724                ModelIoError::new(
725                    ffi::status::NULL_RESULT,
726                    "MDLMeshBufferDataAllocator zone was not MDLMeshBufferZoneDefault",
727                )
728            })
729    }
730}
731
732impl MeshBuffer {
733    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer counterpart.
734    pub fn fill_data(&self, data: &[u8], offset: usize) {
735        // SAFETY: The unsafe operation is valid in this context.
736        unsafe {
737            ffi::mdl_mesh_buffer_fill_data(
738                self.as_ptr(),
739                data.as_ptr(),
740                data.len() as u64,
741                offset as u64,
742            );
743        }
744    }
745
746    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer counterpart.
747    pub fn map(&self) -> Result<MeshBufferMap> {
748        let length = self.info()?.length;
749        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
750        let ptr = unsafe { ffi::mdl_mesh_buffer_map(self.as_ptr()) };
751        Ok(MeshBufferMap::from_handle(
752            required_handle(ptr, "MDLMeshBufferMap")?,
753            length,
754        ))
755    }
756
757    #[must_use]
758    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer counterpart.
759    pub fn allocator(&self) -> Option<MeshBufferAllocator> {
760        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
761        let ptr = unsafe { ffi::mdl_mesh_buffer_allocator(self.as_ptr()) };
762        // SAFETY: The unsafe operation is valid in this context.
763        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferAllocator::from_handle)
764    }
765
766    #[must_use]
767    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer counterpart.
768    pub fn zone(&self) -> Option<MeshBufferZone> {
769        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
770        let ptr = unsafe { ffi::mdl_mesh_buffer_zone(self.as_ptr()) };
771        // SAFETY: The unsafe operation is valid in this context.
772        unsafe { ObjectHandle::from_retained_ptr(ptr) }.map(MeshBufferZone::from_handle)
773    }
774
775    #[must_use]
776    /// Calls the corresponding Model I/O method on the wrapped Model I/O mesh buffer counterpart.
777    pub fn as_data_buffer(&self) -> Option<MeshBufferData> {
778        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
779        if unsafe { ffi::mdl_mesh_buffer_is_data(self.as_ptr()) == 0 } {
780            return None;
781        }
782        // SAFETY: ObjectHandle wraps a valid opaque pointer from Swift; FFI function accepts it safely.
783        let retained = unsafe { ffi::mdl_object_retain(self.as_ptr()) };
784        // SAFETY: The unsafe operation is valid in this context.
785        unsafe { ObjectHandle::from_retained_ptr(retained) }.map(MeshBufferData::from_handle)
786    }
787}