value_bag/internal/cast/
primitive.rs

1/*
2This module generates code to try efficiently convert some arbitrary `T: 'static` into
3a `Internal`.
4*/
5
6#[cfg(feature = "alloc")]
7use crate::std::string::String;
8
9use crate::ValueBag;
10
11// NOTE: The casts for unsized values (str) are dubious here. To really do this properly
12// we need https://github.com/rust-lang/rust/issues/81513
13// NOTE: With some kind of const `Any::is<T>` we could do all this at compile-time
14// Older versions of `value-bag` did this, but the infrastructure just wasn't worth
15// the tiny performance improvement
16use crate::std::any::TypeId;
17
18enum Void {}
19
20#[repr(transparent)]
21struct VoidRef<'a>(*const &'a Void);
22
23macro_rules! check_type_ids {
24    (&$l:lifetime $v:ident => $(
25        $(#[cfg($($cfg:tt)*)])*
26            $ty:ty,
27        )*
28    ) => {
29        $(
30            $(#[cfg($($cfg)*)])*
31            if TypeId::of::<T>() == TypeId::of::<$ty>() {
32                // SAFETY: We verify the value is $ty before casting
33                let v = unsafe { *($v.0 as *const & $l $ty) };
34
35                return Some(ValueBag::from(v));
36            }
37        )*
38        $(
39            $(#[cfg($($cfg)*)])*
40            if TypeId::of::<T>() == TypeId::of::<Option<$ty>>() {
41                // SAFETY: We verify the value is Option<$ty> before casting
42                let v = unsafe { *($v.0 as *const & $l Option<$ty>) };
43
44                if let Some(v) = v {
45                    return Some(ValueBag::from(v));
46                } else {
47                    return Some(ValueBag::empty());
48                }
49            }
50        )*
51    };
52}
53
54pub(in crate::internal) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option<ValueBag<'v>> {
55    let type_ids = |v: VoidRef<'v>| {
56        if TypeId::of::<T>() == TypeId::of::<str>() {
57            // SAFETY: We verify the value is str before casting
58            let v = unsafe { *(v.0 as *const &'v str) };
59
60            return Some(ValueBag::from(v));
61        }
62
63        check_type_ids!(
64            &'v v =>
65                usize,
66                u8,
67                u16,
68                u32,
69                u64,
70                u128,
71                isize,
72                i8,
73                i16,
74                i32,
75                i64,
76                i128,
77                f32,
78                f64,
79                char,
80                bool,
81                &'static str,
82                // We deal with `str` separately because it's unsized
83                // str,
84                #[cfg(feature = "alloc")]
85                String,
86        );
87
88        None
89    };
90
91    (type_ids)(VoidRef(&(value) as *const &'v T as *const &'v Void))
92}
93
94#[cfg(feature = "owned")]
95pub(in crate::internal) fn from_owned_any<'a, T: ?Sized + 'static>(
96    value: &'a T,
97) -> Option<ValueBag<'static>> {
98    let type_ids = |v: VoidRef<'a>| {
99        check_type_ids!(
100            &'a v =>
101                usize,
102                u8,
103                u16,
104                u32,
105                u64,
106                #[cfg(feature = "inline-i128")]
107                u128,
108                isize,
109                i8,
110                i16,
111                i32,
112                i64,
113                #[cfg(feature = "inline-i128")]
114                i128,
115                f32,
116                f64,
117                char,
118                bool,
119        );
120
121        None
122    };
123
124    (type_ids)(VoidRef(&(value) as *const &'a T as *const &'a Void))
125}