#hash-map #map #slab #object #debugging

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

6 releases

0.0.9 Apr 3, 2024
0.0.8 Apr 3, 2024
0.0.7 Dec 23, 2023
0.0.3 Nov 30, 2023
0.0.2 Oct 15, 2023

#371 in Data structures

Download history 1/week @ 2024-02-19 5/week @ 2024-02-26 6/week @ 2024-03-11 213/week @ 2024-04-01

219 downloads per month

MIT license

70KB
2K SLoC

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

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

blazemap approach

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

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 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

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

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"}"#)

Dependencies

~0.4–6.5MB
~16K SLoC