Skip to main content

oxihuman_core/
node_pool.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Node pool: typed object pool with stable handle-based access.
6
7/// Opaque node handle.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[allow(dead_code)]
10pub struct NodeHandle(u32, u32); // (index, generation)
11
12/// Node slot.
13#[derive(Debug)]
14#[allow(dead_code)]
15enum NodeSlot<T> {
16    Occupied {
17        value: T,
18        generation: u32,
19    },
20    Free {
21        next_free: Option<u32>,
22        generation: u32,
23    },
24}
25
26/// Node pool.
27#[derive(Debug)]
28#[allow(dead_code)]
29pub struct NodePool<T> {
30    slots: Vec<NodeSlot<T>>,
31    free_head: Option<u32>,
32    count: usize,
33}
34
35/// Create a new NodePool.
36#[allow(dead_code)]
37pub fn new_node_pool<T>() -> NodePool<T> {
38    NodePool {
39        slots: Vec::new(),
40        free_head: None,
41        count: 0,
42    }
43}
44
45/// Allocate a node; returns its handle.
46#[allow(dead_code)]
47pub fn np_alloc<T>(pool: &mut NodePool<T>, value: T) -> NodeHandle {
48    if let Some(idx) = pool.free_head {
49        let idx_usize = idx as usize;
50        let gen = match &pool.slots[idx_usize] {
51            NodeSlot::Free {
52                generation,
53                next_free,
54            } => {
55                pool.free_head = *next_free;
56                *generation
57            }
58            _ => unreachable!(),
59        };
60        pool.slots[idx_usize] = NodeSlot::Occupied {
61            value,
62            generation: gen,
63        };
64        pool.count += 1;
65        NodeHandle(idx, gen)
66    } else {
67        let idx = pool.slots.len() as u32;
68        pool.slots.push(NodeSlot::Occupied {
69            value,
70            generation: 0,
71        });
72        pool.count += 1;
73        NodeHandle(idx, 0)
74    }
75}
76
77/// Free a node by handle; returns the value if handle was valid.
78#[allow(dead_code)]
79pub fn np_free<T>(pool: &mut NodePool<T>, handle: NodeHandle) -> Option<T> {
80    let idx = handle.0 as usize;
81    if idx >= pool.slots.len() {
82        return None;
83    }
84    let gen = handle.1;
85    let old = std::mem::replace(
86        &mut pool.slots[idx],
87        NodeSlot::Free {
88            next_free: pool.free_head,
89            generation: gen + 1,
90        },
91    );
92    if let NodeSlot::Occupied { value, generation } = old {
93        if generation == gen {
94            pool.free_head = Some(handle.0);
95            pool.count -= 1;
96            return Some(value);
97        }
98        // wrong generation – restore
99        pool.slots[idx] = NodeSlot::Occupied { value, generation };
100    } else {
101        pool.slots[idx] = old;
102    }
103    None
104}
105
106/// Get reference by handle.
107#[allow(dead_code)]
108pub fn np_get<T>(pool: &NodePool<T>, handle: NodeHandle) -> Option<&T> {
109    let idx = handle.0 as usize;
110    if let Some(NodeSlot::Occupied { value, generation }) = pool.slots.get(idx) {
111        if *generation == handle.1 {
112            return Some(value);
113        }
114    }
115    None
116}
117
118/// Get mutable reference by handle.
119#[allow(dead_code)]
120pub fn np_get_mut<T>(pool: &mut NodePool<T>, handle: NodeHandle) -> Option<&mut T> {
121    let idx = handle.0 as usize;
122    if let Some(NodeSlot::Occupied { value, generation }) = pool.slots.get_mut(idx) {
123        if *generation == handle.1 {
124            return Some(value);
125        }
126    }
127    None
128}
129
130/// Whether handle is valid.
131#[allow(dead_code)]
132pub fn np_is_valid<T>(pool: &NodePool<T>, handle: NodeHandle) -> bool {
133    np_get(pool, handle).is_some()
134}
135
136/// Number of live nodes.
137#[allow(dead_code)]
138pub fn np_count<T>(pool: &NodePool<T>) -> usize {
139    pool.count
140}
141
142/// Total capacity (including free slots).
143#[allow(dead_code)]
144pub fn np_capacity<T>(pool: &NodePool<T>) -> usize {
145    pool.slots.len()
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn test_alloc_get() {
154        let mut pool = new_node_pool();
155        let h = np_alloc(&mut pool, 42u32);
156        assert_eq!(np_get(&pool, h), Some(&42));
157    }
158
159    #[test]
160    fn test_free_and_reuse() {
161        let mut pool = new_node_pool();
162        let h1 = np_alloc(&mut pool, 1u32);
163        np_free(&mut pool, h1);
164        let h2 = np_alloc(&mut pool, 2u32);
165        assert_eq!(h2.0, h1.0);
166        assert_ne!(h2.1, h1.1); // generation bumped
167    }
168
169    #[test]
170    fn test_stale_handle() {
171        let mut pool = new_node_pool();
172        let h1 = np_alloc(&mut pool, 1u32);
173        np_free(&mut pool, h1);
174        np_alloc(&mut pool, 2u32);
175        assert_eq!(np_get(&pool, h1), None); // stale
176    }
177
178    #[test]
179    fn test_count() {
180        let mut pool = new_node_pool();
181        let h = np_alloc(&mut pool, 1u32);
182        np_alloc(&mut pool, 2u32);
183        assert_eq!(np_count(&pool), 2);
184        np_free(&mut pool, h);
185        assert_eq!(np_count(&pool), 1);
186    }
187
188    #[test]
189    fn test_get_mut() {
190        let mut pool = new_node_pool();
191        let h = np_alloc(&mut pool, 5u32);
192        if let Some(v) = np_get_mut(&mut pool, h) {
193            *v = 99;
194        }
195        assert_eq!(np_get(&pool, h), Some(&99));
196    }
197
198    #[test]
199    fn test_invalid_handle() {
200        let pool: NodePool<u32> = new_node_pool();
201        assert!(!np_is_valid(&pool, NodeHandle(0, 0)));
202    }
203
204    #[test]
205    fn test_capacity_grows() {
206        let mut pool = new_node_pool();
207        np_alloc(&mut pool, 1u32);
208        np_alloc(&mut pool, 2u32);
209        np_alloc(&mut pool, 3u32);
210        assert_eq!(np_capacity(&pool), 3);
211    }
212
213    #[test]
214    fn test_free_out_of_bounds() {
215        let mut pool: NodePool<u32> = new_node_pool();
216        assert_eq!(np_free(&mut pool, NodeHandle(99, 0)), None);
217    }
218
219    #[test]
220    fn test_is_valid() {
221        let mut pool = new_node_pool();
222        let h = np_alloc(&mut pool, 0u32);
223        assert!(np_is_valid(&pool, h));
224    }
225}