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 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 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
377trait 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 hasher.write(&$ty::to_ne_bytes($ty::NAN));
401 } else if *this == 0.0 {
402 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);