bones_schema/
std_impls.rs

1use std::{alloc::Layout, any::TypeId, ffi::c_void, hash::Hasher, sync::OnceLock, time::Duration};
2
3use fxhash::FxHasher;
4#[cfg(feature = "serde")]
5use serde::{de::Error, Deserialize};
6use ustr::Ustr;
7
8#[cfg(feature = "serde")]
9use crate::ser_de::SchemaDeserialize;
10use crate::{alloc::TypeDatas, prelude::*, raw_fns::*};
11
12macro_rules! impl_primitive {
13    ($t:ty, $prim:expr ) => {
14        unsafe impl HasSchema for $t {
15            fn schema() -> &'static Schema {
16                static S: OnceLock<&'static Schema> = OnceLock::new();
17                S.get_or_init(|| {
18                    SCHEMA_REGISTRY.register(SchemaData {
19                        name: stringify!($t).into(),
20                        full_name: concat!("std::", stringify!($t)).into(),
21                        kind: SchemaKind::Primitive($prim),
22                        type_id: Some(TypeId::of::<$t>()),
23                        clone_fn: Some(<$t as RawClone>::raw_clone_cb()),
24                        drop_fn: Some(<$t as RawDrop>::raw_drop_cb()),
25                        default_fn: Some(<$t as RawDefault>::raw_default_cb()),
26                        hash_fn: Some(<$t as RawHash>::raw_hash_cb()),
27                        eq_fn: Some(<$t as RawEq>::raw_eq_cb()),
28                        type_data: Default::default(),
29                    })
30                })
31            }
32        }
33    };
34}
35
36impl_primitive!(String, Primitive::String);
37impl_primitive!(
38    (),
39    Primitive::Opaque {
40        size: std::mem::size_of::<()>(),
41        align: std::mem::align_of::<()>()
42    }
43);
44impl_primitive!(bool, Primitive::Bool);
45impl_primitive!(u8, Primitive::U8);
46impl_primitive!(u16, Primitive::U16);
47impl_primitive!(u32, Primitive::U32);
48impl_primitive!(u64, Primitive::U64);
49impl_primitive!(u128, Primitive::U128);
50impl_primitive!(i8, Primitive::I8);
51impl_primitive!(i16, Primitive::I16);
52impl_primitive!(i32, Primitive::I32);
53impl_primitive!(i64, Primitive::I64);
54impl_primitive!(i128, Primitive::I128);
55
56macro_rules! schema_impl_float {
57    ($t:ty, $prim:ident) => {
58        unsafe impl HasSchema for $t {
59            fn schema() -> &'static Schema {
60                static S: OnceLock<&'static Schema> = OnceLock::new();
61                S.get_or_init(|| {
62                    SCHEMA_REGISTRY.register(SchemaData {
63                        name: stringify!($t).into(),
64                        full_name: concat!("std::", stringify!($t)).into(),
65                        kind: SchemaKind::Primitive(Primitive::$prim),
66                        type_id: Some(TypeId::of::<$t>()),
67                        clone_fn: Some(<$t as RawClone>::raw_clone_cb()),
68                        drop_fn: Some(<$t as RawDrop>::raw_drop_cb()),
69                        default_fn: Some(<$t as RawDefault>::raw_default_cb()),
70                        hash_fn: Some(<$t as CustomRawFns>::raw_hash_cb()),
71                        eq_fn: Some(<$t as CustomRawFns>::raw_eq_cb()),
72                        type_data: Default::default(),
73                    })
74                })
75            }
76        }
77    };
78}
79
80schema_impl_float!(f32, F32);
81schema_impl_float!(f64, F64);
82
83unsafe impl HasSchema for usize {
84    fn schema() -> &'static Schema {
85        static S: OnceLock<&'static Schema> = OnceLock::new();
86        S.get_or_init(|| {
87            SCHEMA_REGISTRY.register(SchemaData {
88                name: "usize".into(),
89                full_name: "std::usize".into(),
90                kind: SchemaKind::Primitive({
91                    #[cfg(target_pointer_width = "32")]
92                    let p = Primitive::U32;
93                    #[cfg(target_pointer_width = "64")]
94                    let p = Primitive::U64;
95                    p
96                }),
97                type_id: Some(TypeId::of::<usize>()),
98                clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
99                drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
100                default_fn: Some(<Self as RawDefault>::raw_default_cb()),
101                hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
102                eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
103                type_data: Default::default(),
104            })
105        })
106    }
107}
108unsafe impl HasSchema for isize {
109    fn schema() -> &'static Schema {
110        static S: OnceLock<&'static Schema> = OnceLock::new();
111        S.get_or_init(|| {
112            SCHEMA_REGISTRY.register(SchemaData {
113                name: "isize".into(),
114                full_name: "std::isize".into(),
115                kind: SchemaKind::Primitive({
116                    #[cfg(target_pointer_width = "32")]
117                    let p = Primitive::I32;
118                    #[cfg(target_pointer_width = "64")]
119                    let p = Primitive::I64;
120                    p
121                }),
122                type_id: Some(TypeId::of::<Self>()),
123                clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
124                drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
125                default_fn: Some(<Self as RawDefault>::raw_default_cb()),
126                hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
127                eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
128                type_data: Default::default(),
129            })
130        })
131    }
132}
133
134unsafe impl HasSchema for Ustr {
135    fn schema() -> &'static Schema {
136        static S: OnceLock<&'static Schema> = OnceLock::new();
137        let layout = Layout::new::<Self>();
138        S.get_or_init(|| {
139            SCHEMA_REGISTRY.register(SchemaData {
140                name: "Ustr".into(),
141                full_name: "ustr::Ustr".into(),
142                kind: SchemaKind::Primitive(Primitive::Opaque {
143                    size: layout.size(),
144                    align: layout.align(),
145                }),
146                type_id: Some(TypeId::of::<Self>()),
147                clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
148                drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
149                default_fn: Some(<Self as RawDefault>::raw_default_cb()),
150                hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
151                eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
152                type_data: {
153                    let td = TypeDatas::default();
154                    #[cfg(feature = "serde")]
155                    td.insert(SchemaDeserialize {
156                        deserialize_fn: |reference, deserializer| {
157                            Self::schema()
158                                .ensure_match(reference.schema())
159                                .map_err(|e| erased_serde::Error::custom(e.to_string()))?;
160
161                            let s = String::deserialize(deserializer)?;
162                            let us = Ustr::from(&s);
163                            *reference.cast_into_mut() = us;
164
165                            Ok(())
166                        },
167                    })
168                    .unwrap();
169                    td
170                },
171            })
172        })
173    }
174}
175
176unsafe impl HasSchema for Duration {
177    fn schema() -> &'static Schema {
178        static S: OnceLock<&'static Schema> = OnceLock::new();
179        let layout = Layout::new::<Self>();
180        S.get_or_init(|| {
181            SCHEMA_REGISTRY.register(SchemaData {
182                name: "Duration".into(),
183                full_name: "std::Duration".into(),
184                kind: SchemaKind::Primitive(Primitive::Opaque {
185                    size: layout.size(),
186                    align: layout.align(),
187                }),
188                type_id: Some(TypeId::of::<Self>()),
189                clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
190                drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
191                default_fn: Some(<Self as RawDefault>::raw_default_cb()),
192                hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
193                eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
194                type_data: {
195                    let td = TypeDatas::default();
196                    #[cfg(feature = "serde")]
197                    td.insert(SchemaDeserialize {
198                        deserialize_fn: |reference, deserializer| {
199                            Self::schema()
200                                .ensure_match(reference.schema())
201                                .map_err(|e| erased_serde::Error::custom(e.to_string()))?;
202
203                            #[cfg(feature = "humantime")]
204                            {
205                                let s = String::deserialize(deserializer)?;
206                                let d: Duration = s
207                                    .parse::<humantime::Duration>()
208                                    .map_err(|e| erased_serde::Error::custom(e.to_string()))?
209                                    .into();
210                                *reference.cast_into_mut() = d;
211                            }
212
213                            #[cfg(not(feature = "humantime"))]
214                            {
215                                let d = Duration::deserialize(deserializer)?;
216                                *reference.cast_into_mut() = d;
217                            }
218
219                            Ok(())
220                        },
221                    })
222                    .unwrap();
223                    td
224                },
225            })
226        })
227    }
228}
229
230#[cfg(feature = "glam")]
231mod impl_glam {
232    use super::*;
233    use glam::*;
234
235    unsafe impl HasSchema for Quat {
236        fn schema() -> &'static Schema {
237            static S: OnceLock<&'static Schema> = OnceLock::new();
238            let layout = std::alloc::Layout::new::<Quat>();
239            S.get_or_init(|| {
240                SCHEMA_REGISTRY.register(SchemaData {
241                    name: "Quat".into(),
242                    full_name: "glam::Quat".into(),
243                    kind: SchemaKind::Primitive(Primitive::Opaque {
244                        size: layout.size(),
245                        align: layout.align(),
246                    }),
247                    type_id: Some(TypeId::of::<usize>()),
248                    clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
249                    drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
250                    default_fn: Some(<Self as RawDefault>::raw_default_cb()),
251                    // TODO: Get the schema `hash_fn` and `eq_fn` for the `Quat` type.
252                    // Quats don't implement hash and eq by default because of floating point number
253                    // issues, so we'll have to use a workaround like `CustomRawFns` below to create
254                    // valid implementations of Hash and Eq over the floating points inside the
255                    // Quat.
256                    hash_fn: None,
257                    eq_fn: None,
258                    type_data: Default::default(),
259                })
260            })
261        }
262    }
263
264    macro_rules! schema_impl_glam {
265        ($t:ty, $prim:ident, $nprim:ident, $($field:ident),+) => {
266            unsafe impl HasSchema for $t {
267                fn schema() -> &'static Schema {
268                    static S: OnceLock<&'static Schema> = OnceLock::new();
269
270                    S.get_or_init(|| {
271                        let type_id = Some(TypeId::of::<Self>());
272                        let kind = SchemaKind::Struct(StructSchemaInfo {
273                            fields: vec![
274                                $(
275                                    StructFieldInfo {
276                                        name: Some(stringify!($field).into()),
277                                        schema: $nprim::schema(),
278                                    }
279                                ),*
280                            ],
281                        });
282                        SCHEMA_REGISTRY.register(SchemaData {
283                            name: stringify!($t).into(),
284                            full_name: concat!("glam::", stringify!($t)).into(),
285                            type_id,
286                            kind,
287                            type_data: Default::default(),
288                            clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
289                            drop_fn: Some(<Self as RawDrop>::raw_drop_cb()),
290                            default_fn: Some(<Self as RawDefault>::raw_default_cb()),
291                            hash_fn: Some(<Self as CustomRawFns>::raw_hash_cb()),
292                            eq_fn: Some(<Self as CustomRawFns>::raw_eq_cb()),
293                        })
294                    })
295                }
296            }
297        };
298    }
299
300    macro_rules! schema_impl_glam_vecs {
301        ($prim:ident, $nprim:ident, $id:ident) => {
302            paste::paste! {
303                schema_impl_glam!( [< $id 2 >], $prim, $nprim, x, y);
304                schema_impl_glam!( [< $id 3 >], $prim, $nprim, x, y, z);
305                schema_impl_glam!( [< $id 4 >], $prim, $nprim, x, y, z, w);
306            }
307        };
308    }
309
310    schema_impl_glam_vecs!(Bool, bool, BVec);
311    schema_impl_glam_vecs!(U32, u32, UVec);
312    schema_impl_glam_vecs!(I32, i32, IVec);
313    schema_impl_glam_vecs!(F32, f32, Vec);
314    schema_impl_glam_vecs!(F64, f64, DVec);
315
316    // TODO: Implement `HasSchema` for glam matrix types.
317    // We need to implement `HasSchema` for the matrix types, just like we did with the vector
318    // types.
319
320    macro_rules! custom_fns_impl_bvec {
321        ($ty:ident) => {
322            impl CustomRawFns for glam::$ty {
323                unsafe fn raw_hash(ptr: *const c_void) -> u64 {
324                    <Self as RawHash>::raw_hash(ptr)
325                }
326                unsafe fn raw_eq(a: *const c_void, b: *const c_void) -> bool {
327                    <Self as RawEq>::raw_eq(a, b)
328                }
329            }
330        };
331    }
332    custom_fns_impl_bvec!(BVec2);
333    custom_fns_impl_bvec!(BVec3);
334    custom_fns_impl_bvec!(BVec4);
335
336    macro_rules! custom_fns_impl_glam {
337        ($t:ty, $prim:ident, $($field:ident),+) => {
338            impl CustomRawFns for $t {
339                unsafe fn raw_hash(ptr: *const c_void) -> u64 {
340                    let this = unsafe { &*(ptr as *const Self) };
341                    let mut hasher = FxHasher::default();
342                    $(
343                        hasher.write_u64($prim::raw_hash(&this.$field as *const $prim as *const c_void));
344                    )+
345                    hasher.finish()
346                }
347
348                unsafe fn raw_eq(a: *const c_void, b: *const c_void) -> bool {
349                    let a = unsafe { &*(a as *const Self) };
350                    let b = unsafe { &*(b as *const Self) };
351
352                    $(
353                        $prim::raw_eq(
354                            &a.$field as *const $prim as *const c_void,
355                            &b.$field as *const $prim as *const c_void,
356                        )
357                    )&&+
358                }
359            }
360        };
361    }
362    custom_fns_impl_glam!(Vec2, f32, x, y);
363    custom_fns_impl_glam!(Vec3, f32, x, y, z);
364    custom_fns_impl_glam!(Vec4, f32, x, y, z, w);
365    custom_fns_impl_glam!(DVec2, f64, x, y);
366    custom_fns_impl_glam!(DVec3, f64, x, y, z);
367    custom_fns_impl_glam!(DVec4, f64, x, y, z, w);
368    custom_fns_impl_glam!(UVec2, u32, x, y);
369    custom_fns_impl_glam!(UVec3, u32, x, y, z);
370    custom_fns_impl_glam!(UVec4, u32, x, y, z, w);
371    custom_fns_impl_glam!(IVec2, i32, x, y);
372    custom_fns_impl_glam!(IVec3, i32, x, y, z);
373    custom_fns_impl_glam!(IVec4, i32, x, y, z, w);
374    custom_fns_impl_glam!(Quat, f32, x, y, z, w);
375}
376
377/// Trait for types that require specific implementations of eq and hash fns, for use in this module only.
378trait CustomRawFns {
379    unsafe fn raw_hash(ptr: *const c_void) -> u64;
380    fn raw_hash_cb() -> Unsafe<&'static (dyn Fn(*const c_void) -> u64 + Sync + Send + 'static)> {
381        unsafe { Unsafe::new(Box::leak(Box::new(|a| Self::raw_hash(a)))) }
382    }
383    unsafe fn raw_eq(a: *const c_void, b: *const c_void) -> bool;
384    fn raw_eq_cb(
385    ) -> Unsafe<&'static (dyn Fn(*const c_void, *const c_void) -> bool + Sync + Send + 'static)>
386    {
387        unsafe { Unsafe::new(Box::leak(Box::new(|a, b| Self::raw_eq(a, b)))) }
388    }
389}
390
391macro_rules! custom_fns_impl_float {
392    ($ty:ident) => {
393        impl CustomRawFns for $ty {
394            unsafe fn raw_hash(ptr: *const c_void) -> u64 {
395                let this = unsafe { &*(ptr as *const Self) };
396
397                let mut hasher = FxHasher::default();
398                if this.is_nan() {
399                    // Ensure all NaN representations hash to the same value
400                    hasher.write(&$ty::to_ne_bytes($ty::NAN));
401                } else if *this == 0.0 {
402                    // Ensure both zeroes hash to the same value
403                    hasher.write(&$ty::to_ne_bytes(0.0));
404                } else {
405                    hasher.write(&$ty::to_ne_bytes(*this));
406                }
407                hasher.finish()
408            }
409
410            unsafe fn raw_eq(a: *const c_void, b: *const c_void) -> bool {
411                let a = unsafe { &*(a as *const Self) };
412                let b = unsafe { &*(b as *const Self) };
413                if a.is_nan() && a.is_nan() {
414                    true
415                } else {
416                    a == b
417                }
418            }
419        }
420    };
421}
422custom_fns_impl_float!(f32);
423custom_fns_impl_float!(f64);