absurder_sql/storage/
metadata.rs1use std::collections::HashMap;
5use crate::types::DatabaseError;
6
7#[cfg(target_arch = "wasm32")]
8#[allow(unused_imports)]
9use js_sys::Date;
10
11#[cfg(not(target_arch = "wasm32"))]
12#[allow(unused_imports)]
13use std::time::{SystemTime, UNIX_EPOCH};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17#[allow(dead_code)]
18#[cfg_attr(feature = "fs_persist", derive(serde::Serialize, serde::Deserialize))]
19pub enum ChecksumAlgorithm {
20 FastHash,
21 CRC32
22}
23
24#[derive(Clone, Debug)]
26#[cfg_attr(feature = "fs_persist", derive(serde::Serialize, serde::Deserialize))]
27pub struct BlockMetadataPersist {
28 pub checksum: u64,
29 #[allow(dead_code)]
30 pub last_modified_ms: u64,
31 #[allow(dead_code)]
32 pub version: u32,
33 #[allow(dead_code)]
34 pub algo: ChecksumAlgorithm,
35}
36
37pub struct ChecksumManager {
39 checksums: HashMap<u64, u64>,
41 checksum_algos: HashMap<u64, ChecksumAlgorithm>,
43 checksum_algo_default: ChecksumAlgorithm,
45}
46
47impl ChecksumManager {
48 pub fn new(default_algorithm: ChecksumAlgorithm) -> Self {
50 Self {
51 checksums: HashMap::new(),
52 checksum_algos: HashMap::new(),
53 checksum_algo_default: default_algorithm,
54 }
55 }
56
57 pub fn with_data(
59 checksums: HashMap<u64, u64>,
60 checksum_algos: HashMap<u64, ChecksumAlgorithm>,
61 default_algorithm: ChecksumAlgorithm,
62 ) -> Self {
63 Self {
64 checksums,
65 checksum_algos,
66 checksum_algo_default: default_algorithm,
67 }
68 }
69
70 pub fn compute_checksum_with(data: &[u8], algo: ChecksumAlgorithm) -> u64 {
72 match algo {
73 ChecksumAlgorithm::FastHash => {
74 use std::collections::hash_map::DefaultHasher;
75 use std::hash::Hash;
76 use std::hash::Hasher;
77 let mut hasher = DefaultHasher::new();
78 data.hash(&mut hasher);
79 hasher.finish()
80 }
81 ChecksumAlgorithm::CRC32 => {
82 let mut hasher = crc32fast::Hasher::new();
83 hasher.update(data);
84 hasher.finalize() as u64
85 }
86 }
87 }
88
89 pub fn store_checksum(&mut self, block_id: u64, data: &[u8]) {
91 let algo = self.checksum_algos
92 .get(&block_id)
93 .copied()
94 .unwrap_or(self.checksum_algo_default);
95 let csum = Self::compute_checksum_with(data, algo);
96 self.checksums.insert(block_id, csum);
97 self.checksum_algos.insert(block_id, algo);
98 }
99
100 pub fn validate_checksum(&self, block_id: u64, data: &[u8]) -> Result<(), DatabaseError> {
102 if let Some(expected) = self.checksums.get(&block_id) {
103 let algo = self
104 .checksum_algos
105 .get(&block_id)
106 .copied()
107 .unwrap_or(self.checksum_algo_default);
108 let actual = Self::compute_checksum_with(data, algo);
109 if *expected != actual {
110 let known_algos = [ChecksumAlgorithm::FastHash, ChecksumAlgorithm::CRC32];
112 for alt in known_algos.iter().copied().filter(|a| *a != algo) {
113 let alt_sum = Self::compute_checksum_with(data, alt);
114 if *expected == alt_sum {
115 return Err(DatabaseError::new(
116 "ALGO_MISMATCH",
117 &format!(
118 "Checksum algorithm mismatch for block {}: stored algo {:?}, but data matches {:?}",
119 block_id, algo, alt
120 ),
121 ));
122 }
123 }
124 return Err(DatabaseError::new(
125 "CHECKSUM_MISMATCH",
126 &format!(
127 "Checksum mismatch for block {}: expected {}, got {}",
128 block_id, expected, actual
129 ),
130 ));
131 }
132 }
133 Ok(())
134 }
135
136 pub fn remove_checksum(&mut self, block_id: u64) {
138 self.checksums.remove(&block_id);
139 self.checksum_algos.remove(&block_id);
140 }
141
142 pub fn get_checksum(&self, block_id: u64) -> Option<u64> {
144 self.checksums.get(&block_id).copied()
145 }
146
147 pub fn get_algorithm(&self, block_id: u64) -> ChecksumAlgorithm {
149 self.checksum_algos
150 .get(&block_id)
151 .copied()
152 .unwrap_or(self.checksum_algo_default)
153 }
154
155 pub fn replace_all(
157 &mut self,
158 new_checksums: HashMap<u64, u64>,
159 new_algos: HashMap<u64, ChecksumAlgorithm>,
160 ) {
161 self.checksums = new_checksums;
162 self.checksum_algos = new_algos;
163 }
164
165 pub fn checksums(&self) -> &HashMap<u64, u64> {
167 &self.checksums
168 }
169
170 pub fn algorithms(&self) -> &HashMap<u64, ChecksumAlgorithm> {
172 &self.checksum_algos
173 }
174
175 pub fn default_algorithm(&self) -> ChecksumAlgorithm {
177 self.checksum_algo_default
178 }
179
180 #[cfg(any(test, debug_assertions))]
182 pub fn set_checksum_for_testing(&mut self, block_id: u64, checksum: u64) {
183 self.checksums.insert(block_id, checksum);
184 }
185
186 pub fn clear_checksums(&mut self) {
188 self.checksums.clear();
189 self.checksum_algos.clear();
190 }
191}