1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
//! `rsdb` is a flash-sympathetic persistent lock-free B+ tree, pagecache, and log. //! //! # Tree //! //! ``` //! let t = rsdb::Config::default().tree(); //! //! t.set(b"yo!".to_vec(), b"v1".to_vec()); //! //! t.get(b"yo!"); //! //! t.cas(b"yo!".to_vec(), Some(b"v1".to_vec()), Some(b"v2".to_vec())).unwrap(); //! //! let mut iter = t.scan(b"a non-present key before yo!"); //! //! assert_eq!(iter.next(), Some((b"yo!".to_vec(), b"v2".to_vec()))); //! assert_eq!(iter.next(), None); //! //! t.del(b"yo!"); //! ``` //! //! # Working with the `PageCache` //! //! ``` //! extern crate rsdb; //! //! use rsdb::Materializer; //! //! pub struct TestMaterializer; //! //! impl Materializer for TestMaterializer { //! type MaterializedPage = String; //! type PartialPage = String; //! type Recovery = (); //! //! fn materialize(&self, frags: &[String]) -> String { //! self.consolidate(frags).pop().unwrap() //! } //! //! fn consolidate(&self, frags: &[String]) -> Vec<String> { //! let mut consolidated = String::new(); //! for frag in frags.into_iter() { //! consolidated.push_str(&*frag); //! } //! //! vec![consolidated] //! } //! //! fn recover(&self, _: &String) -> Option<()> { //! None //! } //! } //! //! fn main() { //! let path = "test_pagecache_doc.log"; //! //! let conf = rsdb::Config::default().path(Some(path.to_owned())); //! //! let pc = rsdb::PageCache::new(TestMaterializer, conf.clone()); //! //! let (id, key) = pc.allocate(); //! //! let key = pc.prepend(id, key, "a".to_owned()).unwrap(); //! //! let key = pc.prepend(id, key, "b".to_owned()).unwrap(); //! //! let _key = pc.prepend(id, key, "c".to_owned()).unwrap(); //! //! let (consolidated, _) = pc.get(id).unwrap(); //! //! assert_eq!(consolidated, "abc".to_owned()); //! //! std::fs::remove_file(path).unwrap(); //! } //! ``` //! //! # Working with `Log` //! //! ``` //! use rsdb::Log; //! //! let log = rsdb::Config::default().log(); //! let first_offset = log.write(b"1".to_vec()); //! log.write(b"22".to_vec()); //! log.write(b"333".to_vec()); //! //! // stick an abort in the middle, which should not be returned //! let res = log.reserve(b"never_gonna_hit_disk".to_vec()); //! res.abort(); //! //! log.write(b"4444".to_vec()); //! let last_offset = log.write(b"55555".to_vec()); //! log.make_stable(last_offset); //! let mut iter = log.iter_from(first_offset); //! assert_eq!(iter.next().unwrap().1, b"1".to_vec()); //! assert_eq!(iter.next().unwrap().1, b"22".to_vec()); //! assert_eq!(iter.next().unwrap().1, b"333".to_vec()); //! assert_eq!(iter.next().unwrap().1, b"4444".to_vec()); //! assert_eq!(iter.next().unwrap().1, b"55555".to_vec()); //! assert_eq!(iter.next(), None); //! ``` #![deny(missing_docs)] #![cfg_attr(test, deny(warnings))] #![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature="clippy", plugin(clippy))] #![cfg_attr(feature="clippy", allow(inline_always))] extern crate libc; extern crate rayon; extern crate crossbeam; extern crate serde; #[macro_use] extern crate serde_derive; extern crate bincode; extern crate rand; #[macro_use] extern crate log as logger; extern crate tempfile; extern crate zstd; extern crate time; extern crate glob; /// atomic lock-free tree pub use tree::Tree; /// lock-free pagecache pub use page::{Materializer, PageCache}; /// lock-free log-structured storage pub use log::{HEADER_LEN, LockFreeLog, Log, LogRead}; /// lock-free stack use stack::Stack; /// lock-free radix tree pub use radix::Radix; /// general-purpose configuration pub use config::Config; use crc16::crc16_arr; macro_rules! rep_no_copy { ($e:expr; $n:expr) => { { let mut v = Vec::with_capacity($n); for _ in 0..$n { v.push($e); } v } }; } #[cfg(test)] fn test_fail() -> bool { use rand::Rng; rand::thread_rng().gen::<bool>(); // TODO when the time is right, return the gen'd bool false } #[cfg(not(test))] #[inline(always)] fn test_fail() -> bool { false } mod tree; mod bound; mod log; mod crc16; mod crc64; mod stack; mod page; mod radix; mod config; mod thread_cache; use bound::Bound; use page::CacheEntry; use stack::{StackIter, node_from_frag_vec}; use thread_cache::ThreadCache; type LogID = u64; // LogID == file position to simplify file mapping type PageID = usize; type Key = Vec<u8>; type KeyRef<'a> = &'a [u8]; type Value = Vec<u8>; #[inline(always)] fn raw<T>(t: T) -> *const T { Box::into_raw(Box::new(t)) as *const T } // get thread identifier #[inline(always)] fn tn() -> String { use std::thread; thread::current().name().unwrap_or("unknown").to_owned() }