Skip to main content

oxihuman_core/
persistent_hash_map.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Persistent (immutable) hash map stub — each insert or remove produces a
6//! new logical version, represented here as a cloned HashMap, making all
7//! previous versions independently accessible.
8
9use std::collections::HashMap;
10use std::hash::Hash;
11
12/// A version of the persistent map.
13pub type PmapVersion = usize;
14
15/// Persistent hash map that keeps all historical versions.
16pub struct PersistentHashMap<K, V> {
17    versions: Vec<HashMap<K, V>>,
18}
19
20impl<K, V> PersistentHashMap<K, V>
21where
22    K: Eq + Hash + Clone,
23    V: Clone,
24{
25    /// Create a persistent map with an empty initial version.
26    pub fn new() -> Self {
27        Self {
28            versions: vec![HashMap::new()],
29        }
30    }
31
32    /// Current (latest) version number.
33    pub fn current_version(&self) -> PmapVersion {
34        self.versions.len() - 1
35    }
36
37    /// Insert `key`→`value` into the current version, producing a new version.
38    pub fn insert(&mut self, key: K, value: V) -> PmapVersion {
39        let mut next = self.versions.last().cloned().unwrap_or_default();
40        next.insert(key, value);
41        self.versions.push(next);
42        self.current_version()
43    }
44
45    /// Remove `key` from the current version, producing a new version.
46    pub fn remove(&mut self, key: &K) -> PmapVersion {
47        let mut next = self.versions.last().cloned().unwrap_or_default();
48        next.remove(key);
49        self.versions.push(next);
50        self.current_version()
51    }
52
53    /// Get a value at a specific version.
54    pub fn get_at(&self, version: PmapVersion, key: &K) -> Option<&V> {
55        self.versions.get(version)?.get(key)
56    }
57
58    /// Get a value at the current version.
59    pub fn get(&self, key: &K) -> Option<&V> {
60        self.get_at(self.current_version(), key)
61    }
62
63    /// Number of entries in the current version.
64    pub fn len(&self) -> usize {
65        self.versions.last().map(|m| m.len()).unwrap_or(0)
66    }
67
68    /// True if the current version is empty.
69    pub fn is_empty(&self) -> bool {
70        self.len() == 0
71    }
72
73    /// Total versions stored.
74    pub fn version_count(&self) -> usize {
75        self.versions.len()
76    }
77}
78
79impl<K, V> Default for PersistentHashMap<K, V>
80where
81    K: Eq + Hash + Clone,
82    V: Clone,
83{
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89/// Create a new persistent hash map.
90pub fn new_persistent_hash_map<K, V>() -> PersistentHashMap<K, V>
91where
92    K: Eq + Hash + Clone,
93    V: Clone,
94{
95    PersistentHashMap::new()
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_insert_and_get() {
104        let mut m: PersistentHashMap<&str, i32> = PersistentHashMap::new();
105        m.insert("a", 1);
106        assert_eq!(m.get(&"a"), Some(&1)); /* current version has value */
107    }
108
109    #[test]
110    fn test_old_version_preserved() {
111        let mut m: PersistentHashMap<&str, i32> = PersistentHashMap::new();
112        m.insert("a", 1);
113        let v1 = m.insert("b", 2);
114        /* version 1 should only have "a" */
115        assert_eq!(m.get_at(1, &"a"), Some(&1));
116        assert_eq!(m.get_at(1, &"b"), None);
117        /* current version (v1) has both */
118        assert_eq!(m.get_at(v1, &"b"), Some(&2));
119    }
120
121    #[test]
122    fn test_remove_creates_version() {
123        let mut m: PersistentHashMap<&str, i32> = PersistentHashMap::new();
124        let v0 = m.insert("x", 10);
125        let v1 = m.remove(&"x");
126        assert!(m.get_at(v1, &"x").is_none()); /* removed in v1 */
127        assert!(m.get_at(v0, &"x").is_some()); /* still in v0 */
128    }
129
130    #[test]
131    fn test_len() {
132        let mut m: PersistentHashMap<i32, i32> = PersistentHashMap::new();
133        m.insert(1, 1);
134        m.insert(2, 2);
135        assert_eq!(m.len(), 2); /* two entries */
136    }
137
138    #[test]
139    fn test_is_empty_initially() {
140        let m: PersistentHashMap<i32, i32> = PersistentHashMap::new();
141        assert!(m.is_empty()); /* initial version empty */
142    }
143
144    #[test]
145    fn test_version_count() {
146        let mut m: PersistentHashMap<i32, i32> = PersistentHashMap::new();
147        m.insert(1, 1);
148        m.insert(2, 2);
149        assert_eq!(m.version_count(), 3); /* initial + 2 inserts */
150    }
151
152    #[test]
153    fn test_current_version() {
154        let mut m: PersistentHashMap<i32, i32> = PersistentHashMap::new();
155        assert_eq!(m.current_version(), 0);
156        m.insert(1, 1);
157        assert_eq!(m.current_version(), 1);
158    }
159
160    #[test]
161    fn test_default() {
162        let m: PersistentHashMap<i32, i32> = PersistentHashMap::default();
163        assert!(m.is_empty()); /* default is empty */
164    }
165
166    #[test]
167    fn test_new_helper() {
168        let m = new_persistent_hash_map::<i32, i32>();
169        assert!(m.is_empty()); /* helper creates empty map */
170    }
171
172    #[test]
173    fn test_overwrite_key() {
174        let mut m: PersistentHashMap<&str, i32> = PersistentHashMap::new();
175        m.insert("k", 1);
176        m.insert("k", 2);
177        assert_eq!(m.get(&"k"), Some(&2)); /* latest value wins */
178    }
179}