1#![forbid(unsafe_code)]
25#![warn(missing_docs)]
26
27pub mod error;
28pub mod export;
29mod format;
30mod lru;
31mod page;
32mod system;
33mod tree;
34pub mod vfs;
35
36use std::{
37 fmt::Debug,
38 ops::{Bound, RangeBounds},
39 path::{Path, PathBuf},
40 time::{Duration, Instant},
41};
42
43pub use crate::error::Error;
44use crate::format::Format;
45use crate::page::{Metadata as PageMetadata, Page, PageOpenMode, PageTableOptions};
46use crate::tree::{Node, Tree, TreeCursor, TreeMetadata};
47use crate::vfs::{MemoryVfs, OsVfs, ReadOnlyVfs, Vfs, VfsSyncOption};
48
49pub type KeyValuePair = (Vec<u8>, Vec<u8>);
51
52#[derive(Debug, Clone)]
54pub struct Options {
55 pub open_mode: OpenMode,
57
58 pub keys_per_node: usize,
68
69 pub page_cache_size: usize,
75
76 pub file_locking: bool,
79
80 pub file_sync: SyncOption,
83
84 pub automatic_flush: bool,
96
97 pub automatic_flush_threshold: usize,
104
105 pub compression_level: CompressionLevel,
107}
108
109impl Default for Options {
110 fn default() -> Self {
111 Self {
112 open_mode: OpenMode::default(),
113 keys_per_node: 1024,
114 page_cache_size: 64,
115 file_locking: true,
116 file_sync: SyncOption::default(),
117 automatic_flush: true,
118 automatic_flush_threshold: 2048,
119 compression_level: CompressionLevel::default(),
120 }
121 }
122}
123
124impl Options {
125 fn validate(&self) -> Result<(), Error> {
126 if self.keys_per_node < 2 {
127 return Err(Error::InvalidConfig {
128 message: "required keys_per_node >= 2",
129 });
130 }
131 if self.page_cache_size < 1 {
132 return Err(Error::InvalidConfig {
133 message: "required page_cache_size >= 1",
134 });
135 }
136
137 Ok(())
138 }
139}
140
141impl From<Options> for PageTableOptions {
142 fn from(options: Options) -> Self {
143 Self {
144 open_mode: options.open_mode.into(),
145 page_cache_size: options.page_cache_size,
146 file_locking: options.file_locking,
147 file_sync: options.file_sync.into(),
148 keys_per_node: options.keys_per_node,
149 compression_level: options.compression_level.to_zstd(),
150 }
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum OpenMode {
157 LoadOnly,
159 CreateOnly,
161 LoadOrCreate,
163 ReadOnly,
165}
166
167impl Default for OpenMode {
168 fn default() -> Self {
169 Self::LoadOrCreate
170 }
171}
172
173impl From<OpenMode> for PageOpenMode {
174 fn from(option: OpenMode) -> Self {
175 match option {
176 OpenMode::LoadOnly => PageOpenMode::LoadOnly,
177 OpenMode::CreateOnly => PageOpenMode::CreateOnly,
178 OpenMode::LoadOrCreate => PageOpenMode::LoadOrCreate,
179 OpenMode::ReadOnly => PageOpenMode::ReadOnly,
180 }
181 }
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum CompressionLevel {
187 None,
189
190 VeryLow,
194
195 Low,
199
200 Medium,
204
205 High,
209}
210
211impl Default for CompressionLevel {
212 fn default() -> Self {
213 Self::Low
214 }
215}
216
217impl CompressionLevel {
218 fn to_zstd(&self) -> Option<i32> {
219 match self {
220 Self::None => None,
221 Self::VeryLow => Some(1),
222 Self::Low => Some(3),
223 Self::Medium => Some(9),
224 Self::High => Some(19),
225 }
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub enum SyncOption {
234 None,
236
237 Data,
241
242 All,
246}
247
248impl Default for SyncOption {
249 fn default() -> Self {
250 Self::Data
251 }
252}
253
254impl From<SyncOption> for VfsSyncOption {
255 fn from(option: SyncOption) -> Self {
256 match option {
257 SyncOption::None => Self::None,
258 SyncOption::Data => Self::Data,
259 SyncOption::All => Self::All,
260 }
261 }
262}
263
264pub struct Database {
266 options: Options,
267 tree: Tree,
268 flush_tracker: Option<FlushTracker>,
269}
270
271impl Database {
272 pub fn open(vfs: Box<dyn Vfs + Sync + Send>, options: Options) -> Result<Self, Error> {
274 options.validate()?;
275
276 let vfs: Box<dyn Vfs + Sync + Send> = if options.open_mode == OpenMode::ReadOnly {
277 Box::new(ReadOnlyVfs::new(vfs))
278 } else {
279 vfs
280 };
281
282 let mut tree = Tree::open(vfs, options.clone().into())?;
283
284 match options.open_mode {
285 OpenMode::CreateOnly | OpenMode::LoadOrCreate => {
286 tree.init_if_empty()?;
287 tree.upgrade()?;
288 }
289 OpenMode::LoadOnly => {
290 tree.upgrade()?;
291 }
292 _ => {}
293 }
294
295 let flush_tracker = if options.automatic_flush && options.open_mode != OpenMode::ReadOnly {
296 Some(FlushTracker::new(options.automatic_flush_threshold))
297 } else {
298 None
299 };
300
301 Ok(Self {
302 options,
303 tree,
304 flush_tracker,
305 })
306 }
307
308 pub fn open_memory(options: Options) -> Result<Self, Error> {
310 Self::open(Box::new(MemoryVfs::default()), options)
311 }
312
313 pub fn open_path<P>(root_path: P, options: Options) -> Result<Self, Error>
317 where
318 P: Into<PathBuf>,
319 {
320 Self::open(Box::new(OsVfs::new(root_path)), options)
321 }
322
323 pub fn metadata(&self) -> Metadata {
325 Metadata {
326 tree_metadata: self.tree.metadata(),
327 }
328 }
329
330 pub fn contains_key<K>(&mut self, key: K) -> Result<bool, Error>
332 where
333 K: AsRef<[u8]>,
334 {
335 self.tree.contains_key(key.as_ref())
336 }
337
338 pub fn get<K>(&mut self, key: K) -> Result<Option<Vec<u8>>, Error>
340 where
341 K: AsRef<[u8]>,
342 {
343 let mut value = Vec::new();
344 if self.tree.get(key.as_ref(), &mut value)? {
345 Ok(Some(value))
346 } else {
347 Ok(None)
348 }
349 }
350
351 pub fn get_buf<K>(&mut self, key: K, value_destination: &mut Vec<u8>) -> Result<bool, Error>
356 where
357 K: AsRef<[u8]>,
358 {
359 self.tree.get(key.as_ref(), value_destination)
360 }
361
362 pub fn put<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
364 where
365 K: Into<Vec<u8>>,
366 V: Into<Vec<u8>>,
367 {
368 self.maybe_flush(true)?;
369 self.tree.put(key.into(), value.into())
370 }
371
372 pub fn remove<K>(&mut self, key: K) -> Result<(), Error>
376 where
377 K: AsRef<[u8]>,
378 {
379 self.maybe_flush(true)?;
380 self.tree.remove(key.as_ref())
381 }
382
383 pub fn cursor(&mut self) -> Result<Cursor<'_>, Error> {
385 Ok(Cursor::new(&mut self.tree))
386 }
387
388 pub fn cursor_range<K, R>(&mut self, range: R) -> Result<Cursor<'_>, Error>
394 where
395 K: AsRef<[u8]>,
396 R: RangeBounds<K>,
397 {
398 let mut cursor = Cursor::new(&mut self.tree);
399
400 match range.start_bound() {
401 Bound::Included(key) => {
402 cursor.seek(key)?;
403 }
404 Bound::Excluded(key) => {
405 let mut key = key.as_ref().to_vec();
406 key.push(0);
407 cursor.seek(key)?;
408 }
409 Bound::Unbounded => {}
410 }
411
412 cursor.set_range(range);
413
414 Ok(cursor)
415 }
416
417 pub fn flush(&mut self) -> Result<(), Error> {
427 self.tree.flush()
428 }
429
430 pub fn verify<P>(&mut self, progress_callback: P) -> Result<(), Error>
438 where
439 P: FnMut(usize, usize),
440 {
441 self.tree.verify_tree(progress_callback)
442 }
443
444 pub fn debug_print_tree(&mut self) -> Result<(), Error> {
446 self.tree.dump_tree()
447 }
448
449 fn maybe_flush(&mut self, increment: bool) -> Result<(), Error> {
450 if let Some(flush_tracker) = &mut self.flush_tracker {
451 if increment {
452 flush_tracker.increment_modification();
453 }
454
455 if flush_tracker.check_should_flush() {
456 self.flush()?;
457 }
458 }
459
460 Ok(())
461 }
462}
463
464impl Drop for Database {
465 fn drop(&mut self) {
466 if self.options.automatic_flush && self.options.open_mode != OpenMode::ReadOnly {
467 let _ = self.flush();
468 }
469 }
470}
471
472impl Debug for Database {
473 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474 write!(f, "Database {{ open_mode: {:?} }}", self.options.open_mode)
475 }
476}
477
478pub struct Cursor<'a> {
480 tree: &'a mut Tree,
481 tree_cursor: TreeCursor,
482 error: Option<Error>,
483 has_seeked: bool,
484 range: (Bound<Vec<u8>>, Bound<Vec<u8>>),
485}
486
487impl<'a> Cursor<'a> {
488 fn new(tree: &'a mut Tree) -> Self {
489 Self {
490 tree,
491 tree_cursor: TreeCursor::default(),
492 error: None,
493 has_seeked: false,
494 range: (Bound::Unbounded, Bound::Unbounded),
495 }
496 }
497
498 pub fn error(&self) -> Option<&Error> {
500 self.error.as_ref()
501 }
502
503 pub fn seek<K>(&mut self, key: K) -> Result<(), Error>
511 where
512 K: AsRef<[u8]>,
513 {
514 self.has_seeked = true;
515 self.tree.cursor_start(&mut self.tree_cursor, key.as_ref())
516 }
517
518 pub fn set_range<K, R>(&mut self, range: R)
527 where
528 K: AsRef<[u8]>,
529 R: RangeBounds<K>,
530 {
531 self.range = concrete_range(range);
532 }
533
534 pub fn next_buf(&mut self, key: &mut Vec<u8>, value: &mut Vec<u8>) -> Result<bool, Error> {
542 if !self.has_seeked {
543 self.has_seeked = true;
544 self.tree.cursor_start(&mut self.tree_cursor, b"")?;
545 }
546
547 if self
548 .tree
549 .cursor_next(&mut self.tree_cursor, key, value, &slice_range(&self.range))?
550 {
551 Ok(true)
552 } else {
553 Ok(false)
554 }
555 }
556}
557
558impl<'a> Iterator for Cursor<'a> {
559 type Item = KeyValuePair;
560
561 fn next(&mut self) -> Option<Self::Item> {
562 let mut key_buffer = Vec::new();
563 let mut value_buffer = Vec::new();
564
565 match self.next_buf(&mut key_buffer, &mut value_buffer) {
566 Ok(success) => {
567 if success {
568 Some((key_buffer, value_buffer))
569 } else {
570 None
571 }
572 }
573 Err(error) => {
574 self.error = Some(error);
575 None
576 }
577 }
578 }
579}
580
581impl<'a> Debug for Cursor<'a> {
582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
583 write!(f, "DatabaseCursor")
584 }
585}
586
587#[derive(Debug)]
588pub struct Metadata<'a> {
590 tree_metadata: Option<&'a TreeMetadata>,
591}
592
593impl<'a> Metadata<'a> {
594 pub fn key_value_count(&self) -> u64 {
596 if let Some(meta) = self.tree_metadata {
597 meta.key_value_count
598 } else {
599 0
600 }
601 }
602}
603
604struct FlushTracker {
605 base_threshold: usize,
606 modification_count: usize,
607 last_flush_time: Instant,
608}
609
610impl FlushTracker {
611 pub fn new(base_threshold: usize) -> Self {
612 Self {
613 base_threshold,
614 modification_count: 0,
615 last_flush_time: Instant::now(),
616 }
617 }
618
619 pub fn increment_modification(&mut self) {
620 self.modification_count += 1;
621 }
622
623 pub fn check_should_flush(&mut self) -> bool {
624 let level_long = self.modification_count >= self.base_threshold
625 && self.last_flush_time.elapsed() >= Duration::from_secs(300);
626 let level_short = self.modification_count >= self.base_threshold * 2
627 && self.last_flush_time.elapsed() >= Duration::from_secs(60);
628
629 if level_long || level_short {
630 self.modification_count = 0;
631 self.last_flush_time = Instant::now();
632 true
633 } else {
634 false
635 }
636 }
637}
638
639pub fn debug_print_page(path: &Path) -> Result<(), Error> {
641 let mut format = Format::default();
642 let mut vfs = ReadOnlyVfs::new(Box::new(OsVfs::new(path.parent().unwrap())));
643
644 let filename = path.file_name().unwrap().to_str().unwrap();
645
646 if filename.contains("meta") {
647 let payload: PageMetadata<TreeMetadata> = format.read_file(&mut vfs, filename)?;
648
649 eprintln!("{:?}", payload);
650 } else {
651 let payload: Page<Node> = format.read_file(&mut vfs, filename)?;
652
653 eprintln!("{:?}", payload);
654 }
655
656 Ok(())
657}
658
659fn concrete_range<K, R>(range: R) -> (Bound<Vec<u8>>, Bound<Vec<u8>>)
660where
661 K: AsRef<[u8]>,
662 R: RangeBounds<K>,
663{
664 let start_bound: Bound<Vec<u8>> = match range.start_bound() {
665 Bound::Included(bound) => Bound::Included(bound.as_ref().to_vec()),
666 Bound::Excluded(bound) => Bound::Excluded(bound.as_ref().to_vec()),
667 Bound::Unbounded => Bound::Unbounded,
668 };
669 let end_bound: Bound<Vec<u8>> = match range.end_bound() {
670 Bound::Included(bound) => Bound::Included(bound.as_ref().to_vec()),
671 Bound::Excluded(bound) => Bound::Excluded(bound.as_ref().to_vec()),
672 Bound::Unbounded => Bound::Unbounded,
673 };
674 (start_bound, end_bound)
675}
676
677fn slice_range<'a>(
678 range: &'a (Bound<Vec<u8>>, Bound<Vec<u8>>),
679) -> (Bound<&'a [u8]>, Bound<&'a [u8]>) {
680 let start_bound: Bound<&'a [u8]> = match range.start_bound() {
681 Bound::Included(bound) => Bound::Included(bound),
682 Bound::Excluded(bound) => Bound::Excluded(bound),
683 Bound::Unbounded => Bound::Unbounded,
684 };
685 let end_bound: Bound<&'a [u8]> = match range.end_bound() {
686 Bound::Included(bound) => Bound::Included(bound),
687 Bound::Excluded(bound) => Bound::Excluded(bound),
688 Bound::Unbounded => Bound::Unbounded,
689 };
690 (start_bound, end_bound)
691}