oxihuman_core/
ref_counted.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub struct RefEntry<V> {
12 pub value: V,
13 pub ref_count: u32,
14}
15
16pub struct RefCounted<V> {
18 entries: HashMap<String, RefEntry<V>>,
19}
20
21#[allow(dead_code)]
22impl<V: Clone> RefCounted<V> {
23 pub fn new() -> Self {
24 RefCounted {
25 entries: HashMap::new(),
26 }
27 }
28
29 pub fn insert(&mut self, key: &str, value: V) -> bool {
31 if self.entries.contains_key(key) {
32 return false;
33 }
34 self.entries.insert(
35 key.to_string(),
36 RefEntry {
37 value,
38 ref_count: 1,
39 },
40 );
41 true
42 }
43
44 pub fn acquire(&mut self, key: &str) -> Option<u32> {
46 let e = self.entries.get_mut(key)?;
47 e.ref_count += 1;
48 Some(e.ref_count)
49 }
50
51 pub fn release(&mut self, key: &str) -> Option<u32> {
54 let count = {
55 let e = self.entries.get_mut(key)?;
56 e.ref_count = e.ref_count.saturating_sub(1);
57 e.ref_count
58 };
59 if count == 0 {
60 self.entries.remove(key);
61 }
62 Some(count)
63 }
64
65 pub fn get(&self, key: &str) -> Option<&V> {
66 self.entries.get(key).map(|e| &e.value)
67 }
68
69 pub fn ref_count(&self, key: &str) -> Option<u32> {
70 self.entries.get(key).map(|e| e.ref_count)
71 }
72
73 pub fn contains(&self, key: &str) -> bool {
74 self.entries.contains_key(key)
75 }
76
77 pub fn len(&self) -> usize {
78 self.entries.len()
79 }
80
81 pub fn is_empty(&self) -> bool {
82 self.entries.is_empty()
83 }
84
85 pub fn total_refs(&self) -> u64 {
86 self.entries.values().map(|e| e.ref_count as u64).sum()
87 }
88
89 pub fn keys(&self) -> Vec<&str> {
90 self.entries.keys().map(|k| k.as_str()).collect()
91 }
92
93 pub fn clear(&mut self) {
94 self.entries.clear();
95 }
96}
97
98impl<V: Clone> Default for RefCounted<V> {
99 fn default() -> Self {
100 Self::new()
101 }
102}
103
104pub fn new_ref_counted<V: Clone>() -> RefCounted<V> {
105 RefCounted::new()
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn insert_and_get() {
114 let mut rc: RefCounted<i32> = new_ref_counted();
115 assert!(rc.insert("res", 42));
116 assert_eq!(*rc.get("res").expect("should succeed"), 42);
117 }
118
119 #[test]
120 fn duplicate_insert_fails() {
121 let mut rc: RefCounted<i32> = new_ref_counted();
122 rc.insert("k", 1);
123 assert!(!rc.insert("k", 2));
124 }
125
126 #[test]
127 fn acquire_increments() {
128 let mut rc: RefCounted<i32> = new_ref_counted();
129 rc.insert("k", 1);
130 assert_eq!(rc.acquire("k"), Some(2));
131 assert_eq!(rc.ref_count("k"), Some(2));
132 }
133
134 #[test]
135 fn release_decrements() {
136 let mut rc: RefCounted<i32> = new_ref_counted();
137 rc.insert("k", 1);
138 rc.acquire("k");
139 assert_eq!(rc.release("k"), Some(1));
140 }
141
142 #[test]
143 fn release_to_zero_removes() {
144 let mut rc: RefCounted<i32> = new_ref_counted();
145 rc.insert("k", 1);
146 rc.release("k");
147 assert!(!rc.contains("k"));
148 }
149
150 #[test]
151 fn total_refs() {
152 let mut rc: RefCounted<i32> = new_ref_counted();
153 rc.insert("a", 1);
154 rc.insert("b", 2);
155 rc.acquire("a");
156 assert_eq!(rc.total_refs(), 3);
157 }
158
159 #[test]
160 fn contains_check() {
161 let mut rc: RefCounted<i32> = new_ref_counted();
162 rc.insert("x", 0);
163 assert!(rc.contains("x"));
164 assert!(!rc.contains("y"));
165 }
166
167 #[test]
168 fn clear_empties() {
169 let mut rc: RefCounted<i32> = new_ref_counted();
170 rc.insert("a", 1);
171 rc.clear();
172 assert!(rc.is_empty());
173 }
174
175 #[test]
176 fn release_missing_is_none() {
177 let mut rc: RefCounted<i32> = new_ref_counted();
178 assert!(rc.release("ghost").is_none());
179 }
180}