1#![deny(missing_docs)]
2#![allow(internal_features)]
3#![allow(incomplete_features)]
4#![feature(str_from_raw_parts)]
5#![feature(generic_const_exprs)]
6#![feature(nonzero_internals)]
7#![cfg_attr(feature = "specialization", feature(specialization))]
8#![doc = include_str!("../README.md")]
9
10mod remote_impl;
11
12use core::fmt;
13use std::hash::Hash;
14
15pub mod prelude {
17 pub use super::fixed_type_id;
18 pub use super::{fstr_to_str, ConstTypeName, FixedId, FixedTypeId, FixedVersion};
19}
20
21pub use fixed_type_id_macros::fixed_type_id;
22use semver::Version;
23
24#[cfg(feature = "len128")]
26pub const CONST_TYPENAME_LEN: usize = 128;
27
28#[cfg(feature = "len64")]
30pub const CONST_TYPENAME_LEN: usize = 64;
31
32#[cfg(feature = "len256")]
34pub const CONST_TYPENAME_LEN: usize = 256;
35
36#[repr(transparent)]
38#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
39#[cfg_attr(
40 feature = "rkyv",
41 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
42)]
43#[cfg_attr(feature = "rkyv", rkyv(attr(allow(missing_docs))))]
44#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
45#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
46pub struct FixedId(pub u64);
47
48#[cfg(feature = "rkyv")]
49impl From<&ArchivedFixedId> for FixedId {
50 fn from(value: &ArchivedFixedId) -> Self {
51 FixedId(value.0.into())
52 }
53}
54
55impl Hash for FixedId {
57 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
58 state.write_u64(self.0);
59 }
60}
61
62impl FixedId {
63 pub const fn from<Target: 'static + ?Sized + FixedTypeId>() -> Self {
65 Target::TYPE_ID
66 }
67
68 pub const fn as_u64(&self) -> u64 {
70 self.0
71 }
72
73 pub const fn from_type_name(type_name: &'static str, version: Option<FixedVersion>) -> Self {
77 let hash = match version {
78 None => rapidhash::rapidhash(type_name.as_bytes()),
79 Some(version) => {
80 let name_hash = rapidhash::rapidhash(type_name.as_bytes());
82 let version_hash = rapidhash::rapidhash(&version.const_to_bytes());
84 rapid_mix(name_hash, version_hash)
90 }
91 };
92 FixedId(hash)
93 }
94}
95
96const fn u64s_to_bytes<const N: usize>(slice: &[u64; N]) -> [u8; N * 8] {
97 let mut bytes = [0u8; N * 8];
98
99 let mut slice_remaining: &[u64] = slice;
100 let mut i = 0;
101 let mut slice_index = 0;
102 while let [current, tail @ ..] = slice_remaining {
103 let mut current_bytes: &[u8] = ¤t.to_le_bytes();
104 while let [current, tail @ ..] = current_bytes {
105 bytes[i] = *current;
106 i += 1;
107 current_bytes = tail;
108 }
109 slice_index += 1;
110 debug_assert!(i == 8 * slice_index);
111 slice_remaining = tail;
112 }
113
114 bytes
115}
116
117pub const fn usize_to_str(n: usize) -> &'static str {
121 match n {
122 0 => "0",
123 1 => "1",
124 2 => "2",
125 3 => "3",
126 4 => "4",
127 5 => "5",
128 6 => "6",
129 7 => "7",
130 8 => "8",
131 9 => "9",
132 10 => "10",
133 11 => "11",
134 12 => "12",
135 13 => "13",
136 14 => "14",
137 15 => "15",
138 16 => "16",
139 17 => "17",
140 18 => "18",
141 19 => "19",
142 20 => "20",
143 21 => "21",
144 22 => "22",
145 23 => "23",
146 24 => "24",
147 25 => "25",
148 26 => "26",
149 27 => "27",
150 28 => "28",
151 29 => "29",
152 30 => "30",
153 31 => "31",
154 32 => "32",
155 64 => "64",
156 128 => "128",
157 256 => "256",
158 512 => "512",
159 768 => "768",
160 1024 => "1024",
161 2048 => "2048",
162 4096 => "4096",
163 8192 => "8192",
164 16384 => "16384",
165 32768 => "32768",
166 65536 => "65536",
167 _ => "N",
168 }
169}
170
171#[inline(always)]
173const fn rapid_mum(a: u64, b: u64) -> (u64, u64) {
174 let r = a as u128 * b as u128;
175 (r as u64, (r >> 64) as u64)
176}
177
178#[inline(always)]
180const fn rapid_mix(a: u64, b: u64) -> u64 {
181 let (a, b) = rapid_mum(a, b);
182 a ^ b
183}
184
185impl fmt::Display for FixedId {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{}", self.0)
188 }
189}
190
191pub trait FixedTypeId {
193 const TYPE_NAME: &'static str;
200 const TYPE_ID: FixedId = FixedId::from_type_name(Self::TYPE_NAME, Some(Self::TYPE_VERSION));
205 const TYPE_VERSION: FixedVersion = FixedVersion::new(0, 0, 0);
209
210 #[inline(always)]
212 fn ty_name(&self) -> &'static str {
213 Self::TYPE_NAME
214 }
215
216 #[inline(always)]
218 fn ty_id(&self) -> FixedId {
219 Self::TYPE_ID
220 }
221
222 #[inline(always)]
224 fn ty_version(&self) -> FixedVersion {
225 Self::TYPE_VERSION
226 }
227}
228
229#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
231#[cfg_attr(
232 feature = "rkyv",
233 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
234)]
235#[cfg_attr(feature = "rkyv", rkyv(attr(allow(missing_docs))))]
236#[cfg_attr(feature = "rkyv", rkyv(compare(PartialEq), derive(Debug)))]
237#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
238pub struct FixedVersion {
239 pub major: u64,
241 pub minor: u64,
243 pub patch: u64,
245}
246
247impl FixedVersion {
248 #[inline(always)]
250 pub const fn new(major: u64, minor: u64, patch: u64) -> Self {
251 FixedVersion {
252 major,
253 minor,
254 patch,
255 }
256 }
257
258 pub const fn const_to_bytes(&self) -> [u8; 24] {
262 u64s_to_bytes(&[self.major, self.minor, self.patch])
263 }
264
265 pub fn to_bytes(&self) -> [u8; 24] {
267 let mut bytes = [0u8; 24];
268 bytes[0..8].copy_from_slice(&self.major.to_le_bytes());
269 bytes[8..16].copy_from_slice(&self.minor.to_le_bytes());
270 bytes[16..24].copy_from_slice(&self.patch.to_le_bytes());
271 bytes
272 }
273
274 pub fn is_compatible(&self, expected_version: &FixedVersion) -> bool {
276 let compatible_cmp = semver::Comparator {
277 op: semver::Op::Caret,
278 major: expected_version.major,
279 minor: Some(expected_version.minor),
280 patch: Some(expected_version.patch),
281 pre: semver::Prerelease::EMPTY,
282 };
283 compatible_cmp.matches(&Version::new(self.major, self.minor, self.patch))
284 }
285
286 pub fn matches(&self, comparator: &semver::Comparator) -> bool {
288 comparator.matches(&Version::new(self.major, self.minor, self.patch))
289 }
290}
291
292impl From<(u64, u64, u64)> for FixedVersion {
293 fn from(value: (u64, u64, u64)) -> Self {
294 FixedVersion::new(value.0, value.1, value.2)
295 }
296}
297
298impl From<FixedVersion> for (u64, u64, u64) {
299 fn from(value: FixedVersion) -> Self {
300 (value.major, value.minor, value.patch)
301 }
302}
303
304impl From<Version> for FixedVersion {
305 fn from(value: Version) -> Self {
306 FixedVersion::new(value.major, value.minor, value.patch)
307 }
308}
309
310impl From<FixedVersion> for Version {
311 fn from(value: FixedVersion) -> Self {
312 Version::new(value.major, value.minor, value.patch)
313 }
314}
315
316pub fn name_version_to_hash(name: &str, version: &FixedVersion) -> u64 {
320 let name_hash = rapidhash::rapidhash(name.as_bytes());
321 let mut bytes = [0u8; 24];
323 bytes[0..8].copy_from_slice(&version.major.to_le_bytes());
324 bytes[8..16].copy_from_slice(&version.minor.to_le_bytes());
325 bytes[16..24].copy_from_slice(&version.patch.to_le_bytes());
326 rapid_mix(name_hash, rapidhash::rapidhash(&bytes))
327}
328
329pub trait ConstTypeName {
357 const RAW_SLICE: &[&str];
361 const TYPE_NAME_FSTR: fixedstr_ext::fstr<CONST_TYPENAME_LEN> = slice_to_fstr(Self::RAW_SLICE);
363}
364
365#[inline(always)]
367pub fn type_name<T: ?Sized + FixedTypeId>() -> &'static str {
368 T::TYPE_NAME
369}
370
371#[inline(always)]
373pub fn type_id<T: ?Sized + FixedTypeId>() -> FixedId {
374 T::TYPE_ID
375}
376
377#[inline(always)]
379pub fn type_version<T: ?Sized + FixedTypeId>() -> FixedVersion {
380 T::TYPE_VERSION
381}
382
383pub const fn fstr_to_str<const N: usize>(fstr: &'static fixedstr_ext::fstr<N>) -> &'static str {
385 unsafe { core::str::from_raw_parts(fstr.to_ptr(), fstr.len()) }
386}
387
388pub const fn slice_to_fstr<const N: usize>(slice: &[&str]) -> fixedstr_ext::fstr<N> {
390 fixedstr_ext::fstr::<N>::const_create_from_str_slice(slice)
391}
392
393#[cfg(feature = "specialization")]
394impl<T> FixedTypeId for T {
395 default const TYPE_NAME: &'static str = "NOT_IMPLEMENTED";
396
397 default const TYPE_ID: FixedId =
398 FixedId::from_type_name(Self::TYPE_NAME, Some(Self::TYPE_VERSION));
399
400 default const TYPE_VERSION: FixedVersion = FixedVersion::new(0, 0, 0);
401
402 default fn ty_name(&self) -> &'static str {
403 Self::TYPE_NAME
404 }
405
406 default fn ty_id(&self) -> FixedId {
407 Self::TYPE_ID
408 }
409
410 default fn ty_version(&self) -> FixedVersion {
411 Self::TYPE_VERSION
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use crate::name_version_to_hash;
418
419 use super::prelude::*;
420
421 #[test]
422 fn unique_id_typeid_equal_to() {
423 pub struct A1;
424 pub struct A2;
425 fixed_type_id! {
426 #[version((0,1,0))]
427 #[omit_version_hash]
428 A1;
429 }
430 fixed_type_id! {
431 #[version((0,2,0))]
432 #[equal_to(A1)]
433 A2;
434 }
435 assert_eq!(
436 <A1 as FixedTypeId>::TYPE_ID,
437 FixedId::from_type_name(<A1 as FixedTypeId>::TYPE_NAME, None)
438 );
439 assert_eq!(<A1 as FixedTypeId>::TYPE_NAME, "A1");
440 assert_eq!(<A2 as FixedTypeId>::TYPE_NAME, "A2");
441 assert_eq!(<A1 as FixedTypeId>::TYPE_ID, <A2 as FixedTypeId>::TYPE_ID);
442 assert_eq!(
443 <A1 as FixedTypeId>::TYPE_VERSION,
444 FixedVersion::new(0, 1, 0)
445 );
446 assert_eq!(
447 <A2 as FixedTypeId>::TYPE_VERSION,
448 FixedVersion::new(0, 2, 0)
449 );
450 }
451
452 #[test]
453 fn unique_id_generic_ne() {
454 pub struct A<T> {
455 pub _t: T,
456 }
457 fixed_type_id! {
458 A<u8>;
459 A<u16>;
460 }
461 assert_eq!(<A<u8> as FixedTypeId>::TYPE_NAME, "A<u8>");
462 assert_eq!(<A<u16> as FixedTypeId>::TYPE_NAME, "A<u16>");
463 assert_eq!(
464 <A<u8> as FixedTypeId>::TYPE_ID,
465 FixedId::from_type_name(
466 <A<u8> as FixedTypeId>::TYPE_NAME,
467 Some(FixedVersion::new(0, 0, 0))
468 )
469 );
470 assert_eq!(
471 <A<u16> as FixedTypeId>::TYPE_ID,
472 FixedId::from_type_name(
473 <A<u16> as FixedTypeId>::TYPE_NAME,
474 Some(FixedVersion::new(0, 0, 0))
475 )
476 );
477 assert_eq!(
478 <A<u8> as FixedTypeId>::TYPE_VERSION,
479 <A<u16> as FixedTypeId>::TYPE_VERSION
480 );
481 assert_eq!(
482 <A<u8> as FixedTypeId>::TYPE_VERSION,
483 FixedVersion::new(0, 0, 0)
484 );
485 assert_eq!(
486 <A<u16> as FixedTypeId>::TYPE_VERSION,
487 FixedVersion::new(0, 0, 0)
488 );
489 }
490
491 #[test]
492 fn macro_manual_diff() {
493 mod a {
495 use super::fixed_type_id;
496 use super::{FixedId, FixedTypeId, FixedVersion};
497
498 pub struct A;
499 fixed_type_id! {
500 A;
501 }
502 }
503 mod b {
504 use super::FixedTypeId;
505 pub struct A;
506 impl FixedTypeId for A {
507 const TYPE_NAME: &'static str = "A";
508 }
509 }
510 assert_eq!(<b::A as FixedTypeId>::TYPE_ID.0, {
511 name_version_to_hash("A", &(0, 0, 0).into())
512 });
513 assert_eq!(
514 <b::A as FixedTypeId>::TYPE_ID.0,
515 <a::A as FixedTypeId>::TYPE_ID.0
516 );
517 assert_eq!(
518 <a::A as FixedTypeId>::TYPE_VERSION,
519 FixedVersion::new(0, 0, 0)
520 );
521 assert_eq!(
522 <b::A as FixedTypeId>::TYPE_VERSION,
523 <a::A as FixedTypeId>::TYPE_VERSION
524 );
525 assert_eq!(<a::A as FixedTypeId>::TYPE_NAME, "A");
526 assert_eq!(
527 <b::A as FixedTypeId>::TYPE_NAME,
528 <a::A as FixedTypeId>::TYPE_NAME
529 );
530 }
531
532 #[cfg(feature = "specialization")]
533 #[test]
534 fn specialization_for_any_type() {
535 pub struct A {
536 pub _t: u8,
537 pub _x: i32,
538 };
539
540 assert_eq!(<A as FixedTypeId>::TYPE_NAME, "NOT_IMPLEMENTED");
541 assert_eq!(
542 <A as FixedTypeId>::TYPE_ID,
543 FixedId::from_type_name("NOT_IMPLEMENTED", Some(FixedVersion::new(0, 0, 0)))
544 );
545 assert_eq!(<A as FixedTypeId>::TYPE_VERSION, FixedVersion::new(0, 0, 0));
546 }
547
548 #[test]
549 fn generic_auto_1_param() {
550 pub struct GenericType<T> {
551 some: T,
552 u32: u32,
553 }
554 use std::ops::Add;
555 pub trait DefTrait {}
556 fixed_type_id! {
557 #[version((0,1,0))]
558 #[omit_version_hash]
559 tests::generic_auto::GenericType<T: FixedTypeId + DefTrait>;
560 };
561 impl DefTrait for u8 {}
562
563 assert_eq!(
564 <GenericType<u8> as FixedTypeId>::TYPE_NAME,
565 "tests::generic_auto::GenericType<u8>"
566 );
567 assert_eq!(
568 <GenericType<u8> as FixedTypeId>::TYPE_VERSION,
569 FixedVersion::new(0, 1, 0)
570 );
571 assert_eq!(
572 <GenericType<u8> as FixedTypeId>::TYPE_ID,
573 FixedId::from_type_name(<GenericType<u8> as FixedTypeId>::TYPE_NAME, None)
574 )
575 }
576
577 #[test]
578 fn generic_auto_2_param() {
579 pub struct GenericType<T, U> {
580 some_t: T,
581 some_u: U,
582 u32: u32,
583 }
584 pub trait DefTrait {}
585 fixed_type_id! {
586 #[version((0,1,0))]
587 #[omit_version_hash]
588 tests::generic_auto::GenericType<T:, U:FixedTypeId + DefTrait>;
589 };
590 impl DefTrait for u8 {}
591
592 assert_eq!(
593 <GenericType<u8, u8> as FixedTypeId>::TYPE_NAME,
594 "tests::generic_auto::GenericType<u8>"
595 );
596 assert_eq!(
597 <GenericType<u8, u8> as FixedTypeId>::TYPE_VERSION,
598 FixedVersion::new(0, 1, 0)
599 );
600 assert_eq!(
601 <GenericType<u8, u8> as FixedTypeId>::TYPE_ID,
602 FixedId::from_type_name(<GenericType<u8, u8> as FixedTypeId>::TYPE_NAME, None)
603 );
604
605 pub struct GenericType2<T, U> {
606 some_t: T,
607 some_u: U,
608 u32: u32,
609 }
610 fixed_type_id! {
611 #[version((0,1,0))]
612 #[omit_version_hash]
613 tests::generic_auto::GenericType2<T:FixedTypeId + DefTrait, U:FixedTypeId + DefTrait>;
614 };
615
616 assert_eq!(
617 <GenericType2<u8, u8> as FixedTypeId>::TYPE_NAME,
618 "tests::generic_auto::GenericType2<u8,u8>"
619 );
620 assert_eq!(
621 <GenericType2<u8, u8> as FixedTypeId>::TYPE_VERSION,
622 FixedVersion::new(0, 1, 0)
623 );
624 assert_eq!(
625 <GenericType2<u8, u8> as FixedTypeId>::TYPE_ID,
626 FixedId::from_type_name(<GenericType2<u8, u8> as FixedTypeId>::TYPE_NAME, None)
627 )
628 }
629
630 #[test]
631 fn generic_auto_equal_to() {
632 pub struct GenericType<T> {
633 some: T,
634 u32: u32,
635 }
636 pub enum EqualType<T> {
637 X(T),
638 Y(u32),
639 }
640 pub trait DefTrait {}
641 fixed_type_id! {
642 tests::generic_auto::EqualType<T: FixedTypeId>;
643 }
644 fixed_type_id! {
645 #[version((0,1,0))]
646 #[omit_version_hash]
647 #[equal_to(EqualType<T>)]
648 tests::generic_auto::GenericType<T: FixedTypeId + DefTrait>;
649 };
650 impl DefTrait for u8 {}
651
652 assert_eq!(
653 <GenericType<u8> as FixedTypeId>::TYPE_NAME,
654 "tests::generic_auto::GenericType<u8>"
655 );
656 assert_eq!(
657 <GenericType<u8> as FixedTypeId>::TYPE_VERSION,
658 FixedVersion::new(0, 1, 0)
659 );
660 assert_eq!(
661 <GenericType<u8> as FixedTypeId>::TYPE_ID,
662 <EqualType<u8> as FixedTypeId>::TYPE_ID
663 )
664 }
665}