dync/
lib.rs

1//! # Overview
2//!
3//! This crate aims to fill the gap in Rust's dynamic traits system by exposing the control over dynamic
4//! virtual function tables to the user in a safe API. Below is a list of capabilities unlocked by
5//! `dync`.
6//!
7//! - Create homogeneous untyped `Vec`s that store a single virtual function table for all contained
8//!   elements. This functionality is enabled by the `traits` feature. For more details see
9//!   [`vec_dyn`].
10//!
11//! [`vec_dyn`]: vec_dyn/index.html
12
13mod bytes;
14pub mod macros;
15
16#[macro_use]
17mod elem;
18#[macro_use]
19mod copy_value;
20mod vtable;
21
22mod meta;
23
24#[cfg(feature = "traits")]
25pub mod traits;
26
27#[cfg(feature = "traits")]
28#[macro_use]
29mod value;
30
31pub mod index_slice;
32mod vec_void;
33
34mod slice_copy;
35mod vec_copy;
36
37#[cfg(feature = "traits")]
38mod slice;
39
40#[cfg(feature = "traits")]
41mod vec_dyn;
42
43#[cfg(feature = "traits")]
44pub use crate::meta::*;
45pub use copy_value::*;
46#[cfg(feature = "traits")]
47pub use downcast_rs as downcast;
48#[cfg(feature = "traits")]
49pub use dync_derive::dync_mod;
50#[cfg(feature = "traits")]
51pub use dync_derive::dync_trait;
52pub use elem::CopyElem;
53pub use index_slice::*;
54#[cfg(feature = "traits")]
55pub use slice::*;
56pub use slice_copy::*;
57#[cfg(feature = "traits")]
58pub use value::*;
59pub use vec_copy::VecCopy;
60#[cfg(feature = "traits")]
61pub use vec_dyn::*;
62pub use vtable::*;
63
64/// Convert a given container with a dynamic vtable to a concrete type.
65///
66/// This macro will panic if the conversion fails.
67#[cfg(feature = "traits")]
68#[macro_export]
69macro_rules! from_dyn {
70    (Slice < dyn $trait:path as $vtable:path >) => {{
71        from_dyn![@slice Slice < dyn $trait as $vtable >]
72    }};
73    (SliceMut < dyn $trait:path as $vtable:path >) => {{
74        from_dyn![@slice SliceMut < dyn $trait as $vtable >]
75    }};
76    (VecDyn < dyn $trait:path as $vtable:path >) => {{
77        from_dyn![@owned VecDyn < dyn $trait as $vtable >]
78    }};
79    (SliceCopy < dyn $trait:path as $vtable:path >) => {{
80        from_dyn![@slice SliceCopy < dyn $trait as $vtable >]
81    }};
82    (SliceCopyMut < dyn $trait:path as $vtable:path >) => {{
83        from_dyn![@slice SliceCopyMut < dyn $trait as $vtable >]
84    }};
85    (VecCopy < dyn $trait:path as $vtable:path >) => {{
86        from_dyn![@owned VecCopy < dyn $trait as $vtable >]
87    }};
88    (@owned $vec:ident < dyn $trait:path as $vtable:path>) => {{
89        fn from_dyn<V: $trait>(vec: $crate::$vec<dyn $trait>) -> $crate::$vec<V> {
90            unsafe {
91                let (data, vtable) = vec.into_raw_parts();
92                // If vtables were shared with Rc, we would use this:
93                //let updated_vtable: std::rc::Rc<V> = vtable.downcast_rc().ok().unwrap();
94                let updated_vtable: Box<V> = vtable.downcast().ok().unwrap();
95                $vec::from_raw_parts(data, updated_vtable)
96            }
97        }
98
99        from_dyn::<$vtable>
100    }};
101    (@slice $slice:ident < dyn $trait:path >) => {{
102        fn from_dyn<'a, V: ?Sized + HasDrop + std::any::Any>(slice: $crate::$slice<'a, V>) -> $crate::$slice<'a, $vtable> {
103            unsafe {
104                let (data, elem, vtable) = slice.into_raw_parts();
105                match vtable {
106                    $crate::VTableRef::Ref(v) => {
107                        let updated_vtable: &$vtable = v.downcast_ref().unwrap();
108                        $slice::from_raw_parts(data, elem, updated_vtable)
109                    }
110                    $crate::VTableRef::Box(v) => {
111                        let updated_vtable: Box<$vtable> = v.downcast().unwrap();
112                        $slice::from_raw_parts(data, elem, updated_vtable)
113                    }
114                    // $crate::VTableRef::Rc(v) => {
115                    //     let updated_vtable: std::rc::Rc<$vtable> = v.downcast().unwrap();
116                    //     $slice::from_raw_parts(data, elem, updated_vtable)
117                    // }
118                }
119            }
120        }
121
122        from_dyn
123    }};
124}
125
126/// Convert a given container type (e.g. `VecCopy` or `SliceDyn`) to have a dynamic VTable.
127#[cfg(feature = "traits")]
128#[macro_export]
129macro_rules! into_dyn {
130    (Slice < dyn $trait:path >) => {{
131        into_dyn![@slice Slice < dyn $trait >]
132    }};
133    (SliceMut < dyn $trait:ident >) => {{
134        into_dyn![@slice SliceMut < dyn $trait >]
135    }};
136    (VecDyn < dyn $trait:ident >) => {{
137        into_dyn![@owned VecDyn < dyn $trait >]
138    }};
139    (SliceCopy < dyn $trait:ident >) => {{
140        into_dyn![@slice SliceCopy < dyn $trait >]
141    }};
142    (SliceCopyMut < dyn $trait:ident >) => {{
143        into_dyn![@slice SliceCopyMut < dyn $trait >]
144    }};
145    (VecCopy < dyn $trait:ident >) => {{
146        into_dyn![@owned VecCopy < dyn $trait >]
147    }};
148    (@owned $vec:ident < dyn $trait:ident >) => {{
149        fn into_dyn<V: 'static + $trait>(vec: $crate::$vec<V>) -> $crate::$vec<dyn $trait> {
150            unsafe {
151                let (data, vtable) = vec.into_raw_parts();
152                // If vtables were shared with Rc, we would use this:
153                //let updated_vtable: std::rc::Rc<dyn $trait> = vtable;
154                let updated_vtable: Box<dyn $trait> = vtable;
155                $vec::from_raw_parts(data, updated_vtable)
156            }
157        }
158
159        into_dyn
160    }};
161    (@slice $slice:ident < dyn $trait:path >) => {{
162        fn into_dyn<'a, V: 'static + $trait>(slice: $crate::$slice<'a, V>) -> $crate::$slice<'a, dyn $trait> {
163            unsafe {
164                let (data, elem, vtable) = slice.into_raw_parts();
165                match vtable {
166                    $crate::VTableRef::Ref(v) => {
167                        let updated_vtable: &dyn $trait = v;
168                        $slice::from_raw_parts(data, elem, updated_vtable)
169                    }
170                    $crate::VTableRef::Box(v) => {
171                        let updated_vtable: Box<dyn $trait> = v;
172                        $slice::from_raw_parts(data, elem, updated_vtable)
173                    }
174                    // $crate::VTableRef::Rc(v) => {
175                    //     let updated_vtable: std::rc::Rc<dyn $trait> = v;
176                    //     $slice::from_raw_parts(data, elem, updated_vtable)
177                    // }
178                }
179            }
180        }
181
182        into_dyn
183    }};
184}
185
186/// A helper trait for accessing internal byte representations of elements represented as
187/// contiguous byte slices.
188pub(crate) trait ElementBytes {
189    /// Get the slice of bytes representing all the elements.
190    fn bytes(&self) -> &[std::mem::MaybeUninit<u8>];
191
192    /// The size of an element in bytes.
193    fn element_size(&self) -> usize;
194
195    /// Get a range of byte indices representing the given element index.
196    #[inline]
197    fn index_byte_range(&self, i: usize) -> std::ops::Range<usize> {
198        i * self.element_size()..(i + 1) * self.element_size()
199    }
200
201    /// Index into an immutable slice of bytes.
202    #[inline]
203    fn index_byte_slice(&self, i: usize) -> &[std::mem::MaybeUninit<u8>] {
204        &self.bytes()[self.index_byte_range(i)]
205    }
206}
207
208/// A helper trait for mutably accessing internal byte representations of elements represented as
209/// contiguous byte slices.
210pub(crate) trait ElementBytesMut: ElementBytes {
211    /// Get the mutable slice of bytes representing all the elements.
212    unsafe fn bytes_mut(&mut self) -> &mut [std::mem::MaybeUninit<u8>];
213
214    /// Index into a mutable slice of bytes.
215    #[inline]
216    unsafe fn index_byte_slice_mut(&mut self, i: usize) -> &mut [std::mem::MaybeUninit<u8>] {
217        let rng = self.index_byte_range(i);
218        &mut self.bytes_mut()[rng]
219    }
220
221    /// Swap elements at indicies `i` and `j` represented by the bytes.
222    ///
223    /// If `i` is the same as `j` this function does nothing.
224    #[inline]
225    fn swap(&mut self, i: usize, j: usize) {
226        if i == j {
227            return;
228        }
229        let element_size = self.element_size();
230        let r_rng = self.index_byte_range(0);
231        if i < j {
232            let l_rng = self.index_byte_range(i);
233            // SAFETY: it is safe to swap aligned data since we have unique access.
234            unsafe {
235                let (l, r) = self.bytes_mut().split_at_mut(element_size * j);
236                l[l_rng].swap_with_slice(&mut r[r_rng])
237            }
238        } else {
239            let l_rng = self.index_byte_range(j);
240            unsafe {
241                let (l, r) = self.bytes_mut().split_at_mut(element_size * i);
242                l[l_rng].swap_with_slice(&mut r[r_rng])
243            }
244        }
245    }
246}