neutrondb/store/
new.rs

1use fides::BloomFilter;
2use crate::types::into_bytes::IntoBytes;
3use crate::types::try_from_bytes::TryFromBytes;
4use crate::{Store, Table, KeyObject, ValueObject, TABLE_HEADER_SIZE};
5use std::collections::{HashMap, BTreeMap, HashSet};
6use std::error::Error;
7use std::ffi::OsStr;
8use std::fs::File;
9use std::{fs, mem};
10use std::fs::OpenOptions;
11use std::io::{self, Seek, SeekFrom};
12use std::io::Read;
13use std::path::Path;
14use std::str;
15
16
17impl<K,V> Store<K,V>
18        
19        where
20            V: IntoBytes + TryFromBytes + std::fmt::Debug
21    
22    {
23
24    pub fn new(directory: &str) -> Result<Store<K,V>, Box<dyn Error>> {
25        
26        if !Path::new(directory).is_dir() {
27            fs::create_dir_all(directory)?;
28        }
29
30        let graves_location = format!("{}/graves.bin", &directory);
31
32        let graves_path = Path::new(&graves_location);
33
34        let mut graves: HashSet<[u8; 32]> = HashSet::new();
35
36        if graves_path.is_file() {
37
38            let mut graves_file = File::open(graves_path)?;
39
40            let mut graves_buffer = [0u8; 32];
41
42            loop {
43
44                match graves_file.read_exact(&mut graves_buffer) {
45
46                    Ok(_) => {graves.insert(graves_buffer);},
47
48                    Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
49
50                    Err(_) => Err("Graves Read Error!")?
51
52                }
53
54            }
55            
56        };
57
58        let mut tables = Vec::new();
59
60        let tables_location = format!("{}/levels", &directory);
61
62        let tables_path = Path::new(&tables_location);
63
64        if tables_path.is_dir() {
65
66            for level in fs::read_dir(tables_path)? {
67
68                let level = level?;
69                
70                let level_path = level.path();
71
72                let level_name = level.file_name().into_string().unwrap();
73
74                let level = u8::from_str_radix(&level_name, 10)?;
75
76                if level_path.is_dir() {
77                    
78                    for table in fs::read_dir(level_path)? {
79
80                        let table = table?;
81
82                        let table_path = table.path();
83
84                        let table_file_metadata = fs::metadata(&table_path)?;
85
86                        let file_size = table_file_metadata.len();
87
88                        let name = table_path
89                            .file_stem()
90                            .and_then(OsStr::to_str)
91                            .ok_or_else(|| Box::<dyn Error>::from("Invalid filename"))?.to_string();
92
93                        if table_path.is_file() {
94
95                            let mut table_file = File::open(&table_path)?;
96
97                            table_file.seek(SeekFrom::Start(1))?;
98
99                            let mut key_count_bytes = [0; 8];
100                            table_file.read_exact(&mut key_count_bytes)?;
101                            let key_count = u64::from_le_bytes(key_count_bytes);
102
103                            let mut index_position_bytes = [0; 8];
104                            table_file.read_exact(&mut index_position_bytes)?;
105                            let index_position = u64::from_le_bytes(index_position_bytes);
106
107                            let mut key_data_position_bytes = [0; 8];
108                            table_file.read_exact(&mut key_data_position_bytes)?;
109                            let keys_position = u64::from_le_bytes(key_data_position_bytes);
110
111                            let bloom_filter_size = index_position - TABLE_HEADER_SIZE;
112                            let mut bloom_filter_bytes = vec![0; bloom_filter_size.try_into()?];
113                            table_file.read_exact(&mut bloom_filter_bytes)?;
114                            let bloom_filter = BloomFilter::try_from(&bloom_filter_bytes[..])?;
115                            
116                            let table = Table {
117                                bloom_filter,
118                                key_count,
119                                level,
120                                name,
121                                file_size,
122                                index_position,
123                                keys_position,
124                            };
125
126                            tables.push(table)
127
128                        }
129                    }
130
131                    tables.sort_by(|a, b| b.name.cmp(&a.name));
132
133                }
134            }
135        }
136
137        let mut keys: BTreeMap<[u8;32], KeyObject> = BTreeMap::new();
138
139        let mut values: HashMap<[u8;32], ValueObject<V>> = HashMap::new();
140
141        let mut cache_size = 0;
142        
143        let logs_location = format!("{}/logs.bin", &directory);
144
145        let logs_path = Path::new(&logs_location);
146
147        let mut logs_file = OpenOptions::new()
148            .read(true)
149            .write(true)
150            .create(true)
151            .open(logs_path)?;
152        
153        let mut log_type_byte = [0u8;1];
154
155        if logs_file.metadata()?.len() != 0 {
156            
157            loop {
158
159                match logs_file.read_exact(&mut log_type_byte) {
160
161                    Ok(_) => {
162
163                        match log_type_byte {
164
165                            [1] => {
166                                
167                                let mut key_hash = [0u8;32];
168                                logs_file.read_exact(&mut key_hash)?;
169
170                                let mut value_hash = [0u8;32];
171                                logs_file.read_exact(&mut value_hash)?;
172
173                                let mut key_size_bytes = [0u8;8];
174                                logs_file.read_exact(&mut key_size_bytes)?;
175
176                                let key_size_u64 = u64::from_le_bytes(key_size_bytes);
177                                let key_size = key_size_u64 as usize;
178
179                                let key_log_position = logs_file.seek(SeekFrom::Current(0))?;
180                                
181                                let key_object = KeyObject {
182                                    value_hash,
183                                    key_size,
184                                    key_log_position,
185                                };
186
187
188                                // move the pointer past key bytes
189                                logs_file.seek(SeekFrom::Current(key_size_u64 as i64))?;
190
191                                keys.insert(key_hash, key_object);
192
193                            },
194
195                            [2] => {
196
197                                let value_log_position = logs_file.seek(SeekFrom::Current(0))?;
198
199                                let mut value_hash = [0u8;32];
200                                logs_file.read_exact(&mut value_hash)?;
201
202                                let mut value_size_buffer = [0u8;8];
203                                logs_file.read_exact(&mut value_size_buffer)?;
204                                let value_size = u64::from_le_bytes(value_size_buffer) as usize;
205                                
206                                let mut value_buffer = vec![0u8;value_size];
207                                logs_file.read_exact(&mut value_buffer)?;
208
209                                let value = V::try_from_bytes(value_buffer)?;
210
211                                cache_size += mem::size_of_val(&value);
212
213                                let value_object = ValueObject {
214                                    value,
215                                    value_size: 32 + 8 +value_size,
216                                    value_log_position,
217                                };
218
219                                values.insert(value_hash, value_object);
220
221                            },
222
223                            [3] => {
224
225                                let mut grave_hash = [0u8;32];
226                                logs_file.read_exact(&mut grave_hash)?;
227                                graves.insert(grave_hash);
228
229                            },
230
231                            _ => break
232
233                        }
234
235                    },
236
237                    // Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
238
239                    // Err(_) => Err("Logs Read Error!")?
240
241                    Err(_) => break
242
243                }
244
245            }
246        }
247
248        let store = Store {
249            keys,
250            directory: directory.to_string(),
251            graves,
252            tables,
253            logs_file,
254            values,
255            cache_size: cache_size.try_into()?,
256            cache_limit: 1_000_000,
257            phantom: std::marker::PhantomData,
258        };
259
260        Ok(store)
261
262    }
263    
264}