unique_id_lookup 0.2.11

Associative Array specifically designed for integer keys. Significant performance boost over conventional hash maps.
Documentation
use ::unique_id_lookup::UniqueIdLookup;
use std::collections::HashMap;

#[cfg(test)]
mod tests {
    use super::*;

    struct MyPayload {
        counter: u64,
    }

    fn get_test_hashmap(offset: usize, range: usize, step: usize) -> HashMap<usize, MyPayload> {
        let mut hm = HashMap::new();
        (offset..(offset+range)).step_by(step).for_each(|id| {
            hm.insert(id, MyPayload { counter: id as u64 });
        });
        hm
    }

    #[test]
    fn unique_id_lookup_new() {
        let uil: UniqueIdLookup<usize> = UniqueIdLookup::new();
        assert_eq!(uil.capacity(), 0);
        assert_eq!(uil.range_len(), 0);
        assert_eq!(uil.len_occupied(), 0);
        assert_eq!(uil.get_offset(), 0);
        assert_eq!(uil.is_occupied_empty(), true);

        let uil = UniqueIdLookup::from(HashMap::<u16, i8>::new());
        assert_eq!(uil.capacity(), 0);
        assert_eq!(uil.range_len(), 0);
        assert_eq!(uil.len_occupied(), 0); 
        assert_eq!(uil.get_offset(), 0); 
        assert_eq!(uil.is_occupied_empty(), true);             
    }

        #[test]
    fn unique_id_lookup_from() {
        let arr = [(5u16, 'c')];
        let lookup = UniqueIdLookup::from(arr);
        assert_eq!(lookup.len_occupied(), 1); 
        assert_eq!(lookup.range_len(), 1);
        assert_eq!(lookup.capacity(), 1);
        assert_eq!(lookup.get_offset(), 5); 
        assert_eq!(lookup.get(arr[0].0 as usize).unwrap(), arr[0].1);

        let arr = [(5usize, 'c'), (7, 'e')];
        let lookup = UniqueIdLookup::from(arr);
        assert_eq!(lookup.len_occupied(), 2); 
        assert_eq!(lookup.range_len(), 3);
        assert_eq!(lookup.capacity(), 3);
        assert_eq!(lookup.get_offset(), 5); 
        assert_eq!(lookup.get(arr[0].0).unwrap(), arr[0].1);
        assert_eq!(lookup.get(arr[1].0).unwrap(), arr[1].1);
    }

    #[test]
    fn unique_id_lookup_insert_first_element() {
        let mut uil: UniqueIdLookup<i16> = UniqueIdLookup::new();
        uil.insert(5, 17);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 1);
        assert!(uil.capacity() >= 1);
        assert_eq!(uil.get_offset(), 5);
        assert_eq!(uil.get(5).unwrap(), 17); 
        assert_eq!(uil.is_occupied_empty(), false);       


        let mut uil: UniqueIdLookup<i16> = UniqueIdLookup::with_capacity_and_offset(10, 4);
        uil.insert(5, 17);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 2);
        assert_eq!(uil.capacity(), 10);
        assert_eq!(uil.get(5).unwrap(), 17);   
        assert_eq!(uil.get_offset(), 4);
        assert_eq!(uil.is_occupied_empty(), false);       

        let mut uil: UniqueIdLookup<i16> = UniqueIdLookup::with_capacity_and_offset(10, 5);
        uil.insert(5, 17);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 1);
        assert_eq!(uil.capacity(), 10);
        assert_eq!(uil.get(5).unwrap(), 17);   
        assert_eq!(uil.get_offset(), 5);        
        assert_eq!(uil.is_occupied_empty(), false);       

        let mut uil: UniqueIdLookup<i16> = UniqueIdLookup::with_capacity_and_offset(10, 5);
        uil.insert(6, 17);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 2);
        assert_eq!(uil.capacity(), 10);
        assert_eq!(uil.get(6).unwrap(), 17);
        assert_eq!(uil.get_offset(), 5);         
        assert_eq!(uil.is_occupied_empty(), false);           

        let hash_map: HashMap<u16, char> = HashMap::from([(66, 'B')]);
        let mut uil = UniqueIdLookup::from(hash_map);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 1);
        assert_eq!(uil.capacity(), 1);
        assert_eq!(uil.is_occupied_empty(), false);               
        assert_eq!(uil.get_offset(), 66);
        assert_eq!(uil.get(66).unwrap(), 'B');
        uil.insert(67, 'C');
        assert_eq!(uil.get_offset(), 66);
        uil.insert(65, 'A');
        assert_eq!(uil.get_offset(), 65);
        assert_eq!(uil.is_occupied_empty(), false);          
    }

    #[test]
    fn unique_id_lookup_insert_and_get() {
        let mut uil: UniqueIdLookup<i16> = UniqueIdLookup::new();
        assert_eq!(uil.insert(5, 17), &17);
        assert_eq!(uil.len_occupied(), 1);
        assert_eq!(uil.range_len(), 1);
        assert_eq!(uil.get_offset(), 5);
        assert_eq!(uil.get(5).unwrap(), 17);
        assert_eq!(uil.get_or_none(5).unwrap(), 17);
        assert!(uil.get_or_none(4).is_none());
        assert!(uil.get_or_none(6).is_none());

        assert_eq!(uil.insert(3, 42), &42);
        assert_eq!(uil.len_occupied(), 2);
        assert_eq!(uil.range_len(), 3);
        assert_eq!(uil.get_offset(), 3);
        assert_eq!(uil.get(3).unwrap(), 42);
        assert_eq!(uil.get(5).unwrap(), 17);
        assert_eq!(uil.get_or_none(3).unwrap(), 42);
        assert_eq!(uil.get_or_none(5).unwrap(), 17);        
        assert!(uil.get(4).is_none());
        assert!(uil.get_or_none(2).is_none());
        assert!(uil.get_or_none(4).is_none());
        assert!(uil.get_or_none(6).is_none());

        assert_eq!(uil.insert(6, 1), &1);
        assert_eq!(uil.len_occupied(), 3);
        assert_eq!(uil.get_offset(), 3);
        assert_eq!(uil.range_len(), 4);
        assert_eq!(uil.get(3).unwrap(), 42);
        assert_eq!(uil.get(5).unwrap(), 17);
        assert_eq!(uil.get(6).unwrap(), 1);
        assert!(uil.get(4).is_none());

        uil.insert(10, 1024);
        assert_eq!(uil.len_occupied(), 4);
        assert_eq!(uil.get_offset(), 3);
        assert_eq!(uil.range_len(), 8);
        assert_eq!(uil.get(3).unwrap(), 42);
        assert_eq!(uil.get(5).unwrap(), 17);
        assert_eq!(uil.get(6).unwrap(), 1);
        assert_eq!(uil.get(10).unwrap(), 1024);
        assert!(uil.get(4).is_none());
        assert!(uil.get(7).is_none());
        assert!(uil.get(9).is_none());

        uil.insert(2, 111);
        assert_eq!(uil.len_occupied(), 5);
        assert_eq!(uil.get_offset(), 2);
        assert_eq!(uil.range_len(), 9);
        assert_eq!(uil.get(2).unwrap(), 111);
        assert_eq!(uil.get(3).unwrap(), 42);
        assert!(uil.get_or_none(1).is_none());
        assert!(uil.get_or_none(11).is_none());

        // Testing overwrite
        uil.insert(2, 222);
        assert_eq!(uil.get(2).unwrap(), 222);
        assert_eq!(uil.get_offset(), 2);
    }

    #[test]
    fn unique_id_lookup_insert_and_replace() {
        let mut uil: UniqueIdLookup<i128> = UniqueIdLookup::new();
        uil.insert(1, 0);
        uil.insert(1, 42);
        assert_eq!(uil.get(1).unwrap(), 42);
        uil.insert_if_absent(1, 22);
        assert_eq!(uil.get(1).unwrap(), 42);
        let value = uil.insert_if_absent(1, 33);
        *value += 1;
        assert_eq!(uil.get(1).unwrap(), 43);
    }

    #[test]
    fn unique_id_lookup_contains_id() {
        let mut uil: UniqueIdLookup<usize> = UniqueIdLookup::new();
        assert_eq!(uil.insert(3, 42), &42);
        assert_eq!(uil.insert(5, 17), &17);
        assert_eq!(uil.insert(6, 1), &1);
        assert!(!uil.contains_id(0));
        assert!(!uil.contains_id(2));
        assert!(uil.contains_id(3));
        assert!(!uil.contains_id(4));
        assert!(uil.contains_id(5));
        assert!(uil.contains_id(6));
        assert!(!uil.contains_id(7));
        assert_eq!(uil.contains_id(0), false);
        assert_eq!(uil.contains_id(3), true);
    }

    #[test]
    fn unique_id_lookup_from_hash_map() {
        let offset = 7;
        let range = 41;
        let step = 2;
        let uil = UniqueIdLookup::from(get_test_hashmap(offset, range, step));
        assert_eq!(uil.get_offset(), offset);
        assert_eq!(uil.len_occupied(), 21);
        assert_eq!(uil.range_len(), range);
        assert_eq!(uil.is_occupied_empty(), false); 
        let hm = get_test_hashmap(offset, range, step);
        for (id, hash_payload) in hm.iter() {
            let lookup_payload = uil.get(*id);
            assert_eq!(
                lookup_payload.as_ref().unwrap().counter,
                hash_payload.counter
            );
        }
    }

    #[test]
    fn unique_id_lookup_get_mut() {
        let hm = get_test_hashmap(2, 10, 1);
        let mut uil = UniqueIdLookup::from(hm);
        {
            let payload = uil.get_mut(2);
            assert_eq!(payload.as_ref().unwrap().counter, 2);
            payload.unwrap().counter += 1;
        }
        {
            assert_eq!(uil.get(2).as_ref().unwrap().counter, 3);
        }
    }

    #[test]
    fn unique_id_lookup_iter_occupied() {
        let offset = 17;
        let range = 41;
        let step = 3;
        let uil = UniqueIdLookup::from(get_test_hashmap(offset, range, step));
        let hm = get_test_hashmap(offset, range, step);
        let mut count = 0;
        for (id, payload) in uil.iter_occupied() {
            assert_eq!(hm.contains_key(&id), true);
            assert_eq!(payload.counter, hm.get(&id).unwrap().counter);
            count += 1;
        }
        assert_eq!(count, hm.len());
        assert!(count > 0);
    }        

    #[test]
    fn unique_id_lookup_iter_vacant_ids() {
        let arr: [(usize, char); 4] = [(5, 'c'), (6, 'd'), (8, 'f'), (10, 'h')];
        let uil = UniqueIdLookup::from(arr);
        let mut count = 0;
        for id in uil.iter_vacant_ids() {
            assert_eq!([7, 9].contains(&id), true);
            count += 1;
        }
        assert_eq!(count, 2);

        let mut uil: UniqueIdLookup<char> = UniqueIdLookup::with_capacity_and_offset(10, 5);
        uil.insert(6, 'a');
        let mut count = 0;
        for id in uil.iter_vacant_ids() {
            assert_eq!(id, 5);
            count += 1;
        }
        assert_eq!(count, 1);
    }            

    #[test]
    fn unique_id_lookup_remove() {
        let arr = [(5u16, 'c'), (6u16, 'd')];
        let mut lookup = UniqueIdLookup::from(arr);
        assert_eq!(lookup.len_occupied(), 2);
        assert_eq!(lookup.get(5).unwrap(), 'c');
        let removed_value = lookup.remove(5);
        assert_eq!(removed_value.unwrap(), 'c');
        assert_eq!(lookup.len_occupied(), 1);
        assert!(lookup.get(5).is_none());
    }

    #[test]
    fn unique_id_lookup_shrink_to_fit() {
        // Trivial empty case
        let mut lookup: UniqueIdLookup<i32> = UniqueIdLookup::new();
        assert_eq!(lookup.capacity(), 0);
        lookup.shrink_to_fit_and_trim_vacant();
        assert_eq!(lookup.capacity(), 0);
        assert_eq!(lookup.get_offset(), 0);

        // is_occupied_empty = true
        let mut lookup: UniqueIdLookup<i32> = UniqueIdLookup::new();
        lookup.insert(42, 42);
        lookup.remove(42);
        assert_eq!(lookup.get_offset(), 42);
        assert_eq!(lookup.is_occupied_empty(), true);
        lookup.shrink_to_fit_and_trim_vacant();
        assert_eq!(lookup.capacity(), 0);
        assert_eq!(lookup.get_offset(), 0);
        lookup.insert(42, 42);
        assert_eq!(lookup.get_offset(), 42);
        assert_eq!(lookup.get(42).unwrap(), 42);

        // Nothing to shrink
        let hash_map: HashMap<usize, char> = HashMap::from([(5, 'c'), (7, 'd')]);
        let mut lookup = UniqueIdLookup::from(hash_map);
        lookup.shrink_to_fit_and_trim_vacant();
        assert_eq!(lookup.len_occupied(), 2);
        assert_eq!(lookup.capacity(), 3);
        assert_eq!(lookup.get_offset(), 5);

        // Shrink on both sides
        let mut lookup = UniqueIdLookup::with_capacity_and_offset(10, 5);
        assert_eq!(lookup.get_offset(), 5);
        lookup.insert(8, 8);
        lookup.insert(12, 12);
        assert_eq!(lookup.get_offset(), 5);
        lookup.shrink_to_fit_and_trim_vacant();
        assert_eq!(lookup.len_occupied(), 2);
        assert_eq!(lookup.capacity(), 5);
        assert_eq!(lookup.get_offset(), 8);
        assert_eq!(lookup.get(8).unwrap(), 8);
        assert_eq!(lookup.get(12).unwrap(), 12);
    }    
}