absurder_sql/storage/
metadata.rs1use crate::types::DatabaseError;
5#[cfg(not(target_arch = "wasm32"))]
6use parking_lot::Mutex;
7#[cfg(target_arch = "wasm32")]
8use std::cell::RefCell;
9use std::collections::HashMap;
10
11#[allow(unused_macros)]
13#[cfg(target_arch = "wasm32")]
14macro_rules! lock_mutex {
15 ($mutex:expr) => {
16 $mutex
17 .try_borrow_mut()
18 .expect("RefCell borrow failed - reentrancy detected in metadata.rs")
19 };
20}
21
22#[allow(unused_macros)]
23#[cfg(not(target_arch = "wasm32"))]
24macro_rules! lock_mutex {
25 ($mutex:expr) => {
26 $mutex.lock()
27 };
28}
29
30#[cfg(target_arch = "wasm32")]
31#[allow(unused_imports)]
32use js_sys::Date;
33
34#[cfg(not(target_arch = "wasm32"))]
35#[allow(unused_imports)]
36use std::time::{SystemTime, UNIX_EPOCH};
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40#[allow(dead_code)]
41#[cfg_attr(feature = "fs_persist", derive(serde::Serialize, serde::Deserialize))]
42pub enum ChecksumAlgorithm {
43 FastHash,
44 CRC32,
45}
46
47#[derive(Clone, Debug)]
49#[cfg_attr(feature = "fs_persist", derive(serde::Serialize, serde::Deserialize))]
50pub struct BlockMetadataPersist {
51 pub checksum: u64,
52 #[allow(dead_code)]
53 pub last_modified_ms: u64,
54 #[allow(dead_code)]
55 pub version: u32,
56 #[allow(dead_code)]
57 pub algo: ChecksumAlgorithm,
58}
59
60pub struct ChecksumManager {
62 #[cfg(target_arch = "wasm32")]
63 checksums: RefCell<HashMap<u64, u64>>,
64 #[cfg(not(target_arch = "wasm32"))]
65 checksums: Mutex<HashMap<u64, u64>>,
66
67 #[cfg(target_arch = "wasm32")]
68 checksum_algos: RefCell<HashMap<u64, ChecksumAlgorithm>>,
69 #[cfg(not(target_arch = "wasm32"))]
70 checksum_algos: Mutex<HashMap<u64, ChecksumAlgorithm>>,
71
72 checksum_algo_default: ChecksumAlgorithm,
74}
75
76impl ChecksumManager {
77 pub fn new(default_algorithm: ChecksumAlgorithm) -> Self {
79 Self {
80 #[cfg(target_arch = "wasm32")]
81 checksums: RefCell::new(HashMap::new()),
82 #[cfg(not(target_arch = "wasm32"))]
83 checksums: Mutex::new(HashMap::new()),
84
85 #[cfg(target_arch = "wasm32")]
86 checksum_algos: RefCell::new(HashMap::new()),
87 #[cfg(not(target_arch = "wasm32"))]
88 checksum_algos: Mutex::new(HashMap::new()),
89
90 checksum_algo_default: default_algorithm,
91 }
92 }
93
94 pub fn with_data(
96 checksums: HashMap<u64, u64>,
97 checksum_algos: HashMap<u64, ChecksumAlgorithm>,
98 default_algorithm: ChecksumAlgorithm,
99 ) -> Self {
100 Self {
101 #[cfg(target_arch = "wasm32")]
102 checksums: RefCell::new(checksums),
103 #[cfg(not(target_arch = "wasm32"))]
104 checksums: Mutex::new(checksums),
105
106 #[cfg(target_arch = "wasm32")]
107 checksum_algos: RefCell::new(checksum_algos),
108 #[cfg(not(target_arch = "wasm32"))]
109 checksum_algos: Mutex::new(checksum_algos),
110
111 checksum_algo_default: default_algorithm,
112 }
113 }
114
115 pub fn compute_checksum_with(data: &[u8], algo: ChecksumAlgorithm) -> u64 {
117 match algo {
118 ChecksumAlgorithm::FastHash => {
119 use std::collections::hash_map::DefaultHasher;
120 use std::hash::Hash;
121 use std::hash::Hasher;
122 let mut hasher = DefaultHasher::new();
123 data.hash(&mut hasher);
124 hasher.finish()
125 }
126 ChecksumAlgorithm::CRC32 => {
127 let mut hasher = crc32fast::Hasher::new();
128 hasher.update(data);
129 hasher.finalize() as u64
130 }
131 }
132 }
133
134 pub fn store_checksum(&self, block_id: u64, data: &[u8]) {
136 let algo = {
137 let algos = lock_mutex!(self.checksum_algos);
138 algos
139 .get(&block_id)
140 .copied()
141 .unwrap_or(self.checksum_algo_default)
142 };
143 let csum = Self::compute_checksum_with(data, algo);
144 lock_mutex!(self.checksums).insert(block_id, csum);
145 lock_mutex!(self.checksum_algos).insert(block_id, algo);
146 }
147
148 pub fn validate_checksum(&self, block_id: u64, data: &[u8]) -> Result<(), DatabaseError> {
150 let expected_opt = lock_mutex!(self.checksums).get(&block_id).copied();
151 if let Some(expected) = expected_opt {
152 let algo = lock_mutex!(self.checksum_algos)
153 .get(&block_id)
154 .copied()
155 .unwrap_or(self.checksum_algo_default);
156 let actual = Self::compute_checksum_with(data, algo);
157 if expected != actual {
158 let known_algos = [ChecksumAlgorithm::FastHash, ChecksumAlgorithm::CRC32];
160 for alt in known_algos.iter().copied().filter(|a| *a != algo) {
161 let alt_sum = Self::compute_checksum_with(data, alt);
162 if expected == alt_sum {
163 return Err(DatabaseError::new(
164 "ALGO_MISMATCH",
165 &format!(
166 "Checksum algorithm mismatch for block {}: stored algo {:?}, but data matches {:?}",
167 block_id, algo, alt
168 ),
169 ));
170 }
171 }
172 return Err(DatabaseError::new(
173 "CHECKSUM_MISMATCH",
174 &format!(
175 "Checksum mismatch for block {}: expected {}, got {}",
176 block_id, expected, actual
177 ),
178 ));
179 }
180 }
181 Ok(())
182 }
183
184 pub fn remove_checksum(&self, block_id: u64) {
186 lock_mutex!(self.checksums).remove(&block_id);
187 lock_mutex!(self.checksum_algos).remove(&block_id);
188 }
189
190 pub fn get_checksum(&self, block_id: u64) -> Option<u64> {
192 lock_mutex!(self.checksums).get(&block_id).copied()
193 }
194
195 pub fn get_algorithm(&self, block_id: u64) -> ChecksumAlgorithm {
197 lock_mutex!(self.checksum_algos)
198 .get(&block_id)
199 .copied()
200 .unwrap_or(self.checksum_algo_default)
201 }
202
203 pub fn replace_all(
205 &self,
206 new_checksums: HashMap<u64, u64>,
207 new_algos: HashMap<u64, ChecksumAlgorithm>,
208 ) {
209 *lock_mutex!(self.checksums) = new_checksums;
210 *lock_mutex!(self.checksum_algos) = new_algos;
211 }
212
213 pub fn checksums(&self) -> HashMap<u64, u64> {
215 lock_mutex!(self.checksums).clone()
216 }
217
218 pub fn algorithms(&self) -> HashMap<u64, ChecksumAlgorithm> {
220 lock_mutex!(self.checksum_algos).clone()
221 }
222
223 pub fn default_algorithm(&self) -> ChecksumAlgorithm {
225 self.checksum_algo_default
226 }
227
228 pub fn set_checksum_for_testing(&self, block_id: u64, checksum: u64) {
230 lock_mutex!(self.checksums).insert(block_id, checksum);
231 }
232
233 pub fn clear_checksums(&self) {
235 lock_mutex!(self.checksums).clear();
236 lock_mutex!(self.checksum_algos).clear();
237 }
238}