Skip to content

Commit

Permalink
global refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewsonin committed Apr 18, 2024
1 parent 637b012 commit 50a74c4
Show file tree
Hide file tree
Showing 16 changed files with 440 additions and 552 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ edition = "2021"
[dependencies]
once_cell = "1"
parking_lot = "0.12"
read-write-api = "0.17"
serde = { version = "1", optional = true, features = ["derive"] }

[dev-dependencies]
serde_json = "1"
static_assertions = "1"

[features]
Expand Down
147 changes: 2 additions & 145 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,147 +1,4 @@
# blazemap

_Provides a wrapper for replacing a small number of clumsy objects with identifiers, and also implements a vector-based
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_wrapper` macro as well as using them as keys in
the `BlazeMap`
— a vector-based slab-like map with an interface similar to that of `HashMap`.

You can also use the `register_blazemap_id` macro if you want to create a new type based on `usize`
that is generated incrementally to use as such a key.

# No-brain vs `blazemap` approach

The logic behind the `register_blazemap_id_wrapper` macro is shown below.

## Standard no-brain approach

![OldApproach.svg](./docs/drawio/README_-_OldApproach.svg)

```rust
let clumsy = MassiveStruct::new();
let mut map = HashMap::new();
map.insert(clumsy, "clumsy") // Too inefficient
```

## `blazemap` approach

```rust
use blazemap::prelude::{BlazeMap, register_blazemap_id_wrapper};

register_blazemap_id_wrapper! {
struct Id(MassiveStruct)
}

let clumsy = MassiveStruct::new();
let clumsy_id = Id::new(clumsy);

let mut map = BlazeMap::new();
map.insert(clumsy_id, "clumsy") // Very efficient
```

![NewApproach.svg](./docs/drawio/README_-_NewApproach.svg)

# Type-generating macros

## `register_blazemap_id_wrapper`

Creates a new type that acts as an `usize`-based replacement for the old type
that can be used as a key for `blazemap` collections.

This macro supports optional inference of standard traits using the following syntax:

* `Derive(as for Original Type)` — derives traits as for the original type
for which `blazemap` ID is being registered. Each call to methods on these traits
requires an additional `.read` call on the internal synchronization primitive,
so — all other things being equal — their calls may be less optimal
than the corresponding calls on instances of the original key's type.
This method supports inference of the following traits:
* `Default`
* `PartialOrd` (mutually exclusive with `Ord`)
* `Ord` (also derives `PartialOrd`, so mutually exclusive with `PartialOrd`)
* `Debug`
* `Display`
* `Serialize` (with `serde` feature only)
* `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::KeyWrapper::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`.
This method supports inference of the following traits:
* `PartialOrd` (mutually exclusive with `Ord`)
* `Ord` (also derives `PartialOrd`, so mutually exclusive with `PartialOrd`)

### Example

```rust
use blazemap::prelude::{BlazeMap, register_blazemap_id_wrapper};

register_blazemap_id_wrapper! {
pub struct Key(String);
Derive(as for Original Type): { // Optional section
Debug,
Display,
};
Derive(as for Serial Number): { // Optional section
Ord,
}
}

let key_1 = Key::new("first".to_string());
let key_2 = Key::new("second".to_string());
let key_3 = Key::new("third".to_string());

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#"{"first": "1", "second": "2", "third": "3"}"#)
```

## `register_blazemap_id`

Creates a new type based on incrementally generated `usize` instances
that can be used as a key 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 Id(start from: 1); // "(start from: number)" is optional
Derive: { // Derive section is also optional
Ord
};
}

let key_1 = Id::new();
let key_2 = Id::new();
let key_3 = Id::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"}"#)
```
_Implements a vector-based slab-like map with an interface similar to that of `HashMap`,
and also provides tools for generating lightweight identifiers that can be type-safely used as keys for this map._
40 changes: 24 additions & 16 deletions src/blazemap_id.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
use std::borrow::Borrow;
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Range;

use read_write_api::ReadApi;

use crate::orig_type_id_map::StaticInfoApi;
use std::ops::{Deref, Range};

/// Provides interface for `blazemap` id types
/// defined by the [`register_blazemap_id_wrapper`](crate::register_blazemap_id_wrapper)
/// and [`register_blazemap_id`](crate::register_blazemap_id) macros.
/// defined by the [`define_key_wrapper`](crate::define_key_wrapper)
/// and [`define_plain_id`](crate::define_plain_id) macros.
pub trait BlazeMapId: Copy {
/// Original key type.
type OrigType: 'static + Clone + Eq + Hash;

#[doc(hidden)]
type StaticInfoApi: 'static + StaticInfoApi<Self::OrigType>;

#[doc(hidden)]
type StaticInfoApiLock: ReadApi<Target = Self::StaticInfoApi>;

/// Creates an iterator over all identifiers registered at the time this method was called.
#[inline]
fn all_instances_iter() -> AllInstancesIter<Self> {
let num_elems = Self::static_info().read().num_elems();
let num_elems = Self::capacity_info_provider().number_of_register_keys();
AllInstancesIter {
range: 0..num_elems,
phantom: Default::default(),
Expand All @@ -37,16 +28,33 @@ pub trait BlazeMapId: Copy {
unsafe fn from_index_unchecked(index: usize) -> Self;

#[doc(hidden)]
fn static_info() -> Self::StaticInfoApiLock;
fn capacity_info_provider() -> impl Deref<Target = impl CapacityInfoProvider>;

#[doc(hidden)]
fn key_by_index_provider() -> impl Deref<Target = impl KeyByIndexProvider<Self::OrigType>>;
}

/// Provides interface for constructable `blazemap` key-wrapper types
/// defined by the [`register_blazemap_id_wrapper`](crate::register_blazemap_id_wrapper) macro.
/// defined by the [`define_key_wrapper`](crate::define_key_wrapper) macro.
pub trait BlazeMapIdWrapper: BlazeMapId {
/// Creates a new instance of [`Self`] based on the [`Self::OrigType`](BlazeMapId::OrigType) instance.
fn new(key: Self::OrigType) -> Self;
}

#[doc(hidden)]
pub trait CapacityInfoProvider {
fn number_of_register_keys(&self) -> usize;
}

#[doc(hidden)]
pub trait KeyByIndexProvider<K> {
type KeyUnchecked<'a>: Borrow<K>
where
Self: 'a;

unsafe fn key_by_index_unchecked(&self, index: usize) -> Self::KeyUnchecked<'_>;
}

/// Iterator over consecutive `blazemap` identifiers.
pub struct AllInstancesIter<T> {
range: Range<usize>,
Expand Down
12 changes: 5 additions & 7 deletions src/collections/blazemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use std::borrow::Borrow;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;

use read_write_api::ReadApi;

#[cfg(feature = "serde")]
use {
crate::prelude::BlazeMapIdWrapper,
Expand All @@ -15,12 +13,13 @@ use {
};

use crate::blazemap_id::BlazeMapId;
use crate::blazemap_id::CapacityInfoProvider;
use crate::blazemap_id::KeyByIndexProvider;
use crate::collections::blazemap::entry::VacantEntryInner;
pub use crate::collections::blazemap::{
entry::{Entry, OccupiedEntry, VacantEntry},
iter::{Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Values, ValuesMut},
};
use crate::orig_type_id_map::StaticInfoApi;

mod entry;
mod iter;
Expand Down Expand Up @@ -143,7 +142,7 @@ where
/// with capacity equal to the current total number of unique `K` instances.
#[inline]
pub fn with_current_key_wrapper_capacity() -> Self {
let current_capacity = K::static_info().read().num_elems();
let current_capacity = K::capacity_info_provider().number_of_register_keys();
Self {
inner: Vec::with_capacity(current_capacity),
len: 0,
Expand Down Expand Up @@ -312,15 +311,14 @@ impl<K, V> Default for BlazeMap<K, V> {

macro_rules! blaze_map_orig_key_blocking_iter {
($self:ident, $iter:ident, $guard:ident) => {
let $guard = K::static_info();
let $guard = $guard.read();
let $guard = K::key_by_index_provider();
let $iter = $self
.inner
.iter()
.enumerate()
.filter_map(|(idx, value)| Some((idx, value.as_ref()?)))
.map(|(idx, value)| {
let key = unsafe { $guard.get_key_unchecked(idx) };
let key = unsafe { $guard.key_by_index_unchecked(idx) };
(key, value)
});
};
Expand Down
14 changes: 5 additions & 9 deletions src/collections/blazemap/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ use std::marker::PhantomData;
use std::mem::needs_drop;
use std::panic::{RefUnwindSafe, UnwindSafe};

use read_write_api::ReadApi;

use crate::blazemap_id::KeyByIndexProvider;
use crate::collections::blazemap::BlazeMap;
use crate::orig_type_id_map::StaticInfoApi;
use crate::prelude::BlazeMapId;

/// An iterator over the entries of a [`BlazeMap`].
Expand Down Expand Up @@ -514,11 +512,10 @@ where
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let static_info = K::static_info();
let static_info = static_info.read();
let key_provider = K::key_by_index_provider();
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()) };
let key = unsafe { key_provider.key_by_index_unchecked(key.get_index()) };
debug_map.entry(key.borrow(), value);
}
debug_map.finish()
Expand Down Expand Up @@ -574,11 +571,10 @@ where
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let static_info = K::static_info();
let static_info = static_info.read();
let key_provider = K::key_by_index_provider();
let mut debug_list = f.debug_list();
for key in self.into_iter() {
let key = unsafe { static_info.get_key_unchecked(key.get_index()) };
let key = unsafe { key_provider.key_by_index_unchecked(key.get_index()) };
debug_list.entry(key.borrow());
}
debug_list.finish()
Expand Down
Loading

0 comments on commit 50a74c4

Please sign in to comment.