Skip to main content

swiftui_native/
existential.rs

1//! `any View` existential container — pure Rust, no Swift code.
2//!
3//! Layout (arm64, 64-bit):
4//!   [0..24]  inline value buffer (3 words) — types ≤24 bytes stored inline
5//!            OR [0..8] = heap box pointer for types >24 bytes
6//!   [24..32] type metadata pointer
7//!   [32..40] View protocol witness table pointer
8
9use core::ffi::c_void;
10use swift_runtime_sys::SwiftABI::get_value_witness_table;
11
12/// A heap-allocated `any View` existential container.
13///
14/// This is the native alternative to AnyView — constructed entirely from Rust
15/// using the Swift runtime's value witness tables and metadata.
16pub struct ViewExistential {
17    /// Heap-allocated 40-byte existential container.
18    buf: *mut u8,
19}
20
21const CONTAINER_SIZE: usize = 40;
22const INLINE_SIZE: usize = 24;
23
24impl ViewExistential {
25    /// Create a new existential from a concrete view value.
26    ///
27    /// # Safety
28    /// - `value` must point to a valid instance of the type described by `metadata`
29    /// - `metadata` must be a valid Swift type metadata pointer
30    /// - `view_wt` must be a valid View protocol witness table for the type
31    pub unsafe fn new(
32        value: *const c_void,
33        value_size: usize,
34        metadata: *const c_void,
35        view_wt: *const c_void,
36    ) -> Self {
37        unsafe {
38            let buf = libc::malloc(CONTAINER_SIZE) as *mut u8;
39            assert!(!buf.is_null(), "Failed to allocate existential container");
40            core::ptr::write_bytes(buf, 0, CONTAINER_SIZE);
41
42            if value_size <= INLINE_SIZE {
43                // Inline: copy value directly into buffer
44                core::ptr::copy_nonoverlapping(value as *const u8, buf, value_size);
45            } else {
46                // Box: allocate and copy, store box pointer in first word
47                let vwt = &*get_value_witness_table(metadata);
48                let heap_buf = libc::malloc(value_size);
49                assert!(!heap_buf.is_null());
50                // Use VWT initializeWithCopy for proper reference counting
51                (vwt.initialize_with_copy)(heap_buf, value as *mut c_void, metadata);
52                *(buf as *mut *mut c_void) = heap_buf;
53            }
54
55            // Write metadata pointer at offset 24
56            *((buf.add(24)) as *mut *const c_void) = metadata;
57            // Write witness table at offset 32
58            *((buf.add(32)) as *mut *const c_void) = view_wt;
59
60            Self { buf }
61        }
62    }
63
64    /// Create from a zero-size type (EmptyView, Divider).
65        /// # Safety
66    /// `metadata` must describe a type whose size is zero, and `view_wt` must be
67    /// its `View` conformance. A type with a size has a value to point at, and
68    /// this passes none.
69pub unsafe fn from_zero_size(metadata: *const c_void, view_wt: *const c_void) -> Self {
70        unsafe {
71            Self::new(core::ptr::null(), 0, metadata, view_wt)
72        }
73    }
74
75    /// Get the type metadata pointer.
76    pub fn metadata(&self) -> *const c_void {
77        unsafe { *((self.buf.add(24)) as *const *const c_void) }
78    }
79
80    /// Get the View witness table pointer.
81    pub fn witness_table(&self) -> *const c_void {
82        unsafe { *((self.buf.add(32)) as *const *const c_void) }
83    }
84
85    /// Get pointer to the value (inline buffer start).
86    pub fn value_ptr(&self) -> *const c_void {
87        let vwt = unsafe { &*get_value_witness_table(self.metadata()) };
88        if vwt.size <= INLINE_SIZE {
89            self.buf as *const c_void
90        } else {
91            unsafe { *(self.buf as *const *const c_void) }
92        }
93    }
94
95    /// Get the raw existential container pointer (40 bytes).
96    pub fn as_raw(&self) -> *const c_void {
97        self.buf as *const c_void
98    }
99}
100
101impl Drop for ViewExistential {
102    fn drop(&mut self) {
103        if !self.buf.is_null() {
104            let metadata = self.metadata();
105            if !metadata.is_null() {
106                let vwt = unsafe { &*get_value_witness_table(metadata) };
107                if vwt.size <= INLINE_SIZE {
108                    // Destroy inline value
109                    unsafe { (vwt.destroy)(self.buf as *mut c_void, metadata) };
110                } else {
111                    // Destroy boxed value and free box
112                    let box_ptr = unsafe { *(self.buf as *mut *mut c_void) };
113                    if !box_ptr.is_null() {
114                        unsafe { (vwt.destroy)(box_ptr, metadata) };
115                        unsafe { libc::free(box_ptr) };
116                    }
117                }
118            }
119            unsafe { libc::free(self.buf as *mut c_void) };
120            self.buf = core::ptr::null_mut();
121        }
122    }
123}
124
125impl Clone for ViewExistential {
126    fn clone(&self) -> Self {
127        let metadata = self.metadata();
128        let view_wt = self.witness_table();
129        let vwt = unsafe { &*get_value_witness_table(metadata) };
130
131        let new_buf = unsafe { libc::malloc(CONTAINER_SIZE) as *mut u8 };
132        assert!(!new_buf.is_null());
133        unsafe { core::ptr::write_bytes(new_buf, 0, CONTAINER_SIZE) };
134
135        if vwt.size <= INLINE_SIZE {
136            // Copy inline value using VWT
137            unsafe {
138                (vwt.initialize_with_copy)(
139                    new_buf as *mut c_void,
140                    self.buf as *mut c_void,
141                    metadata,
142                );
143            }
144        } else {
145            // Allocate new box and copy
146            let new_box = unsafe { libc::malloc(vwt.size) };
147            assert!(!new_box.is_null());
148            let old_box = unsafe { *(self.buf as *const *mut c_void) };
149            unsafe { (vwt.initialize_with_copy)(new_box, old_box, metadata) };
150            unsafe { *(new_buf as *mut *mut c_void) = new_box };
151        }
152
153        // Copy metadata + witness table
154        unsafe {
155            *((new_buf.add(24)) as *mut *const c_void) = metadata;
156            *((new_buf.add(32)) as *mut *const c_void) = view_wt;
157        }
158
159        Self { buf: new_buf }
160    }
161}
162
163unsafe impl Send for ViewExistential {}