value_bag/internal/cast/
primitive.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
This module generates code to try efficiently convert some arbitrary `T: 'static` into
a `Internal`.
*/

#[cfg(feature = "alloc")]
use crate::std::string::String;

use crate::ValueBag;

// NOTE: The casts for unsized values (str) are dubious here. To really do this properly
// we need https://github.com/rust-lang/rust/issues/81513
// NOTE: With some kind of const `Any::is<T>` we could do all this at compile-time
// Older versions of `value-bag` did this, but the infrastructure just wasn't worth
// the tiny performance improvement
use crate::std::any::TypeId;

enum Void {}

#[repr(transparent)]
struct VoidRef<'a>(*const &'a Void);

macro_rules! check_type_ids {
    (&$l:lifetime $v:ident => $(
        $(#[cfg($($cfg:tt)*)])*
            $ty:ty,
        )*
    ) => {
        $(
            $(#[cfg($($cfg)*)])*
            if TypeId::of::<T>() == TypeId::of::<$ty>() {
                // SAFETY: We verify the value is $ty before casting
                let v = unsafe { *($v.0 as *const & $l $ty) };

                return Some(ValueBag::from(v));
            }
        )*
        $(
            $(#[cfg($($cfg)*)])*
            if TypeId::of::<T>() == TypeId::of::<Option<$ty>>() {
                // SAFETY: We verify the value is Option<$ty> before casting
                let v = unsafe { *($v.0 as *const & $l Option<$ty>) };

                if let Some(v) = v {
                    return Some(ValueBag::from(v));
                } else {
                    return Some(ValueBag::empty());
                }
            }
        )*
    };
}

pub(in crate::internal) fn from_any<'v, T: ?Sized + 'static>(value: &'v T) -> Option<ValueBag<'v>> {
    let type_ids = |v: VoidRef<'v>| {
        if TypeId::of::<T>() == TypeId::of::<str>() {
            // SAFETY: We verify the value is str before casting
            let v = unsafe { *(v.0 as *const &'v str) };

            return Some(ValueBag::from(v));
        }

        check_type_ids!(
            &'v v =>
                usize,
                u8,
                u16,
                u32,
                u64,
                u128,
                isize,
                i8,
                i16,
                i32,
                i64,
                i128,
                f32,
                f64,
                char,
                bool,
                &'static str,
                // We deal with `str` separately because it's unsized
                // str,
                #[cfg(feature = "alloc")]
                String,
        );

        None
    };

    (type_ids)(VoidRef(&(value) as *const &'v T as *const &'v Void))
}

#[cfg(feature = "owned")]
pub(in crate::internal) fn from_owned_any<'a, T: ?Sized + 'static>(
    value: &'a T,
) -> Option<ValueBag<'static>> {
    let type_ids = |v: VoidRef<'a>| {
        check_type_ids!(
            &'a v =>
                usize,
                u8,
                u16,
                u32,
                u64,
                #[cfg(feature = "inline-i128")]
                u128,
                isize,
                i8,
                i16,
                i32,
                i64,
                #[cfg(feature = "inline-i128")]
                i128,
                f32,
                f64,
                char,
                bool,
        );

        None
    };

    (type_ids)(VoidRef(&(value) as *const &'a T as *const &'a Void))
}