1use core::mem::size_of;
2use crate::types::TableDef;
3pub use crate::time_series::TimeSeriesConfig;
4#[cfg(feature = "ha")]
5pub use crate::ha::HAConfig;
6
7pub struct DefaultMemoryAllocator;
9
10impl MemoryAllocator for DefaultMemoryAllocator {
11 fn allocate(&self, size: usize) -> Option<core::ptr::NonNull<u8>> {
12 #[cfg(feature = "std")] {
14 let mut vec = vec![0u8; size];
15 let ptr = vec.as_mut_ptr();
16 std::mem::forget(vec);
18 Some(unsafe { core::ptr::NonNull::new_unchecked(ptr) })
19 }
20 #[cfg(not(feature = "std"))] {
21 None
23 }
24 }
25
26 fn deallocate(&self, ptr: core::ptr::NonNull<u8>, size: usize) {
27 #[cfg(feature = "std")] {
29 unsafe {
30 let slice = core::slice::from_raw_parts_mut(ptr.as_ptr(), size);
31 let vec = Vec::from_raw_parts(slice.as_mut_ptr(), 0, size);
32 drop(vec);
33 }
34 }
35 }
37}
38
39pub trait MemoryAllocator: Sync {
41 fn allocate(&self, size: usize) -> Option<core::ptr::NonNull<u8>>;
43
44 fn deallocate(&self, ptr: core::ptr::NonNull<u8>, size: usize);
46}
47
48#[derive(Copy, Clone, PartialEq)]
50pub enum LogMode {
51 Sync,
53 Async,
55}
56
57pub struct WALConfig {
59 pub log_path: &'static str,
61 pub log_mode: LogMode,
63 pub checkpoint_interval_ms: u64,
65 pub log_file_size_limit: usize,
67 pub log_prealloc_size: usize,
69 pub log_segment_size: usize,
71 pub retained_checkpoints: usize,
73}
74
75pub struct DbConfig {
77 pub tables: &'static [TableDef],
79 pub total_memory: usize,
81 pub low_power_mode_supported: bool,
83 pub low_power_max_records: Option<usize>,
85 pub default_max_records: usize,
87 pub memory_allocator: &'static dyn MemoryAllocator,
89 pub wal_config: WALConfig,
91 pub time_series_defaults: TimeSeriesConfig,
93
94 #[cfg(feature = "pubsub")]
96 pub pubsub_config: Option<crate::pubsub::PubSubConfig>,
97
98 #[cfg(feature = "ha")]
100 pub ha_config: Option<HAConfig>,
101}
102
103
104
105pub const fn validate_config(config: &DbConfig) -> bool {
107 if config.tables.len() > 32 {
109 return false;
110 }
111
112 if let Some(low_power_max) = config.low_power_max_records {
114 if low_power_max > 100000 {
115 return false;
116 }
117 }
118
119 if config.default_max_records > 500000 {
121 return false;
122 }
123
124 if config.wal_config.checkpoint_interval_ms > 3600000 { return false;
127 }
128
129 if config.wal_config.log_file_size_limit < 1024 * 1024 { return false;
131 }
132
133 if config.wal_config.log_prealloc_size > config.wal_config.log_file_size_limit {
134 return false;
135 }
136
137 if config.wal_config.log_segment_size < 1024 * 1024 { return false;
139 }
140
141 if config.wal_config.retained_checkpoints > 10 {
142 return false;
143 }
144
145 #[cfg(feature = "ha")] {
147 if let Some(ha_config) = &config.ha_config {
148 if ha_config.heartbeat_interval_ms < 100 { return false;
150 }
151
152 if ha_config.heartbeat_interval_ms > 60000 { return false;
154 }
155
156 if ha_config.failure_detection_ms < ha_config.heartbeat_interval_ms { return false;
158 }
159
160 if ha_config.failure_detection_ms > 300000 { return false;
162 }
163
164 if ha_config.sync_timeout_ms < 100 { return false;
166 }
167
168 if ha_config.sync_timeout_ms > 10000 { return false;
170 }
171 }
172 }
173
174 let mut i = 0;
176 while i < config.tables.len() {
177 let table = &config.tables[i];
178
179 if table.record_size > 512 {
181 return false;
182 }
183
184 if table.max_records > 500000 {
186 return false;
187 }
188
189 if table.primary_key >= table.fields.len() {
191 return false;
192 }
193
194 let has_secondary = table.secondary_index.is_some();
196 if has_secondary {
197 let secondary_index = table.secondary_index.unwrap();
198 if secondary_index >= table.fields.len() {
199 return false;
200 }
201 }
202
203 i += 1;
204 }
205
206 true
207}
208
209pub const fn table_memory_usage(table: &TableDef) -> usize {
211 let record_memory = table.record_size * table.max_records;
213
214 let index_memory = table.max_records * size_of::<u32>(); let secondary_index_memory = if table.secondary_index.is_some() {
219 match table.secondary_index_type {
220 crate::types::IndexType::SortedArray => {
222 let primary_key_field = &table.fields[table.primary_key];
223 table.max_records * (primary_key_field.size + size_of::<u16>())
224 },
225 crate::types::IndexType::BTree => {
227 const BTREE_NODE_SIZE: usize = 1 + 1 + (64 * 4) + ((size_of::<usize>() * 5) / 8);
229 let max_nodes = table.max_records / 2;
231 max_nodes * BTREE_NODE_SIZE
232 },
233 crate::types::IndexType::TTree => {
235 const TTREE_NODE_SIZE: usize = 1 + (64 * 3) + (size_of::<usize>() * 3);
237 let max_nodes = table.max_records / 2;
239 max_nodes * TTREE_NODE_SIZE
240 },
241 _ => {
243 let primary_key_field = &table.fields[table.primary_key];
244 table.max_records * (primary_key_field.size + size_of::<u16>())
245 }
246 }
247 } else {
248 0
249 };
250
251 record_memory + index_memory + secondary_index_memory
252}
253
254pub const fn total_memory_usage(config: &DbConfig) -> usize {
256 let mut total = 0;
257 let mut i = 0;
258 while i < config.tables.len() {
259 total += table_memory_usage(&config.tables[i]);
260 i += 1;
261 }
262 total
263}