moa_idalloc 0.1.4

id 分配工具箱:单调号 / 回收位图 / 世代槽位表
Documentation
//! `IdTable`(世代槽位表)集成测试。
//!
//! 只能用公开 API:用 [`Id::bits`] 拆出 index/generation 来观察槽位复用与世代递增
//! (低 32 位是 index,高 32 位是 generation),避免依赖私有方法。

use moa_idalloc::table::{Id, IdTable};

/// 取 id 的 index(低 32 位)。返回 `u64` 以避免无意义的截断转换。
fn slot_of(id: Id) -> u64 {
    id.bits() & 0xFFFF_FFFF
}

/// 取 id 的 generation(高 32 位)。
fn gen_of(id: Id) -> u64 {
    id.bits() >> 32
}

#[test]
fn insert_get_remove() {
    let mut t = IdTable::new();
    let a = t.insert("a");
    let b = t.insert("b");
    assert_eq!(t.get(a), Some(&"a"));
    assert_eq!(t.get(b), Some(&"b"));
    assert_eq!(t.len(), 2);
    assert_eq!(t.remove(a), Some("a"));
    assert_eq!(t.get(a), None);
    assert_eq!(t.len(), 1);
    assert!(!t.is_empty());
}

#[test]
fn empty_table_transitions() {
    let mut t = IdTable::new();
    assert!(t.is_empty());
    assert_eq!(t.len(), 0);
    let id = t.insert(());
    assert!(!t.is_empty());
    assert_eq!(t.len(), 1);
    t.remove(id);
    assert!(t.is_empty());
    assert_eq!(t.len(), 0);
}

#[test]
fn default_is_empty() {
    let t: IdTable<i32> = IdTable::default();
    assert!(t.is_empty());
}

#[test]
fn get_mut_updates_value() {
    let mut t = IdTable::new();
    let id = t.insert(1);
    *t.get_mut(id).unwrap() = 99;
    assert_eq!(t.get(id), Some(&99));
}

#[test]
fn double_remove_returns_none() {
    let mut t = IdTable::new();
    let id = t.insert(1);
    assert_eq!(t.remove(id), Some(1));
    assert_eq!(t.remove(id), None);
    assert!(t.get_mut(id).is_none());
}

#[test]
fn never_issued_id_returns_none() {
    let t: IdTable<i32> = IdTable::new();
    assert_eq!(t.get(Id::from_bits(0)), None);
    // index 越界 + 任意 generation
    assert_eq!(t.get(Id::from_bits(0xDEAD_BEEF_0000_0005)), None);
}

#[test]
fn removing_one_keeps_others() {
    let mut t = IdTable::new();
    let ids: Vec<Id> = (0..5).map(|i| t.insert(i)).collect();
    t.remove(ids[2]);
    assert_eq!(t.get(ids[0]), Some(&0));
    assert_eq!(t.get(ids[2]), None);
    assert_eq!(t.get(ids[4]), Some(&4));
    assert_eq!(t.len(), 4);
}

#[test]
fn reuses_freed_slot_before_growing() {
    let mut t = IdTable::new();
    let a = t.insert(1);
    let _b = t.insert(2);
    let a_slot = slot_of(a);
    t.remove(a);
    let c = t.insert(3);
    assert_eq!(slot_of(c), a_slot, "应复用 a 的槽位而非新增");
}

#[test]
fn generation_rejects_stale_id() {
    let mut t = IdTable::new();
    let a = t.insert(10);
    assert_eq!(t.remove(a), Some(10));
    let b = t.insert(20);
    assert_eq!(slot_of(a), slot_of(b), "复用同一槽位");
    assert_ne!(a, b, "generation 不同,id 不等");
    assert_eq!(t.get(a), None, "旧 id 被拒(ABA 安全)");
    assert_eq!(t.remove(a), None);
    assert_eq!(t.get(b), Some(&20));
}

#[test]
fn generation_increments_each_reuse() {
    let mut t = IdTable::new();
    let mut id = t.insert(0);
    let slot = slot_of(id);
    assert_eq!(gen_of(id), 0);
    for expected_gen in 1..=5 {
        t.remove(id);
        id = t.insert(0);
        assert_eq!(slot_of(id), slot, "始终复用同一槽位");
        assert_eq!(gen_of(id), expected_gen, "每次复用 generation +1");
    }
}

#[test]
fn stale_id_stays_rejected_across_many_reuses() {
    let mut t = IdTable::new();
    let stale = t.insert(100);
    let mut cur = stale;
    for _ in 0..8 {
        t.remove(cur);
        cur = t.insert(200);
    }
    assert_eq!(t.get(stale), None, "最初的旧 id 经多轮复用仍被拒");
    assert_eq!(t.get(cur), Some(&200));
}

#[test]
fn bits_roundtrip() {
    let mut t = IdTable::new();
    let a = t.insert(42);
    let restored = Id::from_bits(a.bits());
    assert_eq!(restored, a);
    assert_eq!(t.get(restored), Some(&42));
}

#[test]
fn many_insert_remove_cycles_stay_consistent() {
    let mut t = IdTable::new();
    let mut live: Vec<(Id, usize)> = Vec::new();
    // 交错插入/删除,断言存活项始终可查、计数一致
    for round in 0..500 {
        let id = t.insert(round);
        live.push((id, round));
        if round % 3 == 0 && live.len() > 1 {
            let (rid, rval) = live.remove(0);
            assert_eq!(t.remove(rid), Some(rval));
        }
    }
    assert_eq!(t.len(), live.len());
    for (id, val) in live {
        assert_eq!(t.get(id), Some(&val));
    }
}