Skip to main content

oxihuman_core/
value_cache.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Value cache: memoizes computed f32 values by string key with invalidation.
6
7use std::collections::HashMap;
8
9/// A cached value entry.
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct ValueEntry {
13    pub value: f32,
14    pub version: u32,
15    pub dirty: bool,
16}
17
18/// The value cache.
19#[allow(dead_code)]
20#[derive(Debug, Clone, Default)]
21pub struct ValueCache {
22    entries: HashMap<String, ValueEntry>,
23    global_version: u32,
24}
25
26/// Create a new `ValueCache`.
27#[allow(dead_code)]
28pub fn new_value_cache() -> ValueCache {
29    ValueCache::default()
30}
31
32/// Store a computed value.
33#[allow(dead_code)]
34pub fn vc_store(vc: &mut ValueCache, key: &str, value: f32) {
35    vc.entries.insert(
36        key.to_string(),
37        ValueEntry {
38            value,
39            version: vc.global_version,
40            dirty: false,
41        },
42    );
43}
44
45/// Retrieve a cached value if not dirty.
46#[allow(dead_code)]
47pub fn vc_get(vc: &ValueCache, key: &str) -> Option<f32> {
48    vc.entries
49        .get(key)
50        .and_then(|e| if e.dirty { None } else { Some(e.value) })
51}
52
53/// Mark a key as dirty (needs recompute).
54#[allow(dead_code)]
55pub fn vc_invalidate(vc: &mut ValueCache, key: &str) {
56    if let Some(e) = vc.entries.get_mut(key) {
57        e.dirty = true;
58    }
59}
60
61/// Invalidate all entries.
62#[allow(dead_code)]
63pub fn vc_invalidate_all(vc: &mut ValueCache) {
64    vc.global_version += 1;
65    for e in vc.entries.values_mut() {
66        e.dirty = true;
67    }
68}
69
70/// Whether a key is cached and clean.
71#[allow(dead_code)]
72pub fn vc_is_valid(vc: &ValueCache, key: &str) -> bool {
73    vc.entries.get(key).is_some_and(|e| !e.dirty)
74}
75
76/// Number of cached entries.
77#[allow(dead_code)]
78pub fn vc_len(vc: &ValueCache) -> usize {
79    vc.entries.len()
80}
81
82/// Count of dirty entries.
83#[allow(dead_code)]
84pub fn vc_dirty_count(vc: &ValueCache) -> usize {
85    vc.entries.values().filter(|e| e.dirty).count()
86}
87
88/// Remove a key.
89#[allow(dead_code)]
90pub fn vc_remove(vc: &mut ValueCache, key: &str) {
91    vc.entries.remove(key);
92}
93
94/// Clear all entries.
95#[allow(dead_code)]
96pub fn vc_clear(vc: &mut ValueCache) {
97    vc.entries.clear();
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use std::f32::consts::PI;
104
105    #[test]
106    fn test_store_and_get() {
107        let mut vc = new_value_cache();
108        vc_store(&mut vc, "x", PI);
109        assert!((vc_get(&vc, "x").expect("should succeed") - PI).abs() < 1e-5);
110    }
111
112    #[test]
113    fn test_missing_returns_none() {
114        let vc = new_value_cache();
115        assert!(vc_get(&vc, "y").is_none());
116    }
117
118    #[test]
119    fn test_invalidate() {
120        let mut vc = new_value_cache();
121        vc_store(&mut vc, "a", 1.0);
122        vc_invalidate(&mut vc, "a");
123        assert!(vc_get(&vc, "a").is_none());
124    }
125
126    #[test]
127    fn test_invalidate_all() {
128        let mut vc = new_value_cache();
129        vc_store(&mut vc, "a", 1.0);
130        vc_store(&mut vc, "b", 2.0);
131        vc_invalidate_all(&mut vc);
132        assert_eq!(vc_dirty_count(&vc), 2);
133    }
134
135    #[test]
136    fn test_is_valid() {
137        let mut vc = new_value_cache();
138        vc_store(&mut vc, "v", 5.0);
139        assert!(vc_is_valid(&vc, "v"));
140        vc_invalidate(&mut vc, "v");
141        assert!(!vc_is_valid(&vc, "v"));
142    }
143
144    #[test]
145    fn test_remove() {
146        let mut vc = new_value_cache();
147        vc_store(&mut vc, "k", 7.0);
148        vc_remove(&mut vc, "k");
149        assert!(vc_get(&vc, "k").is_none());
150    }
151
152    #[test]
153    fn test_len() {
154        let mut vc = new_value_cache();
155        vc_store(&mut vc, "a", 1.0);
156        vc_store(&mut vc, "b", 2.0);
157        assert_eq!(vc_len(&vc), 2);
158    }
159
160    #[test]
161    fn test_clear() {
162        let mut vc = new_value_cache();
163        vc_store(&mut vc, "x", 9.0);
164        vc_clear(&mut vc);
165        assert_eq!(vc_len(&vc), 0);
166    }
167
168    #[test]
169    fn test_reclean_after_restore() {
170        let mut vc = new_value_cache();
171        vc_store(&mut vc, "m", 1.0);
172        vc_invalidate(&mut vc, "m");
173        vc_store(&mut vc, "m", 2.0);
174        assert!(vc_is_valid(&vc, "m"));
175    }
176}