Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting Started

Installation

Add PulseMap to your Cargo.toml:

[dependencies]
pulse_map = "0.6.1"

Or via command line:

cargo add pulse_map

Minimum Supported Rust Version

PulseMap requires Rust 1.70.0 or later.


Your First PulseMap

1. Single-Threaded — TypedPulseMap<K, V>

use pulse_map::TypedPulseMap;

fn main() {
    // 64 buckets = 256 slot capacity
    let mut map: TypedPulseMap<String, String> = TypedPulseMap::new(64);

    map.insert("name".to_string(), "Deendayal".to_string());
    map.insert("lang".to_string(), "Rust".to_string());

    assert_eq!(map.get(&"name".to_string()), Some("Deendayal".to_string()));
    assert_eq!(map.get(&"missing".to_string()), None);

    map.remove(&"lang".to_string());

    println!("Entries:   {}", map.len());
    println!("Capacity:  {}", map.capacity());
    println!("Evictions: {}", map.eviction_count());
}

2. Multi-Threaded — ConcurrentPulseMap<K, V>

Good for 1–2 threads:

use pulse_map::ConcurrentPulseMap;
use std::sync::Arc;
use std::thread;

fn main() {
    let map = Arc::new(ConcurrentPulseMap::<String, u64>::with_auto_resize(64));

    let handles: Vec<_> = (0..2).map(|t| {
        let m = map.clone();
        thread::spawn(move || {
            for i in 0..1000 {
                m.insert(format!("t{}_{}", t, i), i as u64);
            }
        })
    }).collect();

    for h in handles { h.join().unwrap(); }
    println!("Total: {}", map.len());
}

Best for 3+ threads — 2.4–3.1x faster than ConcurrentPulseMap:

use pulse_map::ShardedPulseMap;
use std::sync::Arc;
use std::thread;

fn main() {
    // 16 shards × 256 buckets = 16,384 capacity
    let map = Arc::new(ShardedPulseMap::<u32, u64>::new(256));

    let handles: Vec<_> = (0..8).map(|t| {
        let m = map.clone();
        thread::spawn(move || {
            for i in 0..10_000u32 {
                m.insert(t * 10_000 + i, i as u64);
            }
        })
    }).collect();

    for h in handles { h.join().unwrap(); }
    println!("Entries:   {}", map.len());
    println!("Evictions: {}", map.eviction_count());
}

4. With Per-Entry TTL (v0.6.1+)

#![allow(unused)]
fn main() {
use pulse_map::ShardedPulseMap;

let map = ShardedPulseMap::<String, String>::new(256);
map.set_ttl(1000);  // global: expire after 1000 inserts

// Per-entry overrides
map.insert_ttl("session:abc".to_string(), "data".to_string(), 50);     // short-lived
map.insert_ttl("config:key".to_string(), "value".to_string(), u32::MAX); // never expire
map.insert("normal".to_string(), "val".to_string());                     // uses global 1000
}

Choosing the Right Type

TypeThreadsTTLBest For
PulseMapRaw❌ SingleRaw [u8] keys, FFI, max perf
TypedPulseMap<K, V>❌ SingleType-safe single-threaded cache
ConcurrentPulseMap<K, V>✅ 1-2TLow-contention concurrent cache
ShardedPulseMap<K, V>3+THigh-concurrency production

Capacity Planning

actual_buckets = next_power_of_2(num_buckets)
total_capacity = actual_buckets × 4  (4 slots per bucket)

# ShardedPulseMap:
total_capacity = 16 × actual_buckets × 4
num_bucketsActualCapacityMemoryShardedPulseMap
64642564 KB4 KB × 16 = 64 KB
2562561,02416 KB256 KB
1,0241,0244,09664 KB1 MB
65,53665,536262,1444 MB64 MB

Rule of thumb: num_buckets = expected_entries / 3. The ~75% fill rate balances performance with memory.

Running Examples

git clone https://github.com/ddsha441981/pulse_map.git
cd pulse_map

cargo run --example basic
cargo run --example concurrent
cargo bench