bevy_save_erased_serde/
any.rs

1use crate::alloc::Box;
2#[cfg(no_maybe_uninit)]
3use core::marker::PhantomData;
4use core::mem;
5#[cfg(not(no_maybe_uninit))]
6use core::mem::MaybeUninit;
7use core::ptr;
8
9#[cfg(feature = "unstable-debug")]
10use core::any;
11
12pub struct Any {
13    value: Value,
14    drop: unsafe fn(&mut Value),
15    fingerprint: Fingerprint,
16
17    /// For panic messages only. Not used for comparison.
18    #[cfg(feature = "unstable-debug")]
19    type_name: &'static str,
20}
21
22union Value {
23    ptr: *mut (),
24    inline: [MaybeUninit<usize>; 2],
25}
26
27fn is_small<T>() -> bool {
28    cfg!(not(no_maybe_uninit))
29        && mem::size_of::<T>() <= mem::size_of::<Value>()
30        && mem::align_of::<T>() <= mem::align_of::<Value>()
31}
32
33impl Any {
34    // This is unsafe -- caller must not hold on to the Any beyond the lifetime
35    // of T.
36    //
37    // Example of bad code:
38    //
39    //    let s = "bad".to_owned();
40    //    let a = Any::new(&s);
41    //    drop(s);
42    //
43    // Now `a.view()` and `a.take()` return references to a dead String.
44    pub(crate) unsafe fn new<T>(t: T) -> Self {
45        let value: Value;
46        let drop: unsafe fn(&mut Value);
47        let fingerprint = Fingerprint::of::<T>();
48
49        if is_small::<T>() {
50            let mut inline = [MaybeUninit::uninit(); 2];
51            unsafe { ptr::write(inline.as_mut_ptr() as *mut T, t) };
52            value = Value { inline };
53            unsafe fn inline_drop<T>(value: &mut Value) {
54                unsafe { ptr::drop_in_place(value.inline.as_mut_ptr() as *mut T) }
55            }
56            drop = inline_drop::<T>;
57        } else {
58            let ptr = Box::into_raw(Box::new(t)) as *mut ();
59            value = Value { ptr };
60            unsafe fn ptr_drop<T>(value: &mut Value) {
61                mem::drop(unsafe { Box::from_raw(value.ptr as *mut T) });
62            }
63            drop = ptr_drop::<T>;
64        };
65
66        // Once attributes on struct literal fields are stable, do that instead.
67        // https://github.com/rust-lang/rust/issues/41681
68        #[cfg(not(feature = "unstable-debug"))]
69        {
70            Any {
71                value,
72                drop,
73                fingerprint,
74            }
75        }
76
77        #[cfg(feature = "unstable-debug")]
78        {
79            let type_name = any::type_name::<T>();
80            Any {
81                value,
82                drop,
83                fingerprint,
84                type_name,
85            }
86        }
87    }
88
89    // This is unsafe -- caller is responsible that T is the correct type.
90    pub(crate) unsafe fn view<T>(&mut self) -> &mut T {
91        if cfg!(not(miri)) && self.fingerprint != Fingerprint::of::<T>() {
92            self.invalid_cast_to::<T>();
93        }
94
95        let ptr = if is_small::<T>() {
96            unsafe { self.value.inline.as_mut_ptr() as *mut T }
97        } else {
98            unsafe { self.value.ptr as *mut T }
99        };
100
101        unsafe { &mut *ptr }
102    }
103
104    // This is unsafe -- caller is responsible that T is the correct type.
105    pub(crate) unsafe fn take<T>(mut self) -> T {
106        if cfg!(not(miri)) && self.fingerprint != Fingerprint::of::<T>() {
107            self.invalid_cast_to::<T>();
108        }
109
110        if is_small::<T>() {
111            let ptr = unsafe { self.value.inline.as_mut_ptr() as *mut T };
112            let value = unsafe { ptr::read(ptr) };
113            mem::forget(self);
114            value
115        } else {
116            let ptr = unsafe { self.value.ptr as *mut T };
117            let box_t = unsafe { Box::from_raw(ptr) };
118            mem::forget(self);
119            *box_t
120        }
121    }
122
123    #[cfg(not(feature = "unstable-debug"))]
124    fn invalid_cast_to<T>(&self) -> ! {
125        panic!("invalid cast; enable `unstable-debug` feature to debug");
126    }
127
128    #[cfg(feature = "unstable-debug")]
129    fn invalid_cast_to<T>(&self) -> ! {
130        let from = self.type_name;
131        let to = any::type_name::<T>();
132        panic!("invalid cast: {} to {}", from, to);
133    }
134}
135
136impl Drop for Any {
137    fn drop(&mut self) {
138        unsafe { (self.drop)(&mut self.value) }
139    }
140}
141
142#[cfg(no_maybe_uninit)]
143#[derive(Copy, Clone)]
144struct MaybeUninit<T>(PhantomData<T>);
145
146#[cfg(no_maybe_uninit)]
147impl<T> MaybeUninit<T> {
148    fn uninit() -> Self {
149        MaybeUninit(PhantomData)
150    }
151}
152
153#[derive(Debug, Eq, PartialEq)]
154struct Fingerprint {
155    size: usize,
156    align: usize,
157    #[cfg(include_fnptr_in_fingerprint)]
158    id: usize,
159}
160
161impl Fingerprint {
162    fn of<T>() -> Fingerprint {
163        Fingerprint {
164            size: mem::size_of::<T>(),
165            align: mem::align_of::<T>(),
166            // This is not foolproof -- theoretically Rust or LLVM could
167            // deduplicate some or all of these methods. But in practice it's
168            // great in debug mode when running our own test suite for catching
169            // bugs early.
170            #[cfg(include_fnptr_in_fingerprint)]
171            id: Fingerprint::of::<T> as usize,
172        }
173    }
174}
175
176#[test]
177fn test_fingerprint() {
178    assert_eq!(Fingerprint::of::<usize>(), Fingerprint::of::<usize>());
179    assert_eq!(Fingerprint::of::<&str>(), Fingerprint::of::<&'static str>());
180
181    assert_ne!(Fingerprint::of::<u32>(), Fingerprint::of::<[u8; 4]>());
182    assert_ne!(Fingerprint::of::<u32>(), Fingerprint::of::<[u32; 2]>());
183
184    if cfg!(all(include_fnptr_in_fingerprint, not(miri))) {
185        assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<isize>());
186        assert_ne!(Fingerprint::of::<usize>(), Fingerprint::of::<&usize>());
187        assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&&usize>());
188        assert_ne!(Fingerprint::of::<&usize>(), Fingerprint::of::<&mut usize>());
189
190        struct A;
191        struct B;
192        assert_ne!(Fingerprint::of::<A>(), Fingerprint::of::<B>());
193    }
194}