#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SlotKey {
pub index: usize,
pub generation: u32,
}
#[derive(Debug, Clone)]
struct Slot {
value: f32,
generation: u32,
occupied: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SlotMap {
slots: Vec<Slot>,
free_list: Vec<usize>,
count: usize,
}
#[allow(dead_code)]
pub fn new_slot_map() -> SlotMap {
SlotMap {
slots: Vec::new(),
free_list: Vec::new(),
count: 0,
}
}
#[allow(dead_code)]
pub fn slot_insert(map: &mut SlotMap, value: f32) -> SlotKey {
if let Some(idx) = map.free_list.pop() {
let slot = &mut map.slots[idx];
slot.value = value;
slot.occupied = true;
map.count += 1;
SlotKey {
index: idx,
generation: slot.generation,
}
} else {
let idx = map.slots.len();
map.slots.push(Slot {
value,
generation: 0,
occupied: true,
});
map.count += 1;
SlotKey {
index: idx,
generation: 0,
}
}
}
#[allow(dead_code)]
pub fn slot_remove(map: &mut SlotMap, key: SlotKey) -> Option<f32> {
let slot = map.slots.get_mut(key.index)?;
if !slot.occupied || slot.generation != key.generation {
return None;
}
let val = slot.value;
slot.occupied = false;
slot.generation = slot.generation.wrapping_add(1);
map.free_list.push(key.index);
map.count -= 1;
Some(val)
}
#[allow(dead_code)]
pub fn slot_get(map: &SlotMap, key: SlotKey) -> Option<f32> {
let slot = map.slots.get(key.index)?;
if slot.occupied && slot.generation == key.generation {
Some(slot.value)
} else {
None
}
}
#[allow(dead_code)]
pub fn slot_contains(map: &SlotMap, key: SlotKey) -> bool {
slot_get(map, key).is_some()
}
#[allow(dead_code)]
pub fn slot_len(map: &SlotMap) -> usize {
map.count
}
#[allow(dead_code)]
pub fn slot_is_empty(map: &SlotMap) -> bool {
map.count == 0
}
#[allow(dead_code)]
pub fn slot_map_to_json(map: &SlotMap) -> String {
format!(
r#"{{"len":{},"slot_capacity":{}}}"#,
map.count,
map.slots.len()
)
}
#[allow(dead_code)]
pub fn slot_clear(map: &mut SlotMap) {
for slot in &mut map.slots {
if slot.occupied {
slot.occupied = false;
slot.generation = slot.generation.wrapping_add(1);
}
}
map.free_list.clear();
for i in 0..map.slots.len() {
map.free_list.push(i);
}
map.count = 0;
}
#[allow(dead_code)]
pub fn slot_generation(map: &SlotMap, index: usize) -> u32 {
map.slots.get(index).map(|s| s.generation).unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_and_get() {
let mut m = new_slot_map();
let k = slot_insert(&mut m, std::f32::consts::PI);
assert!((slot_get(&m, k).expect("should succeed") - std::f32::consts::PI).abs() < 1e-5);
}
#[test]
fn test_len_increases_on_insert() {
let mut m = new_slot_map();
assert!(slot_is_empty(&m));
slot_insert(&mut m, 1.0);
assert_eq!(slot_len(&m), 1);
}
#[test]
fn test_remove_returns_value() {
let mut m = new_slot_map();
let k = slot_insert(&mut m, 7.0);
let v = slot_remove(&mut m, k).expect("should succeed");
assert!((v - 7.0).abs() < 1e-5);
assert_eq!(slot_len(&m), 0);
}
#[test]
fn test_stale_key_after_remove() {
let mut m = new_slot_map();
let k = slot_insert(&mut m, 1.0);
slot_remove(&mut m, k);
assert!(!slot_contains(&m, k));
assert!(slot_get(&m, k).is_none());
}
#[test]
fn test_old_key_stale_after_reinsert() {
let mut m = new_slot_map();
let k1 = slot_insert(&mut m, 1.0);
slot_remove(&mut m, k1);
let k2 = slot_insert(&mut m, 2.0);
assert!(!slot_contains(&m, k1));
assert!(slot_contains(&m, k2));
assert_eq!(k1.index, k2.index);
assert!(k2.generation > k1.generation);
}
#[test]
fn test_clear_invalidates_all() {
let mut m = new_slot_map();
let k1 = slot_insert(&mut m, 1.0);
let k2 = slot_insert(&mut m, 2.0);
slot_clear(&mut m);
assert!(!slot_contains(&m, k1));
assert!(!slot_contains(&m, k2));
assert!(slot_is_empty(&m));
}
#[test]
fn test_to_json() {
let mut m = new_slot_map();
slot_insert(&mut m, 0.5);
let json = slot_map_to_json(&m);
assert!(json.contains("len"));
assert!(json.contains("slot_capacity"));
}
#[test]
fn test_generation_increments() {
let mut m = new_slot_map();
let k = slot_insert(&mut m, 1.0);
let gen0 = slot_generation(&m, k.index);
slot_remove(&mut m, k);
let gen1 = slot_generation(&m, k.index);
assert!(gen1 > gen0);
}
#[test]
fn test_multiple_inserts_and_removes() {
let mut m = new_slot_map();
let keys: Vec<SlotKey> = (0..10).map(|i| slot_insert(&mut m, i as f32)).collect();
assert_eq!(slot_len(&m), 10);
for k in &keys {
slot_remove(&mut m, *k);
}
assert!(slot_is_empty(&m));
}
}