diff --git a/README.md b/README.md index 13c7339..82c640d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ slab-like map with an interface similar to that of `HashMap`._ Let's imagine that at runtime you create a small number of clumsy objects that are used as keys in hashmaps. This crate allows you to seamlessly replace them with lightweight identifiers in a slab-like manner -using the `register_blazemap_id` macro as well as using them as keys in the `BlazeMap` +using the `register_blazemap_id` and `register_blazemap_id_wrapper` macros as well as using them as keys in +the `BlazeMap` — a vector-based slab-like map with an interface similar to that of `HashMap`. ## Graphical representation of the old approach @@ -17,7 +18,9 @@ using the `register_blazemap_id` macro as well as using them as keys in the `Bla ![NewApproach.svg](./docs/drawio/README_-_NewApproach.svg) -## `register_blazemap_id` macro +## `register_blazemap_id_wrapper` macro + +Creates a new type that is compatible as a key-wrapper for `blazemap` collections. This macro supports optional inference of standard traits using the following syntax: @@ -44,12 +47,30 @@ This macro supports optional inference of standard traits using the following sy * `PartialOrd` (mutually exclusive with `Ord`) * `Ord` (also derives `PartialOrd`, so mutually exclusive with `PartialOrd`) +## `register_blazemap_id` macro + +Creates a new type based on `usize` that is compatible as a key-wrapper for `blazemap` collections. + +This macro supports optional inference of standard traits using the following syntax: + +* `Derive` — derives traits in the same way as for + the serial number assigned when creating a new instance of the type. + Because methods inferred by this option do not require additional + locking on synchronization primitives, + they do not incur any additional overhead compared to methods inferred for plain `usize`. + This method supports inference of the following traits: + * `PartialOrd` (mutually exclusive with `Ord`) + * `Ord` (also derives `PartialOrd`, so mutually exclusive with `PartialOrd`) + * `Serialize` (with `serde` feature only) + ### Example +## `register_blazemap_id_wrapper` macro + ```rust -use blazemap::prelude::{BlazeMap, register_blazemap_id}; +use blazemap::prelude::{BlazeMap, register_blazemap_id_wrapper}; -register_blazemap_id! { +register_blazemap_id_wrapper! { pub struct BlazeMapId(String); Derive(as for Original Type): { // Optional section Debug, @@ -70,4 +91,28 @@ map.insert(key_1, "1"); map.insert(key_3, "3"); assert_eq!(format!("{map:?}"), r#"{"first": "1", "second": "2", "third": "3"}"#) +``` + +## `register_blazemap_id` macro + +```rust +use blazemap::prelude::{BlazeMap, register_blazemap_id}; + +register_blazemap_id! { + pub struct BlazeMapIdExample(start from: 1); // "(start from: number)" is optional + Derive: { // Derive section is also optional + Ord + }; +} + +let key_1 = BlazeMapIdExample::new(); +let key_2 = BlazeMapIdExample::new(); +let key_3 = BlazeMapIdExample::new(); + +let mut map = BlazeMap::new(); +map.insert(key_2, "2"); +map.insert(key_1, "1"); +map.insert(key_3, "3"); + +assert_eq!(format!("{map:?}"), r#"{1: "1", 2: "2", 3: "3"}"#) ``` \ No newline at end of file diff --git a/src/collections/blazemap.rs b/src/collections/blazemap.rs index 320794c..2f24cea 100644 --- a/src/collections/blazemap.rs +++ b/src/collections/blazemap.rs @@ -1,7 +1,8 @@ +use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; -use read_write_api::ReadApi; +use read_write_api::{ReadApi, UpgradableReadApi}; #[cfg(feature = "serde")] use serde::{ de::{MapAccess, Visitor}, @@ -17,8 +18,9 @@ pub use crate::collections::blazemap::{ iter::{Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut}, }; use crate::collections::blazemap::entry::VacantEntryInner; -use crate::id_wrapper::IdWrapper; -use crate::orig_type_id_map::OrigTypeIdMap; +use crate::id_wrapper::BlazeMapId; +use crate::orig_type_id_map::{InsertableStaticInfoApi, StaticInfoApi}; +use crate::prelude::BlazeMapIdWrapper; mod entry; mod iter; @@ -139,7 +141,7 @@ impl BlazeMap impl BlazeMap where - K: IdWrapper + K: BlazeMapId { /// Creates a new instance of the [`BlazeMap`] /// with capacity equal to the current total number of unique `K` instances. @@ -262,7 +264,7 @@ impl BlazeMap impl IntoIterator for BlazeMap where - K: IdWrapper + K: BlazeMapId { type Item = (K, V); type IntoIter = IntoIter; @@ -277,7 +279,7 @@ impl IntoIterator for BlazeMap impl<'a, K, V> IntoIterator for &'a BlazeMap where - K: IdWrapper + K: BlazeMapId { type Item = (K, &'a V); type IntoIter = Iter<'a, K, V>; @@ -290,7 +292,7 @@ impl<'a, K, V> IntoIterator for &'a BlazeMap impl<'a, K, V> IntoIterator for &'a mut BlazeMap where - K: IdWrapper + K: BlazeMapId { type Item = (K, &'a mut V); type IntoIter = IterMut<'a, K, V>; @@ -303,7 +305,7 @@ impl<'a, K, V> IntoIterator for &'a mut BlazeMap impl FromIterator<(K, V)> for BlazeMap where - K: IdWrapper + K: BlazeMapId { #[inline] fn from_iter>(iter: T) -> Self { @@ -340,22 +342,26 @@ macro_rules! blaze_map_orig_key_blocking_iter { impl Debug for BlazeMap where - K: IdWrapper, - ::OrigType: Debug, + K: BlazeMapId, + ::OrigType: Debug, V: Debug { #[inline] fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { blaze_map_orig_key_blocking_iter!(self, iter, guard); - f.debug_map().entries(iter).finish() + let mut debug_map = f.debug_map(); + for (key, value) in iter { + debug_map.entry(key.borrow(), value); + } + debug_map.finish() } } #[cfg(feature = "serde")] impl Serialize for BlazeMap where - K: IdWrapper, - ::OrigType: Serialize, + K: BlazeMapId, + ::OrigType: Serialize, V: Serialize { #[inline] @@ -366,7 +372,7 @@ impl Serialize for BlazeMap blaze_map_orig_key_blocking_iter!(self, iter, guard); let mut serializer = serializer.serialize_map(Some(self.len))?; for (key, value) in iter { - serializer.serialize_entry(key, value)?; + serializer.serialize_entry(key.borrow(), value)?; } serializer.end() } @@ -375,8 +381,10 @@ impl Serialize for BlazeMap #[cfg(feature = "serde")] impl<'de, K, V> Deserialize<'de> for BlazeMap where - K: IdWrapper, - ::OrigType: Deserialize<'de>, + K: BlazeMapIdWrapper, + ::OrigType: Deserialize<'de>, + ::StaticInfoApi: InsertableStaticInfoApi<::OrigType>, + ::StaticInfoApiLock: UpgradableReadApi, V: Deserialize<'de> { #[inline] @@ -391,13 +399,17 @@ impl<'de, K, V> Deserialize<'de> for BlazeMap #[cfg(feature = "serde")] struct BlazeMapDeserializer(PhantomData<(K, V)>) where - K: IdWrapper; + K: BlazeMapIdWrapper, + ::StaticInfoApi: InsertableStaticInfoApi<::OrigType>, + ::StaticInfoApiLock: UpgradableReadApi; #[cfg(feature = "serde")] impl<'de, K, V> Visitor<'de> for BlazeMapDeserializer where - K: IdWrapper, - ::OrigType: Deserialize<'de>, + K: BlazeMapIdWrapper, + ::OrigType: Deserialize<'de>, + ::StaticInfoApi: InsertableStaticInfoApi<::OrigType>, + ::StaticInfoApiLock: UpgradableReadApi, V: Deserialize<'de> { type Value = BlazeMap; diff --git a/src/collections/blazemap/entry.rs b/src/collections/blazemap/entry.rs index 460b329..ea4ea90 100644 --- a/src/collections/blazemap/entry.rs +++ b/src/collections/blazemap/entry.rs @@ -1,4 +1,4 @@ -use crate::prelude::IdWrapper; +use crate::prelude::BlazeMapId; #[derive(Debug)] /// A view into a single entry in a map, which may either be vacant or occupied. @@ -9,7 +9,7 @@ use crate::prelude::IdWrapper; /// [`entry`]: crate::collections::blazemap::BlazeMap::entry pub enum Entry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { /// An occupied entry. Occupied(OccupiedEntry<'a, K, V>), @@ -22,7 +22,7 @@ pub enum Entry<'a, K, V> /// It is part of the [`Entry`] enum. pub struct OccupiedEntry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { pub(in crate::collections::blazemap) key: K, @@ -39,7 +39,7 @@ pub struct OccupiedEntry<'a, K, V> /// It is part of the [`Entry`] enum. pub struct VacantEntry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { pub(in crate::collections::blazemap) key: K, @@ -60,7 +60,7 @@ enum VacantEntryInner<'a, V> { impl<'a, K, V> Entry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { /// Ensures a value is in the entry by inserting the default if empty, /// and returns a mutable reference to the value in the entry. @@ -107,7 +107,7 @@ impl<'a, K, V> Entry<'a, K, V> impl<'a, K, V> Entry<'a, K, V> where - K: IdWrapper, + K: BlazeMapId, V: Default { /// Ensures a value is in the entry by inserting the default value if empty, @@ -123,7 +123,7 @@ impl<'a, K, V> Entry<'a, K, V> impl<'a, K, V> OccupiedEntry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { /// Gets the key in the entry. #[inline] @@ -193,7 +193,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> impl<'a, K, V> VacantEntry<'a, K, V> where - K: IdWrapper + K: BlazeMapId { /// Gets the key that would be used when inserting a value through the [`VacantEntry`]. #[inline] diff --git a/src/collections/blazemap/iter.rs b/src/collections/blazemap/iter.rs index 99473cd..7f56148 100644 --- a/src/collections/blazemap/iter.rs +++ b/src/collections/blazemap/iter.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::mem::needs_drop; @@ -6,8 +7,8 @@ use std::panic::{RefUnwindSafe, UnwindSafe}; use read_write_api::ReadApi; use crate::collections::blazemap::BlazeMap; -use crate::orig_type_id_map::OrigTypeIdMap; -use crate::prelude::IdWrapper; +use crate::orig_type_id_map::StaticInfoApi; +use crate::prelude::BlazeMapId; /// An iterator over the entries of a [`BlazeMap`]. /// @@ -137,7 +138,7 @@ pub struct Drain<'a, K, V> impl<'a, K, V> Iterator for Iter<'a, K, V> where - K: IdWrapper + K: BlazeMapId { type Item = (K, &'a V); @@ -173,7 +174,7 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -183,7 +184,7 @@ impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> impl<'a, K, V> Iterator for IterMut<'a, K, V> where - K: IdWrapper + K: BlazeMapId { type Item = (K, &'a mut V); @@ -213,7 +214,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -223,7 +224,7 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> impl<'a, K, V> Iterator for Keys<'a, K, V> where - K: IdWrapper + K: BlazeMapId { type Item = K; @@ -259,7 +260,7 @@ impl<'a, K, V> Iterator for Keys<'a, K, V> impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -342,7 +343,7 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> impl Iterator for IntoIter where - K: IdWrapper + K: BlazeMapId { type Item = (K, V); @@ -366,7 +367,7 @@ impl Iterator for IntoIter impl ExactSizeIterator for IntoIter where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -376,7 +377,7 @@ impl ExactSizeIterator for IntoIter impl Iterator for IntoKeys where - K: IdWrapper + K: BlazeMapId { type Item = K; @@ -400,7 +401,7 @@ impl Iterator for IntoKeys impl ExactSizeIterator for IntoKeys where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -431,7 +432,7 @@ impl Iterator for IntoValues impl ExactSizeIterator for IntoValues where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -441,7 +442,7 @@ impl ExactSizeIterator for IntoValues impl<'a, K, V> Iterator for Drain<'a, K, V> where - K: IdWrapper + K: BlazeMapId { type Item = (K, V); @@ -473,7 +474,7 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> where - K: IdWrapper + K: BlazeMapId { #[inline] fn len(&self) -> usize { @@ -547,7 +548,7 @@ impl<'a, K, V> Copy for Iter<'a, K, V> {} impl<'a, K, V> Debug for Iter<'a, K, V> where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug, V: Debug { @@ -555,19 +556,18 @@ impl<'a, K, V> Debug for Iter<'a, K, V> fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { let static_info = K::static_info(); let static_info = static_info.read(); - let iter = self.map( - |(key, value)| { - let key = unsafe { static_info.get_key_unchecked(key.get_index()) }; - (key, value) - } - ); - f.debug_map().entries(iter).finish() + let mut debug_map = f.debug_map(); + for (key, value) in self.into_iter() { + let key = unsafe { static_info.get_key_unchecked(key.get_index()) }; + debug_map.entry(key.borrow(), value); + } + debug_map.finish() } } impl<'a, K, V> Debug for IterMut<'a, K, V> where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug, V: Debug { @@ -611,17 +611,19 @@ impl<'a, K, V> Copy for Values<'a, K, V> {} impl<'a, K, V> Debug for Keys<'a, K, V> where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug { #[inline] fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { let static_info = K::static_info(); let static_info = static_info.read(); - let iter = self.map( - |key| unsafe { static_info.get_key_unchecked(key.get_index()) } - ); - f.debug_list().entries(iter).finish() + let mut debug_list = f.debug_list(); + for key in self.into_iter() { + let key = unsafe { static_info.get_key_unchecked(key.get_index()) }; + debug_list.entry(key.borrow()); + } + debug_list.finish() } } @@ -661,7 +663,7 @@ impl<'a, K, V> Debug for ValuesMut<'a, K, V> impl Debug for IntoIter where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug, V: Debug { @@ -673,7 +675,7 @@ impl Debug for IntoIter impl Debug for IntoKeys where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug { #[inline] @@ -694,7 +696,7 @@ impl Debug for IntoValues impl<'a, K, V> Debug for Drain<'a, K, V> where - K: IdWrapper, + K: BlazeMapId, K::OrigType: Debug, V: Debug { diff --git a/src/id_wrapper.rs b/src/id_wrapper.rs index fb3c108..ab0a59a 100644 --- a/src/id_wrapper.rs +++ b/src/id_wrapper.rs @@ -1,23 +1,40 @@ use std::hash::Hash; -use read_write_api::{RwApi, UpgradableReadApi, UpgradableReadGuard}; +use read_write_api::{ReadApi, UpgradableReadApi, UpgradableReadGuard}; -use crate::orig_type_id_map::OrigTypeIdMap; +use crate::orig_type_id_map::{InsertableStaticInfoApi, StaticInfoApi}; -/// Provides interface for `blazemap` key-wrapper types -/// defined by the [`register_blazemap_id`](crate::register_blazemap_id) macro. -pub trait IdWrapper: Copy +/// Provides interface for `blazemap` id types +/// defined by the [`register_blazemap_id_wrapper`](crate::register_blazemap_id_wrapper) macro. +pub trait BlazeMapId: Copy { /// Original key type. type OrigType: 'static + Clone + Eq + Hash; #[doc(hidden)] - type OrigTypeIdMap: 'static + OrigTypeIdMap; + type StaticInfoApi: 'static + StaticInfoApi; #[doc(hidden)] - type OrigTypeIdMapApi: RwApi; + type StaticInfoApiLock: ReadApi; - /// Creates a new instance of [`Self`] based on the [`Self::OrigType`] instance. + #[doc(hidden)] + fn get_index(self) -> usize; + + #[doc(hidden)] + unsafe fn from_index_unchecked(index: usize) -> Self; + + #[doc(hidden)] + fn static_info() -> Self::StaticInfoApiLock; +} + +/// Provides interface for constructable `blazemap` key-wrapper types +/// defined by the [`register_blazemap_id`](crate::register_blazemap_id_wrapper) macro. +pub trait BlazeMapIdWrapper: BlazeMapId + where + Self::StaticInfoApi: InsertableStaticInfoApi, + Self::StaticInfoApiLock: UpgradableReadApi +{ + /// Creates a new instance of [`Self`] based on the [`Self::OrigType`](BlazeMapId::OrigType) instance. #[inline] fn new(key: Self::OrigType) -> Self { unsafe { @@ -32,13 +49,4 @@ pub trait IdWrapper: Copy } } } - - #[doc(hidden)] - fn get_index(self) -> usize; - - #[doc(hidden)] - unsafe fn from_index_unchecked(index: usize) -> Self; - - #[doc(hidden)] - fn static_info() -> Self::OrigTypeIdMapApi; } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index fca5bb0..db40850 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! Provides a [wrapper](register_blazemap_id) for replacing a small number +//! Provides a [wrapper](register_blazemap_id_wrapper) for replacing a small number //! of clumsy objects with identifiers, //! and also implements a [vector-based slab-like map](prelude::BlazeMap) //! with an interface similar to that of [`HashMap`](std::collections::HashMap). @@ -16,8 +16,9 @@ pub mod orig_type_id_map; pub mod prelude { pub use crate::{ collections::blazemap::BlazeMap, - id_wrapper::IdWrapper, + id_wrapper::{BlazeMapId, BlazeMapIdWrapper}, register_blazemap_id, + register_blazemap_id_wrapper, }; } @@ -26,20 +27,20 @@ pub mod external { #[cfg(feature = "serde")] pub use serde; - pub use {once_cell, parking_lot}; + pub use {once_cell, parking_lot, read_write_api}; } #[cfg(test)] mod tests { - use crate::register_blazemap_id; + use crate::register_blazemap_id_wrapper; #[cfg(feature = "serde")] mod serde_compatible { - use crate::register_blazemap_id; + use crate::register_blazemap_id_wrapper; - register_blazemap_id! { + register_blazemap_id_wrapper! { pub struct BlazeMapKeyExample(usize); Derive(as for Original Type): { Default, @@ -52,7 +53,7 @@ mod tests } } - register_blazemap_id! { + register_blazemap_id_wrapper! { pub struct BlazeMapKeyExample(usize); Derive(as for Original Type): { Default, diff --git a/src/macros.rs b/src/macros.rs index ab5f5e7..7e7ea49 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,5 @@ /// Creates a new type that is compatible as a key-wrapper for `blazemap` collections. +/// Use this macro if you want to wrap your clumsy types in `blazemap` collection keys. /// /// This macro supports optional inference of standard traits using the following syntax: /// @@ -17,7 +18,7 @@ /// * `Deserialize` (with `serde` feature only) /// * `Derive(as for Serial Number)` — derives traits in the same way as for /// the serial number assigned when registering an instance of the original type -/// the first time [`IdWrapper::new`](crate::prelude::IdWrapper::new) was called. +/// the first time [`IdWrapper::new`](crate::prelude::BlazeMapIdWrapper::new) was called. /// Because methods inferred by this option do not require additional /// locking on synchronization primitives, /// they do not incur any additional overhead compared to methods inferred for plain `usize`. @@ -28,9 +29,9 @@ /// # Example /// /// ```rust -/// use blazemap::prelude::{BlazeMap, register_blazemap_id}; +/// use blazemap::prelude::{BlazeMap, register_blazemap_id_wrapper}; /// -/// register_blazemap_id! { +/// register_blazemap_id_wrapper! { /// pub struct BlazeMapKeyExample(&'static str); /// Derive(as for Original Type): { // Optional section /// Debug, @@ -53,7 +54,7 @@ /// assert_eq!(format!("{map:?}"), r#"{"first": "1", "second": "2", "third": "3"}"#) /// ``` #[macro_export] -macro_rules! register_blazemap_id { +macro_rules! register_blazemap_id_wrapper { ( $(#[$attrs:meta])* $vis:vis @@ -62,7 +63,7 @@ macro_rules! register_blazemap_id { $(; Derive(as for Serial Number): {$( $to_derive_sn:ident),+ $(,)?} )? $(;)? ) => { - $crate::blazemap_inner! { + $crate::blazemap_id_wrapper_inner! { $(#[$attrs])* $vis struct $new_type($orig_type) @@ -78,7 +79,7 @@ macro_rules! register_blazemap_id { $(; Derive(as for Original Type): {$($to_derive_orig:ident),+ $(,)?} )? $(;)? ) => { - $crate::blazemap_inner! { + $crate::blazemap_id_wrapper_inner! { $(#[$attrs])* $vis struct $new_type($orig_type) @@ -88,9 +89,78 @@ macro_rules! register_blazemap_id { } } +/// Creates a new type based on `usize` that is compatible as a key-wrapper for `blazemap` collections. +/// +/// This macro supports optional inference of standard traits using the following syntax: +/// +/// * `Derive` — derives traits in the same way as for +/// the serial number assigned when creating a new instance of the type. +/// Because methods inferred by this option do not require additional +/// locking on synchronization primitives, +/// they do not incur any additional overhead compared to methods inferred for plain `usize`. +/// This method supports inference of the following traits: +/// * `PartialOrd` (mutually exclusive with `Ord`) +/// * `Ord` (also derives `PartialOrd`, so mutually exclusive with `PartialOrd`) +/// * `Serialize` (with `serde` feature only) +/// +/// # Example +/// +/// ```rust +/// use blazemap::prelude::{BlazeMap, register_blazemap_id}; +/// +/// register_blazemap_id! { +/// pub struct BlazeMapIdExample(start from: 1); // "(start from: number)" is optional +/// Derive: { // Derive section is also optional +/// Ord +/// }; +/// } +/// +/// let key_1 = BlazeMapIdExample::new(); +/// let key_2 = BlazeMapIdExample::new(); +/// let key_3 = BlazeMapIdExample::new(); +/// +/// let mut map = BlazeMap::new(); +/// map.insert(key_2, "2"); +/// map.insert(key_1, "1"); +/// map.insert(key_3, "3"); +/// +/// assert_eq!(format!("{map:?}"), r#"{1: "1", 2: "2", 3: "3"}"#) +/// ``` +#[macro_export] +macro_rules! register_blazemap_id { + ( + $(#[$attrs:meta])* + $vis:vis + struct $new_type:ident(start from: $first_id:literal) + $(; Derive: {$($to_derive_sn:ident),+ $(,)?} )? + $(;)? + ) => { + $crate::blazemap_id_inner! { + $(#[$attrs])* + $vis + struct $new_type($first_id) + } + $($($crate::blazemap_id_inner_derive! {@DERIVE $to_derive_sn $new_type})*)? + }; + ( + $(#[$attrs:meta])* + $vis:vis + struct $new_type:ident + $(; Derive: {$($to_derive_sn:ident),+ $(,)?} )? + $(;)? + ) => { + $crate::blazemap_id_inner! { + $(#[$attrs])* + $vis + struct $new_type(0) + } + $($($crate::blazemap_id_inner_derive! {@DERIVE $to_derive_sn $new_type})*)? + }; +} + #[doc(hidden)] #[macro_export] -macro_rules! blazemap_inner { +macro_rules! blazemap_id_wrapper_inner { ( $(#[$attrs:meta])* $vis:vis @@ -105,15 +175,15 @@ macro_rules! blazemap_inner { { #[inline] pub fn new(value: $orig_type) -> Self { - ::new(value) + ::new(value) } } - impl $crate::prelude::IdWrapper for $new_type + impl $crate::prelude::BlazeMapId for $new_type { type OrigType = $orig_type; - type OrigTypeIdMap = $crate::utils::StaticInfo<$orig_type>; - type OrigTypeIdMapApi = &'static $crate::external::parking_lot::RwLock<&'static mut $crate::utils::StaticInfo<$orig_type>>; + type StaticInfoApi = $crate::utils::IdWrapperStaticInfo<$orig_type>; + type StaticInfoApiLock = &'static $crate::external::parking_lot::RwLock<$crate::utils::IdWrapperStaticInfo<$orig_type>>; #[inline] fn get_index(self) -> usize { @@ -127,19 +197,97 @@ macro_rules! blazemap_inner { } #[inline] - fn static_info() -> &'static $crate::external::parking_lot::RwLock<&'static mut $crate::utils::StaticInfo<$orig_type>> + fn static_info() -> &'static $crate::external::parking_lot::RwLock<$crate::utils::IdWrapperStaticInfo<$orig_type>> { use $crate::external::once_cell::sync::Lazy; use $crate::external::parking_lot::RwLock; - use $crate::utils::StaticInfo; + use $crate::utils::IdWrapperStaticInfo; - static mut STATIC_INFO: StaticInfo<$orig_type> = StaticInfo::new(); - static MAP: Lazy>> = Lazy::new( - || RwLock::new(unsafe { &mut STATIC_INFO }) + static MAP: Lazy>> = Lazy::new( + || RwLock::new(IdWrapperStaticInfo::new()) ); &MAP } } + + impl $crate::prelude::BlazeMapIdWrapper for $new_type + {} + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! blazemap_id_inner { + ( + $(#[$attrs:meta])* + $vis:vis + struct $new_type:ident($first_id:literal) + ) => { + $(#[$attrs])* + #[derive(Clone, Copy, Eq, PartialEq, Hash)] + #[repr(transparent)] + $vis struct $new_type($crate::utils::PrivateIndex); + + impl $new_type + { + #[inline] + pub fn new() -> Self { + use $crate::prelude::BlazeMapId; + + let next_id = Self::static_info().0.next_id(); + Self(unsafe { $crate::utils::PrivateIndex::new(next_id) }) + } + } + + impl $crate::prelude::BlazeMapId for $new_type + { + type OrigType = usize; + type StaticInfoApi = $crate::utils::TrivialIdStaticInfo; + type StaticInfoApiLock = &'static $crate::external::read_write_api::RwApiWrapperOwned<$crate::utils::TrivialIdStaticInfo>; + + #[inline] + fn get_index(self) -> usize { + let Self(index) = self; + index.into_inner() + } + + #[inline(always)] + unsafe fn from_index_unchecked(index: usize) -> Self { + Self($crate::utils::PrivateIndex::new(index)) + } + + #[inline] + fn static_info() -> &'static $crate::external::read_write_api::RwApiWrapperOwned<$crate::utils::TrivialIdStaticInfo> + { + use $crate::utils::TrivialIdStaticInfo; + use $crate::external::read_write_api::RwApiWrapperOwned; + + static INFO: RwApiWrapperOwned = RwApiWrapperOwned(TrivialIdStaticInfo::new($first_id)); + &INFO + } + } + + impl ::std::fmt::Debug for $new_type + { + #[inline] + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + let Self(index) = self; + f.debug_tuple(::std::stringify!($new_type)) + .field(&index.into_inner()) + .finish() + } + } + + impl ::std::fmt::Display for $new_type + { + #[inline] + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + let Self(index) = self; + write!(f, "{}", index.into_inner()) + } + } } } @@ -161,11 +309,11 @@ macro_rules! blazemap_derive_key_inner { #[inline] fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - use $crate::orig_type_id_map::OrigTypeIdMap; + use $crate::orig_type_id_map::StaticInfoApi; let Self(lhs) = self; let Self(rhs) = other; - let guard = ::static_info().read(); + let guard = ::static_info().read(); let (lhs, rhs) = unsafe { ( guard.get_key_unchecked(lhs.into_inner()), @@ -190,11 +338,11 @@ macro_rules! blazemap_derive_key_inner { #[inline] fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - use $crate::orig_type_id_map::OrigTypeIdMap; + use $crate::orig_type_id_map::StaticInfoApi; let Self(lhs) = self; let Self(rhs) = other; - let guard = ::static_info().read(); + let guard = ::static_info().read(); let (lhs, rhs) = unsafe { ( guard.get_key_unchecked(lhs.into_inner()), @@ -211,11 +359,11 @@ macro_rules! blazemap_derive_key_inner { #[inline] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - use $crate::orig_type_id_map::OrigTypeIdMap; + use $crate::orig_type_id_map::StaticInfoApi; let Self(index) = self; let mut f = f.debug_struct(::std::stringify!($new_type)); - let guard = ::static_info().read(); + let guard = ::static_info().read(); let original_key = unsafe { guard.get_key_unchecked(index.into_inner()) }; f.field("original_key", original_key); drop(guard); @@ -231,10 +379,10 @@ macro_rules! blazemap_derive_key_inner { #[inline] fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - use $crate::orig_type_id_map::OrigTypeIdMap; + use $crate::orig_type_id_map::StaticInfoApi; let Self(index) = self; - let guard = ::static_info().read(); + let guard = ::static_info().read(); let original_key = unsafe { guard.get_key_unchecked(index.into_inner()) }; write!(f, "{original_key}") } @@ -248,9 +396,9 @@ macro_rules! blazemap_derive_key_inner { where D: $crate::external::serde::Deserializer<'de> { - let original_key: ::OrigType + let original_key: ::OrigType = $crate::external::serde::Deserialize::deserialize(deserializer)?; - Ok(::new(original_key)) + Ok(::new(original_key)) } } }; @@ -262,11 +410,11 @@ macro_rules! blazemap_derive_key_inner { where S: $crate::external::serde::Serializer { - use $crate::orig_type_id_map::OrigTypeIdMap; + use $crate::orig_type_id_map::StaticInfoApi; let Self(index) = self; unsafe { - ::static_info() + ::static_info() .read() .get_key_unchecked(index.into_inner()) .serialize(serializer) @@ -309,4 +457,52 @@ macro_rules! blazemap_derive_assigned_sn { } } } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! blazemap_id_inner_derive { + (@DERIVE PartialOrd $new_type:ident) => { + impl PartialOrd for $new_type + { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + let Self(lhs) = self; + let Self(rhs) = other; + lhs.into_inner().partial_cmp(&rhs.into_inner()) + } + } + }; + (@DERIVE Ord $new_type:ident) => { + impl PartialOrd for $new_type + { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + Some(self.cmp(other)) + } + } + + impl Ord for $new_type + { + #[inline] + fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + let Self(lhs) = self; + let Self(rhs) = other; + lhs.into_inner().cmp(&rhs.into_inner()) + } + } + }; + (@DERIVE Serialize $new_type:ident) => { + impl $crate::external::serde::Serialize for $new_type + { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: $crate::external::serde::Serializer + { + let Self(index) = self; + index.into_inner().serialize(serializer) + } + } + } } \ No newline at end of file diff --git a/src/orig_type_id_map.rs b/src/orig_type_id_map.rs index 7c16610..216ab2f 100644 --- a/src/orig_type_id_map.rs +++ b/src/orig_type_id_map.rs @@ -1,11 +1,20 @@ +use std::borrow::Borrow; + #[doc(hidden)] -pub trait OrigTypeIdMap +pub trait StaticInfoApi { + type KeyUnchecked<'a>: Borrow + where Self: 'a; + fn num_elems(&self) -> usize; - fn get_index(&self, key: &K) -> Option; + unsafe fn get_key_unchecked(&self, index: usize) -> Self::KeyUnchecked<'_>; +} - unsafe fn get_key_unchecked(&self, index: usize) -> &K; +#[doc(hidden)] +pub trait InsertableStaticInfoApi: StaticInfoApi +{ + fn get_index(&self, key: &K) -> Option; unsafe fn insert_new_key_unchecked(&mut self, key: K) -> usize; } \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index 5ff98d6..4cc3009 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,4 @@ -pub use {private_index::PrivateIndex, static_info::StaticInfo}; +pub use {private_index::PrivateIndex, static_info::{IdWrapperStaticInfo, TrivialIdStaticInfo}}; #[doc(hidden)] mod private_index; diff --git a/src/utils/private_index.rs b/src/utils/private_index.rs index d81098e..8708994 100644 --- a/src/utils/private_index.rs +++ b/src/utils/private_index.rs @@ -1,7 +1,7 @@ use std::hash::Hash; /// Necessary to protect the internal `usize`, which, in the absence of this wrapper, -/// would be public in the module calling [`register_blazemap_id`](crate::register_blazemap_id). +/// would be public in the module calling [`register_blazemap_id_wrapper`](crate::register_blazemap_id_wrapper). /// /// Publicity of the internal `usize` may lead to: /// * UB if the programmer of the downstream crate would accidentally mutate it. @@ -38,7 +38,7 @@ mod tests // These assertions are needed in order to prevent standard traits // from being automatically derived for types - // generated by the [`register_blazemap_id`](crate::register_blazemap_id) macro. + // generated by the [`register_blazemap_id_wrapper`](crate::register_blazemap_id) macro. assert_not_impl_any!(PrivateIndex: Default, Debug, Display, PartialOrd); #[cfg(feature = "serde")] diff --git a/src/utils/static_info.rs b/src/utils/static_info.rs index 252b7e0..f7c5dc1 100644 --- a/src/utils/static_info.rs +++ b/src/utils/static_info.rs @@ -1,22 +1,29 @@ use std::collections::HashMap; use std::hash::Hash; +use std::sync::atomic::{AtomicUsize, Ordering}; use once_cell::sync::Lazy; -use crate::orig_type_id_map::OrigTypeIdMap; +use crate::orig_type_id_map::{InsertableStaticInfoApi, StaticInfoApi}; /// Global, statically initialized structure that contains correspondence mapping /// between blazemap index wrappers and original keys. #[doc(hidden)] #[derive(Debug)] -pub struct StaticInfo { +pub struct IdWrapperStaticInfo { index_to_orig: Vec, orig_to_index: Lazy>, } -impl StaticInfo +#[doc(hidden)] +#[derive(Debug)] +pub struct TrivialIdStaticInfo { + next_id: AtomicUsize, +} + +impl IdWrapperStaticInfo { - /// Creates a new instance of [`StaticInfo`]. + /// Creates a new instance of [`IdWrapperStaticInfo`]. #[inline] pub const fn new() -> Self { Self { @@ -26,25 +33,55 @@ impl StaticInfo } } -impl OrigTypeIdMap for StaticInfo +impl TrivialIdStaticInfo +{ + /// Creates a new instance of [`TrivialIdStaticInfo`]. + #[inline] + pub const fn new(first_id: usize) -> Self { + Self { + next_id: AtomicUsize::new(first_id), + } + } + + /// Returns the next identifier. + #[inline] + pub fn next_id(&self) -> usize { + self.next_id.fetch_update( + Ordering::Relaxed, + Ordering::Relaxed, + |next_id| next_id.checked_add(1), + ) + .expect("usize overflow") + } +} + +impl StaticInfoApi for IdWrapperStaticInfo where K: Clone + Eq + Hash { + type KeyUnchecked<'a> = &'a K + where Self: 'a; + #[inline(always)] fn num_elems(&self) -> usize { self.index_to_orig.len() } - #[inline] - fn get_index(&self, key: &K) -> Option { - self.orig_to_index.get(key).copied() - } - #[inline] unsafe fn get_key_unchecked(&self, index: usize) -> &K { self.index_to_orig.get_unchecked(index) } +} + +impl InsertableStaticInfoApi for IdWrapperStaticInfo + where + K: Clone + Eq + Hash +{ + #[inline] + fn get_index(&self, key: &K) -> Option { + self.orig_to_index.get(key).copied() + } #[inline] unsafe @@ -59,4 +96,20 @@ impl OrigTypeIdMap for StaticInfo orig_to_index.insert(key, next_id); next_id } +} + +impl StaticInfoApi for TrivialIdStaticInfo +{ + type KeyUnchecked<'a> = usize + where Self: 'a; + + #[inline(always)] + fn num_elems(&self) -> usize { + self.next_id.load(Ordering::Relaxed) + } + + #[inline(always)] + unsafe fn get_key_unchecked(&self, index: usize) -> usize { + index + } } \ No newline at end of file