From 38eeb2cd74f42592982047985e2189878d234cc2 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Mon, 25 Dec 2023 21:48:05 +0800 Subject: [PATCH 1/2] wip --- src/internal.rs | 58 +++++++++++++++++++++++-------------------- src/lib.rs | 2 +- src/type/specta_id.rs | 24 +++++++++++++++--- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/internal.rs b/src/internal.rs index c9d7b6b1..3eb79a53 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -154,33 +154,6 @@ pub mod construct { pub const fn impl_location(loc: &'static str) -> ImplLocation { ImplLocation(loc) } - - /// Compute an SID hash for a given type. - /// This will produce a type hash from the arguments. - /// This hashing function was derived from https://stackoverflow.com/a/71464396 - pub const fn sid(type_name: &'static str, type_identifier: &'static str) -> SpectaID { - let mut hash = 0xcbf29ce484222325; - let prime = 0x00000100000001B3; - - let mut bytes = type_name.as_bytes(); - let mut i = 0; - - while i < bytes.len() { - hash ^= bytes[i] as u64; - hash = hash.wrapping_mul(prime); - i += 1; - } - - bytes = type_identifier.as_bytes(); - i = 0; - while i < bytes.len() { - hash ^= bytes[i] as u64; - hash = hash.wrapping_mul(prime); - i += 1; - } - - SpectaID { type_name, hash } - } } pub type NonSkipField<'a> = (&'a Field, &'a DataType); @@ -249,3 +222,34 @@ mod functions { } #[cfg(feature = "functions")] pub use functions::*; + +// This code is taken from `erased-serde` - https://github.com/dtolnay/erased-serde/blob/master/src/any.rs +#[allow(unsafe_code)] +pub(crate) mod type_id { + use std::{any::TypeId, marker::PhantomData}; + + trait NonStaticAny { + fn get_type_id(&self) -> TypeId + where + Self: 'static; + } + + impl NonStaticAny for PhantomData { + fn get_type_id(&self) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + } + + pub fn non_static_type_id() -> TypeId { + let non_static_thing = PhantomData::; + let thing = unsafe { + std::mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>( + &non_static_thing, + ) + }; + NonStaticAny::get_type_id(thing) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7beef1db..3625f41d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![doc = include_str!("./docs.md")] -#![forbid(unsafe_code)] +#![deny(unsafe_code)] #![warn(clippy::all, clippy::unwrap_used, clippy::panic)] // TODO: missing_docs #![allow(clippy::module_inception)] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/type/specta_id.rs b/src/type/specta_id.rs index 3d161583..9ab0729b 100644 --- a/src/type/specta_id.rs +++ b/src/type/specta_id.rs @@ -1,4 +1,6 @@ -use std::cmp::Ordering; +use std::{any::TypeId, cmp::Ordering}; + +use crate::internal::type_id::non_static_type_id; /// The unique Specta ID for the type. /// @@ -10,11 +12,25 @@ use std::cmp::Ordering; /// - `&'a T::SID == &'b T::SID` (unlike std::any::TypeId which forces a static lifetime) /// - `Box == Arc == Rc` (unlike std::any::TypeId) /// +// TODO: Encode the properties above into unit tests. #[allow(clippy::derived_hash_with_manual_eq)] #[derive(Debug, Clone, Copy, Hash)] pub struct SpectaID { pub(crate) type_name: &'static str, - pub(crate) hash: u64, + pub(crate) tid: TypeId, +} + +impl SpectaID { + // TODO: Unit test this well including with non-static types. + pub fn from() -> Self { + let type_name = std::any::type_name::(); + let last_segment = type_name.rfind("::").unwrap_or(type_name.len()); + + SpectaID { + type_name: &type_name[0..last_segment], + tid: non_static_type_id::(), + } + } } // We do custom impls so the order prefers type_name over hash. @@ -22,7 +38,7 @@ impl Ord for SpectaID { fn cmp(&self, other: &Self) -> Ordering { self.type_name .cmp(other.type_name) - .then(self.hash.cmp(&other.hash)) + .then(self.tid.cmp(&other.tid)) } } @@ -39,7 +55,7 @@ impl Eq for SpectaID {} // We do custom impls so equals is by SID exclusively. impl PartialEq for SpectaID { fn eq(&self, other: &Self) -> bool { - self.hash.eq(&other.hash) + self.tid.eq(&other.tid) } } From c2099c15adef571ca00961cce411b2dccd0ecc8c Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Mon, 25 Dec 2023 23:27:59 +0800 Subject: [PATCH 2/2] Yeah this probs won't work --- macros/src/type/mod.rs | 2 +- src/type/specta_id.rs | 18 ++++++++++++++---- tests/duplicate_ty_name.rs | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/macros/src/type/mod.rs b/macros/src/type/mod.rs index 4f00ee20..aaf8a3b6 100644 --- a/macros/src/type/mod.rs +++ b/macros/src/type/mod.rs @@ -44,7 +44,7 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result()); let (inlines, reference, can_flatten) = match data { Data::Struct(data) => { parse_struct(&name, &container_attrs, generics, &crate_ref, &sid, data) diff --git a/src/type/specta_id.rs b/src/type/specta_id.rs index 9ab0729b..054fd7a2 100644 --- a/src/type/specta_id.rs +++ b/src/type/specta_id.rs @@ -21,13 +21,23 @@ pub struct SpectaID { } impl SpectaID { - // TODO: Unit test this well including with non-static types. + // TODO: Unit test this (including with non-static types) pub fn from() -> Self { - let type_name = std::any::type_name::(); - let last_segment = type_name.rfind("::").unwrap_or(type_name.len()); + // I am aware `std::any::type_name`'s format is not guaranteed but plz just let me order my types by name without a macro in peace. + let type_name = std::any::type_name::(); // Current output is in the form `some::Type` + + // Strip from `<` to the end of the string + let end_offset = type_name.rfind("<").unwrap_or(type_name.len()); + + // Strip everything before the last `::` + let start_offset = type_name[0..end_offset] + .rfind("::") + // +2 to skip the :: + .map(|v| v + 2) + .unwrap_or(0); SpectaID { - type_name: &type_name[0..last_segment], + type_name: &type_name[start_offset..end_offset], tid: non_static_type_id::(), } } diff --git a/tests/duplicate_ty_name.rs b/tests/duplicate_ty_name.rs index 48418893..1f4b3ae3 100644 --- a/tests/duplicate_ty_name.rs +++ b/tests/duplicate_ty_name.rs @@ -39,14 +39,14 @@ fn test_duplicate_ty_name() { #[cfg(not(target_os = "windows"))] let err = Err(ExportError::DuplicateTypeName( "One".into(), - impl_location("tests/duplicate_ty_name.rs:19:14"), impl_location("tests/duplicate_ty_name.rs:9:14"), + impl_location("tests/duplicate_ty_name.rs:19:14"), )); #[cfg(target_os = "windows")] let err = Err(ExportError::DuplicateTypeName( "One".into(), - impl_location("tests\\duplicate_ty_name.rs:19:14"), impl_location("tests\\duplicate_ty_name.rs:9:14"), + impl_location("tests\\duplicate_ty_name.rs:19:14"), )); assert_eq!(export::(&Default::default()), err);