Ida

Struct Ida 

Source
pub struct Ida { /* private fields */ }
Expand description

A thread-safe ID allocator for sparse ID spaces.

Ida (ID Allocator) manages a pool of unique integer IDs, implemented as a radix tree for memory efficiency. It’s particularly well-suited for scenarios where allocated IDs may be far apart (e.g., allocating IDs 5 and 5,000,000).

§Thread Safety

All methods are thread-safe and can be safely shared across threads using Arc. The implementation uses a spinlock internally for synchronization.

§Memory Efficiency

The radix tree structure ensures that only allocated regions of the ID space consume memory. Sparse allocations do not waste space on unallocated ranges.

§Examples

Basic usage:

use ida_rs::Ida;

let ida = Ida::new();
let id1 = ida.alloc().unwrap();
let id2 = ida.alloc().unwrap();
ida.free(id1);
let id3 = ida.alloc().unwrap(); // Reuses id1

Multi-threaded usage:

use ida_rs::Ida;
use std::sync::Arc;
use std::thread;

let ida = Arc::new(Ida::new());
let mut handles = vec![];

for _ in 0..4 {
    let ida_clone = Arc::clone(&ida);
    let handle = thread::spawn(move || {
        ida_clone.alloc()
    });
    handles.push(handle);
}

for handle in handles {
    let id = handle.join().unwrap();
    println!("Allocated ID: {:?}", id);
}

Implementations§

Source§

impl Ida

Source

pub fn new() -> Self

Creates a new, empty ID allocator.

The allocator starts with no IDs allocated. The first call to alloc will return ID 0.

§Examples
use ida_rs::Ida;

let ida = Ida::new();
assert_eq!(ida.alloc(), Some(0));
Examples found in repository?
examples/real_world_test.rs (line 8)
5fn main() {
6    println!("Simulating a real-world scenario with multiple threads...");
7
8    let allocator = Arc::new(Ida::new());
9    let mut handles = vec![];
10
11    // Spawn 5 threads. Each will try to allocate 3 IDs.
12    for i in 0..5 {
13        let allocator_clone = Arc::clone(&allocator);
14        let handle = thread::spawn(move || {
15            let mut allocated_ids = vec![];
16            for _ in 0..3 {
17                match allocator_clone.alloc() {
18                    Some(id) => {
19                        println!("Thread {i} allocated ID: {id}");
20                        allocated_ids.push(id);
21                    }
22                    None => {
23                        println!("Thread {i} failed to allocate an ID.");
24                    }
25                }
26                // Simulate some work
27                thread::sleep(std::time::Duration::from_millis(10));
28            }
29            allocated_ids
30        });
31        handles.push(handle);
32    }
33
34    // Wait for all threads to finish and collect the results
35    let mut all_ids = vec![];
36    for handle in handles {
37        all_ids.extend(handle.join().unwrap());
38    }
39
40    println!("\nAll threads finished.");
41    println!("Total IDs allocated: {}", all_ids.len());
42    println!("Allocated IDs: {all_ids:?}");
43
44    // Verify correctness: sort and check for duplicates
45    all_ids.sort();
46    let mut unique_ids = all_ids.clone();
47    unique_ids.dedup();
48
49    assert_eq!(
50        all_ids.len(),
51        unique_ids.len(),
52        "Error: Duplicate IDs were allocated!"
53    );
54
55    println!("\nVerification successful: No duplicate IDs were issued.");
56}
Source

pub fn alloc(&self) -> Option<usize>

Allocates and returns the next available ID.

This method always returns the lowest available ID. If an ID has been freed, it will be reused before allocating higher IDs.

§Returns
  • Some(id) - The allocated ID (a usize value)
  • None - If the allocator has exhausted the available ID space
§Thread Safety

This method is thread-safe and can be called concurrently from multiple threads. Each call is guaranteed to return a unique ID.

§Examples
use ida_rs::Ida;

let ida = Ida::new();

// Allocate IDs sequentially
let id1 = ida.alloc().unwrap();
let id2 = ida.alloc().unwrap();
assert_eq!(id1, 0);
assert_eq!(id2, 1);

// Free an ID and reallocate
ida.free(id1);
let id3 = ida.alloc().unwrap();
assert_eq!(id3, 0); // Reuses the freed ID
Examples found in repository?
examples/real_world_test.rs (line 17)
5fn main() {
6    println!("Simulating a real-world scenario with multiple threads...");
7
8    let allocator = Arc::new(Ida::new());
9    let mut handles = vec![];
10
11    // Spawn 5 threads. Each will try to allocate 3 IDs.
12    for i in 0..5 {
13        let allocator_clone = Arc::clone(&allocator);
14        let handle = thread::spawn(move || {
15            let mut allocated_ids = vec![];
16            for _ in 0..3 {
17                match allocator_clone.alloc() {
18                    Some(id) => {
19                        println!("Thread {i} allocated ID: {id}");
20                        allocated_ids.push(id);
21                    }
22                    None => {
23                        println!("Thread {i} failed to allocate an ID.");
24                    }
25                }
26                // Simulate some work
27                thread::sleep(std::time::Duration::from_millis(10));
28            }
29            allocated_ids
30        });
31        handles.push(handle);
32    }
33
34    // Wait for all threads to finish and collect the results
35    let mut all_ids = vec![];
36    for handle in handles {
37        all_ids.extend(handle.join().unwrap());
38    }
39
40    println!("\nAll threads finished.");
41    println!("Total IDs allocated: {}", all_ids.len());
42    println!("Allocated IDs: {all_ids:?}");
43
44    // Verify correctness: sort and check for duplicates
45    all_ids.sort();
46    let mut unique_ids = all_ids.clone();
47    unique_ids.dedup();
48
49    assert_eq!(
50        all_ids.len(),
51        unique_ids.len(),
52        "Error: Duplicate IDs were allocated!"
53    );
54
55    println!("\nVerification successful: No duplicate IDs were issued.");
56}
Source

pub fn free(&self, id: usize)

Frees a previously allocated ID, making it available for reuse.

Once freed, the ID becomes available for future allocations. The next call to alloc will prefer reusing freed IDs before allocating new ones.

§Parameters
  • id - The ID to free. Must be a value that was previously returned by alloc.
§Behavior
  • Freeing an already-free ID is safe but has no effect
  • Freeing an ID that was never allocated is safe but has no effect
  • After freeing, the internal tree structure may be compacted to save memory
§Thread Safety

This method is thread-safe and can be called concurrently from multiple threads.

§Examples
use ida_rs::Ida;

let ida = Ida::new();
let id = ida.alloc().unwrap();

// Use the ID...

// Free it when done
ida.free(id);

// The ID can now be reallocated
let reused_id = ida.alloc().unwrap();
assert_eq!(id, reused_id);
Source

pub fn is_allocated(&self, id: usize) -> bool

Checks if a given ID is currently allocated.

This method queries whether a specific ID has been allocated and not yet freed. It’s useful for debugging, validation, or tracking ID states.

§Parameters
  • id - The ID to check
§Returns
  • true - If the ID is currently allocated
  • false - If the ID is free or has never been allocated
§Thread Safety

This method is thread-safe and provides a consistent view at the time of the call. Note that in concurrent scenarios, the status may change immediately after this method returns.

§Examples
use ida_rs::Ida;

let ida = Ida::new();

// Initially, no IDs are allocated
assert!(!ida.is_allocated(0));
assert!(!ida.is_allocated(100));

// Allocate an ID
let id = ida.alloc().unwrap();
assert_eq!(id, 0);

// Check allocation status
assert!(ida.is_allocated(0));
assert!(!ida.is_allocated(1));

// Free the ID
ida.free(0);
assert!(!ida.is_allocated(0));

Trait Implementations§

Source§

impl Debug for Ida

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Ida

Source§

fn default() -> Self

Creates a new ID allocator using the default configuration.

This is equivalent to calling Ida::new().

§Examples
use ida_rs::Ida;

let ida = Ida::default();
assert_eq!(ida.alloc(), Some(0));

Auto Trait Implementations§

§

impl !Freeze for Ida

§

impl !RefUnwindSafe for Ida

§

impl Send for Ida

§

impl Sync for Ida

§

impl Unpin for Ida

§

impl UnwindSafe for Ida

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.