diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index 6f2e075..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -tab_spaces = 2 \ No newline at end of file diff --git a/src/database.rs b/src/database.rs index 5096160..2f82db9 100644 --- a/src/database.rs +++ b/src/database.rs @@ -5,91 +5,91 @@ use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Debug)] pub struct DatabaseEntry { - key: Vec, - value: Vec, - timestamp: u128, + key: Vec, + value: Vec, + timestamp: u128, } impl DatabaseEntry { - pub fn key(&self) -> &[u8] { - &self.key - } + pub fn key(&self) -> &[u8] { + &self.key + } - pub fn value(&self) -> &[u8] { - &self.value - } + pub fn value(&self) -> &[u8] { + &self.value + } - pub fn timestamp(&self) -> u128 { - self.timestamp - } + pub fn timestamp(&self) -> u128 { + self.timestamp + } } pub struct Database { - dir: PathBuf, - mem_table: MemTable, - wal: WAL, + dir: PathBuf, + mem_table: MemTable, + wal: WAL, } impl Database { - pub fn new(dir: &str) -> Database { - let dir = PathBuf::from(dir); + pub fn new(dir: &str) -> Database { + let dir = PathBuf::from(dir); - let (wal, mem_table) = WAL::load_from_dir(&dir).unwrap(); + let (wal, mem_table) = WAL::load_from_dir(&dir).unwrap(); - Database { - dir: dir, - mem_table, - wal, + Database { + dir: dir, + mem_table, + wal, + } } - } - - pub fn get(&self, key: &[u8]) -> Option { - if let Some(mem_entry) = self.mem_table.get(key) { - return Some(DatabaseEntry { - key: mem_entry.key.clone(), - value: mem_entry.value.as_ref().unwrap().clone(), - timestamp: mem_entry.timestamp, - }); - } - - None - } - pub fn set(&mut self, key: &[u8], value: &[u8]) -> Result { - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); + pub fn get(&self, key: &[u8]) -> Option { + if let Some(mem_entry) = self.mem_table.get(key) { + return Some(DatabaseEntry { + key: mem_entry.key.clone(), + value: mem_entry.value.as_ref().unwrap().clone(), + timestamp: mem_entry.timestamp, + }); + } - let wal_res = self.wal.set(key, value, timestamp); - if wal_res.is_err() { - return Err(0); - } - if self.wal.flush().is_err() { - return Err(0); + None } - self.mem_table.set(key, value, timestamp); + pub fn set(&mut self, key: &[u8], value: &[u8]) -> Result { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); - Ok(1) - } + let wal_res = self.wal.set(key, value, timestamp); + if wal_res.is_err() { + return Err(0); + } + if self.wal.flush().is_err() { + return Err(0); + } - pub fn delete(&mut self, key: &[u8]) -> Result { - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); + self.mem_table.set(key, value, timestamp); - let wal_res = self.wal.delete(key, timestamp); - if wal_res.is_err() { - return Err(0); - } - if self.wal.flush().is_err() { - return Err(0); + Ok(1) } - self.mem_table.delete(key, timestamp); + pub fn delete(&mut self, key: &[u8]) -> Result { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + + let wal_res = self.wal.delete(key, timestamp); + if wal_res.is_err() { + return Err(0); + } + if self.wal.flush().is_err() { + return Err(0); + } - Ok(1) - } + self.mem_table.delete(key, timestamp); + + Ok(1) + } } diff --git a/src/mem_table.rs b/src/mem_table.rs index e381536..662ff32 100644 --- a/src/mem_table.rs +++ b/src/mem_table.rs @@ -1,9 +1,9 @@ /// MemTable entry. pub struct MemTableEntry { - pub key: Vec, - pub value: Option>, - pub timestamp: u128, - pub deleted: bool, + pub key: Vec, + pub value: Option>, + pub timestamp: u128, + pub deleted: bool, } /// MemTable holds a sorted list of the latest written records. @@ -15,274 +15,273 @@ pub struct MemTableEntry { /// /// Entries are stored in a Vector instead of a HashMap to support Scans. pub struct MemTable { - entries: Vec, - size: usize, + entries: Vec, + size: usize, } impl MemTable { - /// Creates a new empty MemTable - pub fn new() -> MemTable { - MemTable { - entries: Vec::new(), - size: 0, + /// Creates a new empty MemTable + pub fn new() -> MemTable { + MemTable { + entries: Vec::new(), + size: 0, + } } - } - - /// Sets a Key-Value pair in the MemTable. - pub fn set(&mut self, key: &[u8], value: &[u8], timestamp: u128) { - let entry = MemTableEntry { - key: key.to_owned(), - value: Some(value.to_owned()), - timestamp, - deleted: false, - }; - - match self.get_index(key) { - Ok(idx) => { - // If a Value existed on the deleted record, then add the difference of the new and old Value to the MemTable's size. - if let Some(v) = self.entries[idx].value.as_ref() { - if value.len() < v.len() { - self.size -= v.len() - value.len(); - } else { - self.size += value.len() - v.len(); - } - } else { - self.size += value.len(); + + /// Sets a Key-Value pair in the MemTable. + pub fn set(&mut self, key: &[u8], value: &[u8], timestamp: u128) { + let entry = MemTableEntry { + key: key.to_owned(), + value: Some(value.to_owned()), + timestamp, + deleted: false, + }; + + match self.get_index(key) { + Ok(idx) => { + // If a Value existed on the deleted record, then add the difference of the new and old Value to the MemTable's size. + if let Some(v) = self.entries[idx].value.as_ref() { + if value.len() < v.len() { + self.size -= v.len() - value.len(); + } else { + self.size += value.len() - v.len(); + } + } else { + self.size += value.len(); + } + self.entries[idx] = entry; + } + Err(idx) => { + self.size += key.len() + value.len() + 16 + 1; // Increase the size of the MemTable by the Key size, Value size, Timestamp size (16 bytes), Tombstone size (1 byte). + self.entries.insert(idx, entry) + } + } + } + + /// Deletes a Key-Value pair in the MemTable. + /// + /// This is achieved using tombstones. + pub fn delete(&mut self, key: &[u8], timestamp: u128) { + let entry = MemTableEntry { + key: key.to_owned(), + value: None, + timestamp: timestamp, + deleted: true, + }; + match self.get_index(key) { + Ok(idx) => { + // If a Value existed on the deleted record, then subtract the size of the Value from the MemTable. + if let Some(value) = self.entries[idx].value.as_ref() { + self.size -= value.len(); + } + self.entries[idx] = entry; + } + Err(idx) => { + self.size += key.len() + 16 + 1; // Increase the size of the MemTable by the Key size, Timestamp size (16 bytes), Tombstone size (1 byte). + self.entries.insert(idx, entry); + } } - self.entries[idx] = entry; - } - Err(idx) => { - self.size += key.len() + value.len() + 16 + 1; // Increase the size of the MemTable by the Key size, Value size, Timestamp size (16 bytes), Tombstone size (1 byte). - self.entries.insert(idx, entry) - } } - } - - /// Deletes a Key-Value pair in the MemTable. - /// - /// This is achieved using tombstones. - pub fn delete(&mut self, key: &[u8], timestamp: u128) { - let entry = MemTableEntry { - key: key.to_owned(), - value: None, - timestamp: timestamp, - deleted: true, - }; - match self.get_index(key) { - Ok(idx) => { - // If a Value existed on the deleted record, then subtract the size of the Value from the MemTable. - if let Some(value) = self.entries[idx].value.as_ref() { - self.size -= value.len(); + + /// Gets a Key-Value pair from the MemTable.alloc + /// + /// If no record with the same key exists in the MemTable, return None. + pub fn get(&self, key: &[u8]) -> Option<&MemTableEntry> { + if let Ok(idx) = self.get_index(key) { + return Some(&self.entries[idx]); } - self.entries[idx] = entry; - } - Err(idx) => { - self.size += key.len() + 16 + 1; // Increase the size of the MemTable by the Key size, Timestamp size (16 bytes), Tombstone size (1 byte). - self.entries.insert(idx, entry); - } + None + } + + /// Performs Binary Search to find a record in the MemTable. + /// + /// If the record is found `[Result::Ok]` is returned, with the index of record. If the record is not + /// found then `[Result::Err]` is returned, with the index to insert the record at. + fn get_index(&self, key: &[u8]) -> Result { + self.entries + .binary_search_by_key(&key, |e| e.key.as_slice()) + } + + /// Gets the number of records in the MemTable. + pub fn len(&self) -> usize { + self.entries.len() } - } - - /// Gets a Key-Value pair from the MemTable.alloc - /// - /// If no record with the same key exists in the MemTable, return None. - pub fn get(&self, key: &[u8]) -> Option<&MemTableEntry> { - if let Ok(idx) = self.get_index(key) { - return Some(&self.entries[idx]); + + /// Gets all of the records from the MemTable. + pub fn entries(&self) -> &[MemTableEntry] { + &self.entries + } + + /// Gets the total size of the records in the MemTable + pub fn size(&self) -> usize { + self.size } - None - } - - /// Performs Binary Search to find a record in the MemTable. - /// - /// If the record is found `[Result::Ok]` is returned, with the index of record. If the record is not - /// found then `[Result::Err]` is returned, with the index to insert the record at. - fn get_index(&self, key: &[u8]) -> Result { - self - .entries - .binary_search_by_key(&key, |e| e.key.as_slice()) - } - - /// Gets the number of records in the MemTable. - pub fn len(&self) -> usize { - self.entries.len() - } - - /// Gets all of the records from the MemTable. - pub fn entries(&self) -> &[MemTableEntry] { - &self.entries - } - - /// Gets the total size of the records in the MemTable - pub fn size(&self) -> usize { - self.size - } } #[cfg(test)] mod tests { - use crate::mem_table::MemTable; - - #[test] - fn test_mem_table_put_start() { - let mut table = MemTable::new(); - table.set(b"Lime", b"Lime Smoothie", 0); // 17 + 16 + 1 - table.set(b"Orange", b"Orange Smoothie", 10); // 21 + 16 + 1 - - table.set(b"Apple", b"Apple Smoothie", 20); // 19 + 16 + 1 - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); - assert_eq!(table.entries[0].timestamp, 20); - assert_eq!(table.entries[0].deleted, false); - assert_eq!(table.entries[1].key, b"Lime"); - assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); - assert_eq!(table.entries[1].timestamp, 0); - assert_eq!(table.entries[1].deleted, false); - assert_eq!(table.entries[2].key, b"Orange"); - assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); - assert_eq!(table.entries[2].timestamp, 10); - assert_eq!(table.entries[2].deleted, false); - - assert_eq!(table.size, 108); - } - - #[test] - fn test_mem_table_put_middle() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - table.set(b"Orange", b"Orange Smoothie", 10); - - table.set(b"Lime", b"Lime Smoothie", 20); - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); - assert_eq!(table.entries[0].timestamp, 0); - assert_eq!(table.entries[0].deleted, false); - assert_eq!(table.entries[1].key, b"Lime"); - assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); - assert_eq!(table.entries[1].timestamp, 20); - assert_eq!(table.entries[1].deleted, false); - assert_eq!(table.entries[2].key, b"Orange"); - assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); - assert_eq!(table.entries[2].timestamp, 10); - assert_eq!(table.entries[2].deleted, false); - - assert_eq!(table.size, 108); - } - - #[test] - fn test_mem_table_put_end() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - table.set(b"Lime", b"Lime Smoothie", 10); - - table.set(b"Orange", b"Orange Smoothie", 20); - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); - assert_eq!(table.entries[0].timestamp, 0); - assert_eq!(table.entries[0].deleted, false); - assert_eq!(table.entries[1].key, b"Lime"); - assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); - assert_eq!(table.entries[1].timestamp, 10); - assert_eq!(table.entries[1].deleted, false); - assert_eq!(table.entries[2].key, b"Orange"); - assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); - assert_eq!(table.entries[2].timestamp, 20); - assert_eq!(table.entries[2].deleted, false); - - assert_eq!(table.size, 108); - } - - #[test] - fn test_mem_table_put_overwrite() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - table.set(b"Lime", b"Lime Smoothie", 10); - table.set(b"Orange", b"Orange Smoothie", 20); - - table.set(b"Lime", b"A sour fruit", 30); - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); - assert_eq!(table.entries[0].timestamp, 0); - assert_eq!(table.entries[0].deleted, false); - assert_eq!(table.entries[1].key, b"Lime"); - assert_eq!(table.entries[1].value.as_ref().unwrap(), b"A sour fruit"); - assert_eq!(table.entries[1].timestamp, 30); - assert_eq!(table.entries[1].deleted, false); - assert_eq!(table.entries[2].key, b"Orange"); - assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); - assert_eq!(table.entries[2].timestamp, 20); - assert_eq!(table.entries[2].deleted, false); - - assert_eq!(table.size, 107); - } - - #[test] - fn test_mem_table_get_exists() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - table.set(b"Lime", b"Lime Smoothie", 10); - table.set(b"Orange", b"Orange Smoothie", 20); - - let entry = table.get(b"Orange").unwrap(); - - assert_eq!(entry.key, b"Orange"); - assert_eq!(entry.value.as_ref().unwrap(), b"Orange Smoothie"); - assert_eq!(entry.timestamp, 20); - } - - #[test] - fn test_mem_table_get_not_exists() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - table.set(b"Lime", b"Lime Smoothie", 0); - table.set(b"Orange", b"Orange Smoothie", 0); - - let res = table.get(b"Potato"); - assert_eq!(res.is_some(), false); - } - - #[test] - fn test_mem_table_delete_exists() { - let mut table = MemTable::new(); - table.set(b"Apple", b"Apple Smoothie", 0); - - table.delete(b"Apple", 10); - - let res = table.get(b"Apple").unwrap(); - assert_eq!(res.key, b"Apple"); - assert_eq!(res.value, None); - assert_eq!(res.timestamp, 10); - assert_eq!(res.deleted, true); - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value, None); - assert_eq!(table.entries[0].timestamp, 10); - assert_eq!(table.entries[0].deleted, true); - - assert_eq!(table.size, 22); - } - - #[test] - fn test_mem_table_delete_empty() { - let mut table = MemTable::new(); - - table.delete(b"Apple", 10); - - let res = table.get(b"Apple").unwrap(); - assert_eq!(res.key, b"Apple"); - assert_eq!(res.value, None); - assert_eq!(res.timestamp, 10); - assert_eq!(res.deleted, true); - - assert_eq!(table.entries[0].key, b"Apple"); - assert_eq!(table.entries[0].value, None); - assert_eq!(table.entries[0].timestamp, 10); - assert_eq!(table.entries[0].deleted, true); - - assert_eq!(table.size, 22); - } + use crate::mem_table::MemTable; + + #[test] + fn test_mem_table_put_start() { + let mut table = MemTable::new(); + table.set(b"Lime", b"Lime Smoothie", 0); // 17 + 16 + 1 + table.set(b"Orange", b"Orange Smoothie", 10); // 21 + 16 + 1 + + table.set(b"Apple", b"Apple Smoothie", 20); // 19 + 16 + 1 + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); + assert_eq!(table.entries[0].timestamp, 20); + assert_eq!(table.entries[0].deleted, false); + assert_eq!(table.entries[1].key, b"Lime"); + assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); + assert_eq!(table.entries[1].timestamp, 0); + assert_eq!(table.entries[1].deleted, false); + assert_eq!(table.entries[2].key, b"Orange"); + assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); + assert_eq!(table.entries[2].timestamp, 10); + assert_eq!(table.entries[2].deleted, false); + + assert_eq!(table.size, 108); + } + + #[test] + fn test_mem_table_put_middle() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + table.set(b"Orange", b"Orange Smoothie", 10); + + table.set(b"Lime", b"Lime Smoothie", 20); + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); + assert_eq!(table.entries[0].timestamp, 0); + assert_eq!(table.entries[0].deleted, false); + assert_eq!(table.entries[1].key, b"Lime"); + assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); + assert_eq!(table.entries[1].timestamp, 20); + assert_eq!(table.entries[1].deleted, false); + assert_eq!(table.entries[2].key, b"Orange"); + assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); + assert_eq!(table.entries[2].timestamp, 10); + assert_eq!(table.entries[2].deleted, false); + + assert_eq!(table.size, 108); + } + + #[test] + fn test_mem_table_put_end() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + table.set(b"Lime", b"Lime Smoothie", 10); + + table.set(b"Orange", b"Orange Smoothie", 20); + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); + assert_eq!(table.entries[0].timestamp, 0); + assert_eq!(table.entries[0].deleted, false); + assert_eq!(table.entries[1].key, b"Lime"); + assert_eq!(table.entries[1].value.as_ref().unwrap(), b"Lime Smoothie"); + assert_eq!(table.entries[1].timestamp, 10); + assert_eq!(table.entries[1].deleted, false); + assert_eq!(table.entries[2].key, b"Orange"); + assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); + assert_eq!(table.entries[2].timestamp, 20); + assert_eq!(table.entries[2].deleted, false); + + assert_eq!(table.size, 108); + } + + #[test] + fn test_mem_table_put_overwrite() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + table.set(b"Lime", b"Lime Smoothie", 10); + table.set(b"Orange", b"Orange Smoothie", 20); + + table.set(b"Lime", b"A sour fruit", 30); + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value.as_ref().unwrap(), b"Apple Smoothie"); + assert_eq!(table.entries[0].timestamp, 0); + assert_eq!(table.entries[0].deleted, false); + assert_eq!(table.entries[1].key, b"Lime"); + assert_eq!(table.entries[1].value.as_ref().unwrap(), b"A sour fruit"); + assert_eq!(table.entries[1].timestamp, 30); + assert_eq!(table.entries[1].deleted, false); + assert_eq!(table.entries[2].key, b"Orange"); + assert_eq!(table.entries[2].value.as_ref().unwrap(), b"Orange Smoothie"); + assert_eq!(table.entries[2].timestamp, 20); + assert_eq!(table.entries[2].deleted, false); + + assert_eq!(table.size, 107); + } + + #[test] + fn test_mem_table_get_exists() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + table.set(b"Lime", b"Lime Smoothie", 10); + table.set(b"Orange", b"Orange Smoothie", 20); + + let entry = table.get(b"Orange").unwrap(); + + assert_eq!(entry.key, b"Orange"); + assert_eq!(entry.value.as_ref().unwrap(), b"Orange Smoothie"); + assert_eq!(entry.timestamp, 20); + } + + #[test] + fn test_mem_table_get_not_exists() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + table.set(b"Lime", b"Lime Smoothie", 0); + table.set(b"Orange", b"Orange Smoothie", 0); + + let res = table.get(b"Potato"); + assert_eq!(res.is_some(), false); + } + + #[test] + fn test_mem_table_delete_exists() { + let mut table = MemTable::new(); + table.set(b"Apple", b"Apple Smoothie", 0); + + table.delete(b"Apple", 10); + + let res = table.get(b"Apple").unwrap(); + assert_eq!(res.key, b"Apple"); + assert_eq!(res.value, None); + assert_eq!(res.timestamp, 10); + assert_eq!(res.deleted, true); + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value, None); + assert_eq!(table.entries[0].timestamp, 10); + assert_eq!(table.entries[0].deleted, true); + + assert_eq!(table.size, 22); + } + + #[test] + fn test_mem_table_delete_empty() { + let mut table = MemTable::new(); + + table.delete(b"Apple", 10); + + let res = table.get(b"Apple").unwrap(); + assert_eq!(res.key, b"Apple"); + assert_eq!(res.value, None); + assert_eq!(res.timestamp, 10); + assert_eq!(res.deleted, true); + + assert_eq!(table.entries[0].key, b"Apple"); + assert_eq!(table.entries[0].value, None); + assert_eq!(table.entries[0].timestamp, 10); + assert_eq!(table.entries[0].deleted, true); + + assert_eq!(table.size, 22); + } } diff --git a/src/utils.rs b/src/utils.rs index 6885c03..338c5b4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,13 +3,13 @@ use std::path::{Path, PathBuf}; /// Gets the set of files with an extension for a given directory. pub fn files_with_ext(dir: &Path, ext: &str) -> Vec { - let mut files = Vec::new(); - for file in read_dir(dir).unwrap() { - let path = file.unwrap().path(); - if path.extension().unwrap() == ext { - files.push(path); + let mut files = Vec::new(); + for file in read_dir(dir).unwrap() { + let path = file.unwrap().path(); + if path.extension().unwrap() == ext { + files.push(path); + } } - } - files + files } diff --git a/src/wal.rs b/src/wal.rs index 2469066..54d533d 100644 --- a/src/wal.rs +++ b/src/wal.rs @@ -13,351 +13,351 @@ use std::time::{SystemTime, UNIX_EPOCH}; /// An append-only file that holds the operations performed on the MemTable. /// The WAL is intended for recovery of the MemTable when the server is shutdown. pub struct WAL { - path: PathBuf, - file: BufWriter, + path: PathBuf, + file: BufWriter, } impl WAL { - /// Creates a new WAL in a given directory. - pub fn new(dir: &Path) -> io::Result { - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); - - let path = Path::new(dir).join(timestamp.to_string() + ".wal"); - let file = OpenOptions::new().append(true).create(true).open(&path)?; - let file = BufWriter::new(file); - - Ok(WAL { path, file }) - } - - /// Creates a WAL from an existing file path. - pub fn from_path(path: &Path) -> io::Result { - let file = OpenOptions::new().append(true).create(true).open(path)?; - let file = BufWriter::new(file); - - Ok(WAL { - path: path.to_owned(), - file, - }) - } - - /// Loads the WAL(s) within a directory, returning a new WAL and the recovered MemTable. - /// - /// If multiple WALs exist in a directory, they are merged by file date. - pub fn load_from_dir(dir: &Path) -> io::Result<(WAL, MemTable)> { - let mut wal_files = files_with_ext(dir, "wal"); - wal_files.sort(); - - let mut new_mem_table = MemTable::new(); - let mut new_wal = WAL::new(dir)?; - for wal_file in wal_files.iter() { - if let Ok(wal) = WAL::from_path(wal_file) { - for entry in wal.into_iter() { - if entry.deleted { - new_mem_table.delete(entry.key.as_slice(), entry.timestamp); - new_wal.delete(entry.key.as_slice(), entry.timestamp)?; - } else { - new_mem_table.set( - entry.key.as_slice(), - entry.value.as_ref().unwrap().as_slice(), - entry.timestamp, - ); - new_wal.set( - entry.key.as_slice(), - entry.value.unwrap().as_slice(), - entry.timestamp, - )?; - } + /// Creates a new WAL in a given directory. + pub fn new(dir: &Path) -> io::Result { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + + let path = Path::new(dir).join(timestamp.to_string() + ".wal"); + let file = OpenOptions::new().append(true).create(true).open(&path)?; + let file = BufWriter::new(file); + + Ok(WAL { path, file }) + } + + /// Creates a WAL from an existing file path. + pub fn from_path(path: &Path) -> io::Result { + let file = OpenOptions::new().append(true).create(true).open(path)?; + let file = BufWriter::new(file); + + Ok(WAL { + path: path.to_owned(), + file, + }) + } + + /// Loads the WAL(s) within a directory, returning a new WAL and the recovered MemTable. + /// + /// If multiple WALs exist in a directory, they are merged by file date. + pub fn load_from_dir(dir: &Path) -> io::Result<(WAL, MemTable)> { + let mut wal_files = files_with_ext(dir, "wal"); + wal_files.sort(); + + let mut new_mem_table = MemTable::new(); + let mut new_wal = WAL::new(dir)?; + for wal_file in wal_files.iter() { + if let Ok(wal) = WAL::from_path(wal_file) { + for entry in wal.into_iter() { + if entry.deleted { + new_mem_table.delete(entry.key.as_slice(), entry.timestamp); + new_wal.delete(entry.key.as_slice(), entry.timestamp)?; + } else { + new_mem_table.set( + entry.key.as_slice(), + entry.value.as_ref().unwrap().as_slice(), + entry.timestamp, + ); + new_wal.set( + entry.key.as_slice(), + entry.value.unwrap().as_slice(), + entry.timestamp, + )?; + } + } + } } - } + new_wal.flush().unwrap(); + wal_files.into_iter().for_each(|f| remove_file(f).unwrap()); + + Ok((new_wal, new_mem_table)) + } + + /// Sets a Key-Value pair and the operation is appended to the WAL. + pub fn set(&mut self, key: &[u8], value: &[u8], timestamp: u128) -> io::Result<()> { + self.file.write_all(&key.len().to_le_bytes())?; + self.file.write_all(&(false as u8).to_le_bytes())?; + self.file.write_all(&value.len().to_le_bytes())?; + self.file.write_all(key)?; + self.file.write_all(value)?; + self.file.write_all(×tamp.to_le_bytes())?; + + Ok(()) + } + + /// Deletes a Key-Value pair and the operation is appended to the WAL. + /// + /// This is achieved using tombstones. + pub fn delete(&mut self, key: &[u8], timestamp: u128) -> io::Result<()> { + self.file.write_all(&key.len().to_le_bytes())?; + self.file.write_all(&(true as u8).to_le_bytes())?; + self.file.write_all(key)?; + self.file.write_all(×tamp.to_le_bytes())?; + + Ok(()) + } + + /// Flushes the WAL to disk. + /// + /// This is useful for applying bulk operations and flushing the final result to + /// disk. Waiting to flush after the bulk operations have been performed will improve + /// write performance substantially. + pub fn flush(&mut self) -> io::Result<()> { + self.file.flush() } - new_wal.flush().unwrap(); - wal_files.into_iter().for_each(|f| remove_file(f).unwrap()); - - Ok((new_wal, new_mem_table)) - } - - /// Sets a Key-Value pair and the operation is appended to the WAL. - pub fn set(&mut self, key: &[u8], value: &[u8], timestamp: u128) -> io::Result<()> { - self.file.write_all(&key.len().to_le_bytes())?; - self.file.write_all(&(false as u8).to_le_bytes())?; - self.file.write_all(&value.len().to_le_bytes())?; - self.file.write_all(key)?; - self.file.write_all(value)?; - self.file.write_all(×tamp.to_le_bytes())?; - - Ok(()) - } - - /// Deletes a Key-Value pair and the operation is appended to the WAL. - /// - /// This is achieved using tombstones. - pub fn delete(&mut self, key: &[u8], timestamp: u128) -> io::Result<()> { - self.file.write_all(&key.len().to_le_bytes())?; - self.file.write_all(&(true as u8).to_le_bytes())?; - self.file.write_all(key)?; - self.file.write_all(×tamp.to_le_bytes())?; - - Ok(()) - } - - /// Flushes the WAL to disk. - /// - /// This is useful for applying bulk operations and flushing the final result to - /// disk. Waiting to flush after the bulk operations have been performed will improve - /// write performance substantially. - pub fn flush(&mut self) -> io::Result<()> { - self.file.flush() - } } impl IntoIterator for WAL { - type IntoIter = WALIterator; - type Item = WALEntry; + type IntoIter = WALIterator; + type Item = WALEntry; - /// Converts a WAL into a `WALIterator` to iterate over the entries. - fn into_iter(self) -> WALIterator { - WALIterator::new(self.path).unwrap() - } + /// Converts a WAL into a `WALIterator` to iterate over the entries. + fn into_iter(self) -> WALIterator { + WALIterator::new(self.path).unwrap() + } } #[cfg(test)] mod tests { - use crate::wal::WAL; - use std::fs::{metadata, File, OpenOptions}; - use std::io::prelude::*; - use std::io::BufReader; - use std::time::{SystemTime, UNIX_EPOCH}; - use tempfile::tempdir; - - fn check_entry( - reader: &mut BufReader, - key: &[u8], - value: Option<&[u8]>, - timestamp: u128, - deleted: bool, - ) { - let mut len_buffer = [0; 8]; - reader.read_exact(&mut len_buffer).unwrap(); - let file_key_len = usize::from_le_bytes(len_buffer); - assert_eq!(file_key_len, key.len()); - - let mut bool_buffer = [0; 1]; - reader.read_exact(&mut bool_buffer).unwrap(); - let file_deleted = bool_buffer[0] != 0; - assert_eq!(file_deleted, deleted); - - if deleted { - let mut file_key = vec![0; file_key_len]; - reader.read_exact(&mut file_key).unwrap(); - assert_eq!(file_key, key); - } else { - reader.read_exact(&mut len_buffer).unwrap(); - let file_value_len = usize::from_le_bytes(len_buffer); - assert_eq!(file_value_len, value.unwrap().len()); - let mut file_key = vec![0; file_key_len]; - reader.read_exact(&mut file_key).unwrap(); - assert_eq!(file_key, key); - let mut file_value = vec![0; file_value_len]; - reader.read_exact(&mut file_value).unwrap(); - assert_eq!(file_value, value.unwrap()); + use crate::wal::WAL; + use std::fs::{metadata, File, OpenOptions}; + use std::io::prelude::*; + use std::io::BufReader; + use std::time::{SystemTime, UNIX_EPOCH}; + use tempfile::tempdir; + + fn check_entry( + reader: &mut BufReader, + key: &[u8], + value: Option<&[u8]>, + timestamp: u128, + deleted: bool, + ) { + let mut len_buffer = [0; 8]; + reader.read_exact(&mut len_buffer).unwrap(); + let file_key_len = usize::from_le_bytes(len_buffer); + assert_eq!(file_key_len, key.len()); + + let mut bool_buffer = [0; 1]; + reader.read_exact(&mut bool_buffer).unwrap(); + let file_deleted = bool_buffer[0] != 0; + assert_eq!(file_deleted, deleted); + + if deleted { + let mut file_key = vec![0; file_key_len]; + reader.read_exact(&mut file_key).unwrap(); + assert_eq!(file_key, key); + } else { + reader.read_exact(&mut len_buffer).unwrap(); + let file_value_len = usize::from_le_bytes(len_buffer); + assert_eq!(file_value_len, value.unwrap().len()); + let mut file_key = vec![0; file_key_len]; + reader.read_exact(&mut file_key).unwrap(); + assert_eq!(file_key, key); + let mut file_value = vec![0; file_value_len]; + reader.read_exact(&mut file_value).unwrap(); + assert_eq!(file_value, value.unwrap()); + } + + let mut timestamp_buffer = [0; 16]; + reader.read_exact(&mut timestamp_buffer).unwrap(); + let file_timestamp = u128::from_le_bytes(timestamp_buffer); + assert_eq!(file_timestamp, timestamp); } - let mut timestamp_buffer = [0; 16]; - reader.read_exact(&mut timestamp_buffer).unwrap(); - let file_timestamp = u128::from_le_bytes(timestamp_buffer); - assert_eq!(file_timestamp, timestamp); - } - - #[test] - fn test_write_one() { - let dir = tempdir().unwrap(); - - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); - - let mut wal = WAL::new(dir.path()).unwrap(); - wal.set(b"Lime", b"Lime Smoothie", timestamp).unwrap(); - wal.flush().unwrap(); - - let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); - let mut reader = BufReader::new(file); - - check_entry( - &mut reader, - b"Lime", - Some(b"Lime Smoothie"), - timestamp, - false, - ); - } - - #[test] - fn test_write_many() { - let dir = tempdir().unwrap(); - - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); - - let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ - (b"Apple", Some(b"Apple Smoothie")), - (b"Lime", Some(b"Lime Smoothie")), - (b"Orange", Some(b"Orange Smoothie")), - ]; - - let mut wal = WAL::new(dir.path()).unwrap(); - - for e in entries.iter() { - wal.set(e.0, e.1.unwrap(), timestamp).unwrap(); + #[test] + fn test_write_one() { + let dir = tempdir().unwrap(); + + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + + let mut wal = WAL::new(dir.path()).unwrap(); + wal.set(b"Lime", b"Lime Smoothie", timestamp).unwrap(); + wal.flush().unwrap(); + + let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); + let mut reader = BufReader::new(file); + + check_entry( + &mut reader, + b"Lime", + Some(b"Lime Smoothie"), + timestamp, + false, + ); } - wal.flush().unwrap(); - let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); - let mut reader = BufReader::new(file); + #[test] + fn test_write_many() { + let dir = tempdir().unwrap(); - for e in entries.iter() { - check_entry(&mut reader, e.0, e.1, timestamp, false); - } - } + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); - #[test] - fn test_write_delete() { - let dir = tempdir().unwrap(); + let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ + (b"Apple", Some(b"Apple Smoothie")), + (b"Lime", Some(b"Lime Smoothie")), + (b"Orange", Some(b"Orange Smoothie")), + ]; - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); + let mut wal = WAL::new(dir.path()).unwrap(); - let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ - (b"Apple", Some(b"Apple Smoothie")), - (b"Lime", Some(b"Lime Smoothie")), - (b"Orange", Some(b"Orange Smoothie")), - ]; + for e in entries.iter() { + wal.set(e.0, e.1.unwrap(), timestamp).unwrap(); + } + wal.flush().unwrap(); - let mut wal = WAL::new(dir.path()).unwrap(); + let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); + let mut reader = BufReader::new(file); - for e in entries.iter() { - wal.set(e.0, e.1.unwrap(), timestamp).unwrap(); - } - for e in entries.iter() { - wal.delete(e.0, timestamp).unwrap(); + for e in entries.iter() { + check_entry(&mut reader, e.0, e.1, timestamp, false); + } } - wal.flush().unwrap(); + #[test] + fn test_write_delete() { + let dir = tempdir().unwrap(); - let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); - let mut reader = BufReader::new(file); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); - for e in entries.iter() { - check_entry(&mut reader, e.0, e.1, timestamp, false); - } - for e in entries.iter() { - check_entry(&mut reader, e.0, None, timestamp, true); - } - } + let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ + (b"Apple", Some(b"Apple Smoothie")), + (b"Lime", Some(b"Lime Smoothie")), + (b"Orange", Some(b"Orange Smoothie")), + ]; - #[test] - fn test_read_wal_none() { - let dir = tempdir().unwrap(); + let mut wal = WAL::new(dir.path()).unwrap(); - let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); - assert_eq!(new_mem_table.len(), 0); + for e in entries.iter() { + wal.set(e.0, e.1.unwrap(), timestamp).unwrap(); + } + for e in entries.iter() { + wal.delete(e.0, timestamp).unwrap(); + } - let m = metadata(new_wal.path).unwrap(); - assert_eq!(m.len(), 0); - } + wal.flush().unwrap(); - #[test] - fn test_read_wal_one() { - let dir = tempdir().unwrap(); + let file = OpenOptions::new().read(true).open(&wal.path).unwrap(); + let mut reader = BufReader::new(file); - let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ - (b"Apple", Some(b"Apple Smoothie")), - (b"Lime", Some(b"Lime Smoothie")), - (b"Orange", Some(b"Orange Smoothie")), - ]; + for e in entries.iter() { + check_entry(&mut reader, e.0, e.1, timestamp, false); + } + for e in entries.iter() { + check_entry(&mut reader, e.0, None, timestamp, true); + } + } - let mut wal = WAL::new(dir.path()).unwrap(); + #[test] + fn test_read_wal_none() { + let dir = tempdir().unwrap(); - for (i, e) in entries.iter().enumerate() { - wal.set(e.0, e.1.unwrap(), i as u128).unwrap(); + let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); + assert_eq!(new_mem_table.len(), 0); + + let m = metadata(new_wal.path).unwrap(); + assert_eq!(m.len(), 0); } - wal.flush().unwrap(); - let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); + #[test] + fn test_read_wal_one() { + let dir = tempdir().unwrap(); - let file = OpenOptions::new().read(true).open(&new_wal.path).unwrap(); - let mut reader = BufReader::new(file); + let entries: Vec<(&[u8], Option<&[u8]>)> = vec![ + (b"Apple", Some(b"Apple Smoothie")), + (b"Lime", Some(b"Lime Smoothie")), + (b"Orange", Some(b"Orange Smoothie")), + ]; - for (i, e) in entries.iter().enumerate() { - check_entry(&mut reader, e.0, e.1, i as u128, false); + let mut wal = WAL::new(dir.path()).unwrap(); - let mem_e = new_mem_table.get(e.0).unwrap(); - assert_eq!(mem_e.key, e.0); - assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); - assert_eq!(mem_e.timestamp, i as u128); - } - } - - #[test] - fn test_read_wal_multiple() { - let dir = tempdir().unwrap(); - - let entries_1: Vec<(&[u8], Option<&[u8]>)> = vec![ - (b"Apple", Some(b"Apple Smoothie")), - (b"Lime", Some(b"Lime Smoothie")), - (b"Orange", Some(b"Orange Smoothie")), - ]; - let mut wal_1 = WAL::new(dir.path()).unwrap(); - for (i, e) in entries_1.iter().enumerate() { - wal_1.set(e.0, e.1.unwrap(), i as u128).unwrap(); - } - wal_1.flush().unwrap(); - - let entries_2: Vec<(&[u8], Option<&[u8]>)> = vec![ - (b"Strawberry", Some(b"Strawberry Smoothie")), - (b"Blueberry", Some(b"Blueberry Smoothie")), - (b"Orange", Some(b"Orange Milkshake")), - ]; - let mut wal_2 = WAL::new(dir.path()).unwrap(); - for (i, e) in entries_2.iter().enumerate() { - wal_2.set(e.0, e.1.unwrap(), (i + 3) as u128).unwrap(); - } - wal_2.flush().unwrap(); - - let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); - - let file = OpenOptions::new().read(true).open(&new_wal.path).unwrap(); - let mut reader = BufReader::new(file); - - for (i, e) in entries_1.iter().enumerate() { - check_entry(&mut reader, e.0, e.1, i as u128, false); - - let mem_e = new_mem_table.get(e.0).unwrap(); - if i != 2 { - assert_eq!(mem_e.key, e.0); - assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); - assert_eq!(mem_e.timestamp, i as u128); - } else { - assert_eq!(mem_e.key, e.0); - assert_ne!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); - assert_ne!(mem_e.timestamp, i as u128); - } + for (i, e) in entries.iter().enumerate() { + wal.set(e.0, e.1.unwrap(), i as u128).unwrap(); + } + wal.flush().unwrap(); + + let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); + + let file = OpenOptions::new().read(true).open(&new_wal.path).unwrap(); + let mut reader = BufReader::new(file); + + for (i, e) in entries.iter().enumerate() { + check_entry(&mut reader, e.0, e.1, i as u128, false); + + let mem_e = new_mem_table.get(e.0).unwrap(); + assert_eq!(mem_e.key, e.0); + assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); + assert_eq!(mem_e.timestamp, i as u128); + } } - for (i, e) in entries_2.iter().enumerate() { - check_entry(&mut reader, e.0, e.1, (i + 3) as u128, false); - let mem_e = new_mem_table.get(e.0).unwrap(); - assert_eq!(mem_e.key, e.0); - assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); - assert_eq!(mem_e.timestamp, (i + 3) as u128); + #[test] + fn test_read_wal_multiple() { + let dir = tempdir().unwrap(); + + let entries_1: Vec<(&[u8], Option<&[u8]>)> = vec![ + (b"Apple", Some(b"Apple Smoothie")), + (b"Lime", Some(b"Lime Smoothie")), + (b"Orange", Some(b"Orange Smoothie")), + ]; + let mut wal_1 = WAL::new(dir.path()).unwrap(); + for (i, e) in entries_1.iter().enumerate() { + wal_1.set(e.0, e.1.unwrap(), i as u128).unwrap(); + } + wal_1.flush().unwrap(); + + let entries_2: Vec<(&[u8], Option<&[u8]>)> = vec![ + (b"Strawberry", Some(b"Strawberry Smoothie")), + (b"Blueberry", Some(b"Blueberry Smoothie")), + (b"Orange", Some(b"Orange Milkshake")), + ]; + let mut wal_2 = WAL::new(dir.path()).unwrap(); + for (i, e) in entries_2.iter().enumerate() { + wal_2.set(e.0, e.1.unwrap(), (i + 3) as u128).unwrap(); + } + wal_2.flush().unwrap(); + + let (new_wal, new_mem_table) = WAL::load_from_dir(dir.path()).unwrap(); + + let file = OpenOptions::new().read(true).open(&new_wal.path).unwrap(); + let mut reader = BufReader::new(file); + + for (i, e) in entries_1.iter().enumerate() { + check_entry(&mut reader, e.0, e.1, i as u128, false); + + let mem_e = new_mem_table.get(e.0).unwrap(); + if i != 2 { + assert_eq!(mem_e.key, e.0); + assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); + assert_eq!(mem_e.timestamp, i as u128); + } else { + assert_eq!(mem_e.key, e.0); + assert_ne!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); + assert_ne!(mem_e.timestamp, i as u128); + } + } + for (i, e) in entries_2.iter().enumerate() { + check_entry(&mut reader, e.0, e.1, (i + 3) as u128, false); + + let mem_e = new_mem_table.get(e.0).unwrap(); + assert_eq!(mem_e.key, e.0); + assert_eq!(mem_e.value.as_ref().unwrap().as_slice(), e.1.unwrap()); + assert_eq!(mem_e.timestamp, (i + 3) as u128); + } } - } } diff --git a/src/wal_iterator.rs b/src/wal_iterator.rs index c7e381d..7649862 100644 --- a/src/wal_iterator.rs +++ b/src/wal_iterator.rs @@ -4,75 +4,75 @@ use std::io::{self, BufReader}; use std::path::PathBuf; pub struct WALEntry { - pub key: Vec, - pub value: Option>, - pub timestamp: u128, - pub deleted: bool, + pub key: Vec, + pub value: Option>, + pub timestamp: u128, + pub deleted: bool, } /// WAL iterator to iterate over the items in a WAL file. pub struct WALIterator { - reader: BufReader, + reader: BufReader, } impl WALIterator { - /// Creates a new WALIterator from a path to a WAL file. - pub fn new(path: PathBuf) -> io::Result { - let file = OpenOptions::new().read(true).open(path)?; - let reader = BufReader::new(file); - Ok(WALIterator { reader }) - } + /// Creates a new WALIterator from a path to a WAL file. + pub fn new(path: PathBuf) -> io::Result { + let file = OpenOptions::new().read(true).open(path)?; + let reader = BufReader::new(file); + Ok(WALIterator { reader }) + } } impl Iterator for WALIterator { - type Item = WALEntry; + type Item = WALEntry; - /// Gets the next entry in the WAL file. - fn next(&mut self) -> Option { - let mut len_buffer = [0; 8]; - if self.reader.read_exact(&mut len_buffer).is_err() { - return None; - } - let key_len = usize::from_le_bytes(len_buffer); + /// Gets the next entry in the WAL file. + fn next(&mut self) -> Option { + let mut len_buffer = [0; 8]; + if self.reader.read_exact(&mut len_buffer).is_err() { + return None; + } + let key_len = usize::from_le_bytes(len_buffer); - let mut bool_buffer = [0; 1]; - if self.reader.read_exact(&mut bool_buffer).is_err() { - return None; - } - let deleted = bool_buffer[0] != 0; + let mut bool_buffer = [0; 1]; + if self.reader.read_exact(&mut bool_buffer).is_err() { + return None; + } + let deleted = bool_buffer[0] != 0; - let mut key = vec![0; key_len]; - let mut value = None; - if deleted { - if self.reader.read_exact(&mut key).is_err() { - return None; - } - } else { - if self.reader.read_exact(&mut len_buffer).is_err() { - return None; - } - let value_len = usize::from_le_bytes(len_buffer); - if self.reader.read_exact(&mut key).is_err() { - return None; - } - let mut value_buf = vec![0; value_len]; - if self.reader.read_exact(&mut value_buf).is_err() { - return None; - } - value = Some(value_buf); - } + let mut key = vec![0; key_len]; + let mut value = None; + if deleted { + if self.reader.read_exact(&mut key).is_err() { + return None; + } + } else { + if self.reader.read_exact(&mut len_buffer).is_err() { + return None; + } + let value_len = usize::from_le_bytes(len_buffer); + if self.reader.read_exact(&mut key).is_err() { + return None; + } + let mut value_buf = vec![0; value_len]; + if self.reader.read_exact(&mut value_buf).is_err() { + return None; + } + value = Some(value_buf); + } - let mut timestamp_buffer = [0; 16]; - if self.reader.read_exact(&mut timestamp_buffer).is_err() { - return None; - } - let timestamp = u128::from_le_bytes(timestamp_buffer); + let mut timestamp_buffer = [0; 16]; + if self.reader.read_exact(&mut timestamp_buffer).is_err() { + return None; + } + let timestamp = u128::from_le_bytes(timestamp_buffer); - Some(WALEntry { - key, - value, - timestamp, - deleted, - }) - } + Some(WALEntry { + key, + value, + timestamp, + deleted, + }) + } }