diff --git a/Cargo.toml b/Cargo.toml index 7ab820f2..27c5a9ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,14 @@ -[workspace] - -members = [ - "crates/*" -] - [package] name = "agdb" version = "0.1.0" edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "crates/db_error", version = "<=0.1.0" } -agdb_map_common = { path = "crates/map_common", version = "<=0.1.0" } -agdb_multi_map = { path = "crates/multi_map", version = "<=0.1.0" } -agdb_serialize = { path = "crates/serialize", version = "<=0.1.0" } -agdb_storage = { path = "crates/storage", version = "<=0.1.0" } -agdb_storage_map = { path = "crates/storage_map", version = "<=0.1.0" } -agdb_storage_vec = { path = "crates/storage_vec", version = "<=0.1.0" } -agdb_utilities = { path = "crates/utilities", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "crates/test_utilities", version = "<=0.1.0" } +license = "Apache-2.0" +homepage = "https://github.com/agnesoft/agdb" +repository = "https://github.com/agnesoft/agdb" +documentation = "https://github.com/agnesoft/agdb" +readme = "README.md" +description = "Agnesoft Graph Database" + +[lib] +name = "agdb" +path = "src/agdb/lib.rs" diff --git a/crates/bit_set/Cargo.toml b/crates/bit_set/Cargo.toml deleted file mode 100644 index d303ffc4..00000000 --- a/crates/bit_set/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "agdb_bit_set" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/crates/bit_set/src/bit_set.rs b/crates/bit_set/src/bit_set.rs deleted file mode 100644 index 6df0b8f8..00000000 --- a/crates/bit_set/src/bit_set.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[derive(Default)] -pub struct BitSet { - data: Vec, -} - -impl BitSet { - pub fn clear(&mut self) { - self.data.clear(); - } - - pub fn insert(&mut self, value: u64) { - let byte_index = value as usize / 8; - let bit_index = value as usize % 8; - - if self.data.len() <= byte_index { - self.data.resize(byte_index + 1, 0); - } - - self.data[byte_index] |= 1 << bit_index; - } - - pub fn new() -> Self { - Self { data: vec![] } - } - - pub fn remove(&mut self, value: u64) { - let byte_index = value as usize / 8; - - if byte_index < self.data.len() { - let bit_index = value as usize % 8; - self.data[byte_index] ^= 1 << bit_index; - } - } - - pub fn value(&self, value: u64) -> bool { - let byte_index = value as usize / 8; - - if byte_index < self.data.len() { - let bit_index = value as usize % 8; - - return self.data[byte_index] & (1 << bit_index) != 0; - } - - false - } -} diff --git a/crates/bit_set/src/lib.rs b/crates/bit_set/src/lib.rs deleted file mode 100644 index 4cb452f6..00000000 --- a/crates/bit_set/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod bit_set; - -pub use bit_set::BitSet; diff --git a/crates/bit_set/tests/bit_set_test.rs b/crates/bit_set/tests/bit_set_test.rs deleted file mode 100644 index 962adf3d..00000000 --- a/crates/bit_set/tests/bit_set_test.rs +++ /dev/null @@ -1,105 +0,0 @@ -use agdb_bit_set::BitSet; - -#[test] -fn clear() { - let mut bitset = BitSet::new(); - - bitset.insert(10_u64); - bitset.clear(); - - assert!(!bitset.value(10_u64)); -} - -#[test] -fn derived_from_default() { - let _bitset = BitSet::default(); -} - -#[test] -fn insert() { - let mut bitset = BitSet::new(); - - assert!(!bitset.value(10_u64)); - - bitset.insert(10_u64); - - assert!(bitset.value(10_u64)); -} - -#[test] -fn insert_multiple() { - let mut bitset = BitSet::new(); - - assert!(!bitset.value(10_u64)); - assert!(!bitset.value(11_u64)); - assert!(!bitset.value(2_u64)); - - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); - - assert!(bitset.value(10_u64)); - assert!(bitset.value(11_u64)); - assert!(bitset.value(2_u64)); -} - -#[test] -fn remove() { - let mut bitset = BitSet::new(); - - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); - - bitset.remove(11_u64); - - assert!(bitset.value(10_u64)); - assert!(!bitset.value(11_u64)); - assert!(bitset.value(2_u64)); -} - -#[test] -fn remove_unset() { - let mut bitset = BitSet::new(); - - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); - - bitset.remove(9_u64); - - assert!(bitset.value(10_u64)); - assert!(bitset.value(11_u64)); - assert!(bitset.value(2_u64)); -} - -#[test] -fn remove_beyond_length() { - let mut bitset = BitSet::new(); - - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); - - bitset.remove(150_u64); - - assert!(bitset.value(10_u64)); - assert!(bitset.value(11_u64)); - assert!(bitset.value(2_u64)); -} - -#[test] -fn value_missing() { - let mut bitset = BitSet::new(); - - bitset.insert(5_u64); - - assert!(!bitset.value(2_u64)); -} - -#[test] -fn value_beyond_length() { - let bitset = BitSet::new(); - - assert!(!bitset.value(10_u64)); -} diff --git a/crates/commands/Cargo.toml b/crates/commands/Cargo.toml deleted file mode 100644 index 2311abc6..00000000 --- a/crates/commands/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "agdb_commands" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } diff --git a/crates/commands/src/command.rs b/crates/commands/src/command.rs deleted file mode 100644 index e587b5c2..00000000 --- a/crates/commands/src/command.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::Commands; - -pub(crate) struct Command { - pub(crate) commands: Commands, - pub(crate) executed: bool, -} - -impl Command { - pub(crate) fn new(commands: Commands) -> Self { - Self { - commands, - executed: false, - } - } -} diff --git a/crates/commands/src/command_stack.rs b/crates/commands/src/command_stack.rs deleted file mode 100644 index 14cfbd78..00000000 --- a/crates/commands/src/command_stack.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::command::Command; -use crate::command_executor::CommandExecutor; -use crate::commands::Commands; -use agdb_db_error::DbError; - -#[derive(Default)] -pub struct CommandStack { - stack: Vec, -} - -impl CommandStack { - pub fn new() -> Self { - Self::default() - } - - pub fn clear(&mut self) { - self.stack.clear(); - } - - pub fn push(&mut self, command: Commands) { - self.stack.push(Command::new(command)); - } - - pub fn redo( - &mut self, - executor: &mut Executor, - ) -> Result<(), DbError> { - for command in self.stack.iter_mut() { - if !command.executed { - executor.redo(&mut command.commands)?; - command.executed = true; - } - } - - Ok(()) - } - - pub fn undo( - &mut self, - executor: &mut Executor, - ) -> Result<(), DbError> { - for command in self.stack.iter_mut().rev() { - if command.executed { - executor.undo(&mut command.commands)?; - command.executed = false; - } - } - - Ok(()) - } -} diff --git a/crates/commands/src/commands.rs b/crates/commands/src/commands.rs deleted file mode 100644 index e87796d6..00000000 --- a/crates/commands/src/commands.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub enum Commands { - InsertEdge, - InsertNode, -} diff --git a/crates/commands/src/lib.rs b/crates/commands/src/lib.rs deleted file mode 100644 index ac7ddb28..00000000 --- a/crates/commands/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod command; -mod command_executor; -mod command_stack; -mod commands; - -pub use command_executor::CommandExecutor; -pub use command_stack::CommandStack; -pub use commands::Commands; diff --git a/crates/commands/tests/command_stack.rs b/crates/commands/tests/command_stack.rs deleted file mode 100644 index 7b0b9017..00000000 --- a/crates/commands/tests/command_stack.rs +++ /dev/null @@ -1,106 +0,0 @@ -use agdb_commands::CommandExecutor; -use agdb_commands::CommandStack; -use agdb_commands::Commands; -use agdb_db_error::DbError; - -#[derive(Default)] -struct Executor { - redo_result: Vec>, - undo_result: Vec>, -} - -impl CommandExecutor for Executor { - fn redo(&mut self, _command: &mut Commands) -> Result<(), DbError> { - self.redo_result.pop().unwrap() - } - - fn undo(&mut self, _command: &mut Commands) -> Result<(), DbError> { - self.undo_result.pop().unwrap() - } -} - -#[test] -fn clear() { - let mut stack = CommandStack::new(); - stack.clear(); - stack.push(Commands::InsertNode); - stack.clear(); - - let mut executor = Executor { - redo_result: vec![], - undo_result: vec![], - }; - - assert_eq!(stack.redo(&mut executor), Ok(())); -} - -#[test] -fn derived_from_default() { - let mut _stack = CommandStack::default(); -} - -#[test] -fn empty() { - let mut stack = CommandStack::new(); - - let mut executor = Executor { - redo_result: vec![], - undo_result: vec![], - }; - - assert_eq!(stack.redo(&mut executor), Ok(())); - assert_eq!(stack.undo(&mut executor), Ok(())); -} - -#[test] -fn redo() { - let mut stack = CommandStack::new(); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertEdge); - - let mut executor = Executor { - redo_result: vec![Ok(()), Ok(()), Ok(())], - undo_result: vec![], - }; - - assert_eq!(stack.redo(&mut executor), Ok(())); - assert_eq!(stack.redo(&mut executor), Ok(())); - - assert!(executor.redo_result.is_empty()); -} - -#[test] -fn undo() { - let mut stack = CommandStack::new(); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertEdge); - - let mut executor = Executor { - redo_result: vec![Err(DbError::from("error")), Ok(()), Ok(())], - undo_result: vec![Ok(()), Ok(())], - }; - - assert_eq!(stack.redo(&mut executor), Err(DbError::from("error"))); - assert_eq!(stack.undo(&mut executor), Ok(())); - assert_eq!(stack.undo(&mut executor), Ok(())); - - assert!(executor.redo_result.is_empty()); - assert!(executor.undo_result.is_empty()); -} - -#[test] -fn undo_not_redone() { - let mut stack = CommandStack::new(); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertNode); - stack.push(Commands::InsertEdge); - - let mut executor = Executor { - redo_result: vec![], - undo_result: vec![], - }; - - assert_eq!(stack.undo(&mut executor), Ok(())); -} diff --git a/crates/db_error/Cargo.toml b/crates/db_error/Cargo.toml deleted file mode 100644 index 23cba47f..00000000 --- a/crates/db_error/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "agdb_db_error" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/crates/db_error/src/db_error.rs b/crates/db_error/src/db_error.rs deleted file mode 100644 index ce2ea303..00000000 --- a/crates/db_error/src/db_error.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::panic::Location; - -#[derive(Debug)] -pub struct DbError { - pub description: String, - pub cause: Option>, - pub source_location: Location<'static>, -} - -impl DbError { - pub fn caused_by(mut self, error: Self) -> Self { - self.cause = Some(Box::new(error)); - - self - } -} diff --git a/crates/db_error/src/display.rs b/crates/db_error/src/display.rs deleted file mode 100644 index dd41cc5b..00000000 --- a/crates/db_error/src/display.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::DbError; -use std::fmt::Display; -use std::fmt::Formatter; -use std::fmt::Result as FMTResult; - -impl Display for DbError { - fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { - let location = self.source_location.to_string().replace('\\', "/"); - write!(f, "{} (at {})", self.description, location) - } -} diff --git a/crates/db_error/src/error.rs b/crates/db_error/src/error.rs deleted file mode 100644 index cecef72d..00000000 --- a/crates/db_error/src/error.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::DbError; -use std::error::Error; - -impl Error for DbError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - if let Some(cause) = &self.cause { - return Some(cause); - } - - None - } -} diff --git a/crates/db_error/src/from.rs b/crates/db_error/src/from.rs deleted file mode 100644 index 63291c39..00000000 --- a/crates/db_error/src/from.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::DbError; -use std::io::Error; -use std::panic::Location; -use std::string::FromUtf8Error; - -impl From for DbError { - #[track_caller] - fn from(error: Error) -> Self { - DbError::from(error.to_string()) - } -} - -impl From for DbError { - #[track_caller] - fn from(error: FromUtf8Error) -> Self { - DbError::from(error.to_string()) - } -} - -impl From<&str> for DbError { - #[track_caller] - fn from(description: &str) -> Self { - DbError::from(description.to_string()) - } -} - -impl From for DbError { - #[track_caller] - fn from(description: String) -> Self { - DbError { - description, - cause: None, - source_location: *Location::caller(), - } - } -} diff --git a/crates/db_error/src/lib.rs b/crates/db_error/src/lib.rs deleted file mode 100644 index 09b03385..00000000 --- a/crates/db_error/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod db_error; -mod display; -mod error; -mod from; -mod partial_eq; - -pub use self::db_error::DbError; diff --git a/crates/db_error/src/partial_eq.rs b/crates/db_error/src/partial_eq.rs deleted file mode 100644 index 2956bef4..00000000 --- a/crates/db_error/src/partial_eq.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::DbError; - -impl PartialEq for DbError { - fn eq(&self, other: &Self) -> bool { - self.description == other.description && self.cause == other.cause - } -} diff --git a/crates/db_error/tests/db_error_test.rs b/crates/db_error/tests/db_error_test.rs deleted file mode 100644 index bed31158..00000000 --- a/crates/db_error/tests/db_error_test.rs +++ /dev/null @@ -1,84 +0,0 @@ -use agdb_db_error::DbError; -use std::error::Error; -use std::io::Error as IOError; -use std::io::ErrorKind; - -#[test] -fn caused_by() { - let error = DbError::from("file not found"); - let new_error = DbError::from("open error").caused_by(error); - - assert_eq!( - new_error.cause, - Some(Box::new(DbError::from("file not found"))) - ); -} - -#[test] -fn derived_from_debug() { - let error = DbError::from("error"); - - format!("{:?}", error); -} - -#[test] -fn derived_from_display() { - let file = file!(); - let col__ = column!(); - let line = line!(); - let error = DbError::from("file not found"); - - assert_eq!( - error.to_string(), - format!( - "file not found (at {}:{}:{})", - file.replace('\\', "/"), - line + 1, - col__ - ) - ); -} - -#[test] -fn derived_from_partial_eq() { - let left = DbError::from(IOError::from(ErrorKind::NotFound)); - let right = DbError::from(IOError::from(ErrorKind::NotFound)); - - assert_eq!(left, right); -} - -#[test] -fn derived_from_error() { - let file = file!(); - let col__ = column!(); - let line = line!(); - let error = DbError::from("file not found"); - let new_error = DbError::from("open error").caused_by(error); - - assert_eq!( - new_error.source().unwrap().to_string(), - format!( - "file not found (at {}:{}:{})", - file.replace('\\', "/"), - line + 1, - col__ - ) - ); -} - -#[test] -fn from_io_error() { - let _error = DbError::from(IOError::from(ErrorKind::NotFound)); -} - -#[test] -fn from_utf8_error() { - let _error = DbError::from(String::from_utf8(vec![0xdf, 0xff]).unwrap_err()); -} - -#[test] -fn source_none() { - let error = DbError::from("file not found"); - - assert!(error.source().is_none()); -} diff --git a/crates/dictionary/Cargo.toml b/crates/dictionary/Cargo.toml deleted file mode 100644 index 0f0c0f6f..00000000 --- a/crates/dictionary/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "agdb_dictionary" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_map_common = { path = "../map_common", version = "<=0.1.0" } -agdb_multi_map = { path = "../multi_map", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage = { path = "../storage", version = "<=0.1.0" } -agdb_storage_vec = { path = "../storage_vec", version = "<=0.1.0" } -agdb_utilities = { path = "../utilities", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } \ No newline at end of file diff --git a/crates/dictionary/src/dictionary.rs b/crates/dictionary/src/dictionary.rs deleted file mode 100644 index fc3e98db..00000000 --- a/crates/dictionary/src/dictionary.rs +++ /dev/null @@ -1,19 +0,0 @@ -use super::dictionary_data_memory::DictionaryDataMemory; -use super::dictionary_impl::DictionaryImpl; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; -use std::marker::PhantomData; - -pub type Dictionary = DictionaryImpl>; - -impl Dictionary -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, -{ - pub fn new() -> Dictionary { - Dictionary { - data: DictionaryDataMemory::::default(), - phantom_data: PhantomData, - } - } -} diff --git a/crates/dictionary/src/dictionary_data_memory_default.rs b/crates/dictionary/src/dictionary_data_memory_default.rs deleted file mode 100644 index 37d8fbef..00000000 --- a/crates/dictionary/src/dictionary_data_memory_default.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::dictionary_data_memory::DictionaryDataMemory; -use crate::dictionary_index::DictionaryIndex; -use crate::dictionary_value::DictionaryValue; -use agdb_multi_map::MultiMap; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; - -impl Default for DictionaryDataMemory -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, -{ - fn default() -> Self { - Self { - index: MultiMap::::new(), - values: vec![DictionaryValue::::default()], - } - } -} diff --git a/crates/dictionary/src/dictionary_default.rs b/crates/dictionary/src/dictionary_default.rs deleted file mode 100644 index 69daef6e..00000000 --- a/crates/dictionary/src/dictionary_default.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::Dictionary; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; - -impl Default for Dictionary -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, -{ - fn default() -> Self { - Self::new() - } -} diff --git a/crates/dictionary/src/dictionary_index.rs b/crates/dictionary/src/dictionary_index.rs deleted file mode 100644 index eedad3d3..00000000 --- a/crates/dictionary/src/dictionary_index.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct DictionaryIndex { - pub(crate) index: i64, -} - -impl DictionaryIndex { - pub fn as_u64(&self) -> u64 { - self.value() as u64 - } - - pub fn as_usize(&self) -> usize { - self.value() as usize - } - - pub fn is_valid(&self) -> bool { - 0 < self.index - } - - pub fn value(&self) -> i64 { - self.index - } -} diff --git a/crates/dictionary/src/dictionary_index_from.rs b/crates/dictionary/src/dictionary_index_from.rs deleted file mode 100644 index 37e2555c..00000000 --- a/crates/dictionary/src/dictionary_index_from.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::dictionary_index::DictionaryIndex; - -impl From for DictionaryIndex { - fn from(index: i64) -> Self { - Self { index } - } -} diff --git a/crates/dictionary/src/dictionary_index_serialize.rs b/crates/dictionary/src/dictionary_index_serialize.rs deleted file mode 100644 index 62ca49d2..00000000 --- a/crates/dictionary/src/dictionary_index_serialize.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::dictionary_index::DictionaryIndex; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -impl Serialize for DictionaryIndex { - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - index: i64::deserialize(bytes)?, - }) - } - - fn serialize(&self) -> Vec { - self.index.serialize() - } -} diff --git a/crates/dictionary/src/dictionary_value.rs b/crates/dictionary/src/dictionary_value.rs deleted file mode 100644 index 913c2bec..00000000 --- a/crates/dictionary/src/dictionary_value.rs +++ /dev/null @@ -1,12 +0,0 @@ -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; - -#[derive(Clone, Default)] -pub struct DictionaryValue -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, -{ - pub(crate) meta: i64, - pub(crate) hash: u64, - pub(crate) value: T, -} diff --git a/crates/dictionary/src/lib.rs b/crates/dictionary/src/lib.rs deleted file mode 100644 index ffbbb1a2..00000000 --- a/crates/dictionary/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod dictionary; -mod dictionary_data; -mod dictionary_data_memory; -mod dictionary_data_memory_default; -mod dictionary_data_storage; -mod dictionary_data_storage_indexes; -mod dictionary_default; -mod dictionary_impl; -mod dictionary_index; -mod dictionary_index_from; -mod dictionary_index_serialize; -mod dictionary_value; -mod dictionary_value_serialize; -mod storage_dictionary; -mod storage_dictionary_try_from; - -pub use dictionary::Dictionary; -pub use dictionary_index::DictionaryIndex; -pub use storage_dictionary::StorageDictionary; diff --git a/crates/dictionary/src/storage_dictionary.rs b/crates/dictionary/src/storage_dictionary.rs deleted file mode 100644 index 6cd2c608..00000000 --- a/crates/dictionary/src/storage_dictionary.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::dictionary_data_storage::DictionaryDataStorage; -use super::dictionary_impl::DictionaryImpl; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_utilities::StableHash; - -pub type StorageDictionary = - DictionaryImpl>; - -impl StorageDictionary -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, - Data: Storage, -{ - pub fn storage_index(&self) -> StorageIndex { - self.data.storage_index.clone() - } -} diff --git a/crates/dictionary/src/storage_dictionary_try_from.rs b/crates/dictionary/src/storage_dictionary_try_from.rs deleted file mode 100644 index 7722400c..00000000 --- a/crates/dictionary/src/storage_dictionary_try_from.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::dictionary_data_storage::DictionaryDataStorage; -use crate::dictionary_data_storage_indexes::DictionaryDataStorageIndexes; -use crate::dictionary_index::DictionaryIndex; -use crate::dictionary_value::DictionaryValue; -use crate::StorageDictionary; -use agdb_db_error::DbError; -use agdb_multi_map::StorageMultiMap; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use agdb_storage_vec::StorageVec; -use agdb_utilities::StableHash; -use std::cell::RefCell; -use std::marker::PhantomData; -use std::rc::Rc; - -impl TryFrom>> for StorageDictionary -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - let index = StorageMultiMap::::try_from(storage.clone())?; - let mut values = StorageVec::, Data>::try_from(storage.clone())?; - values.push(&DictionaryValue::default())?; - - let storage_index = storage.borrow_mut().insert(&DictionaryDataStorageIndexes { - index: index.storage_index(), - values: values.storage_index(), - })?; - - Ok(StorageDictionary:: { - data: DictionaryDataStorage:: { - storage, - storage_index, - index, - values, - }, - phantom_data: PhantomData, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageDictionary -where - T: Clone + Default + Eq + PartialEq + StableHash + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - let indexes = storage_with_index - .0 - .borrow_mut() - .value::(&storage_with_index.1)?; - let index = StorageMultiMap::::try_from(( - storage_with_index.0.clone(), - indexes.index, - ))?; - let values = StorageVec::, Data>::try_from(( - storage_with_index.0.clone(), - indexes.values, - ))?; - - Ok(StorageDictionary:: { - data: DictionaryDataStorage:: { - storage: storage_with_index.0, - storage_index: storage_with_index.1, - index, - values, - }, - phantom_data: PhantomData, - }) - } -} diff --git a/crates/dictionary/tests/dictionary_get_index_test.rs b/crates/dictionary/tests/dictionary_get_index_test.rs deleted file mode 100644 index ed801b62..00000000 --- a/crates/dictionary/tests/dictionary_get_index_test.rs +++ /dev/null @@ -1,66 +0,0 @@ -use agdb_dictionary::Dictionary; -use agdb_test_utilities::CollidedValue; - -#[test] -fn index() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.index(&10_i64), Ok(Some(index))); -} - -#[test] -fn index_missing_value() { - let dictionary = Dictionary::::new(); - - assert_eq!(dictionary.index(&10), Ok(None)); -} - -#[test] -fn index_removed_value() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.index(&10), Ok(None)); -} - -#[test] -fn index_reuse() { - let mut dictionary = Dictionary::::new(); - - let index1 = dictionary.insert(&5).unwrap(); - let index2 = dictionary.insert(&10).unwrap(); - let index3 = dictionary.insert(&7).unwrap(); - - dictionary.remove(&index2).unwrap(); - dictionary.remove(&index1).unwrap(); - dictionary.remove(&index3).unwrap(); - - assert_eq!(dictionary.count(&index1), Ok(None)); - assert_eq!(dictionary.count(&index2), Ok(None)); - assert_eq!(dictionary.count(&index3), Ok(None)); - - assert_eq!(dictionary.insert(&3), Ok(index3.clone())); - assert_eq!(dictionary.insert(&2), Ok(index1.clone())); - assert_eq!(dictionary.insert(&1), Ok(index2.clone())); - - assert_eq!(dictionary.value(&index1), Ok(Some(2))); - assert_eq!(dictionary.value(&index2), Ok(Some(1))); - assert_eq!(dictionary.value(&index3), Ok(Some(3))); -} - -#[test] -fn index_with_collisions() { - let mut dictionary = Dictionary::>::new(); - - let index1 = dictionary.insert(&CollidedValue::new(1)).unwrap(); - let index2 = dictionary.insert(&CollidedValue::new(2)).unwrap(); - let index3 = dictionary.insert(&CollidedValue::new(3)).unwrap(); - - assert_eq!(dictionary.index(&CollidedValue::new(1)), Ok(Some(index1))); - assert_eq!(dictionary.index(&CollidedValue::new(2)), Ok(Some(index2))); - assert_eq!(dictionary.index(&CollidedValue::new(3)), Ok(Some(index3))); -} diff --git a/crates/dictionary/tests/dictionary_index_test.rs b/crates/dictionary/tests/dictionary_index_test.rs deleted file mode 100644 index b8e43966..00000000 --- a/crates/dictionary/tests/dictionary_index_test.rs +++ /dev/null @@ -1,8 +0,0 @@ -use agdb_dictionary::DictionaryIndex; - -#[test] -fn derived_from_debug() { - let index = DictionaryIndex::default(); - - format!("{:?}", index); -} diff --git a/crates/dictionary/tests/dictionary_insert_test.rs b/crates/dictionary/tests/dictionary_insert_test.rs deleted file mode 100644 index 004a68e6..00000000 --- a/crates/dictionary/tests/dictionary_insert_test.rs +++ /dev/null @@ -1,49 +0,0 @@ -use agdb_dictionary::Dictionary; - -#[test] -fn insert() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); - assert_eq!(dictionary.value(&index), Ok(Some(10_i64))); - assert_eq!(dictionary.count(&index), Ok(Some(1))); -} - -#[test] -fn insert_multiple() { - let mut dictionary = Dictionary::::new(); - - let index1 = dictionary.insert(&10).unwrap(); - let index2 = dictionary.insert(&15).unwrap(); - let index3 = dictionary.insert(&20).unwrap(); - - assert_eq!(dictionary.len(), Ok(3)); - - assert_eq!(dictionary.value(&index1).unwrap(), Some(10_i64)); - assert_eq!(dictionary.count(&index1), Ok(Some(1))); - - assert_eq!(dictionary.value(&index2).unwrap(), Some(15_i64)); - assert_eq!(dictionary.count(&index2), Ok(Some(1))); - - assert_eq!(dictionary.value(&index3).unwrap(), Some(20_i64)); - assert_eq!(dictionary.count(&index3), Ok(Some(1))); -} - -#[test] -fn insert_same() { - let mut dictionary = Dictionary::::new(); - - dictionary.insert(&10).unwrap(); - - let index2 = dictionary.insert(&15).unwrap(); - - assert_eq!(dictionary.insert(&15).unwrap(), index2); - assert_eq!(dictionary.insert(&15).unwrap(), index2); - - dictionary.insert(&20).unwrap(); - - assert_eq!(dictionary.len(), Ok(3)); - assert_eq!(dictionary.count(&index2), Ok(Some(3))); -} diff --git a/crates/dictionary/tests/dictionary_remove_test.rs b/crates/dictionary/tests/dictionary_remove_test.rs deleted file mode 100644 index 221a5594..00000000 --- a/crates/dictionary/tests/dictionary_remove_test.rs +++ /dev/null @@ -1,51 +0,0 @@ -use agdb_dictionary::Dictionary; -use agdb_dictionary::DictionaryIndex; - -#[test] -fn remove() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(None)); - assert_eq!(dictionary.count(&index), Ok(None)); -} - -#[test] -fn remove_duplicated() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.insert(&10).unwrap(); - dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(Some(10))); - assert_eq!(dictionary.count(&index), Ok(Some(3))); - - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(Some(10))); - assert_eq!(dictionary.count(&index), Ok(Some(2))); - - dictionary.remove(&index).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(None)); - assert_eq!(dictionary.count(&index), Ok(None)); -} - -#[test] -fn remove_missing() { - let mut dictionary = Dictionary::::new(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); - - dictionary - .remove(&DictionaryIndex::from(index.value() + 1)) - .unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); -} diff --git a/crates/dictionary/tests/dictionary_test.rs b/crates/dictionary/tests/dictionary_test.rs deleted file mode 100644 index d4496337..00000000 --- a/crates/dictionary/tests/dictionary_test.rs +++ /dev/null @@ -1,21 +0,0 @@ -use agdb_dictionary::Dictionary; -use agdb_dictionary::DictionaryIndex; - -#[test] -fn count_invalid_index() { - let dictionary = Dictionary::::new(); - - assert_eq!(dictionary.count(&DictionaryIndex::default()), Ok(None)); - assert_eq!(dictionary.count(&DictionaryIndex::from(-1_i64)), Ok(None)); -} - -#[test] -fn default() { - let _dictionary = Dictionary::::default(); -} - -#[test] -fn value_missing_index() { - let dictionary = Dictionary::::new(); - assert_eq!(dictionary.value(&DictionaryIndex::from(1_i64)), Ok(None)); -} diff --git a/crates/dictionary/tests/storage_dictionary_get_index_test.rs b/crates/dictionary/tests/storage_dictionary_get_index_test.rs deleted file mode 100644 index 568d54ee..00000000 --- a/crates/dictionary/tests/storage_dictionary_get_index_test.rs +++ /dev/null @@ -1,90 +0,0 @@ -use agdb_dictionary::StorageDictionary; -use agdb_storage::StorageFile; -use agdb_test_utilities::CollidedValue; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.index(&10), Ok(Some(index))); -} - -#[test] -fn index_missing_value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let dictionary = StorageDictionary::::try_from(storage).unwrap(); - - assert_eq!(dictionary.index(&10), Ok(None)); -} - -#[test] -fn index_removed_value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.index(&10), Ok(None)); -} - -#[test] -fn index_reuse() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index1 = dictionary.insert(&5).unwrap(); - let index2 = dictionary.insert(&10).unwrap(); - let index3 = dictionary.insert(&7).unwrap(); - - dictionary.remove(&index2).unwrap(); - dictionary.remove(&index1).unwrap(); - dictionary.remove(&index3).unwrap(); - - assert_eq!(dictionary.count(&index1), Ok(None)); - assert_eq!(dictionary.count(&index2), Ok(None)); - assert_eq!(dictionary.count(&index3), Ok(None)); - - assert_eq!(dictionary.insert(&3), Ok(index3.clone())); - assert_eq!(dictionary.insert(&2), Ok(index1.clone())); - assert_eq!(dictionary.insert(&1), Ok(index2.clone())); - - assert_eq!(dictionary.value(&index1), Ok(Some(2))); - assert_eq!(dictionary.value(&index2), Ok(Some(1))); - assert_eq!(dictionary.value(&index3), Ok(Some(3))); -} - -#[test] -fn index_with_collisions() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::>::try_from(storage).unwrap(); - - let index1 = dictionary.insert(&CollidedValue::new(1)).unwrap(); - let index2 = dictionary.insert(&CollidedValue::new(2)).unwrap(); - let index3 = dictionary.insert(&CollidedValue::new(3)).unwrap(); - - assert_eq!(dictionary.index(&CollidedValue::new(1)), Ok(Some(index1))); - assert_eq!(dictionary.index(&CollidedValue::new(2)), Ok(Some(index2))); - assert_eq!(dictionary.index(&CollidedValue::new(3)), Ok(Some(index3))); -} diff --git a/crates/dictionary/tests/storage_dictionary_insert_test.rs b/crates/dictionary/tests/storage_dictionary_insert_test.rs deleted file mode 100644 index e1c83416..00000000 --- a/crates/dictionary/tests/storage_dictionary_insert_test.rs +++ /dev/null @@ -1,65 +0,0 @@ -use agdb_dictionary::StorageDictionary; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn insert() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); - assert_eq!(dictionary.value(&index), Ok(Some(10_i64))); - assert_eq!(dictionary.count(&index), Ok(Some(1))); -} - -#[test] -fn insert_multiple() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index1 = dictionary.insert(&10).unwrap(); - let index2 = dictionary.insert(&15).unwrap(); - let index3 = dictionary.insert(&20).unwrap(); - - assert_eq!(dictionary.len(), Ok(3)); - - assert_eq!(dictionary.value(&index1).unwrap(), Some(10_i64)); - assert_eq!(dictionary.count(&index1), Ok(Some(1))); - - assert_eq!(dictionary.value(&index2).unwrap(), Some(15_i64)); - assert_eq!(dictionary.count(&index2), Ok(Some(1))); - - assert_eq!(dictionary.value(&index3).unwrap(), Some(20_i64)); - assert_eq!(dictionary.count(&index3), Ok(Some(1))); -} - -#[test] -fn insert_same() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - dictionary.insert(&10).unwrap(); - - let index2 = dictionary.insert(&15).unwrap(); - - assert_eq!(dictionary.insert(&15).unwrap(), index2); - assert_eq!(dictionary.insert(&15).unwrap(), index2); - - dictionary.insert(&20).unwrap(); - - assert_eq!(dictionary.len(), Ok(3)); - assert_eq!(dictionary.count(&index2), Ok(Some(3))); -} diff --git a/crates/dictionary/tests/storage_dictionary_remove_test.rs b/crates/dictionary/tests/storage_dictionary_remove_test.rs deleted file mode 100644 index 595f44b7..00000000 --- a/crates/dictionary/tests/storage_dictionary_remove_test.rs +++ /dev/null @@ -1,67 +0,0 @@ -use agdb_dictionary::DictionaryIndex; -use agdb_dictionary::StorageDictionary; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(None)); - assert_eq!(dictionary.count(&index), Ok(None)); -} - -#[test] -fn remove_duplicated() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - dictionary.insert(&10).unwrap(); - dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(Some(10))); - assert_eq!(dictionary.count(&index), Ok(Some(3))); - - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(Some(10))); - assert_eq!(dictionary.count(&index), Ok(Some(2))); - - dictionary.remove(&index).unwrap(); - dictionary.remove(&index).unwrap(); - - assert_eq!(dictionary.value(&index), Ok(None)); - assert_eq!(dictionary.count(&index), Ok(None)); -} - -#[test] -fn remove_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); - - let index = dictionary.insert(&10).unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); - - dictionary - .remove(&DictionaryIndex::from(index.value() + 1)) - .unwrap(); - - assert_eq!(dictionary.len(), Ok(1)); -} diff --git a/crates/dictionary/tests/storage_dictionary_test.rs b/crates/dictionary/tests/storage_dictionary_test.rs deleted file mode 100644 index 06f43dd4..00000000 --- a/crates/dictionary/tests/storage_dictionary_test.rs +++ /dev/null @@ -1,62 +0,0 @@ -use agdb_dictionary::DictionaryIndex; -use agdb_dictionary::StorageDictionary; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn count_invalid_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let dictionary = StorageDictionary::::try_from(storage).unwrap(); - - assert_eq!(dictionary.count(&DictionaryIndex::from(-1_i64)), Ok(None)); -} - -#[test] -fn restore_from_file() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let storage_index; - let index1; - let index2; - let index3; - let index4; - - { - let mut dictionary = StorageDictionary::::try_from(storage.clone()).unwrap(); - storage_index = dictionary.storage_index(); - - index1 = dictionary.insert(&10).unwrap(); - dictionary.insert(&10).unwrap(); - index2 = dictionary.insert(&15).unwrap(); - index3 = dictionary.insert(&7).unwrap(); - index4 = dictionary.insert(&20).unwrap(); - dictionary.remove(&index2).unwrap(); - } - - let dictionary = StorageDictionary::::try_from((storage, storage_index)).unwrap(); - - assert_eq!(dictionary.len(), Ok(3)); - assert_eq!(dictionary.count(&index1), Ok(Some(2))); - assert_eq!(dictionary.value(&index1), Ok(Some(10))); - assert_eq!(dictionary.value(&index2), Ok(None)); - assert_eq!(dictionary.value(&index3), Ok(Some(7))); - assert_eq!(dictionary.value(&index4), Ok(Some(20))); -} - -#[test] -fn value_missing_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let dictionary = StorageDictionary::::try_from(storage).unwrap(); - assert_eq!(dictionary.value(&DictionaryIndex::from(1_i64)), Ok(None)); -} diff --git a/crates/graph/Cargo.toml b/crates/graph/Cargo.toml deleted file mode 100644 index b775dcfe..00000000 --- a/crates/graph/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "agdb_graph" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage = { path = "../storage", version = "<=0.1.0" } -agdb_storage_vec = { path = "../storage_vec", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/graph/src/graph.rs b/crates/graph/src/graph.rs deleted file mode 100644 index 1b7afe6a..00000000 --- a/crates/graph/src/graph.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::graph_data_memory::GraphDataMemory; -use crate::graph_impl::GraphImpl; - -pub type Graph = GraphImpl; - -impl Graph { - pub fn new() -> Graph { - Graph { - data: GraphDataMemory::default(), - } - } -} diff --git a/crates/graph/src/graph_data_memory_default.rs b/crates/graph/src/graph_data_memory_default.rs deleted file mode 100644 index 7be51fc9..00000000 --- a/crates/graph/src/graph_data_memory_default.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::graph_data_memory::GraphDataMemory; - -impl Default for GraphDataMemory { - fn default() -> Self { - Self { - from: vec![0], - to: vec![0], - from_meta: vec![i64::MIN], - to_meta: vec![0], - } - } -} diff --git a/crates/graph/src/graph_data_storage.rs b/crates/graph/src/graph_data_storage.rs deleted file mode 100644 index 68ace6ce..00000000 --- a/crates/graph/src/graph_data_storage.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::graph_data::GraphData; -use crate::graph_data_storage_indexes::GraphDataStorageIndexes; -use crate::graph_index::GraphIndex; -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_storage_vec::StorageVec; -use std::cell::RefCell; -use std::rc::Rc; - -pub struct GraphDataStorage -where - Data: Storage, -{ - pub(crate) storage: Rc>, - pub(crate) storage_index: StorageIndex, - #[allow(dead_code)] - pub(crate) indexes: GraphDataStorageIndexes, - pub(crate) from: StorageVec, - pub(crate) to: StorageVec, - pub(crate) from_meta: StorageVec, - pub(crate) to_meta: StorageVec, -} - -impl GraphData for GraphDataStorage -where - Data: Storage, -{ - fn capacity(&self) -> Result { - Ok(self.from.len()) - } - - fn commit(&mut self) -> Result<(), DbError> { - self.storage.borrow_mut().commit() - } - - fn free_index(&self) -> Result { - self.from_meta.value(0) - } - - fn from(&self, index: &GraphIndex) -> Result { - self.from.value(index.as_u64()) - } - - fn from_meta(&self, index: &GraphIndex) -> Result { - self.from_meta.value(index.as_u64()) - } - - fn grow(&mut self) -> Result<(), DbError> { - self.from.push(&0)?; - self.to.push(&0)?; - self.from_meta.push(&0)?; - self.to_meta.push(&0) - } - - fn node_count(&self) -> Result { - Ok(self.to_meta.value(0)? as u64) - } - - fn set_from(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { - self.from.set_value(index.as_u64(), &value) - } - - fn set_from_meta(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { - self.from_meta.set_value(index.as_u64(), &value) - } - - fn set_node_count(&mut self, count: u64) -> Result<(), DbError> { - self.to_meta.set_value(0, &(count as i64)) - } - - fn set_to(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { - self.to.set_value(index.as_u64(), &value) - } - - fn set_to_meta(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { - self.to_meta.set_value(index.as_u64(), &value) - } - - fn to(&self, index: &GraphIndex) -> Result { - self.to.value(index.as_u64()) - } - - fn to_meta(&self, index: &GraphIndex) -> Result { - self.to_meta.value(index.as_u64()) - } - - fn transaction(&mut self) { - self.storage.borrow_mut().transaction() - } -} diff --git a/crates/graph/src/graph_data_storage_indexes.rs b/crates/graph/src/graph_data_storage_indexes.rs deleted file mode 100644 index 1d834fb1..00000000 --- a/crates/graph/src/graph_data_storage_indexes.rs +++ /dev/null @@ -1,8 +0,0 @@ -use agdb_storage::StorageIndex; - -pub(crate) struct GraphDataStorageIndexes { - pub(crate) from: StorageIndex, - pub(crate) to: StorageIndex, - pub(crate) from_meta: StorageIndex, - pub(crate) to_meta: StorageIndex, -} diff --git a/crates/graph/src/graph_data_storage_try_from.rs b/crates/graph/src/graph_data_storage_try_from.rs deleted file mode 100644 index a7ee257d..00000000 --- a/crates/graph/src/graph_data_storage_try_from.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::graph_data_storage::GraphDataStorage; -use crate::graph_data_storage_indexes::GraphDataStorageIndexes; -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use agdb_storage_vec::StorageVec; -use std::cell::RefCell; -use std::rc::Rc; - -impl TryFrom>> for GraphDataStorage -where - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - let mut from = StorageVec::::try_from(storage.clone())?; - from.push(&0)?; - let mut to = StorageVec::::try_from(storage.clone())?; - to.push(&0)?; - let mut from_meta = StorageVec::::try_from(storage.clone())?; - from_meta.push(&i64::MIN)?; - let mut to_meta = StorageVec::::try_from(storage.clone())?; - to_meta.push(&0)?; - - let indexes = GraphDataStorageIndexes { - from: from.storage_index(), - to: to.storage_index(), - from_meta: from_meta.storage_index(), - to_meta: to_meta.storage_index(), - }; - - let index = storage.borrow_mut().insert(&indexes)?; - - Ok(GraphDataStorage:: { - storage, - storage_index: index, - indexes, - from, - to, - from_meta, - to_meta, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for GraphDataStorage { - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - let indexes = storage_with_index - .0 - .borrow_mut() - .value::(&storage_with_index.1)?; - - let from = StorageVec::::try_from(( - storage_with_index.0.clone(), - indexes.from.clone(), - ))?; - let to = - StorageVec::::try_from((storage_with_index.0.clone(), indexes.to.clone()))?; - let from_meta = StorageVec::::try_from(( - storage_with_index.0.clone(), - indexes.from_meta.clone(), - ))?; - let to_meta = StorageVec::::try_from(( - storage_with_index.0.clone(), - indexes.to_meta.clone(), - ))?; - - Ok(GraphDataStorage:: { - storage: storage_with_index.0, - storage_index: storage_with_index.1, - indexes, - from, - to, - from_meta, - to_meta, - }) - } -} diff --git a/crates/graph/src/graph_default.rs b/crates/graph/src/graph_default.rs deleted file mode 100644 index e0feb1c5..00000000 --- a/crates/graph/src/graph_default.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::graph::Graph; - -impl Default for Graph { - fn default() -> Self { - Self::new() - } -} diff --git a/crates/graph/src/graph_index.rs b/crates/graph/src/graph_index.rs deleted file mode 100644 index 72d0ac8c..00000000 --- a/crates/graph/src/graph_index.rs +++ /dev/null @@ -1,38 +0,0 @@ -#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] -pub struct GraphIndex { - pub(crate) index: i64, -} - -impl GraphIndex { - pub fn as_u64(&self) -> u64 { - if self.is_edge() { - (-self.value()) as u64 - } else { - self.value() as u64 - } - } - - pub fn as_usize(&self) -> usize { - if self.is_edge() { - (-self.value()) as usize - } else { - self.value() as usize - } - } - - pub fn is_edge(&self) -> bool { - self.index < 0 - } - - pub fn is_node(&self) -> bool { - 0 < self.index - } - - pub fn is_valid(&self) -> bool { - self.index != 0 - } - - pub fn value(&self) -> i64 { - self.index - } -} diff --git a/crates/graph/src/graph_index_from.rs b/crates/graph/src/graph_index_from.rs deleted file mode 100644 index 68d29ce2..00000000 --- a/crates/graph/src/graph_index_from.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::graph_index::GraphIndex; - -impl From for GraphIndex { - fn from(index: i64) -> Self { - Self { index } - } -} diff --git a/crates/graph/src/lib.rs b/crates/graph/src/lib.rs deleted file mode 100644 index f2bed8bf..00000000 --- a/crates/graph/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -mod graph; -mod graph_data; -mod graph_data_memory; -mod graph_data_memory_default; -mod graph_data_storage; -mod graph_data_storage_indexes; -mod graph_data_storage_indexes_serialize; -mod graph_data_storage_try_from; -mod graph_default; -mod graph_edge; -mod graph_edge_iterator; -mod graph_edge_reverse_iterator; -mod graph_impl; -mod graph_index; -mod graph_index_from; -mod graph_node; -mod graph_node_iterator; -mod storage_graph; -mod storage_graph_try_from; - -pub use graph::Graph; -pub use graph_data::GraphData; -pub use graph_impl::GraphImpl; -pub use graph_index::GraphIndex; -pub use storage_graph::StorageGraph; diff --git a/crates/graph/src/storage_graph.rs b/crates/graph/src/storage_graph.rs deleted file mode 100644 index 5ad70bbb..00000000 --- a/crates/graph/src/storage_graph.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::graph_data_storage::GraphDataStorage; -use crate::graph_impl::GraphImpl; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; - -pub type StorageGraph = GraphImpl>; - -impl StorageGraph -where - Data: Storage, -{ - pub fn storage_index(&self) -> StorageIndex { - self.data.storage_index.clone() - } -} diff --git a/crates/graph/src/storage_graph_try_from.rs b/crates/graph/src/storage_graph_try_from.rs deleted file mode 100644 index b1045c37..00000000 --- a/crates/graph/src/storage_graph_try_from.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::graph_data_storage::GraphDataStorage; -use crate::StorageGraph; -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use std::cell::RefCell; -use std::rc::Rc; - -impl TryFrom>> for StorageGraph -where - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - Ok(StorageGraph { - data: GraphDataStorage::::try_from(storage)?, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageGraph { - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - Ok(StorageGraph { - data: GraphDataStorage::::try_from(storage_with_index)?, - }) - } -} diff --git a/crates/graph/tests/graph_edge_test.rs b/crates/graph/tests/graph_edge_test.rs deleted file mode 100644 index 7513e351..00000000 --- a/crates/graph/tests/graph_edge_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn edge_from_index() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - assert_eq!(graph.edge(&index).unwrap().index(), index); -} - -#[test] -fn edge_from_index_missing() { - let graph = Graph::new(); - - assert!(graph.edge(&GraphIndex::from(-3)).is_none()); -} - -#[test] -fn edge_iteration() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node1, &node2).unwrap(); - - let mut actual = Vec::::new(); - - for edge in graph.node(&node1).unwrap().edge_iter_from() { - actual.push(edge.index()); - } - - assert_eq!(actual, vec![edge3, edge2, edge1]); -} - -#[test] -fn edge_iteration_reverse() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node1, &node2).unwrap(); - - let mut actual = Vec::::new(); - - for edge in graph.node(&node2).unwrap().edge_iter_to() { - actual.push(edge.index()); - } - - assert_eq!(actual, vec![edge3, edge2, edge1]); -} - -#[test] -fn from_index() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - - let index = graph.insert_edge(&node1, &node2).unwrap(); - - assert_eq!(graph.edge(&index).unwrap().index_from(), node1); -} - -#[test] -fn to_index() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - - let index = graph.insert_edge(&node1, &node2).unwrap(); - - assert_eq!(graph.edge(&index).unwrap().index_to(), node2); -} diff --git a/crates/graph/tests/graph_index_test.rs b/crates/graph/tests/graph_index_test.rs deleted file mode 100644 index 761655c6..00000000 --- a/crates/graph/tests/graph_index_test.rs +++ /dev/null @@ -1,51 +0,0 @@ -use agdb_graph::GraphIndex; -use std::cmp::Ordering; - -#[test] -fn derived_from_debug() { - let index = GraphIndex::default(); - - format!("{:?}", index); -} - -#[test] -fn derived_from_ord() { - let index = GraphIndex::default(); - assert_eq!(index.cmp(&index), Ordering::Equal); -} - -#[test] -fn is_edge() { - assert!(!GraphIndex::from(1).is_edge()); - assert!(!GraphIndex::default().is_edge()); - assert!(GraphIndex::from(-1).is_edge()); -} - -#[test] -fn is_node() { - assert!(GraphIndex::from(1).is_node()); - assert!(!GraphIndex::default().is_node()); - assert!(!GraphIndex::from(-1).is_node()); -} - -#[test] -fn ordering() { - let mut indexes = vec![ - GraphIndex::default(), - GraphIndex::from(100_i64), - GraphIndex::from(-1_i64), - GraphIndex::from(1_i64), - ]; - - indexes.sort(); - - assert_eq!( - indexes, - vec![ - GraphIndex::from(-1_i64), - GraphIndex::default(), - GraphIndex::from(1_i64), - GraphIndex::from(100_i64), - ] - ) -} diff --git a/crates/graph/tests/graph_insert_edge_test.rs b/crates/graph/tests/graph_insert_edge_test.rs deleted file mode 100644 index bb3ffb58..00000000 --- a/crates/graph/tests/graph_insert_edge_test.rs +++ /dev/null @@ -1,60 +0,0 @@ -use agdb_db_error::DbError; -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn insert_edge() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(GraphIndex::from(-3_i64))); -} - -#[test] -fn insert_edge_after_removed() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(index)); -} - -#[test] -fn insert_edge_after_several_removed() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index1).unwrap(); - graph.remove_edge(&index2).unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(index2)); -} - -#[test] -fn insert_edge_invalid_from() { - let mut graph = Graph::new(); - - assert_eq!( - graph.insert_edge(&GraphIndex::from(1), &GraphIndex::from(2)), - Err(DbError::from("'1' is invalid index")) - ); -} - -#[test] -fn insert_edge_invalid_to() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - - assert_eq!( - graph.insert_edge(&from, &GraphIndex::from(2)), - Err(DbError::from("'2' is invalid index")) - ); -} diff --git a/crates/graph/tests/graph_insert_node_test.rs b/crates/graph/tests/graph_insert_node_test.rs deleted file mode 100644 index 5679cb7b..00000000 --- a/crates/graph/tests/graph_insert_node_test.rs +++ /dev/null @@ -1,38 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn insert_node() { - let mut graph = Graph::new(); - - assert_eq!(graph.insert_node(), Ok(GraphIndex::from(1))); -} - -#[test] -fn insert_node_after_removal() { - let mut graph = Graph::new(); - graph.insert_node().unwrap(); - let index = graph.insert_node().unwrap(); - graph.insert_node().unwrap(); - - graph.remove_node(&index).unwrap(); - - assert_eq!(graph.insert_node(), Ok(index)); -} - -#[test] -fn node_count() { - let mut graph = Graph::new(); - - assert_eq!(graph.node_count().unwrap(), 0); - - graph.insert_node().unwrap(); - let index = graph.insert_node().unwrap(); - graph.insert_node().unwrap(); - - assert_eq!(graph.node_count(), Ok(3)); - - graph.remove_node(&index).unwrap(); - - assert_eq!(graph.node_count(), Ok(2)); -} diff --git a/crates/graph/tests/graph_node_test.rs b/crates/graph/tests/graph_node_test.rs deleted file mode 100644 index 613fa2e9..00000000 --- a/crates/graph/tests/graph_node_test.rs +++ /dev/null @@ -1,58 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn node_from_index() { - let mut graph = Graph::new(); - let index = graph.insert_node().unwrap(); - - assert_eq!(graph.node(&index).unwrap().index(), index); -} - -#[test] -fn node_from_index_missing() { - let graph = Graph::new(); - - let node = graph.node(&GraphIndex::from(1)); - - assert!(node.is_none()); -} - -#[test] -fn node_iteration() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let expected = vec![node1, node2, node3]; - let mut nodes = Vec::::new(); - - for node in graph.node_iter() { - nodes.push(node.index()); - } - - assert_eq!(nodes, expected); -} - -#[test] -fn node_iteration_with_removed_nodes() { - let mut graph = Graph::new(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - let node5 = graph.insert_node().unwrap(); - - graph.remove_node(&node2).unwrap(); - graph.remove_node(&node5).unwrap(); - - let expected = vec![node1, node3, node4]; - let mut nodes = Vec::::new(); - - for node in graph.node_iter() { - nodes.push(node.index()); - } - - assert_eq!(nodes, expected); -} diff --git a/crates/graph/tests/graph_remove_edge_test.rs b/crates/graph/tests/graph_remove_edge_test.rs deleted file mode 100644 index 2a577c44..00000000 --- a/crates/graph/tests/graph_remove_edge_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn remove_edge_circular() { - let mut graph = Graph::new(); - let node = graph.insert_node().unwrap(); - let index = graph.insert_edge(&node, &node).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert!(graph.edge(&index).is_none()); -} - -#[test] -fn remove_edge_first() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index3).unwrap(); - - assert!(graph.edge(&index1).is_some()); - assert!(graph.edge(&index2).is_some()); - assert!(graph.edge(&index3).is_none()); -} - -#[test] -fn remove_edge_last() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index1).unwrap(); - - assert!(graph.edge(&index1).is_none()); - assert!(graph.edge(&index2).is_some()); - assert!(graph.edge(&index3).is_some()); -} - -#[test] -fn remove_edge_middle() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index2).unwrap(); - - assert!(graph.edge(&index1).is_some()); - assert!(graph.edge(&index2).is_none()); - assert!(graph.edge(&index3).is_some()); -} - -#[test] -fn remove_edge_missing() { - let mut graph = Graph::new(); - graph.remove_edge(&GraphIndex::from(-3)).unwrap(); -} - -#[test] -fn remove_edge_only() { - let mut graph = Graph::new(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert!(graph.edge(&index).is_none()); -} diff --git a/crates/graph/tests/graph_remove_node_test.rs b/crates/graph/tests/graph_remove_node_test.rs deleted file mode 100644 index e0aedf11..00000000 --- a/crates/graph/tests/graph_remove_node_test.rs +++ /dev/null @@ -1,62 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; - -#[test] -fn remove_node_circular_edge() { - let mut graph = Graph::new(); - let index = graph.insert_node().unwrap(); - let edge = graph.insert_edge(&index, &index).unwrap(); - - graph.remove_node(&index).unwrap(); - - assert!(graph.node(&index).is_none()); - assert!(graph.edge(&edge).is_none()); -} - -#[test] -fn remove_node_only() { - let mut graph = Graph::new(); - let index = graph.insert_node().unwrap(); - - graph.remove_node(&index).unwrap(); - - assert!(graph.node(&index).is_none()); -} - -#[test] -fn remove_node_missing() { - let mut graph = Graph::new(); - graph.remove_node(&GraphIndex::from(1)).unwrap(); -} - -#[test] -fn remove_nodes_with_edges() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node1).unwrap(); - let edge3 = graph.insert_edge(&node1, &node3).unwrap(); - let edge4 = graph.insert_edge(&node2, &node1).unwrap(); - let edge5 = graph.insert_edge(&node3, &node1).unwrap(); - - let edge6 = graph.insert_edge(&node3, &node2).unwrap(); - let edge7 = graph.insert_edge(&node2, &node3).unwrap(); - - graph.remove_node(&node1).unwrap(); - - assert!(graph.node(&node1).is_none()); - assert!(graph.edge(&edge1).is_none()); - assert!(graph.edge(&edge2).is_none()); - assert!(graph.edge(&edge3).is_none()); - assert!(graph.edge(&edge4).is_none()); - assert!(graph.edge(&edge5).is_none()); - - assert!(graph.node(&node2).is_some()); - assert!(graph.node(&node3).is_some()); - assert!(graph.edge(&edge6).is_some()); - assert!(graph.edge(&edge7).is_some()); -} diff --git a/crates/graph/tests/graph_test.rs b/crates/graph/tests/graph_test.rs deleted file mode 100644 index 6dd5a4f1..00000000 --- a/crates/graph/tests/graph_test.rs +++ /dev/null @@ -1,6 +0,0 @@ -use agdb_graph::Graph; - -#[test] -fn derived_from_default() { - let _graph = Graph::default(); -} diff --git a/crates/graph/tests/storage_graph_edge_test.rs b/crates/graph/tests/storage_graph_edge_test.rs deleted file mode 100644 index 88896d84..00000000 --- a/crates/graph/tests/storage_graph_edge_test.rs +++ /dev/null @@ -1,55 +0,0 @@ -use agdb_graph::GraphIndex; -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn edge_from_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - assert_eq!(graph.edge(&index).unwrap().index(), index); -} - -#[test] -fn edge_from_index_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let graph = StorageGraph::try_from(storage).unwrap(); - - assert!(graph.edge(&GraphIndex::from(-3)).is_none()); -} - -#[test] -fn edge_iteration() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node1, &node2).unwrap(); - - let mut actual = Vec::::new(); - - for edge in graph.node(&node1).unwrap().edge_iter_from() { - actual.push(edge.index()); - } - - assert_eq!(actual, vec![edge3, edge2, edge1]); -} diff --git a/crates/graph/tests/storage_graph_insert_edge_test.rs b/crates/graph/tests/storage_graph_insert_edge_test.rs deleted file mode 100644 index 306da34b..00000000 --- a/crates/graph/tests/storage_graph_insert_edge_test.rs +++ /dev/null @@ -1,84 +0,0 @@ -use agdb_db_error::DbError; -use agdb_graph::GraphIndex; -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn insert_edge() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(GraphIndex::from(-3_i64))); -} - -#[test] -fn insert_edge_after_removed() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(index)); -} - -#[test] -fn insert_edge_after_several_removed() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index1).unwrap(); - graph.remove_edge(&index2).unwrap(); - - assert_eq!(graph.insert_edge(&from, &to), Ok(index2)); -} - -#[test] -fn insert_edge_invalid_from() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - - assert_eq!( - graph.insert_edge(&GraphIndex::from(1), &GraphIndex::from(2)), - Err(DbError::from("'1' is invalid index")) - ); -} - -#[test] -fn insert_edge_invalid_to() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - - assert_eq!( - graph.insert_edge(&from, &GraphIndex::from(2)), - Err(DbError::from("'2' is invalid index")) - ); -} diff --git a/crates/graph/tests/storage_graph_insert_node_test.rs b/crates/graph/tests/storage_graph_insert_node_test.rs deleted file mode 100644 index 184b0227..00000000 --- a/crates/graph/tests/storage_graph_insert_node_test.rs +++ /dev/null @@ -1,54 +0,0 @@ -use agdb_graph::GraphIndex; -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn insert_node() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - - assert_eq!(graph.insert_node(), Ok(GraphIndex::from(1))); -} - -#[test] -fn insert_node_after_removal() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - graph.insert_node().unwrap(); - let index = graph.insert_node().unwrap(); - graph.insert_node().unwrap(); - - graph.remove_node(&index).unwrap(); - - assert_eq!(graph.insert_node().unwrap(), index); -} - -#[test] -fn node_count() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - - assert_eq!(graph.node_count().unwrap(), 0); - - graph.insert_node().unwrap(); - let index = graph.insert_node().unwrap(); - graph.insert_node().unwrap(); - - assert_eq!(graph.node_count().unwrap(), 3); - - graph.remove_node(&index).unwrap(); - - assert_eq!(graph.node_count().unwrap(), 2); -} diff --git a/crates/graph/tests/storage_graph_node_test.rs b/crates/graph/tests/storage_graph_node_test.rs deleted file mode 100644 index 77f02d78..00000000 --- a/crates/graph/tests/storage_graph_node_test.rs +++ /dev/null @@ -1,78 +0,0 @@ -use agdb_graph::GraphIndex; -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn node_from_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let index = graph.insert_node().unwrap(); - - assert_eq!(graph.node(&index).unwrap().index(), index); -} - -#[test] -fn node_from_index_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let graph = StorageGraph::try_from(storage).unwrap(); - - let node = graph.node(&GraphIndex::from(1)); - - assert!(node.is_none()); -} - -#[test] -fn node_iteration() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let expected = vec![node1, node2, node3]; - let mut nodes = Vec::::new(); - - for node in graph.node_iter() { - nodes.push(node.index()); - } - - assert_eq!(nodes, expected); -} - -#[test] -fn node_iteration_with_removed_nodes() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - let node5 = graph.insert_node().unwrap(); - - graph.remove_node(&node2).unwrap(); - graph.remove_node(&node5).unwrap(); - - let expected = vec![node1, node3, node4]; - let mut nodes = Vec::::new(); - - for node in graph.node_iter() { - nodes.push(node.index()); - } - - assert_eq!(nodes, expected); -} diff --git a/crates/graph/tests/storage_graph_remove_edge_test.rs b/crates/graph/tests/storage_graph_remove_edge_test.rs deleted file mode 100644 index 888fb3d3..00000000 --- a/crates/graph/tests/storage_graph_remove_edge_test.rs +++ /dev/null @@ -1,106 +0,0 @@ -use agdb_graph::{GraphIndex, StorageGraph}; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove_edge_circular() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let node = graph.insert_node().unwrap(); - let index = graph.insert_edge(&node, &node).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert!(graph.edge(&index).is_none()); -} - -#[test] -fn remove_edge_first() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index3).unwrap(); - - assert!(graph.edge(&index1).is_some()); - assert!(graph.edge(&index2).is_some()); - assert!(graph.edge(&index3).is_none()); -} - -#[test] -fn remove_edge_last() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index1).unwrap(); - - assert!(graph.edge(&index1).is_none()); - assert!(graph.edge(&index2).is_some()); - assert!(graph.edge(&index3).is_some()); -} - -#[test] -fn remove_edge_middle() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index1 = graph.insert_edge(&from, &to).unwrap(); - let index2 = graph.insert_edge(&from, &to).unwrap(); - let index3 = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index2).unwrap(); - - assert!(graph.edge(&index1).is_some()); - assert!(graph.edge(&index2).is_none()); - assert!(graph.edge(&index3).is_some()); -} - -#[test] -fn remove_edge_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - graph.remove_edge(&GraphIndex::from(-3)).unwrap(); -} - -#[test] -fn remove_edge_only() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let from = graph.insert_node().unwrap(); - let to = graph.insert_node().unwrap(); - let index = graph.insert_edge(&from, &to).unwrap(); - - graph.remove_edge(&index).unwrap(); - - assert!(graph.edge(&index).is_none()); -} diff --git a/crates/graph/tests/storage_graph_remove_node_test.rs b/crates/graph/tests/storage_graph_remove_node_test.rs deleted file mode 100644 index 8c1c762d..00000000 --- a/crates/graph/tests/storage_graph_remove_node_test.rs +++ /dev/null @@ -1,82 +0,0 @@ -use agdb_graph::GraphIndex; -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove_node_circular_edge() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let index = graph.insert_node().unwrap(); - let edge = graph.insert_edge(&index, &index).unwrap(); - - graph.remove_node(&index).unwrap(); - - assert!(graph.node(&index).is_none()); - assert!(graph.edge(&edge).is_none()); -} - -#[test] -fn remove_node_only() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - let index = graph.insert_node().unwrap(); - - graph.remove_node(&index).unwrap(); - - assert!(graph.node(&index).is_none()); -} - -#[test] -fn remove_node_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - graph.remove_node(&GraphIndex::from(1)).unwrap(); -} - -#[test] -fn remove_nodes_with_edges() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut graph = StorageGraph::try_from(storage).unwrap(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node1).unwrap(); - let edge3 = graph.insert_edge(&node1, &node3).unwrap(); - let edge4 = graph.insert_edge(&node2, &node1).unwrap(); - let edge5 = graph.insert_edge(&node3, &node1).unwrap(); - - let edge6 = graph.insert_edge(&node3, &node2).unwrap(); - let edge7 = graph.insert_edge(&node2, &node3).unwrap(); - - graph.remove_node(&node1).unwrap(); - - assert!(graph.node(&node1).is_none()); - assert!(graph.edge(&edge1).is_none()); - assert!(graph.edge(&edge2).is_none()); - assert!(graph.edge(&edge3).is_none()); - assert!(graph.edge(&edge4).is_none()); - assert!(graph.edge(&edge5).is_none()); - - assert!(graph.node(&node2).is_some()); - assert!(graph.node(&node3).is_some()); - assert!(graph.edge(&edge6).is_some()); - assert!(graph.edge(&edge7).is_some()); -} diff --git a/crates/graph/tests/storage_graph_test.rs b/crates/graph/tests/storage_graph_test.rs deleted file mode 100644 index b9c90201..00000000 --- a/crates/graph/tests/storage_graph_test.rs +++ /dev/null @@ -1,46 +0,0 @@ -use agdb_graph::StorageGraph; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn restore_from_file() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let index; - - let node1; - let node2; - let node3; - - let edge1; - let edge2; - let edge3; - - { - let mut graph = StorageGraph::try_from(storage.clone()).unwrap(); - - index = graph.storage_index(); - - node1 = graph.insert_node().unwrap(); - node2 = graph.insert_node().unwrap(); - node3 = graph.insert_node().unwrap(); - - edge1 = graph.insert_edge(&node1, &node2).unwrap(); - edge2 = graph.insert_edge(&node2, &node3).unwrap(); - edge3 = graph.insert_edge(&node3, &node1).unwrap(); - } - - let graph = StorageGraph::try_from((storage, index)).unwrap(); - - assert!(graph.node(&node1).is_some()); - assert!(graph.node(&node2).is_some()); - assert!(graph.node(&node3).is_some()); - assert!(graph.edge(&edge1).is_some()); - assert!(graph.edge(&edge2).is_some()); - assert!(graph.edge(&edge3).is_some()); -} diff --git a/crates/graph_search/Cargo.toml b/crates/graph_search/Cargo.toml deleted file mode 100644 index 97428d83..00000000 --- a/crates/graph_search/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "agdb_graph_search" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_graph = { path = "../graph", version = "<=0.1.0" } -agdb_bit_set = { path = "../bit_set", version = "<=0.1.0" } diff --git a/crates/graph_search/src/breadth_first_search.rs b/crates/graph_search/src/breadth_first_search.rs deleted file mode 100644 index 8e8f0f27..00000000 --- a/crates/graph_search/src/breadth_first_search.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::search_index::SearchIndex; -use crate::search_iterator::SearchIterator; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; -use std::mem::swap; -use std::vec::IntoIter; - -pub(crate) struct BreadthFirstSearch { - stack_iterator: IntoIter, -} - -impl BreadthFirstSearch { - fn take_stack(stack: &mut Vec) -> Vec { - let mut res = Vec::::new(); - swap(&mut res, stack); - - res - } -} - -impl SearchIterator for BreadthFirstSearch { - fn expand_edge( - index: &GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> GraphIndex { - graph.edge(index).unwrap().index_to() - } - - fn expand_node( - index: &GraphIndex, - graph: &GraphImpl, - ) -> Vec { - graph - .node(index) - .expect("invalid index, expected a valid node index") - .edge_iter_from() - .map(|edge| edge.index()) - .collect() - } - - fn new(stack: &mut Vec) -> Self { - Self { - stack_iterator: Self::take_stack(stack).into_iter(), - } - } - - fn next(&mut self) -> Option { - self.stack_iterator.next() - } -} diff --git a/crates/graph_search/src/breadth_first_search_reverse.rs b/crates/graph_search/src/breadth_first_search_reverse.rs deleted file mode 100644 index 80f13efe..00000000 --- a/crates/graph_search/src/breadth_first_search_reverse.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::search_index::SearchIndex; -use crate::search_iterator::SearchIterator; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; -use std::mem::swap; -use std::vec::IntoIter; - -pub(crate) struct BreadthFirstSearchReverse { - stack_iterator: IntoIter, -} - -impl BreadthFirstSearchReverse { - fn take_stack(stack: &mut Vec) -> Vec { - let mut res = Vec::::new(); - swap(&mut res, stack); - - res - } -} - -impl SearchIterator for BreadthFirstSearchReverse { - fn expand_edge( - index: &GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> GraphIndex { - graph.edge(index).unwrap().index_from() - } - - fn expand_node( - index: &GraphIndex, - graph: &GraphImpl, - ) -> Vec { - graph - .node(index) - .expect("invalid index, expected a valid node index") - .edge_iter_to() - .map(|edge| edge.index()) - .collect() - } - - fn new(stack: &mut Vec) -> Self { - Self { - stack_iterator: Self::take_stack(stack).into_iter(), - } - } - - fn next(&mut self) -> Option { - self.stack_iterator.next() - } -} diff --git a/crates/graph_search/src/depth_first_search.rs b/crates/graph_search/src/depth_first_search.rs deleted file mode 100644 index 244c3659..00000000 --- a/crates/graph_search/src/depth_first_search.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::search_index::SearchIndex; -use crate::search_iterator::SearchIterator; - -pub(crate) struct DepthFirstSearch { - index: Option, -} - -impl SearchIterator for DepthFirstSearch { - fn expand_edge( - index: &agdb_graph::GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> agdb_graph::GraphIndex { - graph - .edge(index) - .expect("invalid index, expected a valid edge index") - .index_to() - } - - fn expand_node( - index: &agdb_graph::GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> Vec { - graph - .node(index) - .expect("invalid index, expected a valid node index") - .edge_iter_from() - .map(|edge| edge.index()) - .collect() - } - - fn new(stack: &mut Vec) -> Self { - Self { index: stack.pop() } - } - - fn next(&mut self) -> Option { - self.index.take() - } -} diff --git a/crates/graph_search/src/depth_first_search_reverse.rs b/crates/graph_search/src/depth_first_search_reverse.rs deleted file mode 100644 index fc883df3..00000000 --- a/crates/graph_search/src/depth_first_search_reverse.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::search_index::SearchIndex; -use crate::search_iterator::SearchIterator; - -pub(crate) struct DepthFirstSearchReverse { - index: Option, -} - -impl SearchIterator for DepthFirstSearchReverse { - fn expand_edge( - index: &agdb_graph::GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> agdb_graph::GraphIndex { - graph - .edge(index) - .expect("invalid index, expected a valid edge index") - .index_from() - } - - fn expand_node( - index: &agdb_graph::GraphIndex, - graph: &agdb_graph::GraphImpl, - ) -> Vec { - graph - .node(index) - .expect("invalid index, expected a valid node index") - .edge_iter_to() - .map(|edge| edge.index()) - .collect() - } - - fn new(stack: &mut Vec) -> Self { - Self { index: stack.pop() } - } - - fn next(&mut self) -> Option { - self.index.take() - } -} diff --git a/crates/graph_search/src/graph_search_from.rs b/crates/graph_search/src/graph_search_from.rs deleted file mode 100644 index 7569bbc1..00000000 --- a/crates/graph_search/src/graph_search_from.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::graph_search::GraphSearch; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; - -impl<'a, Data> From<&'a GraphImpl> for GraphSearch<'a, Data> -where - Data: GraphData, -{ - fn from(graph: &'a GraphImpl) -> Self { - GraphSearch { graph } - } -} diff --git a/crates/graph_search/src/lib.rs b/crates/graph_search/src/lib.rs deleted file mode 100644 index 0ea6a035..00000000 --- a/crates/graph_search/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod breadth_first_search; -mod breadth_first_search_reverse; -mod depth_first_search; -mod depth_first_search_reverse; -mod graph_search; -mod graph_search_from; -mod path; -mod path_search_handler; -mod path_search_impl; -mod search_control; -mod search_handler; -mod search_impl; -mod search_index; -mod search_iterator; - -pub use graph_search::GraphSearch; -pub use path_search_handler::PathSearchHandler; -pub use search_control::SearchControl; -pub use search_handler::SearchHandler; diff --git a/crates/graph_search/src/path_search_impl.rs b/crates/graph_search/src/path_search_impl.rs deleted file mode 100644 index 8a73ba49..00000000 --- a/crates/graph_search/src/path_search_impl.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::path::Path; -use crate::PathSearchHandler; -use agdb_bit_set::BitSet; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; -use std::cmp::Ordering; -use std::mem::swap; -use std::mem::take; - -pub(crate) struct PathSearchImpl<'a, Data, Handler> -where - Data: GraphData, - Handler: PathSearchHandler, -{ - pub(crate) current_path: Path, - pub(crate) destination: GraphIndex, - pub(crate) graph: &'a GraphImpl, - pub(crate) handler: &'a Handler, - pub(crate) paths: Vec, - pub(crate) result: Vec, - pub(crate) visited: BitSet, -} - -impl<'a, Data, Handler> PathSearchImpl<'a, Data, Handler> -where - Data: GraphData, - Handler: PathSearchHandler, -{ - pub(crate) fn new( - graph: &'a GraphImpl, - from: GraphIndex, - to: GraphIndex, - handler: &'a Handler, - ) -> Self { - Self { - current_path: Path { - elements: vec![], - cost: 0, - }, - destination: to, - graph, - handler, - paths: vec![Path { - elements: vec![from], - cost: 0, - }], - result: vec![], - visited: BitSet::new(), - } - } - - pub(crate) fn search(&mut self) -> Vec { - while !self.is_finished() { - self.sort_paths(); - self.process_last_path(); - } - - take(&mut self.result) - } - - fn expand_edge(&mut self, mut path: Path, index: &GraphIndex, node_index: &GraphIndex) { - let cost = self - .handler - .process(index, &(self.current_path.elements.len() as u64 + 1)); - - if cost != 0 && !self.visited.value(node_index.as_u64()) { - path.elements.push(index.clone()); - path.cost += cost; - self.expand_node(path, node_index); - } - } - - fn expand_node(&mut self, mut path: Path, index: &GraphIndex) { - let cost = self - .handler - .process(index, &(self.current_path.elements.len() as u64 + 1)); - - if cost != 0 { - path.elements.push(index.clone()); - path.cost += cost; - self.paths.push(path); - } - } - - fn expand(&mut self, index: &GraphIndex) { - let node = self - .graph - .node(index) - .expect("unexpected invalid node index"); - for edge in node.edge_iter_from() { - self.expand_edge(self.current_path.clone(), &edge.index(), &edge.index_to()); - } - } - - fn is_finished(&self) -> bool { - self.paths.is_empty() || !self.result.is_empty() - } - - fn process_path(&mut self) { - let index = self - .current_path - .elements - .last() - .map_or(GraphIndex::default(), |index| index.clone()); - self.process_index(&index); - } - - fn process_index(&mut self, index: &GraphIndex) { - if !self.visited.value(index.as_u64()) { - if index.value() == self.destination.value() { - swap(&mut self.result, &mut self.current_path.elements); - } else { - self.visited.insert(index.as_u64()); - self.expand(index); - } - } - } - - fn process_last_path(&mut self) { - self.current_path = self.paths.pop().unwrap_or(Path { - elements: vec![], - cost: 0, - }); - self.process_path(); - } - - fn sort_paths(&mut self) { - self.paths.sort_by(|left, right| { - let ordering = left.cost.cmp(&right.cost); - - if ordering == Ordering::Equal { - return left.elements.len().cmp(&right.elements.len()).reverse(); - } - - ordering.reverse() - }); - } -} diff --git a/crates/graph_search/tests/breadth_first_search_test.rs b/crates/graph_search/tests/breadth_first_search_test.rs deleted file mode 100644 index 6c72ddcd..00000000 --- a/crates/graph_search/tests/breadth_first_search_test.rs +++ /dev/null @@ -1,215 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; -use agdb_graph_search::GraphSearch; -use agdb_graph_search::SearchControl; -use agdb_graph_search::SearchHandler; - -struct Handler { - pub processor: fn(&GraphIndex, &u64) -> SearchControl, -} - -impl Default for Handler { - fn default() -> Self { - Self { - processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), - } - } -} - -impl SearchHandler for Handler { - fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { - (self.processor)(index, distance) - } -} - -#[test] -fn empty_graph() { - let graph = Graph::new(); - - let result = - GraphSearch::from(&graph).breadth_first_search(&GraphIndex::default(), &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn empty_graph_reverse() { - let graph = Graph::new(); - - let result = GraphSearch::from(&graph) - .breadth_first_search_reverse(&GraphIndex::default(), &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn cyclic_graph() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node2, &node3).unwrap(); - let edge4 = graph.insert_edge(&node2, &node3).unwrap(); - let edge5 = graph.insert_edge(&node3, &node1).unwrap(); - let edge6 = graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); - - assert_eq!( - result, - vec![node1, edge2, edge1, node2, edge4, edge3, node3, edge6, edge5] - ); -} - -#[test] -fn full_search() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node3).unwrap(); - let edge3 = graph.insert_edge(&node1, &node4).unwrap(); - - let result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); - - assert_eq!( - result, - vec![node1, edge3, edge2, edge1, node4, node3, node2] - ); -} - -#[test] -fn filter_edges() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node1, &node3).unwrap(); - graph.insert_edge(&node1, &node4).unwrap(); - - let result = GraphSearch::from(&graph).breadth_first_search( - &node1, - &Handler { - processor: |index: &GraphIndex, _distance: &u64| { - SearchControl::Continue(index.is_node()) - }, - }, - ); - - assert_eq!(result, vec![node1, node4, node3, node2]); -} - -#[test] -fn finish_search() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node2, &node3).unwrap(); - graph.insert_edge(&node2, &node3).unwrap(); - graph.insert_edge(&node3, &node1).unwrap(); - graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).breadth_first_search( - &node1, - &Handler { - processor: |index: &GraphIndex, _distance: &u64| { - if index.value() == 2 { - SearchControl::Finish(true) - } else { - SearchControl::Continue(false) - } - }, - }, - ); - - assert_eq!(result, vec![node2]); -} - -#[test] -fn search_twice() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node3).unwrap(); - let edge3 = graph.insert_edge(&node1, &node4).unwrap(); - - let mut result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); - let expected = vec![node1.clone(), edge3, edge2, edge1, node4, node3, node2]; - - assert_eq!(result, expected); - - result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); - - assert_eq!(result, expected); -} - -#[test] -fn search_in_reverse() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node2, &node3).unwrap(); - let edge3 = graph.insert_edge(&node3, &node4).unwrap(); - - let result = - GraphSearch::from(&graph).breadth_first_search_reverse(&node4, &Handler::default()); - let expected = vec![node4.clone(), edge3, node3, edge2, node2, edge1, node1]; - - assert_eq!(result, expected); -} - -#[test] -fn stop_at_distance() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); - let _edge4 = graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).breadth_first_search( - &node1, - &Handler { - processor: |_index: &GraphIndex, distance: &u64| { - if *distance == 2 { - SearchControl::Stop(true) - } else { - SearchControl::Continue(true) - } - }, - }, - ); - - assert_eq!(result, vec![node1, edge2, edge1, node2]); -} diff --git a/crates/graph_search/tests/depth_first_search_test.rs b/crates/graph_search/tests/depth_first_search_test.rs deleted file mode 100644 index c6043dfa..00000000 --- a/crates/graph_search/tests/depth_first_search_test.rs +++ /dev/null @@ -1,214 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; -use agdb_graph_search::GraphSearch; -use agdb_graph_search::SearchControl; -use agdb_graph_search::SearchHandler; - -struct Handler { - pub processor: fn(&GraphIndex, &u64) -> SearchControl, -} - -impl Default for Handler { - fn default() -> Self { - Self { - processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), - } - } -} - -impl SearchHandler for Handler { - fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { - (self.processor)(index, distance) - } -} - -#[test] -fn empty_graph() { - let graph = Graph::new(); - - let result = - GraphSearch::from(&graph).depth_first_search(&GraphIndex::default(), &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn empty_graph_reverse() { - let graph = Graph::new(); - - let result = GraphSearch::from(&graph) - .depth_first_search_reverse(&GraphIndex::default(), &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn cyclic_graph() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node2, &node3).unwrap(); - let edge4 = graph.insert_edge(&node2, &node3).unwrap(); - let edge5 = graph.insert_edge(&node3, &node1).unwrap(); - let edge6 = graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); - - assert_eq!( - result, - vec![node1, edge1, node2, edge3, node3, edge5, edge6, edge4, edge2] - ); -} - -#[test] -fn full_search() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node3).unwrap(); - let edge3 = graph.insert_edge(&node1, &node4).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); - - assert_eq!( - result, - vec![node1, edge1, node2, edge2, node3, edge3, node4] - ); -} - -#[test] -fn filter_edges() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node1, &node3).unwrap(); - graph.insert_edge(&node1, &node4).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search( - &node1, - &Handler { - processor: |index: &GraphIndex, _distance: &u64| { - SearchControl::Continue(index.is_node()) - }, - }, - ); - - assert_eq!(result, vec![node1, node2, node3, node4]); -} - -#[test] -fn finish_search() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node1, &node2).unwrap(); - graph.insert_edge(&node2, &node3).unwrap(); - graph.insert_edge(&node2, &node3).unwrap(); - graph.insert_edge(&node3, &node1).unwrap(); - graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search( - &node1, - &Handler { - processor: |index: &GraphIndex, _distance: &u64| { - if index.value() == 2 { - SearchControl::Finish(true) - } else { - SearchControl::Continue(false) - } - }, - }, - ); - - assert_eq!(result, vec![node2]); -} - -#[test] -fn search_twice() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node3).unwrap(); - let edge3 = graph.insert_edge(&node1, &node4).unwrap(); - - let mut result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); - let expected = vec![node1.clone(), edge1, node2, edge2, node3, edge3, node4]; - - assert_eq!(result, expected); - - result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); - - assert_eq!(result, expected); -} - -#[test] -fn search_in_reverse() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - let node4 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node2, &node3).unwrap(); - let edge3 = graph.insert_edge(&node3, &node4).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search_reverse(&node4, &Handler::default()); - let expected = vec![node4.clone(), edge3, node3, edge2, node2, edge1, node1]; - - assert_eq!(result, expected); -} - -#[test] -fn stop_at_distance() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); - let _edge4 = graph.insert_edge(&node3, &node1).unwrap(); - - let result = GraphSearch::from(&graph).depth_first_search( - &node1, - &Handler { - processor: |_index: &GraphIndex, distance: &u64| { - if *distance == 2 { - SearchControl::Stop(true) - } else { - SearchControl::Continue(true) - } - }, - }, - ); - - assert_eq!(result, vec![node1, edge1, node2, edge2]); -} diff --git a/crates/graph_search/tests/path_search_test.rs b/crates/graph_search/tests/path_search_test.rs deleted file mode 100644 index 8289aaee..00000000 --- a/crates/graph_search/tests/path_search_test.rs +++ /dev/null @@ -1,152 +0,0 @@ -use agdb_graph::Graph; -use agdb_graph::GraphIndex; -use agdb_graph_search::GraphSearch; -use agdb_graph_search::PathSearchHandler; - -struct Handler { - pub processor: fn(&GraphIndex, &u64) -> u64, -} - -impl Default for Handler { - fn default() -> Self { - Self { - processor: |_index: &GraphIndex, _distance: &u64| 1_u64, - } - } -} - -impl PathSearchHandler for Handler { - fn process(&self, index: &GraphIndex, distance: &u64) -> u64 { - (self.processor)(index, distance) - } -} - -#[test] -fn circular_path() { - let mut graph = Graph::new(); - let node = graph.insert_node().unwrap(); - let _edge = graph.insert_edge(&node, &node).unwrap(); - - let result = GraphSearch::from(&graph).path(&node, &node, &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn empty_graph() { - let graph = Graph::new(); - - let result = GraphSearch::from(&graph).path( - &GraphIndex::default(), - &GraphIndex::default(), - &Handler::default(), - ); - - assert_eq!(result, vec![]); -} - -#[test] -fn multi_edge_path() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let _edge2 = graph.insert_edge(&node1, &node2).unwrap(); - - let edge3 = graph.insert_edge(&node2, &node3).unwrap(); - let _edge4 = graph.insert_edge(&node2, &node3).unwrap(); - - let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); - - assert_eq!(result, vec![node1, edge1, node2, edge3, node3]); -} - -#[test] -fn origin_is_destination() { - let mut graph = Graph::new(); - let node = graph.insert_node().unwrap(); - - let result = GraphSearch::from(&graph).path(&node, &node, &Handler::default()); - - assert_eq!(result, vec![]); -} - -#[test] -fn short_circuit_path() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node3).unwrap(); - let _edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); - - let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); - - assert_eq!(result, vec![node1, edge1, node3]); -} - -#[test] -fn single_path() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let edge1 = graph.insert_edge(&node1, &node2).unwrap(); - let edge2 = graph.insert_edge(&node2, &node3).unwrap(); - - let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); - - assert_eq!(result, vec![node1, edge1, node2, edge2, node3]); -} - -#[test] -fn skip_short_circuit_path() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let _edge1 = graph.insert_edge(&node1, &node3).unwrap(); - let edge2 = graph.insert_edge(&node1, &node2).unwrap(); - let edge3 = graph.insert_edge(&node2, &node3).unwrap(); - - let result = GraphSearch::from(&graph).path( - &node1, - &node3, - &Handler { - processor: |index: &GraphIndex, _distance: &u64| { - if index.value() == -4 { - return 0; - } - - 1 - }, - }, - ); - - assert_eq!(result, vec![node1, edge2, node2, edge3, node3]); -} - -#[test] -fn unconnected() { - let mut graph = Graph::new(); - - let node1 = graph.insert_node().unwrap(); - let node2 = graph.insert_node().unwrap(); - let node3 = graph.insert_node().unwrap(); - - let _edge1 = graph.insert_edge(&node1, &node2).unwrap(); - - let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); - - assert_eq!(result, vec![]); -} diff --git a/crates/map_common/Cargo.toml b/crates/map_common/Cargo.toml deleted file mode 100644 index 8c2c0da8..00000000 --- a/crates/map_common/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "agdb_map_common" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_utilities = { path = "../utilities", version = "<=0.1.0" } \ No newline at end of file diff --git a/crates/map_common/src/lib.rs b/crates/map_common/src/lib.rs deleted file mode 100644 index 0d50e72f..00000000 --- a/crates/map_common/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod map_common; -mod map_common_from; -mod map_data; -mod map_iterator; -mod map_value; -mod map_value_serialize; -mod map_value_state; -mod map_value_state_serialize; - -pub use map_common::MapCommon; -pub use map_data::MapData; -pub use map_iterator::MapIterator; -pub use map_value::MapValue; -pub use map_value_state::MapValueState; diff --git a/crates/map_common/src/map_common_from.rs b/crates/map_common/src/map_common_from.rs deleted file mode 100644 index 8849dc8d..00000000 --- a/crates/map_common/src/map_common_from.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::map_common::MapCommon; -use crate::map_data::MapData; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; -use std::hash::Hash; -use std::marker::PhantomData; - -impl From for MapCommon -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: MapData, -{ - fn from(data: Data) -> Self { - Self { - data, - phantom_data: PhantomData, - } - } -} diff --git a/crates/map_common/src/map_value.rs b/crates/map_common/src/map_value.rs deleted file mode 100644 index 60ed7ad1..00000000 --- a/crates/map_common/src/map_value.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::map_value_state::MapValueState; -use agdb_serialize::Serialize; - -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct MapValue -where - K: Clone + Default + Serialize, - T: Clone + Default + Serialize, -{ - pub state: MapValueState, - pub key: K, - pub value: T, -} diff --git a/crates/map_common/src/map_value_serialize.rs b/crates/map_common/src/map_value_serialize.rs deleted file mode 100644 index c2bbcb0c..00000000 --- a/crates/map_common/src/map_value_serialize.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::map_value::MapValue; -use crate::map_value_state::MapValueState; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -impl Serialize for MapValue -where - K: Clone + Default + Serialize, - T: Clone + Default + Serialize, -{ - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - state: MapValueState::deserialize(bytes)?, - key: K::deserialize(&bytes[(MapValueState::serialized_size() as usize)..])?, - value: T::deserialize( - &bytes[((MapValueState::serialized_size() + K::serialized_size()) as usize)..], - )?, - }) - } - - fn serialize(&self) -> Vec { - let mut data = Vec::::new(); - data.reserve(Self::serialized_size() as usize); - data.extend(self.state.serialize()); - data.extend(self.key.serialize()); - data.extend(self.value.serialize()); - - data - } - - fn serialized_size() -> u64 { - MapValueState::serialized_size() + K::serialized_size() + T::serialized_size() - } -} diff --git a/crates/map_common/src/map_value_state.rs b/crates/map_common/src/map_value_state.rs deleted file mode 100644 index 7be84195..00000000 --- a/crates/map_common/src/map_value_state.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[derive(Clone, Default, Debug, Eq, PartialEq)] -pub enum MapValueState { - #[default] - Empty, - Deleted, - Valid, -} diff --git a/crates/map_common/src/map_value_state_serialize.rs b/crates/map_common/src/map_value_state_serialize.rs deleted file mode 100644 index 90101134..00000000 --- a/crates/map_common/src/map_value_state_serialize.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::map_value_state::MapValueState; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::mem::size_of; - -impl Serialize for MapValueState { - fn deserialize(bytes: &[u8]) -> Result { - match bytes.first() { - Some(0) => Ok(MapValueState::Empty), - Some(1) => Ok(MapValueState::Valid), - Some(2) => Ok(MapValueState::Deleted), - _ => Err(DbError::from("value out of bounds")), - } - } - - fn serialize(&self) -> Vec { - match self { - MapValueState::Empty => vec![0_u8], - MapValueState::Deleted => vec![2_u8], - MapValueState::Valid => vec![1_u8], - } - } - - fn serialized_size() -> u64 { - size_of::() as u64 - } -} diff --git a/crates/map_common/tests/map_value_state_test.rs b/crates/map_common/tests/map_value_state_test.rs deleted file mode 100644 index 68ecac0d..00000000 --- a/crates/map_common/tests/map_value_state_test.rs +++ /dev/null @@ -1,41 +0,0 @@ -use agdb_db_error::DbError; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; - -#[test] -fn bad_deserialization() { - assert_eq!( - MapValueState::deserialize(&[10_u8]), - Err(DbError::from("value out of bounds")) - ); -} - -#[test] -fn derived_from_default() { - assert_eq!(MapValueState::default(), MapValueState::Empty); -} - -#[test] -fn derived_from_debug() { - let value = MapValueState::Deleted; - - format!("{:?}", value); -} - -#[test] -fn serialize() { - let data = vec![ - MapValueState::Valid, - MapValueState::Empty, - MapValueState::Deleted, - ]; - let bytes = data.serialize(); - let other = Vec::::deserialize(&bytes).unwrap(); - - assert_eq!(data, other); -} - -#[test] -fn serialized_size() { - assert_eq!(MapValueState::serialized_size(), 1); -} diff --git a/crates/map_common/tests/map_value_test.rs b/crates/map_common/tests/map_value_test.rs deleted file mode 100644 index 8db73a9c..00000000 --- a/crates/map_common/tests/map_value_test.rs +++ /dev/null @@ -1,54 +0,0 @@ -use agdb_map_common::MapValue; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; - -#[test] -fn derived_from_debug() { - let key_value = MapValue::::default(); - - format!("{:?}", key_value); -} - -#[test] -fn derived_from_default() { - let key_value = MapValue::::default(); - - assert_eq!( - key_value, - MapValue:: { - state: MapValueState::Empty, - key: 0, - value: 0, - } - ) -} - -#[test] -fn i64_i64() { - let key_value = MapValue { - state: MapValueState::Valid, - key: 1_i64, - value: 10_i64, - }; - let bytes = key_value.serialize(); - let other = MapValue::deserialize(&bytes); - - assert_eq!(other, Ok(key_value)); -} - -#[test] -fn out_of_bounds() { - let bytes = vec![0_u8; 16]; - - assert_eq!( - MapValue::::deserialize(&bytes) - .unwrap_err() - .description, - "i64 deserialization error: out of bounds" - ); -} - -#[test] -fn serialized_size() { - assert_eq!(MapValue::::serialized_size(), 17); -} diff --git a/crates/multi_map/Cargo.toml b/crates/multi_map/Cargo.toml deleted file mode 100644 index 54c2e01c..00000000 --- a/crates/multi_map/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "agdb_multi_map" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_map_common = { path = "../map_common", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage = { path = "../storage", version = "<=0.1.0" } -agdb_storage_map = { path = "../storage_map", version = "<=0.1.0" } -agdb_utilities = { path = "../utilities", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/multi_map/src/lib.rs b/crates/multi_map/src/lib.rs deleted file mode 100644 index 643bf09f..00000000 --- a/crates/multi_map/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod map_data_memory; -mod map_data_memory_default; -mod multi_map; -mod multi_map_default; -mod multi_map_impl; -mod storage_multi_map; - -pub use multi_map::MultiMap; -pub use storage_multi_map::StorageMultiMap; diff --git a/crates/multi_map/src/map_data_memory_default.rs b/crates/multi_map/src/map_data_memory_default.rs deleted file mode 100644 index 9b513898..00000000 --- a/crates/multi_map/src/map_data_memory_default.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::map_data_memory::MapDataMemory; -use agdb_map_common::MapValue; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; -use std::hash::Hash; - -impl Default for MapDataMemory -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - fn default() -> Self { - Self { - data: vec![MapValue::::default()], - count: 0, - } - } -} diff --git a/crates/multi_map/src/multi_map.rs b/crates/multi_map/src/multi_map.rs deleted file mode 100644 index a652178b..00000000 --- a/crates/multi_map/src/multi_map.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::map_data_memory::MapDataMemory; -use crate::multi_map_impl::MultiMapImpl; -use agdb_map_common::MapCommon; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; -use std::hash::Hash; - -pub type MultiMap = MultiMapImpl>; - -impl MultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - pub fn new() -> MultiMap { - MultiMap:: { - map_common: MapCommon::>::from( - MapDataMemory::::default(), - ), - } - } -} diff --git a/crates/multi_map/src/multi_map_default.rs b/crates/multi_map/src/multi_map_default.rs deleted file mode 100644 index b2c04fa8..00000000 --- a/crates/multi_map/src/multi_map_default.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::MultiMap; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; -use std::hash::Hash; - -impl Default for MultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - fn default() -> Self { - Self::new() - } -} diff --git a/crates/multi_map/src/storage_multi_map.rs b/crates/multi_map/src/storage_multi_map.rs deleted file mode 100644 index fbc4a783..00000000 --- a/crates/multi_map/src/storage_multi_map.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::map_data_memory::MapDataMemory; -use crate::multi_map::MultiMap; -use crate::multi_map_impl::MultiMapImpl; -use agdb_db_error::DbError; -use agdb_map_common::MapCommon; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_storage_map::MapDataStorage; -use agdb_utilities::StableHash; -use std::cell::RefCell; -use std::hash::Hash; -use std::rc::Rc; - -pub type StorageMultiMap = MultiMapImpl>; - -impl StorageMultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: Storage, -{ - pub fn storage_index(&self) -> StorageIndex { - self.map_common.data.storage_index() - } - - pub fn to_multi_map(&self) -> Result, DbError> { - Ok(MultiMap:: { - map_common: MapCommon::from(MapDataMemory:: { - data: self.map_common.data.values()?, - count: self.map_common.data.count(), - }), - }) - } -} - -impl TryFrom>> for StorageMultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - Ok(Self { - map_common: MapCommon::from(MapDataStorage::try_from(storage)?), - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageMultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - Ok(Self { - map_common: MapCommon::from(MapDataStorage::try_from(storage_with_index)?), - }) - } -} diff --git a/crates/multi_map/tests/multi_map_insert_test.rs b/crates/multi_map/tests/multi_map_insert_test.rs deleted file mode 100644 index 49fe97e4..00000000 --- a/crates/multi_map/tests/multi_map_insert_test.rs +++ /dev/null @@ -1,61 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn insert() { - let mut map = MultiMap::::new(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(15))); - assert_eq!(map.value(&7), Ok(Some(20))); -} - -#[test] -fn insert_reallocate() { - let mut map = MultiMap::::new(); - - assert_eq!(map.capacity(), 1); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - assert_eq!(map.value(&i), Ok(Some(i))); - } -} - -#[test] -fn insert_reallocate_with_collisions() { - let mut map = MultiMap::::new(); - - for i in 0..50 { - map.insert(i * 64, i).unwrap(); - map.insert(i * 64, i + 1).unwrap(); - } - - for i in 0..50 { - assert_eq!(map.value(&(i * 64)), Ok(Some(i))); - } -} - -#[test] -fn insert_same_key() { - let mut map = MultiMap::::new(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - assert_eq!(map.count(), 2); - map.insert(5, 20).unwrap(); - assert_eq!(map.count(), 3); - - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(15))); -} diff --git a/crates/multi_map/tests/multi_map_remove_key_test.rs b/crates/multi_map/tests/multi_map_remove_key_test.rs deleted file mode 100644 index d1a4d3e0..00000000 --- a/crates/multi_map/tests/multi_map_remove_key_test.rs +++ /dev/null @@ -1,68 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn remove_deleted_key() { - let mut map = MultiMap::::new(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 2); - assert_eq!(map.value(&5), Ok(None)); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 2); -} - -#[test] -fn remove_key() { - let mut map = MultiMap::::new(); - - map.insert(1, 7).unwrap(); - map.insert(5, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 20).unwrap(); - - assert_eq!(map.count(), 4); - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 1); - assert_eq!(map.value(&1), Ok(Some(7))); - assert_eq!(map.values(&5), Ok(Vec::::new())); -} - -#[test] -fn remove_missing_key() { - let mut map = MultiMap::::new(); - - assert_eq!(map.count(), 0); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 0); -} - -#[test] -fn remove_shrinks_capacity() { - let mut map = MultiMap::::new(); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - map.remove_key(&i).unwrap(); - } - - assert_eq!(map.count(), 0); - assert_eq!(map.capacity(), 64); -} diff --git a/crates/multi_map/tests/multi_map_remove_value_test.rs b/crates/multi_map/tests/multi_map_remove_value_test.rs deleted file mode 100644 index ac45b9db..00000000 --- a/crates/multi_map/tests/multi_map_remove_value_test.rs +++ /dev/null @@ -1,27 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn remove_value() { - let mut map = MultiMap::::new(); - - map.insert(1, 7).unwrap(); - map.insert(5, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 20).unwrap(); - - assert_eq!(map.count(), 4); - map.remove_value(&5, &15).unwrap(); - - assert_eq!(map.count(), 3); - assert_eq!(map.value(&1), Ok(Some(7))); - assert_eq!(map.values(&5), Ok(vec![10_i64, 20_i64])); -} - -#[test] -fn remove_missing_value() { - let mut map = MultiMap::::new(); - - map.remove_value(&5, &10).unwrap(); - - assert_eq!(map.count(), 0); -} diff --git a/crates/multi_map/tests/multi_map_reserve_test.rs b/crates/multi_map/tests/multi_map_reserve_test.rs deleted file mode 100644 index 5f8af261..00000000 --- a/crates/multi_map/tests/multi_map_reserve_test.rs +++ /dev/null @@ -1,45 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn reserve_larger() { - let mut map = MultiMap::::new(); - map.insert(1, 1).unwrap(); - - let capacity = map.capacity() + 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); - assert_eq!(map.value(&1), Ok(Some(1))); -} - -#[test] -fn reserve_same() { - let mut map = MultiMap::::new(); - map.insert(1, 1).unwrap(); - - let capacity = map.capacity(); - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); -} - -#[test] -fn reserve_smaller() { - let mut map = MultiMap::::new(); - map.insert(1, 1).unwrap(); - - let current_capacity = map.capacity(); - let capacity = current_capacity - 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), current_capacity); - assert_eq!(map.count(), size); -} diff --git a/crates/multi_map/tests/multi_map_test.rs b/crates/multi_map/tests/multi_map_test.rs deleted file mode 100644 index 9508fc0c..00000000 --- a/crates/multi_map/tests/multi_map_test.rs +++ /dev/null @@ -1,26 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn derived_from_default() { - let mut _map = MultiMap::::default(); -} - -#[test] -fn iter() { - let mut map = MultiMap::::new(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - map.insert(2, 30).unwrap(); - map.insert(2, 50).unwrap(); - map.insert(4, 13).unwrap(); - map.remove_key(&7).unwrap(); - - let mut actual = map.iter().collect::>(); - actual.sort(); - let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (2, 50), (4, 13), (5, 15), (5, 15)]; - - assert_eq!(actual, expected); -} diff --git a/crates/multi_map/tests/multi_map_value_test.rs b/crates/multi_map/tests/multi_map_value_test.rs deleted file mode 100644 index 6ec4f639..00000000 --- a/crates/multi_map/tests/multi_map_value_test.rs +++ /dev/null @@ -1,21 +0,0 @@ -use agdb_multi_map::MultiMap; - -#[test] -fn value_missing() { - let map = MultiMap::::new(); - - assert_eq!(map.value(&0), Ok(None)); -} - -#[test] -fn values_at_end() { - let mut map = MultiMap::::new(); - - map.insert(127, 10).unwrap(); - map.insert(255, 11).unwrap(); - map.insert(191, 12).unwrap(); - - assert_eq!(map.value(&127), Ok(Some(10))); - assert_eq!(map.value(&255), Ok(Some(11))); - assert_eq!(map.value(&191), Ok(Some(12))); -} diff --git a/crates/multi_map/tests/storage_multi_map_insert_test.rs b/crates/multi_map/tests/storage_multi_map_insert_test.rs deleted file mode 100644 index 29b69143..00000000 --- a/crates/multi_map/tests/storage_multi_map_insert_test.rs +++ /dev/null @@ -1,81 +0,0 @@ -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn insert() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(15))); - assert_eq!(map.value(&7), Ok(Some(20))); -} - -#[test] -fn insert_reallocate() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - assert_eq!(map.capacity(), 1); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - assert_eq!(map.value(&i), Ok(Some(i))); - } -} - -#[test] -fn insert_reallocate_with_collisions() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - for i in 0..50 { - map.insert(i * 64, i).unwrap(); - map.insert(i * 64, i + 1).unwrap(); - } - - for i in 0..50 { - assert_eq!(map.value(&(i * 64)), Ok(Some(i))); - } -} - -#[test] -fn insert_same_key() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - assert_eq!(map.count(), 2); - map.insert(5, 20).unwrap(); - assert_eq!(map.count(), 3); - - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(15))); -} diff --git a/crates/multi_map/tests/storage_multi_map_remove_key_test.rs b/crates/multi_map/tests/storage_multi_map_remove_key_test.rs deleted file mode 100644 index 5c7dc106..00000000 --- a/crates/multi_map/tests/storage_multi_map_remove_key_test.rs +++ /dev/null @@ -1,88 +0,0 @@ -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove_deleted_key() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 2); - assert_eq!(map.value(&5), Ok(None)); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 2); -} - -#[test] -fn remove_key() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 7).unwrap(); - map.insert(5, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 20).unwrap(); - - assert_eq!(map.count(), 4); - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 1); - assert_eq!(map.value(&1), Ok(Some(7))); - assert_eq!(map.values(&5), Ok(Vec::::new())); -} - -#[test] -fn remove_missing_key() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - assert_eq!(map.count(), 0); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 0); -} - -#[test] -fn remove_shrinks_capacity() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - map.remove_key(&i).unwrap(); - } - - assert_eq!(map.count(), 0); - assert_eq!(map.capacity(), 64); -} diff --git a/crates/multi_map/tests/storage_multi_map_remove_value_test.rs b/crates/multi_map/tests/storage_multi_map_remove_value_test.rs deleted file mode 100644 index e7d748c4..00000000 --- a/crates/multi_map/tests/storage_multi_map_remove_value_test.rs +++ /dev/null @@ -1,39 +0,0 @@ -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove_value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 7).unwrap(); - map.insert(5, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 20).unwrap(); - - assert_eq!(map.count(), 4); - map.remove_value(&5, &15).unwrap(); - - assert_eq!(map.count(), 3); - assert_eq!(map.value(&1), Ok(Some(7))); - assert_eq!(map.values(&5), Ok(vec![10_i64, 20_i64])); -} - -#[test] -fn remove_missing_value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.remove_value(&5, &10).unwrap(); - - assert_eq!(map.count(), 0); -} diff --git a/crates/multi_map/tests/storage_multi_map_reserve_test.rs b/crates/multi_map/tests/storage_multi_map_reserve_test.rs deleted file mode 100644 index 38bf6424..00000000 --- a/crates/multi_map/tests/storage_multi_map_reserve_test.rs +++ /dev/null @@ -1,64 +0,0 @@ -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn reserve_larger() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 1).unwrap(); - - let capacity = map.capacity() + 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); - assert_eq!(map.value(&1), Ok(Some(1))); -} - -#[test] -fn reserve_same() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 1).unwrap(); - - let capacity = map.capacity(); - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); -} - -#[test] -fn reserve_smaller() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 1).unwrap(); - - let current_capacity = map.capacity(); - let capacity = current_capacity - 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), current_capacity); - assert_eq!(map.count(), size); -} diff --git a/crates/multi_map/tests/storage_multi_map_test.rs b/crates/multi_map/tests/storage_multi_map_test.rs deleted file mode 100644 index 2103489a..00000000 --- a/crates/multi_map/tests/storage_multi_map_test.rs +++ /dev/null @@ -1,107 +0,0 @@ -use agdb_db_error::DbError; -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn iter() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - map.insert(2, 30).unwrap(); - map.insert(2, 50).unwrap(); - map.insert(4, 13).unwrap(); - map.remove_key(&7).unwrap(); - - let mut actual = map.iter().collect::>(); - actual.sort(); - let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (2, 50), (4, 13), (5, 15), (5, 15)]; - - assert_eq!(actual, expected); -} - -#[test] -fn to_multi_map() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - map.remove_key(&5).unwrap(); - - let other = map.to_multi_map().unwrap(); - - assert_eq!(other.count(), 2); - assert_eq!(other.value(&1).unwrap(), Some(10)); - assert_eq!(other.value(&5).unwrap(), None); - assert_eq!(other.value(&7).unwrap(), Some(20)); -} - -#[test] -fn to_multi_map_empty() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let map = StorageMultiMap::::try_from(storage).unwrap(); - let other = map.to_multi_map().unwrap(); - - assert_eq!(other.count(), 0); -} - -#[test] -fn try_from_storage_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let index; - - { - let mut map = StorageMultiMap::::try_from(storage.clone()).unwrap(); - map.insert(1, 1).unwrap(); - map.insert(3, 2).unwrap(); - map.insert(3, 3).unwrap(); - map.insert(5, 3).unwrap(); - map.remove_key(&1).unwrap(); - index = map.storage_index(); - } - - let map = StorageMultiMap::::try_from((storage, index)).unwrap(); - - assert_eq!( - map.iter().collect::>(), - vec![(3_i64, 2_i64), (3_i64, 3_i64), (5_i64, 3_i64)] - ); -} - -#[test] -fn try_from_storage_missing_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - assert_eq!( - StorageMultiMap::::try_from((storage, StorageIndex::from(1_i64))) - .err() - .unwrap(), - DbError::from("index '1' not found") - ); -} diff --git a/crates/multi_map/tests/storage_multi_map_value_test.rs b/crates/multi_map/tests/storage_multi_map_value_test.rs deleted file mode 100644 index d4df3be3..00000000 --- a/crates/multi_map/tests/storage_multi_map_value_test.rs +++ /dev/null @@ -1,33 +0,0 @@ -use agdb_multi_map::StorageMultiMap; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn value_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let map = StorageMultiMap::::try_from(storage).unwrap(); - - assert_eq!(map.value(&0), Ok(None)); -} - -#[test] -fn values_at_end() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - let mut map = StorageMultiMap::::try_from(storage).unwrap(); - - map.insert(127, 10).unwrap(); - map.insert(255, 11).unwrap(); - map.insert(191, 12).unwrap(); - - assert_eq!(map.value(&127), Ok(Some(10))); - assert_eq!(map.value(&255), Ok(Some(11))); - assert_eq!(map.value(&191), Ok(Some(12))); -} diff --git a/crates/serialize/Cargo.toml b/crates/serialize/Cargo.toml deleted file mode 100644 index 5e6e8999..00000000 --- a/crates/serialize/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "agdb_serialize" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } diff --git a/crates/serialize/src/i64.rs b/crates/serialize/src/i64.rs deleted file mode 100644 index d98f1836..00000000 --- a/crates/serialize/src/i64.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::Serialize; -use agdb_db_error::DbError; -use std::mem::size_of; - -impl Serialize for i64 { - fn deserialize(bytes: &[u8]) -> Result { - let buffer: [u8; size_of::()] = bytes - .get(0..size_of::()) - .ok_or_else(|| DbError::from("i64 deserialization error: out of bounds"))? - .try_into() - .unwrap(); - Ok(Self::from_le_bytes(buffer)) - } - - fn serialize(&self) -> Vec { - self.to_le_bytes().into() - } -} diff --git a/crates/serialize/src/lib.rs b/crates/serialize/src/lib.rs deleted file mode 100644 index 58daa039..00000000 --- a/crates/serialize/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod i64; -mod serialize; -mod string; -mod u64; -mod vec; -mod vec_u8; - -pub use serialize::Serialize; diff --git a/crates/serialize/src/serialize.rs b/crates/serialize/src/serialize.rs deleted file mode 100644 index 5a0eb046..00000000 --- a/crates/serialize/src/serialize.rs +++ /dev/null @@ -1,11 +0,0 @@ -use agdb_db_error::DbError; -use std::mem::size_of; - -pub trait Serialize: Sized { - fn deserialize(bytes: &[u8]) -> Result; - fn serialize(&self) -> Vec; - - fn serialized_size() -> u64 { - size_of::() as u64 - } -} diff --git a/crates/serialize/src/string.rs b/crates/serialize/src/string.rs deleted file mode 100644 index fa75d010..00000000 --- a/crates/serialize/src/string.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::Serialize; -use agdb_db_error::DbError; - -impl Serialize for String { - fn deserialize(bytes: &[u8]) -> Result { - Ok(String::from_utf8(bytes.to_vec())?) - } - - fn serialize(&self) -> Vec { - self.as_bytes().to_vec() - } - - fn serialized_size() -> u64 { - 0 - } -} diff --git a/crates/serialize/src/u64.rs b/crates/serialize/src/u64.rs deleted file mode 100644 index c5fcfbe0..00000000 --- a/crates/serialize/src/u64.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::Serialize; -use agdb_db_error::DbError; -use std::mem::size_of; - -impl Serialize for u64 { - fn deserialize(bytes: &[u8]) -> Result { - let buffer: [u8; size_of::()] = bytes - .get(0..size_of::()) - .ok_or_else(|| DbError::from("u64 deserialization error: out of bounds"))? - .try_into() - .unwrap(); - Ok(Self::from_le_bytes(buffer)) - } - - fn serialize(&self) -> Vec { - self.to_le_bytes().into() - } -} diff --git a/crates/serialize/src/vec.rs b/crates/serialize/src/vec.rs deleted file mode 100644 index 31f1dd43..00000000 --- a/crates/serialize/src/vec.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::mem::size_of; - -use crate::Serialize; -use agdb_db_error::DbError; - -impl Serialize for Vec { - fn deserialize(bytes: &[u8]) -> Result { - const SIZE_OFFSET: usize = size_of::(); - let value_offset = T::serialized_size(); - let size = u64::deserialize(bytes)? as usize; - let mut data: Self = vec![]; - - data.reserve(size); - - for i in 0..size { - let offset = SIZE_OFFSET + value_offset as usize * i; - data.push(T::deserialize(&bytes[offset..])?); - } - - Ok(data) - } - - fn serialize(&self) -> Vec { - const SIZE_OFFSET: usize = size_of::(); - let value_offset: usize = size_of::(); - let mut bytes: Vec = vec![]; - - bytes.reserve(SIZE_OFFSET + value_offset * self.len()); - bytes.extend((self.len() as u64).serialize()); - - for value in self { - bytes.extend(value.serialize()); - } - - bytes - } - - fn serialized_size() -> u64 { - 0 - } -} diff --git a/crates/serialize/src/vec_u8.rs b/crates/serialize/src/vec_u8.rs deleted file mode 100644 index ea1bfe7e..00000000 --- a/crates/serialize/src/vec_u8.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::Serialize; -use agdb_db_error::DbError; - -impl Serialize for Vec { - fn deserialize(bytes: &[u8]) -> Result { - Ok(bytes.to_vec()) - } - - fn serialize(&self) -> Vec { - self.to_vec() - } -} diff --git a/crates/serialize/tests/serialize_test.rs b/crates/serialize/tests/serialize_test.rs deleted file mode 100644 index 59c41d85..00000000 --- a/crates/serialize/tests/serialize_test.rs +++ /dev/null @@ -1,102 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -#[test] -fn i64() { - let number = -10_i64; - let bytes = number.serialize(); - let actual = i64::deserialize(&bytes); - - assert_eq!(actual, Ok(number)); -} - -#[test] -fn i64_out_of_bounds() { - let bytes = vec![0_u8; 4]; - - assert_eq!( - i64::deserialize(&bytes), - Err(DbError::from("i64 deserialization error: out of bounds")) - ); -} - -#[test] -fn serialized_size() { - assert_eq!(i64::serialized_size(), 8); - assert_eq!(u64::serialized_size(), 8); - assert_eq!(Vec::::serialized_size(), 0); - assert_eq!(String::serialized_size(), 0); -} - -#[test] -fn string() { - let value = "Hello, World!".to_string(); - let bytes = value.serialize(); - let actual = String::deserialize(&bytes); - - assert_eq!(actual, Ok(value)); -} - -#[test] -fn string_bad_bytes() { - let bad_bytes = vec![0xdf, 0xff]; - - assert!(String::deserialize(&bad_bytes).is_err()); -} - -#[test] -fn u64() { - let number = 10_u64; - let bytes = number.serialize(); - let actual = u64::deserialize(&bytes); - - assert_eq!(actual, Ok(number)); -} - -#[test] -fn u64_out_of_bounds() { - let bytes = vec![0_u8; 4]; - - assert_eq!( - u64::deserialize(&bytes), - Err(DbError::from("u64 deserialization error: out of bounds")) - ); -} - -#[test] -fn vec_i64() { - let data = vec![1_i64, 2_i64, 3_i64]; - let bytes = data.serialize(); - let actual = Vec::::deserialize(&bytes); - - assert_eq!(actual, Ok(data)); -} - -#[test] -fn vec_size_out_of_bounds() { - let bytes = vec![0_u8; 4]; - - assert_eq!( - Vec::::deserialize(&bytes), - Err(DbError::from("u64 deserialization error: out of bounds")) - ); -} - -#[test] -fn vec_u8() { - let data = vec![1_u8, 2_u8, 3_u8]; - let bytes = data.serialize(); - let actual = Vec::::deserialize(&bytes); - - assert_eq!(actual, Ok(data)); -} - -#[test] -fn vec_value_out_of_bounds() { - let bytes = 1_u64.serialize(); - - assert_eq!( - Vec::::deserialize(&bytes), - Err(DbError::from("i64 deserialization error: out of bounds")) - ); -} diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml deleted file mode 100644 index 7eae07de..00000000 --- a/crates/storage/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "agdb_storage" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage_index = { path = "../storage_index", version = "<=0.1.0" } -agdb_write_ahead_log = { path = "../write_ahead_log", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs deleted file mode 100644 index b1be0fe6..00000000 --- a/crates/storage/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -mod storage; -mod storage_data; -mod storage_data_file; -mod storage_data_file_try_from; -mod storage_file; -mod storage_file_try_from; -mod storage_impl; -mod storage_impl_drop; -mod storage_impl_storage; - -pub use agdb_storage_index::StorageIndex; -pub use storage::Storage; -pub use storage_file::StorageFile; diff --git a/crates/storage/src/storage_data_file_try_from.rs b/crates/storage/src/storage_data_file_try_from.rs deleted file mode 100644 index 45c3c41e..00000000 --- a/crates/storage/src/storage_data_file_try_from.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::storage_data_file::StorageDataFile; -use agdb_db_error::DbError; -use agdb_storage_index::StorageRecords; -use agdb_write_ahead_log::WriteAheadLog; - -impl TryFrom for StorageDataFile { - type Error = DbError; - - fn try_from(filename: String) -> Result { - Ok(StorageDataFile { - file: std::fs::OpenOptions::new() - .write(true) - .create(true) - .read(true) - .open(&filename)?, - filename: filename.clone(), - records: StorageRecords::default(), - wal: WriteAheadLog::try_from(&filename)?, - wal_filename: filename, - transactions: 0, - }) - } -} diff --git a/crates/storage/src/storage_file.rs b/crates/storage/src/storage_file.rs deleted file mode 100644 index 933ec8c6..00000000 --- a/crates/storage/src/storage_file.rs +++ /dev/null @@ -1,4 +0,0 @@ -use crate::storage_data_file::StorageDataFile; -use crate::storage_impl::StorageImpl; - -pub type StorageFile = StorageImpl; diff --git a/crates/storage/src/storage_file_try_from.rs b/crates/storage/src/storage_file_try_from.rs deleted file mode 100644 index 858d1f04..00000000 --- a/crates/storage/src/storage_file_try_from.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::storage_data_file::StorageDataFile; -use crate::storage_file::StorageFile; -use agdb_db_error::DbError; - -impl TryFrom for StorageFile { - type Error = DbError; - - fn try_from(filename: String) -> Result { - let mut storage = StorageFile { - data: StorageDataFile::try_from(filename)?, - }; - - storage.apply_wal()?; - storage.read_records()?; - - Ok(storage) - } -} - -impl TryFrom<&str> for StorageFile { - type Error = DbError; - - fn try_from(filename: &str) -> Result { - Self::try_from(filename.to_string()) - } -} diff --git a/crates/storage/src/storage_impl_drop.rs b/crates/storage/src/storage_impl_drop.rs deleted file mode 100644 index 688e7d91..00000000 --- a/crates/storage/src/storage_impl_drop.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::storage_data::StorageData; -use crate::storage_impl::StorageImpl; - -impl Drop for StorageImpl { - fn drop(&mut self) { - if self.apply_wal().is_ok() { - let _ = self.data.clear_wal(); - } - } -} diff --git a/crates/storage/src/storage_impl_storage.rs b/crates/storage/src/storage_impl_storage.rs deleted file mode 100644 index 386bc46f..00000000 --- a/crates/storage/src/storage_impl_storage.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::io::SeekFrom; - -use crate::storage::Storage; -use crate::storage_data::StorageData; -use crate::storage_impl::StorageImpl; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage_index::StorageIndex; - -impl Storage for StorageImpl { - fn commit(&mut self) -> Result<(), DbError> { - if self.data.end_transaction() { - self.data.clear_wal()?; - } - - Ok(()) - } - - fn insert(&mut self, value: &V) -> Result { - self.transaction(); - let position = self.size()?; - let bytes = value.serialize(); - let record = self.data.create_record(position, bytes.len() as u64); - - self.append(&record.serialize())?; - self.append(&bytes)?; - self.commit()?; - - Ok(record.index) - } - - fn insert_at( - &mut self, - index: &StorageIndex, - offset: u64, - value: &V, - ) -> Result<(), DbError> { - self.transaction(); - let mut record = self.data.record(index)?; - let bytes = V::serialize(value); - self.ensure_record_size(&mut record, index, offset, bytes.len() as u64)?; - self.write(Self::value_position(record.position, offset), &bytes)?; - self.commit() - } - - fn move_at( - &mut self, - index: &StorageIndex, - offset_from: u64, - offset_to: u64, - size: u64, - ) -> Result<(), DbError> { - if offset_from == offset_to || size == 0 { - return Ok(()); - } - - let mut record = self.data.record(index)?; - Self::validate_move_size(offset_from, size, record.size)?; - self.transaction(); - self.ensure_record_size(&mut record, index, offset_to, size)?; - self.move_bytes( - Self::value_position_u64(record.position, offset_from), - Self::value_position_u64(record.position, offset_to), - size, - )?; - self.commit()?; - - Ok(()) - } - - fn remove(&mut self, index: &StorageIndex) -> Result<(), DbError> { - self.transaction(); - let position = self.data.record(index)?.position; - self.invalidate_record(position)?; - self.data.remove_index(index); - self.commit() - } - - fn resize_value(&mut self, index: &StorageIndex, new_size: u64) -> Result<(), DbError> { - if new_size == 0 { - return Err(DbError::from("value size cannot be 0")); - } - - let mut record = self.data.record(index)?; - - if record.size != new_size { - self.transaction(); - self.resize_record(index, new_size, new_size, &mut record)?; - self.commit()?; - } - - Ok(()) - } - - fn shrink_to_fit(&mut self) -> Result<(), DbError> { - self.transaction(); - let indexes = self.data.indexes_by_position(); - let size = self.shrink_indexes(indexes)?; - self.truncate(size)?; - self.commit() - } - - fn size(&mut self) -> Result { - self.data.seek(SeekFrom::End(0)) - } - - fn transaction(&mut self) { - self.data.begin_transaction(); - } - - fn value(&mut self, index: &StorageIndex) -> Result { - let record = self.data.record(index)?; - V::deserialize(&self.read(Self::value_position(record.position, 0), record.size)?) - } - - fn value_at(&mut self, index: &StorageIndex, offset: u64) -> Result { - let record = self.data.record(index)?; - let bytes = self.read( - Self::value_position(record.position, offset), - Self::value_read_size::(record.size, offset)?, - ); - - V::deserialize(&bytes?) - } - - fn value_size(&self, index: &StorageIndex) -> Result { - Ok(self.data.record(index)?.size) - } -} diff --git a/crates/storage/tests/storage_file_insert_test.rs b/crates/storage/tests/storage_file_insert_test.rs deleted file mode 100644 index f3a452f5..00000000 --- a/crates/storage/tests/storage_file_insert_test.rs +++ /dev/null @@ -1,93 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn insert() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&10_i64); - - assert_eq!(index, Ok(StorageIndex::from(1_i64))); -} - -#[test] -fn insert_at() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size()) as u64; - storage.insert_at(&index, offset, &10_i64).unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 10_i64, 3_i64] - ); -} - -#[test] -fn insert_at_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - assert_eq!( - storage.insert_at(&StorageIndex::from(1_i64), 8, &1_i64), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn insert_at_value_end() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size() * 3) as u64; - storage.insert_at(&index, 0, &4_u64).unwrap(); - storage.insert_at(&index, offset, &10_i64).unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 2_i64, 3_i64, 10_i64] - ); -} - -#[test] -fn insert_at_beyond_end() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size() * 4) as u64; - storage.insert_at(&index, 0, &5_u64).unwrap(); - storage.insert_at(&index, offset, &10_i64).unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 2_i64, 3_i64, 0_i64, 10_i64] - ); -} - -#[test] -fn insert_at_bytes() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size()) as u64; - let size = i64::serialized_size() * 2; - - storage - .insert_at(&index, offset, &vec![0_u8; size as usize]) - .unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 0_i64, 0_i64] - ); -} diff --git a/crates/storage/tests/storage_file_move_at_test.rs b/crates/storage/tests/storage_file_move_at_test.rs deleted file mode 100644 index b53b4296..00000000 --- a/crates/storage/tests/storage_file_move_at_test.rs +++ /dev/null @@ -1,103 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn move_at() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset_from = (u64::serialized_size() + i64::serialized_size() * 2) as u64; - let offset_to = (u64::serialized_size() + i64::serialized_size()) as u64; - let size = u64::serialized_size(); - - storage - .move_at(&index, offset_from, offset_to, size) - .unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 3_i64, 0_i64] - ) -} - -#[test] -fn move_at_beyond_end() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset_from = (u64::serialized_size() + i64::serialized_size()) as u64; - let offset_to = (u64::serialized_size() + i64::serialized_size() * 4) as u64; - let size = u64::serialized_size(); - - storage - .move_at(&index, offset_from, offset_to, size) - .unwrap(); - - storage.insert_at(&index, 0, &5_u64).unwrap(); - - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 0_i64, 3_i64, 0_i64, 2_i64] - ) -} - -#[test] -fn move_at_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - assert_eq!( - storage.move_at(&StorageIndex::from(1_i64), 0, 1, 10), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn move_at_same_offset() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - - assert_eq!(storage.move_at(&index, 0, 0, 10), Ok(())); - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 2_i64, 3_i64] - ); -} - -#[test] -fn move_at_size_out_of_bounds() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - let offset_from = (u64::serialized_size() + i64::serialized_size() * 3) as u64; - let offset_to = (u64::serialized_size() + i64::serialized_size() * 2) as u64; - let size = (u64::serialized_size() * 10) as u64; - - assert_eq!( - storage.move_at(&index, offset_from, offset_to, size), - Err(DbError::from("move size out of bounds")) - ); -} - -#[test] -fn move_at_zero_size() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); - - assert_eq!(storage.move_at(&index, 0, 1, 0), Ok(())); - assert_eq!( - storage.value::>(&index).unwrap(), - vec![1_i64, 2_i64, 3_i64] - ); -} diff --git a/crates/storage/tests/storage_file_remove_test.rs b/crates/storage/tests/storage_file_remove_test.rs deleted file mode 100644 index 775816da..00000000 --- a/crates/storage/tests/storage_file_remove_test.rs +++ /dev/null @@ -1,30 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn remove() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - let index = storage.insert(&1_i64).unwrap(); - storage.remove(&index).unwrap(); - - assert_eq!( - storage.value::(&index), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn remove_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - assert_eq!( - storage.remove(&StorageIndex::from(1_i64)), - Err(DbError::from("index '1' not found")) - ); -} diff --git a/crates/storage/tests/storage_file_resize_value_test.rs b/crates/storage/tests/storage_file_resize_value_test.rs deleted file mode 100644 index b7ada43d..00000000 --- a/crates/storage/tests/storage_file_resize_value_test.rs +++ /dev/null @@ -1,126 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn resize_at_end_does_not_move() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&1_i64).unwrap(); - let size = storage.size().unwrap(); - let value_size = storage.value_size(&index).unwrap(); - - storage.resize_value(&index, value_size + 8).unwrap(); - - assert_eq!(storage.size(), Ok(size + 8)); -} - -#[test] -fn resize_value_greater() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - let expected_size = i64::serialized_size(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); - - storage.resize_value(&index, expected_size * 2).unwrap(); - - assert_eq!(storage.value_size(&index), Ok(expected_size * 2)); -} - -#[test] -fn resize_value_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!( - storage.resize_value(&StorageIndex::from(1_i64), 1), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn resize_value_same() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - let expected_size = i64::serialized_size(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); - - storage.resize_value(&index, expected_size).unwrap(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); -} - -#[test] -fn resize_value_smaller() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - let expected_size = i64::serialized_size(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); - - storage.resize_value(&index, expected_size / 2).unwrap(); - - assert_eq!(storage.value_size(&index), Ok(expected_size / 2)); -} - -#[test] -fn resize_value_zero() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - let expected_size = i64::serialized_size(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); - - assert_eq!( - storage.resize_value(&index, 0), - Err(DbError::from("value size cannot be 0")) - ); -} - -#[test] -fn resize_value_resizes_file() { - let test_file = TestFile::new(); - - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - let index = storage.insert(&3_i64).unwrap(); - let size = u64::serialized_size() + i64::serialized_size() * 3; - storage.resize_value(&index, size).unwrap(); - - assert_eq!(storage.value::>(&index), Ok(vec![0_i64; 3])); -} - -#[test] -fn resize_value_invalidates_original_position() { - let test_file = TestFile::new(); - - let index; - - { - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - index = storage.insert(&10_i64).unwrap(); - storage.insert(&5_i64).unwrap(); - storage.resize_value(&index, 1).unwrap(); - storage.remove(&index).unwrap(); - } - - let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); - - assert_eq!( - storage.value::(&index), - Err(DbError::from("index '1' not found")) - ); -} diff --git a/crates/storage/tests/storage_file_shrink_to_fit_test.rs b/crates/storage/tests/storage_file_shrink_to_fit_test.rs deleted file mode 100644 index 0f156482..00000000 --- a/crates/storage/tests/storage_file_shrink_to_fit_test.rs +++ /dev/null @@ -1,80 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; -use std::fs::metadata; - -#[test] -fn shrink_to_fit() { - let test_file = TestFile::new(); - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - let index1 = storage.insert(&1_i64).unwrap(); - let index2 = storage.insert(&2_i64).unwrap(); - let index3 = storage.insert(&3_i64).unwrap(); - storage.remove(&index2).unwrap(); - storage.shrink_to_fit().unwrap(); - - let actual_size = metadata(test_file.file_name()).unwrap().len(); - let expected_size = (u64::serialized_size() * 2) * 2 + i64::serialized_size() * 2; - - assert_eq!(actual_size, expected_size); - assert_eq!(storage.value(&index1), Ok(1_i64)); - assert_eq!(storage.value(&index3), Ok(3_i64)); -} - -#[test] -fn shrink_to_fit_no_change() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - let index1 = storage.insert(&1_i64).unwrap(); - let index2 = storage.insert(&2_i64).unwrap(); - let index3 = storage.insert(&3_i64).unwrap(); - - let actual_size = metadata(test_file.file_name()).unwrap().len(); - - storage.shrink_to_fit().unwrap(); - - assert_eq!(actual_size, metadata(test_file.file_name()).unwrap().len()); - assert_eq!(storage.value(&index1), Ok(1_i64)); - assert_eq!(storage.value(&index2), Ok(2_i64)); - assert_eq!(storage.value(&index3), Ok(3_i64)); -} - -#[test] -fn shrink_to_fit_uncommitted() { - let test_file = TestFile::new(); - - let expected_size; - let index1; - let index2; - let index3; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - index1 = storage.insert(&1_i64).unwrap(); - index2 = storage.insert(&2_i64).unwrap(); - index3 = storage.insert(&3_i64).unwrap(); - storage.remove(&index2).unwrap(); - - expected_size = metadata(test_file.file_name()).unwrap().len(); - - storage.transaction(); - storage.shrink_to_fit().unwrap(); - } - - let actual_size = metadata(test_file.file_name()).unwrap().len(); - assert_eq!(actual_size, expected_size); - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - assert_eq!(storage.value(&index1), Ok(1_i64)); - assert_eq!( - storage.value::(&index2), - Err(DbError::from(format!( - "index '{}' not found", - index2.value() - ))) - ); - assert_eq!(storage.value(&index3), Ok(3_i64)); -} diff --git a/crates/storage/tests/storage_file_test.rs b/crates/storage/tests/storage_file_test.rs deleted file mode 100644 index adfe939d..00000000 --- a/crates/storage/tests/storage_file_test.rs +++ /dev/null @@ -1,118 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn bad_file() { - assert!(StorageFile::try_from("/a/").is_err()); -} - -#[test] -fn restore_from_file() { - let test_file = TestFile::new(); - let value1 = vec![1_i64, 2_i64, 3_i64]; - let value2 = 64_u64; - let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; - let index1; - let index2; - let index3; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - index1 = storage.insert(&value1).unwrap(); - index2 = storage.insert(&value2).unwrap(); - index3 = storage.insert(&value3).unwrap(); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!(storage.value::>(&index1), Ok(value1)); - assert_eq!(storage.value::(&index2), Ok(value2)); - assert_eq!(storage.value::>(&index3), Ok(value3)); -} - -#[test] -fn restore_from_file_with_removed_index() { - let test_file = TestFile::new(); - let value1 = vec![1_i64, 2_i64, 3_i64]; - let value2 = 64_u64; - let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; - let index1; - let index2; - let index3; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - index1 = storage.insert(&value1).unwrap(); - index2 = storage.insert(&value2).unwrap(); - index3 = storage.insert(&value3).unwrap(); - storage.remove(&index2).unwrap(); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!(storage.value::>(&index1), Ok(value1)); - assert_eq!( - storage.value::(&StorageIndex::default()), - Err(DbError::from(format!("index '{}' not found", 0))) - ); - assert_eq!( - storage.value::(&index2), - Err(DbError::from(format!( - "index '{}' not found", - index2.value() - ))) - ); - assert_eq!(storage.value::>(&index3), Ok(value3)); -} - -#[test] -fn restore_from_file_with_all_indexes_removed() { - let test_file = TestFile::new(); - let value1 = vec![1_i64, 2_i64, 3_i64]; - let value2 = 64_u64; - let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; - let index1; - let index2; - let index3; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - index1 = storage.insert(&value1).unwrap(); - index2 = storage.insert(&value2).unwrap(); - index3 = storage.insert(&value3).unwrap(); - storage.remove(&index1).unwrap(); - storage.remove(&index2).unwrap(); - storage.remove(&index3).unwrap(); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!( - storage.value::(&StorageIndex::default()), - Err(DbError::from(format!("index '{}' not found", 0))) - ); - assert_eq!( - storage.value::>(&index1), - Err(DbError::from(format!( - "index '{}' not found", - index1.value() - ))) - ); - assert_eq!( - storage.value::(&index2), - Err(DbError::from(format!( - "index '{}' not found", - index2.value() - ))) - ); - assert_eq!( - storage.value::>(&index3), - Err(DbError::from(format!( - "index '{}' not found", - index3.value() - ))) - ); -} diff --git a/crates/storage/tests/storage_file_transaction_test.rs b/crates/storage/tests/storage_file_transaction_test.rs deleted file mode 100644 index 5d87c966..00000000 --- a/crates/storage/tests/storage_file_transaction_test.rs +++ /dev/null @@ -1,74 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_test_utilities::TestFile; - -#[test] -fn transaction_commit() { - let test_file = TestFile::from("file_storage-transaction_commit.agdb"); - let index; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - storage.transaction(); - index = storage.insert(&1_i64).unwrap(); - storage.commit().unwrap(); - assert_eq!(storage.value::(&index), Ok(1_i64)); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - assert_eq!(storage.value::(&index), Ok(1_i64)); -} - -#[test] -fn transaction_commit_no_transaction() { - let test_file = TestFile::from("file_storage-transaction_commit_no_transaction.agdb"); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - assert_eq!(storage.commit(), Ok(())); -} - -#[test] -fn transaction_unfinished() { - let test_file = TestFile::new(); - let index; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - storage.transaction(); - index = storage.insert(&1_i64).unwrap(); - assert_eq!(storage.value::(&index), Ok(1_i64)); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - assert_eq!( - storage.value::(&index), - Err(DbError::from(format!( - "index '{}' not found", - index.value() - ))) - ); -} - -#[test] -fn transaction_nested_unfinished() { - let test_file = TestFile::new(); - let index; - - { - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - storage.transaction(); - storage.transaction(); - index = storage.insert(&1_i64).unwrap(); - assert_eq!(storage.value::(&index), Ok(1_i64)); - storage.commit().unwrap(); - } - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - assert_eq!( - storage.value::(&index), - Err(DbError::from(format!( - "index '{}' not found", - index.value() - ))) - ); -} diff --git a/crates/storage/tests/storage_file_value_test.rs b/crates/storage/tests/storage_file_value_test.rs deleted file mode 100644 index 6003177f..00000000 --- a/crates/storage/tests/storage_file_value_test.rs +++ /dev/null @@ -1,131 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_index::StorageIndex; -use agdb_test_utilities::TestFile; - -#[test] -fn value() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - let index = storage.insert(&10_i64).unwrap(); - - assert_eq!(storage.value::(&index), Ok(10_i64)); -} - -#[test] -fn value_at() { - let test_file = TestFile::new(); - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - let data = vec![1_i64, 2_i64, 3_i64]; - - let index = storage.insert(&data).unwrap(); - let offset = u64::serialized_size() + i64::serialized_size(); - - assert_eq!(storage.value_at::(&index, offset), Ok(2_i64)); -} - -#[test] -fn value_at_dynamic_size() { - let test_file = TestFile::new(); - - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - let data = vec![2_i64, 1_i64, 2_i64]; - - let index = storage.insert(&data).unwrap(); - let offset = u64::serialized_size(); - - assert_eq!( - storage.value_at::>(&index, offset), - Ok(vec![1_i64, 2_i64]) - ); -} - -#[test] -fn value_at_of_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!( - storage.value_at::(&StorageIndex::from(1_i64), 8), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn value_at_out_of_bounds() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let data = vec![1_i64, 2_i64]; - let index = storage.insert(&data).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size() * 2) as u64; - - assert_eq!( - storage.value_at::(&index, offset), - Err(DbError::from("deserialization error: value out of bounds")) - ); -} - -#[test] -fn value_at_offset_overflow() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let data = vec![1_i64, 2_i64]; - let index = storage.insert(&data).unwrap(); - let offset = (u64::serialized_size() + i64::serialized_size() * 3) as u64; - - assert_eq!( - storage.value_at::(&index, offset), - Err(DbError::from("deserialization error: offset out of bounds")) - ); -} - -#[test] -fn value_of_missing_index() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!( - storage.value::(&StorageIndex::from(1_i64)), - Err(DbError::from("index '1' not found")) - ); -} - -#[test] -fn value_out_of_bounds() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - - assert_eq!( - storage.value::>(&index), - Err(DbError::from("i64 deserialization error: out of bounds")) - ); -} - -#[test] -fn value_size() { - let test_file = TestFile::new(); - let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - let index = storage.insert(&10_i64).unwrap(); - let expected_size = i64::serialized_size(); - - assert_eq!(storage.value_size(&index), Ok(expected_size)); -} - -#[test] -fn value_size_of_missing_index() { - let test_file = TestFile::new(); - let storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); - - assert_eq!( - storage.value_size(&StorageIndex::from(1_i64)), - Err(DbError::from("index '1' not found")) - ); -} diff --git a/crates/storage_index/Cargo.toml b/crates/storage_index/Cargo.toml deleted file mode 100644 index 990a567a..00000000 --- a/crates/storage_index/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "agdb_storage_index" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } diff --git a/crates/storage_index/src/lib.rs b/crates/storage_index/src/lib.rs deleted file mode 100644 index f1b3e793..00000000 --- a/crates/storage_index/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod storage_index; -mod storage_index_from; -mod storage_index_serialize; -mod storage_record; -mod storage_record_serialize; -mod storage_records; -mod storage_records_default; -mod storage_records_from; - -pub use storage_index::StorageIndex; -pub use storage_record::StorageRecord; -pub use storage_records::StorageRecords; diff --git a/crates/storage_index/src/storage_index.rs b/crates/storage_index/src/storage_index.rs deleted file mode 100644 index d61e90cc..00000000 --- a/crates/storage_index/src/storage_index.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] -pub struct StorageIndex { - pub(crate) index: i64, -} - -impl StorageIndex { - pub fn as_u64(&self) -> u64 { - self.value() as u64 - } - - pub fn as_usize(&self) -> usize { - self.value() as usize - } - - pub fn is_valid(&self) -> bool { - 0 < self.index - } - - pub fn value(&self) -> i64 { - self.index - } -} diff --git a/crates/storage_index/src/storage_index_from.rs b/crates/storage_index/src/storage_index_from.rs deleted file mode 100644 index 0dc587d6..00000000 --- a/crates/storage_index/src/storage_index_from.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::storage_index::StorageIndex; - -impl From for StorageIndex { - fn from(index: i64) -> Self { - Self { index } - } -} - -impl From for StorageIndex { - fn from(index: u64) -> Self { - Self { - index: index as i64, - } - } -} - -impl From for StorageIndex { - fn from(index: usize) -> Self { - Self { - index: index as i64, - } - } -} diff --git a/crates/storage_index/src/storage_index_serialize.rs b/crates/storage_index/src/storage_index_serialize.rs deleted file mode 100644 index 9f10521c..00000000 --- a/crates/storage_index/src/storage_index_serialize.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::storage_index::StorageIndex; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -impl Serialize for StorageIndex { - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - index: i64::deserialize(bytes)?, - }) - } - - fn serialize(&self) -> Vec { - self.index.serialize() - } -} diff --git a/crates/storage_index/src/storage_record.rs b/crates/storage_index/src/storage_record.rs deleted file mode 100644 index 06a09e22..00000000 --- a/crates/storage_index/src/storage_record.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::StorageIndex; - -#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] -pub struct StorageRecord { - pub index: StorageIndex, - pub position: u64, - pub size: u64, -} diff --git a/crates/storage_index/src/storage_record_serialize.rs b/crates/storage_index/src/storage_record_serialize.rs deleted file mode 100644 index 0132cd34..00000000 --- a/crates/storage_index/src/storage_record_serialize.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::storage_record::StorageRecord; -use crate::StorageIndex; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::mem::size_of; - -impl Serialize for StorageRecord { - fn deserialize(bytes: &[u8]) -> Result { - Ok(StorageRecord { - index: StorageIndex::deserialize(bytes)?, - position: 0, - size: u64::deserialize(&bytes[(StorageIndex::serialized_size() as usize)..])?, - }) - } - - fn serialize(&self) -> Vec { - let mut bytes = self.index.serialize(); - bytes.extend(self.size.serialize()); - - bytes - } - - fn serialized_size() -> u64 { - StorageIndex::serialized_size() + size_of::() as u64 - } -} diff --git a/crates/storage_index/src/storage_records.rs b/crates/storage_index/src/storage_records.rs deleted file mode 100644 index 862a4c52..00000000 --- a/crates/storage_index/src/storage_records.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::storage_index::StorageIndex; -use crate::storage_record::StorageRecord; - -pub struct StorageRecords { - pub(crate) records: Vec, -} - -impl StorageRecords { - pub fn create(&mut self, position: u64, size: u64) -> StorageRecord { - let index; - - if let Some(free_index) = self.free_index() { - index = free_index.as_usize(); - self.records[index] = StorageRecord { - index: free_index, - position, - size, - }; - } else { - index = self.records.len(); - self.records.push(StorageRecord { - index: StorageIndex::from(index), - position, - size, - }); - } - - self.records[index].clone() - } - - pub fn get(&self, index: &StorageIndex) -> Option<&StorageRecord> { - if let Some(record) = self.records.get(index.as_usize()) { - if record.size != 0 { - return Some(record); - } - } - - None - } - - pub fn get_mut(&mut self, index: &StorageIndex) -> Option<&mut StorageRecord> { - if let Some(record) = self.records.get_mut(index.as_usize()) { - if record.size != 0 { - return Some(record); - } - } - - None - } - - pub fn indexes_by_position(&self) -> Vec { - let mut indexes = Vec::::new(); - - for index in 1..self.records.len() { - if self.records[index].size != 0 { - indexes.push(StorageIndex::from(index)); - } - } - - indexes.sort_by(|left, right| { - self.records[left.as_usize()] - .position - .cmp(&self.records[right.as_usize()].position) - }); - - indexes - } - - pub fn remove(&mut self, index: &StorageIndex) { - if let Some(_record) = self.get_mut(index) { - self.add_free_index(index); - } - } - - pub(crate) fn add_free_index(&mut self, index: &StorageIndex) { - self.records[index.as_usize()].position = self.records[0].position; - self.records[index.as_usize()].size = 0; - self.records[0].position = index.as_u64(); - } - - fn free_index(&mut self) -> Option { - let free = self.records[0].position; - - if free != 0 { - self.records[0].position = self.records[free as usize].position; - return Some(StorageIndex::from(free)); - } - - None - } -} diff --git a/crates/storage_index/src/storage_records_default.rs b/crates/storage_index/src/storage_records_default.rs deleted file mode 100644 index aee16587..00000000 --- a/crates/storage_index/src/storage_records_default.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::storage_record::StorageRecord; -use crate::storage_records::StorageRecords; - -impl Default for StorageRecords { - fn default() -> Self { - Self { - records: vec![StorageRecord::default()], - } - } -} diff --git a/crates/storage_index/src/storage_records_from.rs b/crates/storage_index/src/storage_records_from.rs deleted file mode 100644 index 91b000e3..00000000 --- a/crates/storage_index/src/storage_records_from.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::storage_index::StorageIndex; -use crate::storage_record::StorageRecord; -use crate::storage_records::StorageRecords; - -impl From> for StorageRecords { - fn from(mut records: Vec) -> Self { - records.sort(); - - let mut file_records = StorageRecords::default(); - - if let Some(last) = records.last() { - if last.index.is_valid() { - file_records = StorageRecords { - records: vec![StorageRecord::default(); last.index.as_usize() + 1], - }; - } - } - - let mut last_index = 0; - - for record in records { - if !record.index.is_valid() { - continue; - } - - for index in (last_index + 1)..record.index.value() { - file_records.add_free_index(&StorageIndex::from(index)); - } - - file_records.records[record.index.as_usize()].position = record.position; - file_records.records[record.index.as_usize()].size = record.size; - last_index = record.index.value(); - } - - file_records - } -} diff --git a/crates/storage_index/tests/storage_index_test.rs b/crates/storage_index/tests/storage_index_test.rs deleted file mode 100644 index 970c1611..00000000 --- a/crates/storage_index/tests/storage_index_test.rs +++ /dev/null @@ -1,42 +0,0 @@ -use agdb_serialize::Serialize; -use agdb_storage_index::StorageIndex; - -#[test] -fn is_valid() { - assert!(!StorageIndex::default().is_valid()); - assert!(!StorageIndex::from(-1_i64).is_valid()); - - assert!(StorageIndex::from(1_i64).is_valid()); - assert!(StorageIndex::from(100_i64).is_valid()); -} - -#[test] -fn ordering() { - let mut indexes = vec![ - StorageIndex::default(), - StorageIndex::from(100_i64), - StorageIndex::from(-1_i64), - StorageIndex::from(1_i64), - ]; - - indexes.sort(); - - assert_eq!( - indexes, - vec![ - StorageIndex::from(-1_i64), - StorageIndex::default(), - StorageIndex::from(1_i64), - StorageIndex::from(100_i64), - ] - ) -} - -#[test] -fn serialize() { - let index = StorageIndex::from(1_i64); - let bytes = index.serialize(); - let other = StorageIndex::deserialize(&bytes).unwrap(); - - assert_eq!(index, other); -} diff --git a/crates/storage_index/tests/storage_record_test.rs b/crates/storage_index/tests/storage_record_test.rs deleted file mode 100644 index 5a8354a0..00000000 --- a/crates/storage_index/tests/storage_record_test.rs +++ /dev/null @@ -1,31 +0,0 @@ -use agdb_serialize::Serialize; -use agdb_storage_index::StorageIndex; -use agdb_storage_index::StorageRecord; -use std::cmp::Ordering; - -#[test] -fn derived_from_debug() { - let record = StorageRecord::default(); - format!("{:?}", record); -} - -#[test] -fn derived_from_ord() { - let record = StorageRecord::default(); - assert_eq!(record.cmp(&record), Ordering::Equal); -} - -#[test] -fn serialize() { - let bytes = StorageRecord { - index: StorageIndex::from(1_i64), - position: 64, - size: 128, - } - .serialize(); - let record = StorageRecord::deserialize(&bytes).unwrap(); - - assert_eq!(record.index, StorageIndex::from(1_i64)); - assert_eq!(record.position, 0); - assert_eq!(record.size, 128); -} diff --git a/crates/storage_index/tests/storage_records_test.rs b/crates/storage_index/tests/storage_records_test.rs deleted file mode 100644 index ea2b77ab..00000000 --- a/crates/storage_index/tests/storage_records_test.rs +++ /dev/null @@ -1,244 +0,0 @@ -use agdb_storage_index::StorageIndex; -use agdb_storage_index::StorageRecord; -use agdb_storage_index::StorageRecords; - -#[test] -fn create() { - let mut file_records = StorageRecords::default(); - - let record = file_records.create(1, 4); - - assert_eq!( - record, - StorageRecord { - index: StorageIndex::from(1_i64), - position: 1, - size: 4 - } - ); -} - -#[test] -fn default_constructed() { - let _records = StorageRecords::default(); -} - -#[test] -fn from_records() { - let index1 = StorageIndex::from(2_i64); - let index2 = StorageIndex::from(1_i64); - let index3 = StorageIndex::from(3_i64); - - let file_records = StorageRecords::from(vec![ - StorageRecord { - index: index1.clone(), - position: 8, - size: 16, - }, - StorageRecord { - index: index2.clone(), - position: 24, - size: 16, - }, - StorageRecord { - index: index3.clone(), - position: 40, - size: 16, - }, - ]); - - assert_eq!( - file_records.get(&index1), - Some(&StorageRecord { - index: StorageIndex::default(), - position: 8, - size: 16 - }) - ); - assert_eq!( - file_records.get(&index2), - Some(&StorageRecord { - index: StorageIndex::default(), - position: 24, - size: 16 - }) - ); - assert_eq!( - file_records.get(&index3), - Some(&StorageRecord { - index: StorageIndex::default(), - position: 40, - size: 16 - }) - ); -} - -#[test] -fn from_records_with_index_gaps() { - let record1 = StorageRecord { - index: StorageIndex::from(5_i64), - position: 24, - size: 16, - }; - let record2 = StorageRecord { - index: StorageIndex::from(1_i64), - position: 40, - size: 16, - }; - let record3 = StorageRecord { - index: StorageIndex::from(2_i64), - position: 40, - size: 16, - }; - - let mut file_records = StorageRecords::from(vec![record1, record2, record3]); - - let record1 = file_records.create(2, 2); - let record2 = file_records.create(4, 4); - let record3 = file_records.create(6, 6); - - assert_eq!( - record1, - StorageRecord { - index: StorageIndex::from(4_i64), - position: 2, - size: 2 - } - ); - assert_eq!( - record2, - StorageRecord { - index: StorageIndex::from(3_i64), - position: 4, - size: 4 - } - ); - assert_eq!( - record3, - StorageRecord { - index: StorageIndex::from(6_i64), - position: 6, - size: 6 - } - ); -} - -#[test] -fn from_records_with_removed_index() { - let record1 = StorageRecord { - index: StorageIndex::from(1_i64), - position: 24, - size: 16, - }; - let record2 = StorageRecord { - index: StorageIndex::from(-2_i64), - position: 40, - size: 16, - }; - let record3 = StorageRecord { - index: StorageIndex::from(3_i64), - position: 40, - size: 16, - }; - - let file_records = StorageRecords::from(vec![record1, record2, record3]); - - assert_eq!(file_records.get(&StorageIndex::default()), None); -} - -#[test] -fn get() { - let mut file_records = StorageRecords::default(); - let position = 32_u64; - let size = 64_u64; - - let record = file_records.create(position, size); - let expected_record = StorageRecord { - index: StorageIndex::from(1_i64), - position, - size, - }; - - assert_eq!(file_records.get(&record.index), Some(&expected_record)); -} - -#[test] -fn get_mut() { - let mut file_records = StorageRecords::default(); - let position = 32_u64; - let size = 64_u64; - - let record = file_records.create(position, size); - let mut expected_record = StorageRecord { - index: StorageIndex::from(1_i64), - position, - size, - }; - - assert_eq!( - file_records.get_mut(&record.index), - Some(&mut expected_record) - ); -} - -#[test] -fn get_mut_invalid_index() { - let mut file_records = StorageRecords::default(); - - assert_eq!(file_records.get_mut(&StorageIndex::from(-1_i64)), None); -} - -#[test] -fn get_mut_zero_index() { - let mut file_records = StorageRecords::default(); - - assert_eq!(file_records.get_mut(&StorageIndex::default()), None); -} - -#[test] -fn get_zero_index() { - let file_records = StorageRecords::default(); - - assert_eq!(file_records.get(&StorageIndex::default()), None); -} - -#[test] -fn indexes_by_position() { - let mut file_records = StorageRecords::default(); - let index1 = file_records.create(30, 8).index; - let index2 = file_records.create(20, 8).index; - let index3 = file_records.create(10, 8).index; - file_records.remove(&index2); - - assert_eq!(file_records.indexes_by_position(), vec![index3, index1]); -} - -#[test] -fn remove() { - let mut file_records = StorageRecords::default(); - let record = file_records.create(8u64, 16u64); - - file_records.remove(&record.index); - - assert_eq!(file_records.get(&record.index), None); -} - -#[test] -fn remove_invalid_index() { - let mut file_records = StorageRecords::default(); - let record = file_records.create(16_u64, 48_u64); - - file_records.remove(&StorageIndex::from(-1_i64)); - - assert_eq!(file_records.get(&record.index), Some(&record)); -} - -#[test] -fn reuse_indexes() { - let mut file_records = StorageRecords::default(); - let record = file_records.create(8u64, 16u64); - file_records.remove(&record.index); - let other = file_records.create(16u64, 32u64); - - assert_eq!(record.index, other.index); -} diff --git a/crates/storage_map/Cargo.toml b/crates/storage_map/Cargo.toml deleted file mode 100644 index a0a17900..00000000 --- a/crates/storage_map/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "agdb_storage_map" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_map_common = { path = "../map_common", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage = { path = "../storage", version = "<=0.1.0" } -agdb_utilities = { path = "../utilities", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/storage_map/src/lib.rs b/crates/storage_map/src/lib.rs deleted file mode 100644 index 61278235..00000000 --- a/crates/storage_map/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod map_data_storage; -mod map_data_storage_try_from; -mod map_impl; -mod storage_map; -mod storage_map_try_from; - -pub use map_data_storage::MapDataStorage; -pub use storage_map::StorageMap; diff --git a/crates/storage_map/src/map_data_storage_try_from.rs b/crates/storage_map/src/map_data_storage_try_from.rs deleted file mode 100644 index de3f0b63..00000000 --- a/crates/storage_map/src/map_data_storage_try_from.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::map_data_storage::MapDataStorage; -use agdb_db_error::DbError; -use agdb_map_common::MapValue; -use agdb_utilities::StableHash; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use std::cell::RefCell; -use std::hash::Hash; -use std::marker::PhantomData; -use std::mem::size_of; -use std::rc::Rc; - -impl TryFrom>> for MapDataStorage -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - let storage_index = storage.borrow_mut().insert(&0_u64)?; - storage.borrow_mut().insert_at( - &storage_index, - size_of::() as u64, - &vec![MapValue::::default()], - )?; - - Ok(Self { - storage, - storage_index, - count: 0, - capacity: 1, - phantom_data: PhantomData, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for MapDataStorage -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - let count = storage_with_index - .0 - .borrow_mut() - .value_at::(&storage_with_index.1, 0)?; - let capacity = storage_with_index - .0 - .borrow_mut() - .value_at::(&storage_with_index.1, size_of::() as u64)?; - - Ok(Self { - storage: storage_with_index.0, - storage_index: storage_with_index.1, - count, - capacity, - phantom_data: PhantomData, - }) - } -} diff --git a/crates/storage_map/src/storage_map.rs b/crates/storage_map/src/storage_map.rs deleted file mode 100644 index cf6a2211..00000000 --- a/crates/storage_map/src/storage_map.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::map_data_storage::MapDataStorage; -use crate::map_impl::MapImpl; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_utilities::StableHash; -use std::hash::Hash; - -pub type StorageMap = MapImpl>; - -impl StorageMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - pub fn storage_index(&self) -> StorageIndex { - self.map_common.data.storage_index() - } -} diff --git a/crates/storage_map/src/storage_map_try_from.rs b/crates/storage_map/src/storage_map_try_from.rs deleted file mode 100644 index bf50aa93..00000000 --- a/crates/storage_map/src/storage_map_try_from.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::map_data_storage::MapDataStorage; -use crate::StorageMap; -use agdb_db_error::DbError; -use agdb_map_common::MapCommon; -use agdb_map_common::MapValue; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use agdb_utilities::StableHash; -use std::cell::RefCell; -use std::hash::Hash; -use std::marker::PhantomData; -use std::mem::size_of; -use std::rc::Rc; - -impl TryFrom>> for StorageMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - let storage_index = storage.borrow_mut().insert(&0_u64)?; - storage.borrow_mut().insert_at( - &storage_index, - size_of::() as u64, - &vec![MapValue::::default()], - )?; - - Ok(Self { - map_common: MapCommon::from(MapDataStorage::try_from(storage)?), - phantom_data: PhantomData, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - Ok(Self { - map_common: MapCommon::from(MapDataStorage::try_from(storage_with_index)?), - phantom_data: PhantomData, - }) - } -} diff --git a/crates/storage_map/tests/storage_map_insert_test.rs b/crates/storage_map/tests/storage_map_insert_test.rs deleted file mode 100644 index 65fe9ab9..00000000 --- a/crates/storage_map/tests/storage_map_insert_test.rs +++ /dev/null @@ -1,84 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_map::StorageMap; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn insert() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(15))); - assert_eq!(map.value(&7), Ok(Some(20))); -} - -#[test] -fn insert_reallocate() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - assert_eq!(map.capacity(), 1); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - assert_eq!(map.value(&i), Ok(Some(i))); - } -} - -#[test] -fn insert_reallocate_with_collisions() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - for i in 0..100 { - map.insert(i * 64, i).unwrap(); - } - - for i in 0..100 { - assert_eq!(map.value(&(i * 64)), Ok(Some(i))); - } -} - -#[test] -fn insert_same_key() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - assert_eq!(map.insert(1, 10), Ok(None)); - assert_eq!(map.insert(5, 15), Ok(None)); - assert_eq!(map.count(), 2); - assert_eq!(map.insert(5, 20), Ok(Some(15))); - assert_eq!(map.count(), 2); - - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(Some(20))); -} diff --git a/crates/storage_map/tests/storage_map_remove_test.rs b/crates/storage_map/tests/storage_map_remove_test.rs deleted file mode 100644 index 16d4e539..00000000 --- a/crates/storage_map/tests/storage_map_remove_test.rs +++ /dev/null @@ -1,90 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_map::StorageMap; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - map.remove(&5).unwrap(); - - assert_eq!(map.count(), 2); - assert_eq!(map.value(&1), Ok(Some(10))); - assert_eq!(map.value(&5), Ok(None)); - assert_eq!(map.value(&7), Ok(Some(20))); -} - -#[test] -fn remove_deleted() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - - assert_eq!(map.count(), 3); - - map.remove(&5).unwrap(); - - assert_eq!(map.count(), 2); - assert_eq!(map.value(&5), Ok(None)); - - map.remove(&5).unwrap(); - - assert_eq!(map.count(), 2); -} - -#[test] -fn remove_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - assert_eq!(map.count(), 0); - assert_eq!(map.remove(&0), Ok(())); - assert_eq!(map.count(), 0); -} - -#[test] -fn remove_shrinks_capacity() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - for i in 0..100 { - map.insert(i, i).unwrap(); - } - - assert_eq!(map.count(), 100); - assert_eq!(map.capacity(), 128); - - for i in 0..100 { - map.remove(&i).unwrap(); - } - - assert_eq!(map.count(), 0); - assert_eq!(map.capacity(), 64); -} diff --git a/crates/storage_map/tests/storage_map_reserve_test.rs b/crates/storage_map/tests/storage_map_reserve_test.rs deleted file mode 100644 index c8c7e489..00000000 --- a/crates/storage_map/tests/storage_map_reserve_test.rs +++ /dev/null @@ -1,64 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_map::StorageMap; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn reserve_larger() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - map.insert(1, 1).unwrap(); - - let capacity = map.capacity() + 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); - assert_eq!(map.value(&1), Ok(Some(1))); -} - -#[test] -fn reserve_same() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - map.insert(1, 1).unwrap(); - - let capacity = map.capacity(); - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), capacity); - assert_eq!(map.count(), size); -} - -#[test] -fn reserve_smaller() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - map.insert(1, 1).unwrap(); - - let current_capacity = map.capacity(); - let capacity = current_capacity - 10; - let size = map.count(); - - map.reserve(capacity).unwrap(); - - assert_eq!(map.capacity(), current_capacity); - assert_eq!(map.count(), size); -} diff --git a/crates/storage_map/tests/storage_map_test.rs b/crates/storage_map/tests/storage_map_test.rs deleted file mode 100644 index ae204679..00000000 --- a/crates/storage_map/tests/storage_map_test.rs +++ /dev/null @@ -1,107 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_storage_map::StorageMap; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; - -#[test] -fn iter() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - map.insert(2, 30).unwrap(); - map.insert(4, 13).unwrap(); - map.remove(&7).unwrap(); - - let mut actual = map.iter().collect::>(); - actual.sort(); - let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (4, 13), (5, 15)]; - - assert_eq!(actual, expected); -} - -#[test] -fn to_hash_map() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - map.insert(1, 10).unwrap(); - map.insert(5, 15).unwrap(); - map.insert(7, 20).unwrap(); - map.remove(&5).unwrap(); - - let other = map.to_hash_map().unwrap(); - - assert_eq!(other.len(), 2); - assert_eq!(other.get(&1), Some(&10)); - assert_eq!(other.get(&5), None); - assert_eq!(other.get(&7), Some(&20)); -} - -#[test] -fn to_hash_map_empty() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let map = StorageMap::::try_from(storage).unwrap(); - let other = map.to_hash_map().unwrap(); - - assert_eq!(other.len(), 0); -} - -#[test] -fn try_from_storage_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let index; - - { - let mut map = StorageMap::::try_from(storage.clone()).unwrap(); - map.insert(1, 1).unwrap(); - map.insert(3, 2).unwrap(); - map.insert(5, 3).unwrap(); - map.remove(&3).unwrap(); - index = map.storage_index(); - } - - let map = StorageMap::::try_from((storage, index)).unwrap(); - - let mut expected = HashMap::::new(); - expected.insert(1, 1); - expected.insert(5, 3); - - assert_eq!(map.to_hash_map(), Ok(expected)); -} - -#[test] -fn try_from_storage_missing_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - assert_eq!( - StorageMap::::try_from((storage, StorageIndex::from(1_i64))) - .err() - .unwrap(), - DbError::from("index '1' not found") - ); -} diff --git a/crates/storage_map/tests/storage_map_value_test.rs b/crates/storage_map/tests/storage_map_value_test.rs deleted file mode 100644 index 05ca7da2..00000000 --- a/crates/storage_map/tests/storage_map_value_test.rs +++ /dev/null @@ -1,35 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_map::StorageMap; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn value_missing() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let map = StorageMap::::try_from(storage).unwrap(); - - assert_eq!(map.value(&0), Ok(None)); -} - -#[test] -fn values_at_end() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut map = StorageMap::::try_from(storage).unwrap(); - - map.insert(127, 10).unwrap(); - map.insert(255, 11).unwrap(); - map.insert(191, 12).unwrap(); - - assert_eq!(map.value(&127), Ok(Some(10))); - assert_eq!(map.value(&255), Ok(Some(11))); - assert_eq!(map.value(&191), Ok(Some(12))); -} diff --git a/crates/storage_vec/Cargo.toml b/crates/storage_vec/Cargo.toml deleted file mode 100644 index b122ac44..00000000 --- a/crates/storage_vec/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "agdb_storage_vec" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_storage = { path = "../storage", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/storage_vec/src/lib.rs b/crates/storage_vec/src/lib.rs deleted file mode 100644 index 32c7f2bd..00000000 --- a/crates/storage_vec/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod storage_try_from; -mod storage_vec; -mod vec_iterator; - -pub use storage_vec::StorageVec; diff --git a/crates/storage_vec/src/storage_try_from.rs b/crates/storage_vec/src/storage_try_from.rs deleted file mode 100644 index 1000c153..00000000 --- a/crates/storage_vec/src/storage_try_from.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::storage_vec::StorageVec; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use std::cell::RefCell; -use std::marker::PhantomData; -use std::rc::Rc; - -impl TryFrom>> for StorageVec -where - T: Serialize, - Data: Storage, -{ - type Error = DbError; - - fn try_from(storage: Rc>) -> Result { - let index = storage.borrow_mut().insert(&0_u64)?; - - Ok(Self { - storage, - storage_index: index, - len: 0, - capacity: 0, - phantom_data: PhantomData, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> - for StorageVec -{ - type Error = DbError; - - fn try_from( - storage_with_index: (Rc>, StorageIndex), - ) -> Result { - let byte_size = storage_with_index - .0 - .borrow_mut() - .value_size(&storage_with_index.1)?; - let size = storage_with_index - .0 - .borrow_mut() - .value_at::(&storage_with_index.1, 0)?; - - Ok(Self { - storage: storage_with_index.0, - storage_index: storage_with_index.1, - len: size, - capacity: Self::capacity_from_bytes(byte_size), - phantom_data: PhantomData, - }) - } -} diff --git a/crates/storage_vec/src/storage_vec.rs b/crates/storage_vec/src/storage_vec.rs deleted file mode 100644 index c9240be8..00000000 --- a/crates/storage_vec/src/storage_vec.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::vec_iterator::VecIterator; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use std::cell::RefCell; -use std::cell::RefMut; -use std::cmp::max; -use std::marker::PhantomData; -use std::rc::Rc; - -pub struct StorageVec -where - T: Serialize, - Data: Storage, -{ - pub(crate) storage: Rc>, - pub(crate) storage_index: StorageIndex, - pub(crate) len: u64, - pub(crate) capacity: u64, - pub(crate) phantom_data: PhantomData, -} - -impl StorageVec -where - T: Serialize, - Data: Storage, -{ - pub fn capacity(&self) -> u64 { - self.capacity - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - pub fn iter(&self) -> VecIterator { - VecIterator:: { - index: 0, - vec: self, - phantom_data: PhantomData, - } - } - - pub fn len(&self) -> u64 { - self.len - } - - pub fn push(&mut self, value: &T) -> Result<(), DbError> { - let mut ref_storage = self.storage.borrow_mut(); - ref_storage.transaction(); - - if self.len() == self.capacity() { - let current_capacity = self.capacity(); - Self::reallocate( - &mut self.capacity, - max(current_capacity * 2, 64), - &mut ref_storage, - &self.storage_index, - )?; - } - - ref_storage.insert_at(&self.storage_index, Self::value_offset(self.len()), value)?; - self.len += 1; - ref_storage.insert_at(&self.storage_index, 0, &self.len())?; - ref_storage.commit() - } - - pub fn remove(&mut self, index: u64) -> Result<(), DbError> { - if self.len() <= index { - return Err(DbError::from("index out of bounds")); - } - - let offset_from = Self::value_offset(index + 1); - let offset_to = Self::value_offset(index); - let size = Self::value_offset(self.len()) - offset_from; - - let mut ref_storage = self.storage.borrow_mut(); - ref_storage.transaction(); - ref_storage.move_at(&self.storage_index, offset_from, offset_to, size)?; - self.len -= 1; - ref_storage.insert_at(&self.storage_index, 0, &self.len())?; - ref_storage.commit() - } - - pub fn reserve(&mut self, capacity: u64) -> Result<(), DbError> { - if capacity <= self.capacity() { - return Ok(()); - } - - let mut ref_storage = self.storage.borrow_mut(); - Self::reallocate( - &mut self.capacity, - capacity, - &mut ref_storage, - &self.storage_index, - ) - } - - pub fn resize(&mut self, size: u64) -> Result<(), DbError> { - if self.len() == size { - return Ok(()); - } - - let mut ref_storage = self.storage.borrow_mut(); - ref_storage.transaction(); - - if size < self.len() { - let offset = Self::value_offset(size); - let byte_size = Self::value_offset(self.len()) - offset; - ref_storage.insert_at(&self.storage_index, offset, &vec![0_u8; byte_size as usize])?; - } else if self.capacity() < size { - Self::reallocate( - &mut self.capacity, - size, - &mut ref_storage, - &self.storage_index, - )?; - } - - self.len = size; - ref_storage.insert_at(&self.storage_index, 0, &self.len())?; - ref_storage.commit() - } - - pub fn set_value(&mut self, index: u64, value: &T) -> Result<(), DbError> { - if self.len() <= index { - return Err(DbError::from("index out of bounds")); - } - - self.storage - .borrow_mut() - .insert_at(&self.storage_index, Self::value_offset(index), value) - } - - pub fn shrink_to_fit(&mut self) -> Result<(), DbError> { - let current_len = self.len(); - let mut ref_storage = self.storage.borrow_mut(); - Self::reallocate( - &mut self.capacity, - current_len, - &mut ref_storage, - &self.storage_index, - ) - } - - pub fn storage_index(&self) -> StorageIndex { - self.storage_index.clone() - } - - #[allow(clippy::wrong_self_convention)] - pub fn to_vec(&self) -> Result, DbError> { - self.storage.borrow_mut().value(&self.storage_index) - } - - pub fn value(&self, index: u64) -> Result { - if self.len() <= index { - return Err(DbError::from("index out of bounds")); - } - - self.storage - .borrow_mut() - .value_at::(&self.storage_index, Self::value_offset(index)) - } - - pub fn value_offset(index: u64) -> u64 { - u64::serialized_size() + index * T::serialized_size() - } - - pub(crate) fn capacity_from_bytes(len: u64) -> u64 { - (len - u64::serialized_size()) / T::serialized_size() - } - - fn reallocate( - capacity: &mut u64, - new_capacity: u64, - storage: &mut RefMut, - storage_index: &StorageIndex, - ) -> Result<(), DbError> { - *capacity = new_capacity; - storage.resize_value(storage_index, Self::value_offset(new_capacity)) - } -} diff --git a/crates/storage_vec/tests/storage_vec_push_test.rs b/crates/storage_vec/tests/storage_vec_push_test.rs deleted file mode 100644 index a56a3c74..00000000 --- a/crates/storage_vec/tests/storage_vec_push_test.rs +++ /dev/null @@ -1,26 +0,0 @@ -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn push() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(vec![1_i64, 3_i64, 5_i64]) - ); -} diff --git a/crates/storage_vec/tests/storage_vec_remove_test.rs b/crates/storage_vec/tests/storage_vec_remove_test.rs deleted file mode 100644 index a3bdc49a..00000000 --- a/crates/storage_vec/tests/storage_vec_remove_test.rs +++ /dev/null @@ -1,75 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn remove() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.remove(1).unwrap(); - - assert_eq!(vec.to_vec(), Ok(vec![1, 5])); -} - -#[test] -fn remove_at_end() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.remove(2).unwrap(); - - assert_eq!(vec.to_vec(), Ok(vec![1, 3])); -} - -#[test] -fn remove_index_out_of_bounds() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!(vec.remove(0), Err(DbError::from("index out of bounds"))); -} - -#[test] -fn remove_size_updated() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.remove(1).unwrap(); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(vec![1_i64, 5_i64]) - ); -} diff --git a/crates/storage_vec/tests/storage_vec_reserve_test.rs b/crates/storage_vec/tests/storage_vec_reserve_test.rs deleted file mode 100644 index 2a9ae625..00000000 --- a/crates/storage_vec/tests/storage_vec_reserve_test.rs +++ /dev/null @@ -1,34 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn reserve_larger() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - assert_eq!(vec.capacity(), 0); - - vec.reserve(20).unwrap(); - - assert_eq!(vec.capacity(), 20); -} - -#[test] -fn reserve_smaller() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.reserve(20).unwrap(); - vec.reserve(10).unwrap(); - - assert_eq!(vec.capacity(), 20); -} diff --git a/crates/storage_vec/tests/storage_vec_resize_test.rs b/crates/storage_vec/tests/storage_vec_resize_test.rs deleted file mode 100644 index 453be110..00000000 --- a/crates/storage_vec/tests/storage_vec_resize_test.rs +++ /dev/null @@ -1,102 +0,0 @@ -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn resize_larger() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.resize(6).unwrap(); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(vec![1_i64, 3_i64, 5_i64, 0, 0, 0]) - ); -} - -#[test] -fn resize_over_capacity() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.resize(100).unwrap(); - - let mut expected = vec![0_i64; 100]; - expected[0] = 1; - expected[1] = 3; - expected[2] = 5; - - assert_eq!(vec.len(), 100); - assert_eq!(vec.capacity(), 100); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(expected) - ); -} - -#[test] -fn resize_same() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.resize(3).unwrap(); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(vec![1_i64, 3_i64, 5_i64]) - ); -} - -#[test] -fn resize_smaller() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.resize(1).unwrap(); - - assert_eq!( - storage - .borrow_mut() - .value::>(&vec.storage_index()), - Ok(vec![1_i64]) - ); -} diff --git a/crates/storage_vec/tests/storage_vec_set_value_test.rs b/crates/storage_vec/tests/storage_vec_set_value_test.rs deleted file mode 100644 index 095b45e5..00000000 --- a/crates/storage_vec/tests/storage_vec_set_value_test.rs +++ /dev/null @@ -1,40 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn set_value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - vec.set_value(1, &10).unwrap(); - - assert_eq!(vec.value(0), Ok(1)); - assert_eq!(vec.value(1), Ok(10)); - assert_eq!(vec.value(2), Ok(5)); -} - -#[test] -fn set_value_out_of_bounds() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!( - vec.set_value(0, &10), - Err(DbError::from("index out of bounds")) - ); -} diff --git a/crates/storage_vec/tests/storage_vec_shrink_to_fit_test.rs b/crates/storage_vec/tests/storage_vec_shrink_to_fit_test.rs deleted file mode 100644 index 8106acd0..00000000 --- a/crates/storage_vec/tests/storage_vec_shrink_to_fit_test.rs +++ /dev/null @@ -1,44 +0,0 @@ -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn shrink_to_fit() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.capacity(), 64); - - vec.shrink_to_fit().unwrap(); - - assert_eq!(vec.capacity(), 3); - - vec.shrink_to_fit().unwrap(); - - assert_eq!(vec.capacity(), 3); -} - -#[test] -fn shrink_to_fit_empty() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!(vec.capacity(), 0); - - vec.shrink_to_fit().unwrap(); - - assert_eq!(vec.capacity(), 0); -} diff --git a/crates/storage_vec/tests/storage_vec_test.rs b/crates/storage_vec/tests/storage_vec_test.rs deleted file mode 100644 index bb0edf59..00000000 --- a/crates/storage_vec/tests/storage_vec_test.rs +++ /dev/null @@ -1,126 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn iter() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.iter().collect::>(), vec![1_i64, 3_i64, 5_i64]); -} - -#[test] -fn is_empty() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert!(vec.is_empty()); - - vec.push(&1).unwrap(); - - assert!(!vec.is_empty()); -} - -#[test] -fn len() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!(vec.len(), 0); - - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.len(), 3) -} - -#[test] -fn min_capacity() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!(vec.capacity(), 0); - - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.capacity(), 64); -} - -#[test] -fn to_vec() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.to_vec(), Ok(vec![1_i64, 3_i64, 5_i64])); -} - -#[test] -fn try_from_storage_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let index; - - { - let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - index = vec.storage_index(); - } - - let vec = StorageVec::::try_from((storage, index)).unwrap(); - - assert_eq!(vec.to_vec(), Ok(vec![1_i64, 3_i64, 5_i64])); -} - -#[test] -fn try_from_storage_missing_index() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - assert_eq!( - StorageVec::::try_from((storage, StorageIndex::from(1_i64))) - .err() - .unwrap(), - DbError::from("index '1' not found") - ); -} diff --git a/crates/storage_vec/tests/storage_vec_value_test.rs b/crates/storage_vec/tests/storage_vec_value_test.rs deleted file mode 100644 index 0dd04ab8..00000000 --- a/crates/storage_vec/tests/storage_vec_value_test.rs +++ /dev/null @@ -1,35 +0,0 @@ -use agdb_db_error::DbError; -use agdb_storage::StorageFile; -use agdb_storage_vec::StorageVec; -use agdb_test_utilities::TestFile; -use std::cell::RefCell; -use std::rc::Rc; - -#[test] -fn value() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let mut vec = StorageVec::::try_from(storage).unwrap(); - vec.push(&1).unwrap(); - vec.push(&3).unwrap(); - vec.push(&5).unwrap(); - - assert_eq!(vec.value(0), Ok(1)); - assert_eq!(vec.value(1), Ok(3)); - assert_eq!(vec.value(2), Ok(5)); -} - -#[test] -fn value_out_of_bounds() { - let test_file = TestFile::new(); - let storage = Rc::new(RefCell::new( - StorageFile::try_from(test_file.file_name().clone()).unwrap(), - )); - - let vec = StorageVec::::try_from(storage).unwrap(); - - assert_eq!(vec.value(0), Err(DbError::from("index out of bounds"))); -} diff --git a/crates/test_utilities/Cargo.toml b/crates/test_utilities/Cargo.toml deleted file mode 100644 index f4955328..00000000 --- a/crates/test_utilities/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "agdb_test_utilities" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } -agdb_utilities = { path = "../utilities", version = "<=0.1.0" } diff --git a/crates/test_utilities/src/collided_value.rs b/crates/test_utilities/src/collided_value.rs deleted file mode 100644 index c1a3b511..00000000 --- a/crates/test_utilities/src/collided_value.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct CollidedValue { - pub value: T, -} - -impl CollidedValue { - pub fn new(value: T) -> Self { - CollidedValue { value } - } -} diff --git a/crates/test_utilities/src/collided_value_serialize.rs b/crates/test_utilities/src/collided_value_serialize.rs deleted file mode 100644 index cbeafa15..00000000 --- a/crates/test_utilities/src/collided_value_serialize.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::collided_value::CollidedValue; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -impl Serialize for CollidedValue -where - T: Serialize, -{ - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - value: T::deserialize(bytes)?, - }) - } - - fn serialize(&self) -> Vec { - self.value.serialize() - } -} diff --git a/crates/test_utilities/src/collided_value_stable_hash.rs b/crates/test_utilities/src/collided_value_stable_hash.rs deleted file mode 100644 index 3e003258..00000000 --- a/crates/test_utilities/src/collided_value_stable_hash.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::collided_value::CollidedValue; -use agdb_utilities::StableHash; - -impl StableHash for CollidedValue { - fn stable_hash(&self) -> u64 { - 1 - } -} diff --git a/crates/test_utilities/src/lib.rs b/crates/test_utilities/src/lib.rs deleted file mode 100644 index 50d8ff38..00000000 --- a/crates/test_utilities/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -mod collided_value; -mod collided_value_serialize; -mod collided_value_stable_hash; -mod test_file; -mod test_file_default; -mod test_file_drop; -mod test_file_from; - -pub use collided_value::CollidedValue; -pub use test_file::TestFile; diff --git a/crates/test_utilities/src/test_file.rs b/crates/test_utilities/src/test_file.rs deleted file mode 100644 index bc9b8c2b..00000000 --- a/crates/test_utilities/src/test_file.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::fs::remove_file; -use std::panic::Location; -use std::path::Path; - -pub struct TestFile { - pub(crate) filename: String, -} - -impl TestFile { - pub fn file_name(&self) -> &String { - &self.filename - } - - #[track_caller] - pub fn new() -> TestFile { - let caller = Location::caller(); - let file = format!( - "./{}.{}.{}.testfile", - Path::new(caller.file()) - .file_name() - .unwrap() - .to_str() - .unwrap(), - caller.line(), - caller.column() - ); - - TestFile::from(file) - } - - pub(crate) fn hidden_filename(filename: &String) -> String { - let path = Path::new(filename); - let name: String = path.file_name().unwrap().to_str().unwrap().to_string(); - let parent = path.parent().unwrap(); - - parent - .join(&Path::new(&(".".to_string() + &name))) - .to_str() - .unwrap() - .to_string() - } - - pub(crate) fn remove_file_if_exists(filename: &String) { - if Path::new(filename).exists() { - remove_file(filename).unwrap(); - } - } -} diff --git a/crates/test_utilities/src/test_file_default.rs b/crates/test_utilities/src/test_file_default.rs deleted file mode 100644 index 926553d7..00000000 --- a/crates/test_utilities/src/test_file_default.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::TestFile; - -impl Default for TestFile { - #[track_caller] - fn default() -> Self { - Self::new() - } -} diff --git a/crates/test_utilities/src/test_file_drop.rs b/crates/test_utilities/src/test_file_drop.rs deleted file mode 100644 index 196723c1..00000000 --- a/crates/test_utilities/src/test_file_drop.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::TestFile; - -impl Drop for TestFile { - fn drop(&mut self) { - Self::remove_file_if_exists(&self.filename); - Self::remove_file_if_exists(&Self::hidden_filename(&self.filename)); - } -} diff --git a/crates/test_utilities/src/test_file_from.rs b/crates/test_utilities/src/test_file_from.rs deleted file mode 100644 index 2cc33158..00000000 --- a/crates/test_utilities/src/test_file_from.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::TestFile; - -impl From<&str> for TestFile { - fn from(filename: &str) -> Self { - TestFile::from(filename.to_string()) - } -} - -impl From for TestFile { - fn from(filename: String) -> Self { - Self::remove_file_if_exists(&filename); - Self::remove_file_if_exists(&Self::hidden_filename(&filename)); - - TestFile { filename } - } -} diff --git a/crates/test_utilities/tests/collided_value_test.rs b/crates/test_utilities/tests/collided_value_test.rs deleted file mode 100644 index 6b6722b8..00000000 --- a/crates/test_utilities/tests/collided_value_test.rs +++ /dev/null @@ -1,34 +0,0 @@ -use agdb_serialize::Serialize; -use agdb_test_utilities::CollidedValue; -use agdb_utilities::StableHash; - -#[test] -fn derived_from_clone() { - let value = CollidedValue::new(1_i64); - let other = value.clone(); - - assert_eq!(value, other); -} - -#[test] -fn derived_from_debug() { - let value = CollidedValue::new(1_i64); - - format!("{:?}", value); -} - -#[test] -fn serialize() { - let value = CollidedValue::new(1_i64); - let bytes = value.serialize(); - let other = CollidedValue::deserialize(&bytes).unwrap(); - - assert_eq!(value, other); -} - -#[test] -fn stable_hash() { - let value = CollidedValue::new(1_i64); - - assert_eq!(value.stable_hash(), 1_u64); -} diff --git a/crates/test_utilities/tests/test_file_test.rs b/crates/test_utilities/tests/test_file_test.rs deleted file mode 100644 index 2ecd6710..00000000 --- a/crates/test_utilities/tests/test_file_test.rs +++ /dev/null @@ -1,106 +0,0 @@ -use agdb_test_utilities::TestFile; -use std::fs::OpenOptions; -use std::panic::Location; -use std::path::Path; - -fn ensure_file(filename: &str) { - OpenOptions::new() - .write(true) - .create_new(true) - .open(filename) - .unwrap(); -} - -#[test] -fn default() { - let caller = Location::caller(); - let current_source_file = Path::new(caller.file()) - .file_name() - .unwrap() - .to_str() - .unwrap(); - - let test_file = TestFile::default(); - assert!(!test_file.file_name().is_empty()); - assert!(test_file.file_name().contains(¤t_source_file)); -} - -#[test] -fn created_from_str_ref() { - let filename = "./test_file-created_from_str_ref"; - let _test_file = TestFile::from(filename); -} - -#[test] -fn created_from_string() { - let filename = "./test_file-created_from_string".to_string(); - let _test_file = TestFile::from(filename); -} - -#[test] -fn existing_file_is_deleted_on_construction() { - let filename = "./test_file-existing_file_is_deleted_on_construction"; - ensure_file(filename); - let _test_file = TestFile::from(filename); - assert!(!Path::new(filename).exists()); -} - -#[test] -fn file_is_deleted_on_destruction() { - let filename = "./test_file-file_is_deleted_on_destruction"; - - { - let _test_file = TestFile::from(filename); - ensure_file(filename); - } - - assert!(!Path::new(filename).exists()); -} - -#[test] -fn get_file_name() { - let filename = "./test_file-get_file_name"; - let test_file = TestFile::from(filename); - - assert_eq!(test_file.file_name(), filename); -} - -#[test] -fn hidden_file_is_deleted_on_construction() { - let filename = "./test_file-hidden_file_is_deleted_on_construction"; - let hidden_filename = "./.test_file-hidden_file_is_deleted_on_construction"; - ensure_file(filename); - ensure_file(hidden_filename); - let _test_file = TestFile::from(filename); - assert!(!Path::new(filename).exists()); - assert!(!Path::new(hidden_filename).exists()); -} - -#[test] -fn hidden_file_is_deleted_on_destruction() { - let filename = "test_file-hidden_file_is_deleted_on_destruction"; - let hidden_filename = ".test_file-hidden_file_is_deleted_on_destruction"; - - { - let _test_file = TestFile::from(filename); - ensure_file(filename); - ensure_file(hidden_filename); - } - - assert!(!Path::new(filename).exists()); - assert!(!Path::new(hidden_filename).exists()); -} - -#[test] -fn new() { - let caller = Location::caller(); - let current_source_file = Path::new(caller.file()) - .file_name() - .unwrap() - .to_str() - .unwrap(); - - let test_file = TestFile::new(); - assert!(!test_file.file_name().is_empty()); - assert!(test_file.file_name().contains(¤t_source_file)); -} diff --git a/crates/utilities/Cargo.toml b/crates/utilities/Cargo.toml deleted file mode 100644 index 41ff4ec0..00000000 --- a/crates/utilities/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "agdb_utilities" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/crates/utilities/src/lib.rs b/crates/utilities/src/lib.rs deleted file mode 100644 index 7a106ef0..00000000 --- a/crates/utilities/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod stable_hash; - -pub use stable_hash::StableHash; diff --git a/crates/utilities/tests/stable_hash_test.rs b/crates/utilities/tests/stable_hash_test.rs deleted file mode 100644 index 0beb1aa3..00000000 --- a/crates/utilities/tests/stable_hash_test.rs +++ /dev/null @@ -1,11 +0,0 @@ -use agdb_utilities::StableHash; - -#[test] -fn i64() { - assert_eq!(10_i64.stable_hash(), 10_u64); -} - -#[test] -fn u64() { - assert_eq!(10_u64.stable_hash(), 10_u64); -} diff --git a/crates/write_ahead_log/Cargo.toml b/crates/write_ahead_log/Cargo.toml deleted file mode 100644 index 4e0e1e86..00000000 --- a/crates/write_ahead_log/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "agdb_write_ahead_log" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -agdb_db_error = { path = "../db_error", version = "<=0.1.0" } -agdb_serialize = { path = "../serialize", version = "<=0.1.0" } - -[dev-dependencies] -agdb_test_utilities = { path = "../test_utilities", version = "<=0.1.0" } diff --git a/crates/write_ahead_log/src/lib.rs b/crates/write_ahead_log/src/lib.rs deleted file mode 100644 index 9e3c4b6a..00000000 --- a/crates/write_ahead_log/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod write_ahead_log; -mod write_ahead_log_record; - -pub use write_ahead_log::WriteAheadLog; -pub use write_ahead_log_record::WriteAheadLogRecord; diff --git a/crates/write_ahead_log/src/write_ahead_log.rs b/crates/write_ahead_log/src/write_ahead_log.rs deleted file mode 100644 index c8c57f47..00000000 --- a/crates/write_ahead_log/src/write_ahead_log.rs +++ /dev/null @@ -1,87 +0,0 @@ -use crate::write_ahead_log_record::WriteAheadLogRecord; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::fs::File; -use std::fs::OpenOptions; -use std::io::Read; -use std::io::Seek; -use std::io::SeekFrom; -use std::io::Write; - -pub struct WriteAheadLog { - file: File, -} - -impl WriteAheadLog { - pub fn clear(&mut self) -> Result<(), DbError> { - Ok(self.file.set_len(0)?) - } - - pub fn insert(&mut self, record: WriteAheadLogRecord) -> Result<(), DbError> { - self.file.seek(SeekFrom::End(0))?; - self.file.write_all(&record.position.serialize())?; - self.file - .write_all(&(record.bytes.len() as u64).serialize())?; - self.file.write_all(&record.bytes)?; - - Ok(()) - } - - pub fn records(&mut self) -> Result, DbError> { - let mut records = Vec::::new(); - let size = self.file.seek(SeekFrom::End(0))?; - self.file.seek(SeekFrom::Start(0))?; - - while self.file.seek(SeekFrom::Current(0))? < size { - records.push(Self::read_record(&mut self.file)?); - } - - Ok(records) - } - - fn read_exact(file: &mut File, size: u64) -> Result, DbError> { - let mut buffer = vec![0_u8; size as usize]; - file.read_exact(&mut buffer)?; - Ok(buffer) - } - - fn read_record(file: &mut File) -> Result { - let position = u64::deserialize(&Self::read_exact(file, u64::serialized_size())?)?; - let size = u64::deserialize(&Self::read_exact(file, u64::serialized_size())?)?; - - Ok(WriteAheadLogRecord { - position, - bytes: Self::read_exact(file, size)?, - }) - } - - pub(crate) fn wal_filename(filename: &str) -> String { - let pos; - - if let Some(slash) = filename.rfind('/') { - pos = slash + 1; - } else if let Some(backslash) = filename.rfind('\\') { - pos = backslash + 1 - } else { - pos = 0; - } - - let mut copy = filename.to_owned(); - copy.insert(pos, '.'); - copy - } -} - -impl TryFrom<&String> for WriteAheadLog { - type Error = DbError; - - fn try_from(filename: &String) -> Result { - let file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(WriteAheadLog::wal_filename(filename))?; - - Ok(WriteAheadLog { file }) - } -} diff --git a/crates/write_ahead_log/src/write_ahead_log_record.rs b/crates/write_ahead_log/src/write_ahead_log_record.rs deleted file mode 100644 index 91c7261b..00000000 --- a/crates/write_ahead_log/src/write_ahead_log_record.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct WriteAheadLogRecord { - pub position: u64, - pub bytes: Vec, -} diff --git a/crates/write_ahead_log/tests/write_ahead_log_record_test.rs b/crates/write_ahead_log/tests/write_ahead_log_record_test.rs deleted file mode 100644 index 364e290f..00000000 --- a/crates/write_ahead_log/tests/write_ahead_log_record_test.rs +++ /dev/null @@ -1,7 +0,0 @@ -use agdb_write_ahead_log::WriteAheadLogRecord; - -#[test] -fn derived_from_debug() { - let record = WriteAheadLogRecord::default(); - format!("{:?}", record); -} diff --git a/crates/write_ahead_log/tests/write_ahead_log_test.rs b/crates/write_ahead_log/tests/write_ahead_log_test.rs deleted file mode 100644 index bace88d5..00000000 --- a/crates/write_ahead_log/tests/write_ahead_log_test.rs +++ /dev/null @@ -1,75 +0,0 @@ -use agdb_test_utilities::TestFile; -use agdb_write_ahead_log::WriteAheadLog; -use agdb_write_ahead_log::WriteAheadLogRecord; - -#[test] -fn clear() { - let test_file = TestFile::new(); - - let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); - let record = WriteAheadLogRecord { - position: 1, - bytes: vec![1_u8; 5], - }; - - wal.insert(record).unwrap(); - wal.clear().unwrap(); - - assert_eq!(wal.records(), Ok(vec![])); -} - -#[test] -fn filename_constructed() { - let test_file = TestFile::new(); - WriteAheadLog::try_from(test_file.file_name()).unwrap(); -} - -#[test] -fn insert() { - let test_file = TestFile::from(".\\write_ahead_log_test.rs-insert.testfile"); - - let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); - let record = WriteAheadLogRecord { - position: 1, - bytes: vec![1_u8; 5], - }; - - wal.insert(record.clone()).unwrap(); - - assert_eq!(wal.records(), Ok(vec![record])); -} - -#[test] -fn insert_empty() { - let test_file = TestFile::from("./write_ahead_log_test.rs-insert_empty.testfile"); - - let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); - let record = WriteAheadLogRecord { - position: 16, - bytes: vec![], - }; - - wal.insert(record.clone()).unwrap(); - - assert_eq!(wal.records(), Ok(vec![record])); -} - -#[test] -fn records() { - let test_file = TestFile::from("write_ahead_log_test.rs-records.testfile"); - - let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); - let record1 = WriteAheadLogRecord { - position: 1, - bytes: vec![1_u8; 5], - }; - let record2 = WriteAheadLogRecord { - position: 15, - bytes: vec![2_u8; 3], - }; - - wal.insert(record1.clone()).unwrap(); - wal.insert(record2.clone()).unwrap(); - - assert_eq!(wal.records(), Ok(vec![record1, record2])); -} diff --git a/src/agdb/collections.rs b/src/agdb/collections.rs new file mode 100644 index 00000000..b4c73c91 --- /dev/null +++ b/src/agdb/collections.rs @@ -0,0 +1,11 @@ +pub mod bit_set; +pub mod dictionary; +pub mod multi_map; +pub mod storage_dictionary; +pub mod storage_map; +pub mod storage_multi_map; +pub mod storage_vec; + +mod map; +mod map_common; +mod vec; diff --git a/src/agdb/collections/bit_set.rs b/src/agdb/collections/bit_set.rs new file mode 100644 index 00000000..ec65d42f --- /dev/null +++ b/src/agdb/collections/bit_set.rs @@ -0,0 +1,157 @@ +#[derive(Default)] +pub struct BitSet { + #[allow(dead_code)] + data: Vec, +} + +#[allow(dead_code)] +impl BitSet { + pub fn clear(&mut self) { + self.data.clear(); + } + + pub fn insert(&mut self, value: u64) { + let byte_index = value as usize / 8; + let bit_index = value as usize % 8; + + if self.data.len() <= byte_index { + self.data.resize(byte_index + 1, 0); + } + + self.data[byte_index] |= 1 << bit_index; + } + + pub fn new() -> Self { + Self { data: vec![] } + } + + pub fn remove(&mut self, value: u64) { + let byte_index = value as usize / 8; + + if byte_index < self.data.len() { + let bit_index = value as usize % 8; + self.data[byte_index] ^= 1 << bit_index; + } + } + + pub fn value(&self, value: u64) -> bool { + let byte_index = value as usize / 8; + + if byte_index < self.data.len() { + let bit_index = value as usize % 8; + + return self.data[byte_index] & (1 << bit_index) != 0; + } + + false + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn clear() { + let mut bitset = BitSet::new(); + + bitset.insert(10_u64); + bitset.clear(); + + assert!(!bitset.value(10_u64)); + } + + #[test] + fn derived_from_default() { + let _bitset = BitSet::default(); + } + + #[test] + fn insert() { + let mut bitset = BitSet::new(); + + assert!(!bitset.value(10_u64)); + + bitset.insert(10_u64); + + assert!(bitset.value(10_u64)); + } + + #[test] + fn insert_multiple() { + let mut bitset = BitSet::new(); + + assert!(!bitset.value(10_u64)); + assert!(!bitset.value(11_u64)); + assert!(!bitset.value(2_u64)); + + bitset.insert(10_u64); + bitset.insert(11_u64); + bitset.insert(2_u64); + + assert!(bitset.value(10_u64)); + assert!(bitset.value(11_u64)); + assert!(bitset.value(2_u64)); + } + + #[test] + fn remove() { + let mut bitset = BitSet::new(); + + bitset.insert(10_u64); + bitset.insert(11_u64); + bitset.insert(2_u64); + + bitset.remove(11_u64); + + assert!(bitset.value(10_u64)); + assert!(!bitset.value(11_u64)); + assert!(bitset.value(2_u64)); + } + + #[test] + fn remove_unset() { + let mut bitset = BitSet::new(); + + bitset.insert(10_u64); + bitset.insert(11_u64); + bitset.insert(2_u64); + + bitset.remove(9_u64); + + assert!(bitset.value(10_u64)); + assert!(bitset.value(11_u64)); + assert!(bitset.value(2_u64)); + } + + #[test] + fn remove_beyond_length() { + let mut bitset = BitSet::new(); + + bitset.insert(10_u64); + bitset.insert(11_u64); + bitset.insert(2_u64); + + bitset.remove(150_u64); + + assert!(bitset.value(10_u64)); + assert!(bitset.value(11_u64)); + assert!(bitset.value(2_u64)); + } + + #[test] + fn value_missing() { + let mut bitset = BitSet::new(); + + bitset.insert(5_u64); + + assert!(!bitset.value(2_u64)); + } + + #[test] + fn value_beyond_length() { + let bitset = BitSet::new(); + + assert!(!bitset.value(10_u64)); + } +} diff --git a/src/agdb/collections/dictionary.rs b/src/agdb/collections/dictionary.rs new file mode 100644 index 00000000..89451272 --- /dev/null +++ b/src/agdb/collections/dictionary.rs @@ -0,0 +1,224 @@ +pub mod dictionary_data_storage; +pub mod dictionary_data_storage_indexes; +pub mod dictionary_impl; +pub mod dictionary_index; +pub mod dictionary_value; + +mod dictionary_data; +mod dictionary_data_memory; + +use self::dictionary_data_memory::DictionaryDataMemory; +use self::dictionary_impl::DictionaryImpl; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; +use std::marker::PhantomData; + +pub type Dictionary = DictionaryImpl>; + +impl Dictionary +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, +{ + pub fn new() -> Dictionary { + Dictionary { + data: DictionaryDataMemory::::default(), + phantom_data: PhantomData, + } + } +} + +impl Default for Dictionary +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, +{ + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::super::dictionary::dictionary_index::DictionaryIndex; + use super::*; + use crate::test_utilities::collided_value::CollidedValue; + + #[test] + fn count_invalid_index() { + let dictionary = Dictionary::::new(); + + assert_eq!(dictionary.count(&DictionaryIndex::default()), Ok(None)); + assert_eq!(dictionary.count(&DictionaryIndex::from(-1_i64)), Ok(None)); + } + + #[test] + fn derived_from_default() { + let _dictionary = Dictionary::::default(); + } + + #[test] + fn index() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.index(&10_i64), Ok(Some(index))); + } + + #[test] + fn index_missing_value() { + let dictionary = Dictionary::::new(); + + assert_eq!(dictionary.index(&10), Ok(None)); + } + + #[test] + fn index_removed_value() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.index(&10), Ok(None)); + } + + #[test] + fn index_reuse() { + let mut dictionary = Dictionary::::new(); + + let index1 = dictionary.insert(&5).unwrap(); + let index2 = dictionary.insert(&10).unwrap(); + let index3 = dictionary.insert(&7).unwrap(); + + dictionary.remove(&index2).unwrap(); + dictionary.remove(&index1).unwrap(); + dictionary.remove(&index3).unwrap(); + + assert_eq!(dictionary.count(&index1), Ok(None)); + assert_eq!(dictionary.count(&index2), Ok(None)); + assert_eq!(dictionary.count(&index3), Ok(None)); + + assert_eq!(dictionary.insert(&3), Ok(index3.clone())); + assert_eq!(dictionary.insert(&2), Ok(index1.clone())); + assert_eq!(dictionary.insert(&1), Ok(index2.clone())); + + assert_eq!(dictionary.value(&index1), Ok(Some(2))); + assert_eq!(dictionary.value(&index2), Ok(Some(1))); + assert_eq!(dictionary.value(&index3), Ok(Some(3))); + } + + #[test] + fn index_with_collisions() { + let mut dictionary = Dictionary::>::new(); + + let index1 = dictionary.insert(&CollidedValue::new(1)).unwrap(); + let index2 = dictionary.insert(&CollidedValue::new(2)).unwrap(); + let index3 = dictionary.insert(&CollidedValue::new(3)).unwrap(); + + assert_eq!(dictionary.index(&CollidedValue::new(1)), Ok(Some(index1))); + assert_eq!(dictionary.index(&CollidedValue::new(2)), Ok(Some(index2))); + assert_eq!(dictionary.index(&CollidedValue::new(3)), Ok(Some(index3))); + } + + #[test] + fn insert() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + assert_eq!(dictionary.value(&index), Ok(Some(10_i64))); + assert_eq!(dictionary.count(&index), Ok(Some(1))); + } + + #[test] + fn insert_multiple() { + let mut dictionary = Dictionary::::new(); + + let index1 = dictionary.insert(&10).unwrap(); + let index2 = dictionary.insert(&15).unwrap(); + let index3 = dictionary.insert(&20).unwrap(); + + assert_eq!(dictionary.len(), Ok(3)); + + assert_eq!(dictionary.value(&index1).unwrap(), Some(10_i64)); + assert_eq!(dictionary.count(&index1), Ok(Some(1))); + + assert_eq!(dictionary.value(&index2).unwrap(), Some(15_i64)); + assert_eq!(dictionary.count(&index2), Ok(Some(1))); + + assert_eq!(dictionary.value(&index3).unwrap(), Some(20_i64)); + assert_eq!(dictionary.count(&index3), Ok(Some(1))); + } + + #[test] + fn insert_same() { + let mut dictionary = Dictionary::::new(); + + dictionary.insert(&10).unwrap(); + + let index2 = dictionary.insert(&15).unwrap(); + + assert_eq!(dictionary.insert(&15).unwrap(), index2); + assert_eq!(dictionary.insert(&15).unwrap(), index2); + + dictionary.insert(&20).unwrap(); + + assert_eq!(dictionary.len(), Ok(3)); + assert_eq!(dictionary.count(&index2), Ok(Some(3))); + } + + #[test] + fn remove() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(None)); + assert_eq!(dictionary.count(&index), Ok(None)); + } + + #[test] + fn remove_duplicated() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.insert(&10).unwrap(); + dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(Some(10))); + assert_eq!(dictionary.count(&index), Ok(Some(3))); + + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(Some(10))); + assert_eq!(dictionary.count(&index), Ok(Some(2))); + + dictionary.remove(&index).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(None)); + assert_eq!(dictionary.count(&index), Ok(None)); + } + + #[test] + fn remove_missing() { + let mut dictionary = Dictionary::::new(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + + dictionary + .remove(&DictionaryIndex::from(index.value() + 1)) + .unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + } + + #[test] + fn value_missing_index() { + let dictionary = Dictionary::::new(); + assert_eq!(dictionary.value(&DictionaryIndex::from(1_i64)), Ok(None)); + } +} diff --git a/crates/dictionary/src/dictionary_data.rs b/src/agdb/collections/dictionary/dictionary_data.rs similarity index 86% rename from crates/dictionary/src/dictionary_data.rs rename to src/agdb/collections/dictionary/dictionary_data.rs index 0fbee788..f12b48cf 100644 --- a/crates/dictionary/src/dictionary_data.rs +++ b/src/agdb/collections/dictionary/dictionary_data.rs @@ -1,9 +1,8 @@ -use crate::dictionary_index::DictionaryIndex; - +use super::dictionary_index::DictionaryIndex; use super::dictionary_value::DictionaryValue; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; pub trait DictionaryData where diff --git a/crates/dictionary/src/dictionary_data_memory.rs b/src/agdb/collections/dictionary/dictionary_data_memory.rs similarity index 76% rename from crates/dictionary/src/dictionary_data_memory.rs rename to src/agdb/collections/dictionary/dictionary_data_memory.rs index 5d898439..5af37813 100644 --- a/crates/dictionary/src/dictionary_data_memory.rs +++ b/src/agdb/collections/dictionary/dictionary_data_memory.rs @@ -1,10 +1,10 @@ -use crate::dictionary_data::DictionaryData; -use crate::dictionary_index::DictionaryIndex; -use crate::dictionary_value::DictionaryValue; -use agdb_db_error::DbError; -use agdb_multi_map::MultiMap; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use super::dictionary_data::DictionaryData; +use super::dictionary_index::DictionaryIndex; +use super::dictionary_value::DictionaryValue; +use crate::collections::multi_map::MultiMap; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; pub struct DictionaryDataMemory where @@ -80,3 +80,15 @@ where Ok(self.values[index.as_usize()].clone()) } } + +impl Default for DictionaryDataMemory +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, +{ + fn default() -> Self { + Self { + index: MultiMap::::new(), + values: vec![DictionaryValue::::default()], + } + } +} diff --git a/crates/dictionary/src/dictionary_data_storage.rs b/src/agdb/collections/dictionary/dictionary_data_storage.rs similarity index 85% rename from crates/dictionary/src/dictionary_data_storage.rs rename to src/agdb/collections/dictionary/dictionary_data_storage.rs index d0775979..7bb1c5e2 100644 --- a/crates/dictionary/src/dictionary_data_storage.rs +++ b/src/agdb/collections/dictionary/dictionary_data_storage.rs @@ -1,23 +1,25 @@ -use crate::dictionary_data::DictionaryData; -use crate::dictionary_index::DictionaryIndex; -use crate::dictionary_value::DictionaryValue; -use agdb_db_error::DbError; -use agdb_multi_map::StorageMultiMap; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageFile; -use agdb_storage::StorageIndex; -use agdb_storage_vec::StorageVec; -use agdb_utilities::StableHash; +use crate::collections::storage_multi_map::StorageMultiMap; +use crate::collections::storage_vec::StorageVec; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::cell::RefCell; use std::rc::Rc; +use super::dictionary_data::DictionaryData; +use super::dictionary_index::DictionaryIndex; +use super::dictionary_value::DictionaryValue; + pub struct DictionaryDataStorage where T: Clone + Default + Eq + PartialEq + StableHash + Serialize, Data: Storage, { pub(crate) storage: Rc>, + #[allow(dead_code)] pub(crate) storage_index: StorageIndex, pub(crate) index: StorageMultiMap, pub(crate) values: StorageVec, Data>, diff --git a/crates/dictionary/src/dictionary_data_storage_indexes.rs b/src/agdb/collections/dictionary/dictionary_data_storage_indexes.rs similarity index 85% rename from crates/dictionary/src/dictionary_data_storage_indexes.rs rename to src/agdb/collections/dictionary/dictionary_data_storage_indexes.rs index 7f4f42e6..72f5eb4b 100644 --- a/crates/dictionary/src/dictionary_data_storage_indexes.rs +++ b/src/agdb/collections/dictionary/dictionary_data_storage_indexes.rs @@ -1,6 +1,6 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::StorageIndex; +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::utilities::serialize::Serialize; use std::mem::size_of; pub(crate) struct DictionaryDataStorageIndexes { diff --git a/crates/dictionary/src/dictionary_impl.rs b/src/agdb/collections/dictionary/dictionary_impl.rs similarity index 94% rename from crates/dictionary/src/dictionary_impl.rs rename to src/agdb/collections/dictionary/dictionary_impl.rs index 80c3d5a2..766bbaf0 100644 --- a/crates/dictionary/src/dictionary_impl.rs +++ b/src/agdb/collections/dictionary/dictionary_impl.rs @@ -1,9 +1,9 @@ -use crate::dictionary_data::DictionaryData; -use crate::dictionary_index::DictionaryIndex; -use crate::dictionary_value::DictionaryValue; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use super::dictionary_data::DictionaryData; +use super::dictionary_index::DictionaryIndex; +use super::dictionary_value::DictionaryValue; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::marker::PhantomData; pub struct DictionaryImpl @@ -15,6 +15,7 @@ where pub(crate) phantom_data: PhantomData, } +#[allow(dead_code)] impl DictionaryImpl where T: Clone + Default + Eq + PartialEq + StableHash + Serialize, diff --git a/src/agdb/collections/dictionary/dictionary_index.rs b/src/agdb/collections/dictionary/dictionary_index.rs new file mode 100644 index 00000000..9a64a3ea --- /dev/null +++ b/src/agdb/collections/dictionary/dictionary_index.rs @@ -0,0 +1,55 @@ +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct DictionaryIndex { + pub(crate) index: i64, +} + +impl DictionaryIndex { + pub fn as_u64(&self) -> u64 { + self.value() as u64 + } + + pub fn as_usize(&self) -> usize { + self.value() as usize + } + + pub fn is_valid(&self) -> bool { + 0 < self.index + } + + pub fn value(&self) -> i64 { + self.index + } +} + +impl From for DictionaryIndex { + fn from(index: i64) -> Self { + Self { index } + } +} + +impl Serialize for DictionaryIndex { + fn deserialize(bytes: &[u8]) -> Result { + Ok(Self { + index: i64::deserialize(bytes)?, + }) + } + + fn serialize(&self) -> Vec { + self.index.serialize() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derived_from_debug() { + let index = DictionaryIndex::default(); + + format!("{:?}", index); + } +} diff --git a/crates/dictionary/src/dictionary_value_serialize.rs b/src/agdb/collections/dictionary/dictionary_value.rs similarity index 67% rename from crates/dictionary/src/dictionary_value_serialize.rs rename to src/agdb/collections/dictionary/dictionary_value.rs index fcaf523c..146ded84 100644 --- a/crates/dictionary/src/dictionary_value_serialize.rs +++ b/src/agdb/collections/dictionary/dictionary_value.rs @@ -1,7 +1,16 @@ -use crate::dictionary_value::DictionaryValue; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; + +#[derive(Clone, Default)] +pub struct DictionaryValue +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, +{ + pub(crate) meta: i64, + pub(crate) hash: u64, + pub(crate) value: T, +} impl Serialize for DictionaryValue where diff --git a/src/agdb/collections/map.rs b/src/agdb/collections/map.rs new file mode 100644 index 00000000..d1e516a5 --- /dev/null +++ b/src/agdb/collections/map.rs @@ -0,0 +1 @@ +pub mod map_impl; diff --git a/crates/storage_map/src/map_impl.rs b/src/agdb/collections/map/map_impl.rs similarity index 89% rename from crates/storage_map/src/map_impl.rs rename to src/agdb/collections/map/map_impl.rs index 23b76a67..b602f6ff 100644 --- a/crates/storage_map/src/map_impl.rs +++ b/src/agdb/collections/map/map_impl.rs @@ -1,10 +1,10 @@ -use agdb_db_error::DbError; -use agdb_map_common::MapCommon; -use agdb_map_common::MapData; -use agdb_map_common::MapIterator; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use crate::collections::map_common::map_data::MapData; +use crate::collections::map_common::map_iterator::MapIterator; +use crate::collections::map_common::map_value_state::MapValueState; +use crate::collections::map_common::MapCommon; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::collections::HashMap; use std::hash::Hash; use std::marker::PhantomData; @@ -19,6 +19,7 @@ where pub(crate) phantom_data: PhantomData<(K, T)>, } +#[allow(dead_code)] impl MapImpl where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, diff --git a/crates/map_common/src/map_common.rs b/src/agdb/collections/map_common.rs similarity index 83% rename from crates/map_common/src/map_common.rs rename to src/agdb/collections/map_common.rs index f8964abd..7ee9af78 100644 --- a/crates/map_common/src/map_common.rs +++ b/src/agdb/collections/map_common.rs @@ -1,10 +1,17 @@ -use super::map_data::MapData; -use super::map_iterator::MapIterator; -use super::map_value::MapValue; -use super::map_value_state::MapValueState; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +pub mod map_data; +pub mod map_data_memory; +pub mod map_data_storage; +pub mod map_iterator; +pub mod map_value; +pub mod map_value_state; + +use self::map_data::MapData; +use self::map_iterator::MapIterator; +use self::map_value::MapValue; +use self::map_value_state::MapValueState; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::cmp::max; use std::hash::Hash; use std::marker::PhantomData; @@ -19,6 +26,7 @@ where pub(crate) phantom_data: PhantomData<(K, T)>, } +#[allow(dead_code)] impl MapCommon where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, @@ -146,3 +154,17 @@ where new_data } } + +impl From for MapCommon +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: MapData, +{ + fn from(data: Data) -> Self { + Self { + data, + phantom_data: PhantomData, + } + } +} diff --git a/crates/map_common/src/map_data.rs b/src/agdb/collections/map_common/map_data.rs similarity index 88% rename from crates/map_common/src/map_data.rs rename to src/agdb/collections/map_common/map_data.rs index 68a290f4..9d0cddc0 100644 --- a/crates/map_common/src/map_data.rs +++ b/src/agdb/collections/map_common/map_data.rs @@ -1,8 +1,8 @@ use super::map_value::MapValue; use super::map_value_state::MapValueState; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::hash::Hash; pub trait MapData diff --git a/crates/multi_map/src/map_data_memory.rs b/src/agdb/collections/map_common/map_data_memory.rs similarity index 75% rename from crates/multi_map/src/map_data_memory.rs rename to src/agdb/collections/map_common/map_data_memory.rs index ec35fbb6..84a2f4de 100644 --- a/crates/multi_map/src/map_data_memory.rs +++ b/src/agdb/collections/map_common/map_data_memory.rs @@ -1,9 +1,9 @@ -use agdb_db_error::DbError; -use agdb_map_common::MapData; -use agdb_map_common::MapValue; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use super::map_data::MapData; +use super::map_value::MapValue; +use super::map_value_state::MapValueState; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::hash::Hash; use std::mem::take; @@ -71,3 +71,16 @@ where fn transaction(&mut self) {} } + +impl Default for MapDataMemory +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, +{ + fn default() -> Self { + Self { + data: vec![MapValue::::default()], + count: 0, + } + } +} diff --git a/crates/storage_map/src/map_data_storage.rs b/src/agdb/collections/map_common/map_data_storage.rs similarity index 61% rename from crates/storage_map/src/map_data_storage.rs rename to src/agdb/collections/map_common/map_data_storage.rs index c338ba93..1812386a 100644 --- a/crates/storage_map/src/map_data_storage.rs +++ b/src/agdb/collections/map_common/map_data_storage.rs @@ -1,11 +1,11 @@ -use agdb_db_error::DbError; -use agdb_map_common::MapData; -use agdb_map_common::MapValue; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; -use agdb_storage::Storage; -use agdb_storage::StorageIndex; -use agdb_utilities::StableHash; +use super::map_data::MapData; +use super::map_value::MapValue; +use super::map_value_state::MapValueState; +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::cell::RefCell; use std::hash::Hash; use std::marker::PhantomData; @@ -24,6 +24,7 @@ where pub(crate) phantom_data: PhantomData<(K, T)>, } +#[allow(dead_code)] impl MapDataStorage where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, @@ -115,3 +116,59 @@ where self.storage.borrow_mut().transaction() } } + +impl TryFrom>> for MapDataStorage +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + let storage_index = storage.borrow_mut().insert(&0_u64)?; + storage.borrow_mut().insert_at( + &storage_index, + size_of::() as u64, + &vec![MapValue::::default()], + )?; + + Ok(Self { + storage, + storage_index, + count: 0, + capacity: 1, + phantom_data: PhantomData, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for MapDataStorage +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + let count = storage_with_index + .0 + .borrow_mut() + .value_at::(&storage_with_index.1, 0)?; + let capacity = storage_with_index + .0 + .borrow_mut() + .value_at::(&storage_with_index.1, size_of::() as u64)?; + + Ok(Self { + storage: storage_with_index.0, + storage_index: storage_with_index.1, + count, + capacity, + phantom_data: PhantomData, + }) + } +} diff --git a/crates/map_common/src/map_iterator.rs b/src/agdb/collections/map_common/map_iterator.rs similarity index 91% rename from crates/map_common/src/map_iterator.rs rename to src/agdb/collections/map_common/map_iterator.rs index f1da93f0..070641f6 100644 --- a/crates/map_common/src/map_iterator.rs +++ b/src/agdb/collections/map_common/map_iterator.rs @@ -1,7 +1,7 @@ use super::map_data::MapData; use super::map_value_state::MapValueState; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::hash::Hash; use std::marker::PhantomData; diff --git a/src/agdb/collections/map_common/map_value.rs b/src/agdb/collections/map_common/map_value.rs new file mode 100644 index 00000000..a888d885 --- /dev/null +++ b/src/agdb/collections/map_common/map_value.rs @@ -0,0 +1,92 @@ +use super::map_value_state::MapValueState; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct MapValue +where + K: Clone + Default + Serialize, + T: Clone + Default + Serialize, +{ + pub state: MapValueState, + pub key: K, + pub value: T, +} + +impl Serialize for MapValue +where + K: Clone + Default + Serialize, + T: Clone + Default + Serialize, +{ + fn deserialize(bytes: &[u8]) -> Result { + Ok(Self { + state: MapValueState::deserialize(bytes)?, + key: K::deserialize(&bytes[(MapValueState::serialized_size() as usize)..])?, + value: T::deserialize( + &bytes[((MapValueState::serialized_size() + K::serialized_size()) as usize)..], + )?, + }) + } + + fn serialize(&self) -> Vec { + let mut data = Vec::::new(); + data.reserve(Self::serialized_size() as usize); + data.extend(self.state.serialize()); + data.extend(self.key.serialize()); + data.extend(self.value.serialize()); + + data + } + + fn serialized_size() -> u64 { + MapValueState::serialized_size() + K::serialized_size() + T::serialized_size() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derived_from_debug() { + let key_value = MapValue::::default(); + format!("{:?}", key_value); + } + #[test] + fn derived_from_default() { + let key_value = MapValue::::default(); + assert_eq!( + key_value, + MapValue:: { + state: MapValueState::Empty, + key: 0, + value: 0, + } + ) + } + #[test] + fn i64_i64() { + let key_value = MapValue { + state: MapValueState::Valid, + key: 1_i64, + value: 10_i64, + }; + let bytes = key_value.serialize(); + let other = MapValue::deserialize(&bytes); + assert_eq!(other, Ok(key_value)); + } + #[test] + fn out_of_bounds() { + let bytes = vec![0_u8; 16]; + assert_eq!( + MapValue::::deserialize(&bytes) + .unwrap_err() + .description, + "i64 deserialization error: out of bounds" + ); + } + #[test] + fn serialized_size() { + assert_eq!(MapValue::::serialized_size(), 17); + } +} diff --git a/src/agdb/collections/map_common/map_value_state.rs b/src/agdb/collections/map_common/map_value_state.rs new file mode 100644 index 00000000..95872436 --- /dev/null +++ b/src/agdb/collections/map_common/map_value_state.rs @@ -0,0 +1,71 @@ +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use std::mem::size_of; + +#[derive(Clone, Default, Debug, Eq, PartialEq)] +pub enum MapValueState { + #[default] + Empty, + Deleted, + Valid, +} + +impl Serialize for MapValueState { + fn deserialize(bytes: &[u8]) -> Result { + match bytes.first() { + Some(0) => Ok(MapValueState::Empty), + Some(1) => Ok(MapValueState::Valid), + Some(2) => Ok(MapValueState::Deleted), + _ => Err(DbError::from("value out of bounds")), + } + } + + fn serialize(&self) -> Vec { + match self { + MapValueState::Empty => vec![0_u8], + MapValueState::Deleted => vec![2_u8], + MapValueState::Valid => vec![1_u8], + } + } + + fn serialized_size() -> u64 { + size_of::() as u64 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bad_deserialization() { + assert_eq!( + MapValueState::deserialize(&[10_u8]), + Err(DbError::from("value out of bounds")) + ); + } + #[test] + fn derived_from_default() { + assert_eq!(MapValueState::default(), MapValueState::Empty); + } + #[test] + fn derived_from_debug() { + let value = MapValueState::Deleted; + format!("{:?}", value); + } + #[test] + fn serialize() { + let data = vec![ + MapValueState::Valid, + MapValueState::Empty, + MapValueState::Deleted, + ]; + let bytes = data.serialize(); + let other = Vec::::deserialize(&bytes).unwrap(); + assert_eq!(data, other); + } + #[test] + fn serialized_size() { + assert_eq!(MapValueState::serialized_size(), 1); + } +} diff --git a/src/agdb/collections/multi_map.rs b/src/agdb/collections/multi_map.rs new file mode 100644 index 00000000..615d2637 --- /dev/null +++ b/src/agdb/collections/multi_map.rs @@ -0,0 +1,281 @@ +pub mod multi_map_impl; + +use self::multi_map_impl::MultiMapImpl; +use super::map_common::map_data_memory::MapDataMemory; +use super::map_common::MapCommon; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; +use std::hash::Hash; + +pub type MultiMap = MultiMapImpl>; + +impl MultiMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, +{ + pub fn new() -> MultiMap { + MultiMap:: { + map_common: MapCommon::>::from( + MapDataMemory::::default(), + ), + } + } +} + +impl Default for MultiMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, +{ + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derived_from_default() { + let mut _map = MultiMap::::default(); + } + + #[test] + fn insert() { + let mut map = MultiMap::::new(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(15))); + assert_eq!(map.value(&7), Ok(Some(20))); + } + + #[test] + fn insert_reallocate() { + let mut map = MultiMap::::new(); + + assert_eq!(map.capacity(), 1); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + assert_eq!(map.value(&i), Ok(Some(i))); + } + } + + #[test] + fn insert_reallocate_with_collisions() { + let mut map = MultiMap::::new(); + + for i in 0..50 { + map.insert(i * 64, i).unwrap(); + map.insert(i * 64, i + 1).unwrap(); + } + + for i in 0..50 { + assert_eq!(map.value(&(i * 64)), Ok(Some(i))); + } + } + + #[test] + fn insert_same_key() { + let mut map = MultiMap::::new(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + assert_eq!(map.count(), 2); + map.insert(5, 20).unwrap(); + assert_eq!(map.count(), 3); + + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(15))); + } + + #[test] + fn iter() { + let mut map = MultiMap::::new(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + map.insert(2, 30).unwrap(); + map.insert(2, 50).unwrap(); + map.insert(4, 13).unwrap(); + map.remove_key(&7).unwrap(); + + let mut actual = map.iter().collect::>(); + actual.sort(); + let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (2, 50), (4, 13), (5, 15), (5, 15)]; + + assert_eq!(actual, expected); + } + + #[test] + fn remove_deleted_key() { + let mut map = MultiMap::::new(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 2); + assert_eq!(map.value(&5), Ok(None)); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 2); + } + + #[test] + fn remove_key() { + let mut map = MultiMap::::new(); + + map.insert(1, 7).unwrap(); + map.insert(5, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 20).unwrap(); + + assert_eq!(map.count(), 4); + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 1); + assert_eq!(map.value(&1), Ok(Some(7))); + assert_eq!(map.values(&5), Ok(Vec::::new())); + } + + #[test] + fn remove_missing_key() { + let mut map = MultiMap::::new(); + + assert_eq!(map.count(), 0); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 0); + } + + #[test] + fn remove_shrinks_capacity() { + let mut map = MultiMap::::new(); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + map.remove_key(&i).unwrap(); + } + + assert_eq!(map.count(), 0); + assert_eq!(map.capacity(), 64); + } + + #[test] + fn remove_value() { + let mut map = MultiMap::::new(); + + map.insert(1, 7).unwrap(); + map.insert(5, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 20).unwrap(); + + assert_eq!(map.count(), 4); + map.remove_value(&5, &15).unwrap(); + + assert_eq!(map.count(), 3); + assert_eq!(map.value(&1), Ok(Some(7))); + assert_eq!(map.values(&5), Ok(vec![10_i64, 20_i64])); + } + + #[test] + fn remove_missing_value() { + let mut map = MultiMap::::new(); + + map.remove_value(&5, &10).unwrap(); + + assert_eq!(map.count(), 0); + } + + #[test] + fn reserve_larger() { + let mut map = MultiMap::::new(); + map.insert(1, 1).unwrap(); + + let capacity = map.capacity() + 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + assert_eq!(map.value(&1), Ok(Some(1))); + } + + #[test] + fn reserve_same() { + let mut map = MultiMap::::new(); + map.insert(1, 1).unwrap(); + + let capacity = map.capacity(); + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn reserve_smaller() { + let mut map = MultiMap::::new(); + map.insert(1, 1).unwrap(); + + let current_capacity = map.capacity(); + let capacity = current_capacity - 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), current_capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn value_missing() { + let map = MultiMap::::new(); + + assert_eq!(map.value(&0), Ok(None)); + } + + #[test] + fn values_at_end() { + let mut map = MultiMap::::new(); + + map.insert(127, 10).unwrap(); + map.insert(255, 11).unwrap(); + map.insert(191, 12).unwrap(); + + assert_eq!(map.value(&127), Ok(Some(10))); + assert_eq!(map.value(&255), Ok(Some(11))); + assert_eq!(map.value(&191), Ok(Some(12))); + } +} diff --git a/crates/multi_map/src/multi_map_impl.rs b/src/agdb/collections/multi_map/multi_map_impl.rs similarity index 92% rename from crates/multi_map/src/multi_map_impl.rs rename to src/agdb/collections/multi_map/multi_map_impl.rs index 8c77b478..5ee306cc 100644 --- a/crates/multi_map/src/multi_map_impl.rs +++ b/src/agdb/collections/multi_map/multi_map_impl.rs @@ -1,10 +1,10 @@ -use agdb_db_error::DbError; -use agdb_map_common::MapCommon; -use agdb_map_common::MapData; -use agdb_map_common::MapIterator; -use agdb_map_common::MapValueState; -use agdb_serialize::Serialize; -use agdb_utilities::StableHash; +use super::MapCommon; +use crate::collections::map_common::map_data::MapData; +use crate::collections::map_common::map_iterator::MapIterator; +use crate::collections::map_common::map_value_state::MapValueState; +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; use std::hash::Hash; pub struct MultiMapImpl @@ -16,6 +16,7 @@ where pub(crate) map_common: MapCommon, } +#[allow(dead_code)] impl MultiMapImpl where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, diff --git a/src/agdb/collections/storage_dictionary.rs b/src/agdb/collections/storage_dictionary.rs new file mode 100644 index 00000000..dc77cc5e --- /dev/null +++ b/src/agdb/collections/storage_dictionary.rs @@ -0,0 +1,361 @@ +use super::dictionary::dictionary_data_storage::DictionaryDataStorage; +use super::dictionary::dictionary_data_storage_indexes::DictionaryDataStorageIndexes; +use super::dictionary::dictionary_impl::DictionaryImpl; +use super::dictionary::dictionary_index::DictionaryIndex; +use super::dictionary::dictionary_value::DictionaryValue; +use super::storage_multi_map::StorageMultiMap; +use super::storage_vec::StorageVec; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; +use std::cell::RefCell; +use std::marker::PhantomData; +use std::rc::Rc; + +pub type StorageDictionary = + DictionaryImpl>; + +#[allow(dead_code)] +impl StorageDictionary +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, + Data: Storage, +{ + pub fn storage_index(&self) -> StorageIndex { + self.data.storage_index.clone() + } +} + +impl TryFrom>> for StorageDictionary +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + let index = StorageMultiMap::::try_from(storage.clone())?; + let mut values = StorageVec::, Data>::try_from(storage.clone())?; + values.push(&DictionaryValue::default())?; + + let storage_index = storage.borrow_mut().insert(&DictionaryDataStorageIndexes { + index: index.storage_index(), + values: values.storage_index(), + })?; + + Ok(StorageDictionary:: { + data: DictionaryDataStorage:: { + storage, + storage_index, + index, + values, + }, + phantom_data: PhantomData, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for StorageDictionary +where + T: Clone + Default + Eq + PartialEq + StableHash + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + let indexes = storage_with_index + .0 + .borrow_mut() + .value::(&storage_with_index.1)?; + let index = StorageMultiMap::::try_from(( + storage_with_index.0.clone(), + indexes.index, + ))?; + let values = StorageVec::, Data>::try_from(( + storage_with_index.0.clone(), + indexes.values, + ))?; + + Ok(StorageDictionary:: { + data: DictionaryDataStorage:: { + storage: storage_with_index.0, + storage_index: storage_with_index.1, + index, + values, + }, + phantom_data: PhantomData, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utilities::{collided_value::CollidedValue, test_file::TestFile}; + + #[test] + fn count_invalid_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let dictionary = StorageDictionary::::try_from(storage).unwrap(); + + assert_eq!(dictionary.count(&DictionaryIndex::from(-1_i64)), Ok(None)); + } + + #[test] + fn index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.index(&10), Ok(Some(index))); + } + + #[test] + fn index_missing_value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let dictionary = StorageDictionary::::try_from(storage).unwrap(); + + assert_eq!(dictionary.index(&10), Ok(None)); + } + + #[test] + fn index_removed_value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.index(&10), Ok(None)); + } + + #[test] + fn index_reuse() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index1 = dictionary.insert(&5).unwrap(); + let index2 = dictionary.insert(&10).unwrap(); + let index3 = dictionary.insert(&7).unwrap(); + + dictionary.remove(&index2).unwrap(); + dictionary.remove(&index1).unwrap(); + dictionary.remove(&index3).unwrap(); + + assert_eq!(dictionary.count(&index1), Ok(None)); + assert_eq!(dictionary.count(&index2), Ok(None)); + assert_eq!(dictionary.count(&index3), Ok(None)); + + assert_eq!(dictionary.insert(&3), Ok(index3.clone())); + assert_eq!(dictionary.insert(&2), Ok(index1.clone())); + assert_eq!(dictionary.insert(&1), Ok(index2.clone())); + + assert_eq!(dictionary.value(&index1), Ok(Some(2))); + assert_eq!(dictionary.value(&index2), Ok(Some(1))); + assert_eq!(dictionary.value(&index3), Ok(Some(3))); + } + + #[test] + fn index_with_collisions() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::>::try_from(storage).unwrap(); + + let index1 = dictionary.insert(&CollidedValue::new(1)).unwrap(); + let index2 = dictionary.insert(&CollidedValue::new(2)).unwrap(); + let index3 = dictionary.insert(&CollidedValue::new(3)).unwrap(); + + assert_eq!(dictionary.index(&CollidedValue::new(1)), Ok(Some(index1))); + assert_eq!(dictionary.index(&CollidedValue::new(2)), Ok(Some(index2))); + assert_eq!(dictionary.index(&CollidedValue::new(3)), Ok(Some(index3))); + } + + #[test] + fn insert() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + assert_eq!(dictionary.value(&index), Ok(Some(10_i64))); + assert_eq!(dictionary.count(&index), Ok(Some(1))); + } + + #[test] + fn insert_multiple() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index1 = dictionary.insert(&10).unwrap(); + let index2 = dictionary.insert(&15).unwrap(); + let index3 = dictionary.insert(&20).unwrap(); + + assert_eq!(dictionary.len(), Ok(3)); + + assert_eq!(dictionary.value(&index1).unwrap(), Some(10_i64)); + assert_eq!(dictionary.count(&index1), Ok(Some(1))); + + assert_eq!(dictionary.value(&index2).unwrap(), Some(15_i64)); + assert_eq!(dictionary.count(&index2), Ok(Some(1))); + + assert_eq!(dictionary.value(&index3).unwrap(), Some(20_i64)); + assert_eq!(dictionary.count(&index3), Ok(Some(1))); + } + + #[test] + fn insert_same() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + dictionary.insert(&10).unwrap(); + + let index2 = dictionary.insert(&15).unwrap(); + + assert_eq!(dictionary.insert(&15).unwrap(), index2); + assert_eq!(dictionary.insert(&15).unwrap(), index2); + + dictionary.insert(&20).unwrap(); + + assert_eq!(dictionary.len(), Ok(3)); + assert_eq!(dictionary.count(&index2), Ok(Some(3))); + } + + #[test] + fn remove() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(None)); + assert_eq!(dictionary.count(&index), Ok(None)); + } + + #[test] + fn remove_duplicated() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + dictionary.insert(&10).unwrap(); + dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(Some(10))); + assert_eq!(dictionary.count(&index), Ok(Some(3))); + + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(Some(10))); + assert_eq!(dictionary.count(&index), Ok(Some(2))); + + dictionary.remove(&index).unwrap(); + dictionary.remove(&index).unwrap(); + + assert_eq!(dictionary.value(&index), Ok(None)); + assert_eq!(dictionary.count(&index), Ok(None)); + } + + #[test] + fn remove_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut dictionary = StorageDictionary::::try_from(storage).unwrap(); + + let index = dictionary.insert(&10).unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + + dictionary + .remove(&DictionaryIndex::from(index.value() + 1)) + .unwrap(); + + assert_eq!(dictionary.len(), Ok(1)); + } + + #[test] + fn restore_from_file() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let storage_index; + let index1; + let index2; + let index3; + let index4; + + { + let mut dictionary = StorageDictionary::::try_from(storage.clone()).unwrap(); + storage_index = dictionary.storage_index(); + + index1 = dictionary.insert(&10).unwrap(); + dictionary.insert(&10).unwrap(); + index2 = dictionary.insert(&15).unwrap(); + index3 = dictionary.insert(&7).unwrap(); + index4 = dictionary.insert(&20).unwrap(); + dictionary.remove(&index2).unwrap(); + } + + let dictionary = StorageDictionary::::try_from((storage, storage_index)).unwrap(); + + assert_eq!(dictionary.len(), Ok(3)); + assert_eq!(dictionary.count(&index1), Ok(Some(2))); + assert_eq!(dictionary.value(&index1), Ok(Some(10))); + assert_eq!(dictionary.value(&index2), Ok(None)); + assert_eq!(dictionary.value(&index3), Ok(Some(7))); + assert_eq!(dictionary.value(&index4), Ok(Some(20))); + } + + #[test] + fn value_missing_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let dictionary = StorageDictionary::::try_from(storage).unwrap(); + assert_eq!(dictionary.value(&DictionaryIndex::from(1_i64)), Ok(None)); + } +} diff --git a/src/agdb/collections/storage_map.rs b/src/agdb/collections/storage_map.rs new file mode 100644 index 00000000..3231e0fb --- /dev/null +++ b/src/agdb/collections/storage_map.rs @@ -0,0 +1,430 @@ +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; +use std::cell::RefCell; +use std::hash::Hash; +use std::marker::PhantomData; +use std::mem::size_of; +use std::rc::Rc; + +use super::map::map_impl::MapImpl; +use super::map_common::map_data_storage::MapDataStorage; +use super::map_common::map_value::MapValue; +use super::map_common::MapCommon; + +pub type StorageMap = MapImpl>; + +#[allow(dead_code)] +impl StorageMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: Storage, +{ + pub fn storage_index(&self) -> StorageIndex { + self.map_common.data.storage_index() + } +} + +impl TryFrom>> for StorageMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + let storage_index = storage.borrow_mut().insert(&0_u64)?; + storage.borrow_mut().insert_at( + &storage_index, + size_of::() as u64, + &vec![MapValue::::default()], + )?; + + Ok(Self { + map_common: MapCommon::from(MapDataStorage::try_from(storage)?), + phantom_data: PhantomData, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for StorageMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + Ok(Self { + map_common: MapCommon::from(MapDataStorage::try_from(storage_with_index)?), + phantom_data: PhantomData, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utilities::test_file::TestFile; + use std::collections::HashMap; + + #[test] + fn insert() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(15))); + assert_eq!(map.value(&7), Ok(Some(20))); + } + + #[test] + fn insert_reallocate() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + assert_eq!(map.capacity(), 1); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + assert_eq!(map.value(&i), Ok(Some(i))); + } + } + + #[test] + fn insert_reallocate_with_collisions() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + for i in 0..100 { + map.insert(i * 64, i).unwrap(); + } + + for i in 0..100 { + assert_eq!(map.value(&(i * 64)), Ok(Some(i))); + } + } + + #[test] + fn insert_same_key() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + assert_eq!(map.insert(1, 10), Ok(None)); + assert_eq!(map.insert(5, 15), Ok(None)); + assert_eq!(map.count(), 2); + assert_eq!(map.insert(5, 20), Ok(Some(15))); + assert_eq!(map.count(), 2); + + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(20))); + } + + #[test] + fn iter() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + map.insert(2, 30).unwrap(); + map.insert(4, 13).unwrap(); + map.remove(&7).unwrap(); + + let mut actual = map.iter().collect::>(); + actual.sort(); + let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (4, 13), (5, 15)]; + + assert_eq!(actual, expected); + } + + #[test] + fn remove() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + map.remove(&5).unwrap(); + + assert_eq!(map.count(), 2); + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(None)); + assert_eq!(map.value(&7), Ok(Some(20))); + } + + #[test] + fn remove_deleted() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + + map.remove(&5).unwrap(); + + assert_eq!(map.count(), 2); + assert_eq!(map.value(&5), Ok(None)); + + map.remove(&5).unwrap(); + + assert_eq!(map.count(), 2); + } + + #[test] + fn remove_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + assert_eq!(map.count(), 0); + assert_eq!(map.remove(&0), Ok(())); + assert_eq!(map.count(), 0); + } + + #[test] + fn remove_shrinks_capacity() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + map.remove(&i).unwrap(); + } + + assert_eq!(map.count(), 0); + assert_eq!(map.capacity(), 64); + } + + #[test] + fn reserve_larger() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + map.insert(1, 1).unwrap(); + + let capacity = map.capacity() + 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + assert_eq!(map.value(&1), Ok(Some(1))); + } + + #[test] + fn reserve_same() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + map.insert(1, 1).unwrap(); + + let capacity = map.capacity(); + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn reserve_smaller() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + map.insert(1, 1).unwrap(); + + let current_capacity = map.capacity(); + let capacity = current_capacity - 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), current_capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn to_hash_map() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + map.remove(&5).unwrap(); + + let other = map.to_hash_map().unwrap(); + + assert_eq!(other.len(), 2); + assert_eq!(other.get(&1), Some(&10)); + assert_eq!(other.get(&5), None); + assert_eq!(other.get(&7), Some(&20)); + } + + #[test] + fn to_hash_map_empty() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let map = StorageMap::::try_from(storage).unwrap(); + let other = map.to_hash_map().unwrap(); + + assert_eq!(other.len(), 0); + } + + #[test] + fn try_from_storage_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let index; + + { + let mut map = StorageMap::::try_from(storage.clone()).unwrap(); + map.insert(1, 1).unwrap(); + map.insert(3, 2).unwrap(); + map.insert(5, 3).unwrap(); + map.remove(&3).unwrap(); + index = map.storage_index(); + } + + let map = StorageMap::::try_from((storage, index)).unwrap(); + + let mut expected = HashMap::::new(); + expected.insert(1, 1); + expected.insert(5, 3); + + assert_eq!(map.to_hash_map(), Ok(expected)); + } + + #[test] + fn try_from_storage_missing_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + assert_eq!( + StorageMap::::try_from((storage, StorageIndex::from(1_i64))) + .err() + .unwrap(), + DbError::from("index '1' not found") + ); + } + + #[test] + fn value_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let map = StorageMap::::try_from(storage).unwrap(); + + assert_eq!(map.value(&0), Ok(None)); + } + + #[test] + fn values_at_end() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut map = StorageMap::::try_from(storage).unwrap(); + + map.insert(127, 10).unwrap(); + map.insert(255, 11).unwrap(); + map.insert(191, 12).unwrap(); + + assert_eq!(map.value(&127), Ok(Some(10))); + assert_eq!(map.value(&255), Ok(Some(11))); + assert_eq!(map.value(&191), Ok(Some(12))); + } +} diff --git a/src/agdb/collections/storage_multi_map.rs b/src/agdb/collections/storage_multi_map.rs new file mode 100644 index 00000000..d8de90f7 --- /dev/null +++ b/src/agdb/collections/storage_multi_map.rs @@ -0,0 +1,455 @@ +use super::map_common::map_data_memory::MapDataMemory; +use super::map_common::map_data_storage::MapDataStorage; +use super::map_common::MapCommon; +use super::multi_map::multi_map_impl::MultiMapImpl; +use super::multi_map::MultiMap; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; +use std::cell::RefCell; +use std::hash::Hash; +use std::rc::Rc; + +pub type StorageMultiMap = MultiMapImpl>; + +#[allow(dead_code)] +impl StorageMultiMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, + Data: Storage, +{ + pub fn storage_index(&self) -> StorageIndex { + self.map_common.data.storage_index() + } + + pub fn to_multi_map(&self) -> Result, DbError> { + Ok(MultiMap:: { + map_common: MapCommon::from(MapDataMemory:: { + data: self.map_common.data.values()?, + count: self.map_common.data.count(), + }), + }) + } +} + +impl TryFrom>> for StorageMultiMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + Ok(Self { + map_common: MapCommon::from(MapDataStorage::try_from(storage)?), + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for StorageMultiMap +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + Ok(Self { + map_common: MapCommon::from(MapDataStorage::try_from(storage_with_index)?), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utilities::test_file::TestFile; + + #[test] + fn insert() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(15))); + assert_eq!(map.value(&7), Ok(Some(20))); + } + + #[test] + fn insert_reallocate() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + assert_eq!(map.capacity(), 1); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + assert_eq!(map.value(&i), Ok(Some(i))); + } + } + + #[test] + fn insert_reallocate_with_collisions() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + for i in 0..50 { + map.insert(i * 64, i).unwrap(); + map.insert(i * 64, i + 1).unwrap(); + } + + for i in 0..50 { + assert_eq!(map.value(&(i * 64)), Ok(Some(i))); + } + } + + #[test] + fn insert_same_key() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + assert_eq!(map.count(), 2); + map.insert(5, 20).unwrap(); + assert_eq!(map.count(), 3); + + assert_eq!(map.value(&1), Ok(Some(10))); + assert_eq!(map.value(&5), Ok(Some(15))); + } + + #[test] + fn iter() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + map.insert(2, 30).unwrap(); + map.insert(2, 50).unwrap(); + map.insert(4, 13).unwrap(); + map.remove_key(&7).unwrap(); + + let mut actual = map.iter().collect::>(); + actual.sort(); + let expected: Vec<(i64, i64)> = vec![(1, 10), (2, 30), (2, 50), (4, 13), (5, 15), (5, 15)]; + + assert_eq!(actual, expected); + } + + #[test] + fn remove_deleted_key() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + + assert_eq!(map.count(), 3); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 2); + assert_eq!(map.value(&5), Ok(None)); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 2); + } + + #[test] + fn remove_key() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 7).unwrap(); + map.insert(5, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 20).unwrap(); + + assert_eq!(map.count(), 4); + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 1); + assert_eq!(map.value(&1), Ok(Some(7))); + assert_eq!(map.values(&5), Ok(Vec::::new())); + } + + #[test] + fn remove_missing_key() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + assert_eq!(map.count(), 0); + + map.remove_key(&5).unwrap(); + + assert_eq!(map.count(), 0); + } + + #[test] + fn remove_shrinks_capacity() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + for i in 0..100 { + map.insert(i, i).unwrap(); + } + + assert_eq!(map.count(), 100); + assert_eq!(map.capacity(), 128); + + for i in 0..100 { + map.remove_key(&i).unwrap(); + } + + assert_eq!(map.count(), 0); + assert_eq!(map.capacity(), 64); + } + + #[test] + fn remove_value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 7).unwrap(); + map.insert(5, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(5, 20).unwrap(); + + assert_eq!(map.count(), 4); + map.remove_value(&5, &15).unwrap(); + + assert_eq!(map.count(), 3); + assert_eq!(map.value(&1), Ok(Some(7))); + assert_eq!(map.values(&5), Ok(vec![10_i64, 20_i64])); + } + + #[test] + fn remove_missing_value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.remove_value(&5, &10).unwrap(); + + assert_eq!(map.count(), 0); + } + + #[test] + fn reserve_larger() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 1).unwrap(); + + let capacity = map.capacity() + 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + assert_eq!(map.value(&1), Ok(Some(1))); + } + + #[test] + fn reserve_same() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 1).unwrap(); + + let capacity = map.capacity(); + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn reserve_smaller() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 1).unwrap(); + + let current_capacity = map.capacity(); + let capacity = current_capacity - 10; + let size = map.count(); + + map.reserve(capacity).unwrap(); + + assert_eq!(map.capacity(), current_capacity); + assert_eq!(map.count(), size); + } + + #[test] + fn to_multi_map() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(1, 10).unwrap(); + map.insert(5, 15).unwrap(); + map.insert(7, 20).unwrap(); + map.remove_key(&5).unwrap(); + + let other = map.to_multi_map().unwrap(); + + assert_eq!(other.count(), 2); + assert_eq!(other.value(&1).unwrap(), Some(10)); + assert_eq!(other.value(&5).unwrap(), None); + assert_eq!(other.value(&7).unwrap(), Some(20)); + } + + #[test] + fn to_multi_map_empty() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let map = StorageMultiMap::::try_from(storage).unwrap(); + let other = map.to_multi_map().unwrap(); + + assert_eq!(other.count(), 0); + } + + #[test] + fn try_from_storage_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let index; + + { + let mut map = StorageMultiMap::::try_from(storage.clone()).unwrap(); + map.insert(1, 1).unwrap(); + map.insert(3, 2).unwrap(); + map.insert(3, 3).unwrap(); + map.insert(5, 3).unwrap(); + map.remove_key(&1).unwrap(); + index = map.storage_index(); + } + + let map = StorageMultiMap::::try_from((storage, index)).unwrap(); + + assert_eq!( + map.iter().collect::>(), + vec![(3_i64, 2_i64), (3_i64, 3_i64), (5_i64, 3_i64)] + ); + } + + #[test] + fn try_from_storage_missing_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + assert_eq!( + StorageMultiMap::::try_from((storage, StorageIndex::from(1_i64))) + .err() + .unwrap(), + DbError::from("index '1' not found") + ); + } + + #[test] + fn value_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let map = StorageMultiMap::::try_from(storage).unwrap(); + + assert_eq!(map.value(&0), Ok(None)); + } + + #[test] + fn values_at_end() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut map = StorageMultiMap::::try_from(storage).unwrap(); + + map.insert(127, 10).unwrap(); + map.insert(255, 11).unwrap(); + map.insert(191, 12).unwrap(); + + assert_eq!(map.value(&127), Ok(Some(10))); + assert_eq!(map.value(&255), Ok(Some(11))); + assert_eq!(map.value(&191), Ok(Some(12))); + } +} diff --git a/src/agdb/collections/storage_vec.rs b/src/agdb/collections/storage_vec.rs new file mode 100644 index 00000000..e757379e --- /dev/null +++ b/src/agdb/collections/storage_vec.rs @@ -0,0 +1,672 @@ +use super::vec::storage_vec_iterator::StorageVecIterator; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; +use std::cell::RefCell; +use std::cell::RefMut; +use std::cmp::max; +use std::marker::PhantomData; +use std::rc::Rc; + +pub struct StorageVec +where + T: Serialize, + Data: Storage, +{ + pub(crate) storage: Rc>, + pub(crate) storage_index: StorageIndex, + pub(crate) len: u64, + pub(crate) capacity: u64, + pub(crate) phantom_data: PhantomData, +} + +#[allow(dead_code)] +impl StorageVec +where + T: Serialize, + Data: Storage, +{ + pub fn capacity(&self) -> u64 { + self.capacity + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn iter(&self) -> StorageVecIterator { + StorageVecIterator:: { + index: 0, + vec: self, + phantom_data: PhantomData, + } + } + + pub fn len(&self) -> u64 { + self.len + } + + pub fn push(&mut self, value: &T) -> Result<(), DbError> { + let mut ref_storage = self.storage.borrow_mut(); + ref_storage.transaction(); + + if self.len() == self.capacity() { + let current_capacity = self.capacity(); + Self::reallocate( + &mut self.capacity, + max(current_capacity * 2, 64), + &mut ref_storage, + &self.storage_index, + )?; + } + + ref_storage.insert_at(&self.storage_index, Self::value_offset(self.len()), value)?; + self.len += 1; + ref_storage.insert_at(&self.storage_index, 0, &self.len())?; + ref_storage.commit() + } + + pub fn remove(&mut self, index: u64) -> Result<(), DbError> { + if self.len() <= index { + return Err(DbError::from("index out of bounds")); + } + + let offset_from = Self::value_offset(index + 1); + let offset_to = Self::value_offset(index); + let size = Self::value_offset(self.len()) - offset_from; + + let mut ref_storage = self.storage.borrow_mut(); + ref_storage.transaction(); + ref_storage.move_at(&self.storage_index, offset_from, offset_to, size)?; + self.len -= 1; + ref_storage.insert_at(&self.storage_index, 0, &self.len())?; + ref_storage.commit() + } + + pub fn reserve(&mut self, capacity: u64) -> Result<(), DbError> { + if capacity <= self.capacity() { + return Ok(()); + } + + let mut ref_storage = self.storage.borrow_mut(); + Self::reallocate( + &mut self.capacity, + capacity, + &mut ref_storage, + &self.storage_index, + ) + } + + pub fn resize(&mut self, size: u64) -> Result<(), DbError> { + if self.len() == size { + return Ok(()); + } + + let mut ref_storage = self.storage.borrow_mut(); + ref_storage.transaction(); + + if size < self.len() { + let offset = Self::value_offset(size); + let byte_size = Self::value_offset(self.len()) - offset; + ref_storage.insert_at(&self.storage_index, offset, &vec![0_u8; byte_size as usize])?; + } else if self.capacity() < size { + Self::reallocate( + &mut self.capacity, + size, + &mut ref_storage, + &self.storage_index, + )?; + } + + self.len = size; + ref_storage.insert_at(&self.storage_index, 0, &self.len())?; + ref_storage.commit() + } + + pub fn set_value(&mut self, index: u64, value: &T) -> Result<(), DbError> { + if self.len() <= index { + return Err(DbError::from("index out of bounds")); + } + + self.storage + .borrow_mut() + .insert_at(&self.storage_index, Self::value_offset(index), value) + } + + pub fn shrink_to_fit(&mut self) -> Result<(), DbError> { + let current_len = self.len(); + let mut ref_storage = self.storage.borrow_mut(); + Self::reallocate( + &mut self.capacity, + current_len, + &mut ref_storage, + &self.storage_index, + ) + } + + pub fn storage_index(&self) -> StorageIndex { + self.storage_index.clone() + } + + #[allow(clippy::wrong_self_convention)] + pub fn to_vec(&self) -> Result, DbError> { + self.storage.borrow_mut().value(&self.storage_index) + } + + pub fn value(&self, index: u64) -> Result { + if self.len() <= index { + return Err(DbError::from("index out of bounds")); + } + + self.storage + .borrow_mut() + .value_at::(&self.storage_index, Self::value_offset(index)) + } + + pub fn value_offset(index: u64) -> u64 { + u64::serialized_size() + index * T::serialized_size() + } + + pub(crate) fn capacity_from_bytes(len: u64) -> u64 { + (len - u64::serialized_size()) / T::serialized_size() + } + + fn reallocate( + capacity: &mut u64, + new_capacity: u64, + storage: &mut RefMut, + storage_index: &StorageIndex, + ) -> Result<(), DbError> { + *capacity = new_capacity; + storage.resize_value(storage_index, Self::value_offset(new_capacity)) + } +} + +impl TryFrom>> for StorageVec +where + T: Serialize, + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + let index = storage.borrow_mut().insert(&0_u64)?; + + Ok(Self { + storage, + storage_index: index, + len: 0, + capacity: 0, + phantom_data: PhantomData, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> + for StorageVec +{ + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + let byte_size = storage_with_index + .0 + .borrow_mut() + .value_size(&storage_with_index.1)?; + let size = storage_with_index + .0 + .borrow_mut() + .value_at::(&storage_with_index.1, 0)?; + + Ok(Self { + storage: storage_with_index.0, + storage_index: storage_with_index.1, + len: size, + capacity: Self::capacity_from_bytes(byte_size), + phantom_data: PhantomData, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utilities::test_file::TestFile; + + #[test] + fn iter() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.iter().collect::>(), vec![1_i64, 3_i64, 5_i64]); + } + + #[test] + fn is_empty() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert!(vec.is_empty()); + + vec.push(&1).unwrap(); + + assert!(!vec.is_empty()); + } + + #[test] + fn len() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!(vec.len(), 0); + + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.len(), 3) + } + + #[test] + fn min_capacity() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!(vec.capacity(), 0); + + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.capacity(), 64); + } + + #[test] + fn push() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(vec![1_i64, 3_i64, 5_i64]) + ); + } + + #[test] + fn remove() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.remove(1).unwrap(); + + assert_eq!(vec.to_vec(), Ok(vec![1, 5])); + } + + #[test] + fn remove_at_end() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.remove(2).unwrap(); + + assert_eq!(vec.to_vec(), Ok(vec![1, 3])); + } + + #[test] + fn remove_index_out_of_bounds() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!(vec.remove(0), Err(DbError::from("index out of bounds"))); + } + + #[test] + fn remove_size_updated() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.remove(1).unwrap(); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(vec![1_i64, 5_i64]) + ); + } + + #[test] + fn reserve_larger() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + assert_eq!(vec.capacity(), 0); + + vec.reserve(20).unwrap(); + + assert_eq!(vec.capacity(), 20); + } + + #[test] + fn reserve_smaller() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.reserve(20).unwrap(); + vec.reserve(10).unwrap(); + + assert_eq!(vec.capacity(), 20); + } + + #[test] + fn resize_larger() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.resize(6).unwrap(); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(vec![1_i64, 3_i64, 5_i64, 0, 0, 0]) + ); + } + + #[test] + fn resize_over_capacity() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.resize(100).unwrap(); + + let mut expected = vec![0_i64; 100]; + expected[0] = 1; + expected[1] = 3; + expected[2] = 5; + + assert_eq!(vec.len(), 100); + assert_eq!(vec.capacity(), 100); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(expected) + ); + } + + #[test] + fn resize_same() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.resize(3).unwrap(); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(vec![1_i64, 3_i64, 5_i64]) + ); + } + + #[test] + fn resize_smaller() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.resize(1).unwrap(); + + assert_eq!( + storage + .borrow_mut() + .value::>(&vec.storage_index()), + Ok(vec![1_i64]) + ); + } + + #[test] + fn set_value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + vec.set_value(1, &10).unwrap(); + + assert_eq!(vec.value(0), Ok(1)); + assert_eq!(vec.value(1), Ok(10)); + assert_eq!(vec.value(2), Ok(5)); + } + + #[test] + fn set_value_out_of_bounds() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!( + vec.set_value(0, &10), + Err(DbError::from("index out of bounds")) + ); + } + + #[test] + fn shrink_to_fit() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.capacity(), 64); + + vec.shrink_to_fit().unwrap(); + + assert_eq!(vec.capacity(), 3); + + vec.shrink_to_fit().unwrap(); + + assert_eq!(vec.capacity(), 3); + } + + #[test] + fn shrink_to_fit_empty() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!(vec.capacity(), 0); + + vec.shrink_to_fit().unwrap(); + + assert_eq!(vec.capacity(), 0); + } + + #[test] + fn to_vec() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.to_vec(), Ok(vec![1_i64, 3_i64, 5_i64])); + } + + #[test] + fn try_from_storage_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let index; + + { + let mut vec = StorageVec::::try_from(storage.clone()).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + index = vec.storage_index(); + } + + let vec = StorageVec::::try_from((storage, index)).unwrap(); + + assert_eq!(vec.to_vec(), Ok(vec![1_i64, 3_i64, 5_i64])); + } + + #[test] + fn try_from_storage_missing_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + assert_eq!( + StorageVec::::try_from((storage, StorageIndex::from(1_i64))) + .err() + .unwrap(), + DbError::from("index '1' not found") + ); + } + + #[test] + fn value() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let mut vec = StorageVec::::try_from(storage).unwrap(); + vec.push(&1).unwrap(); + vec.push(&3).unwrap(); + vec.push(&5).unwrap(); + + assert_eq!(vec.value(0), Ok(1)); + assert_eq!(vec.value(1), Ok(3)); + assert_eq!(vec.value(2), Ok(5)); + } + + #[test] + fn value_out_of_bounds() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let vec = StorageVec::::try_from(storage).unwrap(); + + assert_eq!(vec.value(0), Err(DbError::from("index out of bounds"))); + } +} diff --git a/src/agdb/collections/vec.rs b/src/agdb/collections/vec.rs new file mode 100644 index 00000000..35e52fd4 --- /dev/null +++ b/src/agdb/collections/vec.rs @@ -0,0 +1 @@ +pub mod storage_vec_iterator; diff --git a/crates/storage_vec/src/vec_iterator.rs b/src/agdb/collections/vec/storage_vec_iterator.rs similarity index 64% rename from crates/storage_vec/src/vec_iterator.rs rename to src/agdb/collections/vec/storage_vec_iterator.rs index 9b1db41c..701e06cc 100644 --- a/crates/storage_vec/src/vec_iterator.rs +++ b/src/agdb/collections/vec/storage_vec_iterator.rs @@ -1,9 +1,9 @@ -use crate::storage_vec::StorageVec; -use agdb_serialize::Serialize; -use agdb_storage::Storage; +use crate::collections::storage_vec::StorageVec; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; use std::marker::PhantomData; -pub struct VecIterator<'a, T, Data> +pub struct StorageVecIterator<'a, T, Data> where T: Serialize, Data: Storage, @@ -13,7 +13,7 @@ where pub(crate) phantom_data: PhantomData, } -impl<'a, T, Data> Iterator for VecIterator<'a, T, Data> +impl<'a, T, Data> Iterator for StorageVecIterator<'a, T, Data> where T: Serialize, Data: Storage, diff --git a/src/agdb/commands.rs b/src/agdb/commands.rs new file mode 100644 index 00000000..50089421 --- /dev/null +++ b/src/agdb/commands.rs @@ -0,0 +1,10 @@ +pub mod command_executor; +pub mod command_stack; + +mod cmd; + +#[allow(dead_code)] +pub enum Commands { + InsertEdge, + InsertNode, +} diff --git a/src/agdb/commands/cmd.rs b/src/agdb/commands/cmd.rs new file mode 100644 index 00000000..b823b9e9 --- /dev/null +++ b/src/agdb/commands/cmd.rs @@ -0,0 +1,7 @@ +use super::Commands; + +#[allow(dead_code)] +pub(crate) struct Cmd { + pub(crate) commands: Commands, + pub(crate) executed: bool, +} diff --git a/crates/commands/src/command_executor.rs b/src/agdb/commands/command_executor.rs similarity index 77% rename from crates/commands/src/command_executor.rs rename to src/agdb/commands/command_executor.rs index 29c0280c..000dc071 100644 --- a/crates/commands/src/command_executor.rs +++ b/src/agdb/commands/command_executor.rs @@ -1,5 +1,5 @@ -use crate::Commands; -use agdb_db_error::DbError; +use super::Commands; +use crate::db_error::DbError; pub trait CommandExecutor { fn redo(&mut self, command: &mut Commands) -> Result<(), DbError>; diff --git a/src/agdb/commands/command_stack.rs b/src/agdb/commands/command_stack.rs new file mode 100644 index 00000000..295c714e --- /dev/null +++ b/src/agdb/commands/command_stack.rs @@ -0,0 +1,165 @@ +use super::cmd::Cmd; +use super::command_executor::CommandExecutor; +use super::Commands; +use crate::DbError; + +#[derive(Default)] +pub struct CommandStack { + stack: Vec, +} + +#[allow(dead_code)] +impl CommandStack { + pub fn new() -> Self { + Self::default() + } + + pub fn clear(&mut self) { + self.stack.clear(); + } + + pub fn push(&mut self, command: Commands) { + self.stack.push(Cmd { + commands: command, + executed: false, + }); + } + + pub fn redo( + &mut self, + executor: &mut Executor, + ) -> Result<(), DbError> { + for command in self.stack.iter_mut() { + if !command.executed { + executor.redo(&mut command.commands)?; + command.executed = true; + } + } + + Ok(()) + } + + pub fn undo( + &mut self, + executor: &mut Executor, + ) -> Result<(), DbError> { + for command in self.stack.iter_mut().rev() { + if command.executed { + executor.undo(&mut command.commands)?; + command.executed = false; + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::commands::command_executor::CommandExecutor; + use crate::commands::Commands; + use crate::DbError; + + use super::*; + + struct Executor { + redo_result: Vec>, + undo_result: Vec>, + } + + impl CommandExecutor for Executor { + fn redo(&mut self, _command: &mut Commands) -> Result<(), DbError> { + self.redo_result.pop().unwrap() + } + + fn undo(&mut self, _command: &mut Commands) -> Result<(), DbError> { + self.undo_result.pop().unwrap() + } + } + + #[test] + fn clear() { + let mut stack = CommandStack::new(); + stack.clear(); + stack.push(Commands::InsertNode); + stack.clear(); + + let mut executor = Executor { + redo_result: vec![], + undo_result: vec![], + }; + + assert_eq!(stack.redo(&mut executor), Ok(())); + } + + #[test] + fn derived_from_default() { + let mut _stack = CommandStack::default(); + } + + #[test] + fn empty() { + let mut stack = CommandStack::new(); + + let mut executor = Executor { + redo_result: vec![], + undo_result: vec![], + }; + + assert_eq!(stack.redo(&mut executor), Ok(())); + assert_eq!(stack.undo(&mut executor), Ok(())); + } + + #[test] + fn redo() { + let mut stack = CommandStack::new(); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertEdge); + + let mut executor = Executor { + redo_result: vec![Ok(()), Ok(()), Ok(())], + undo_result: vec![], + }; + + assert_eq!(stack.redo(&mut executor), Ok(())); + assert_eq!(stack.redo(&mut executor), Ok(())); + + assert!(executor.redo_result.is_empty()); + } + + #[test] + fn undo() { + let mut stack = CommandStack::new(); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertEdge); + + let mut executor = Executor { + redo_result: vec![Err(DbError::from("error")), Ok(()), Ok(())], + undo_result: vec![Ok(()), Ok(())], + }; + + assert_eq!(stack.redo(&mut executor), Err(DbError::from("error"))); + assert_eq!(stack.undo(&mut executor), Ok(())); + assert_eq!(stack.undo(&mut executor), Ok(())); + + assert!(executor.redo_result.is_empty()); + assert!(executor.undo_result.is_empty()); + } + + #[test] + fn undo_not_redone() { + let mut stack = CommandStack::new(); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertNode); + stack.push(Commands::InsertEdge); + + let mut executor = Executor { + redo_result: vec![], + undo_result: vec![], + }; + + assert_eq!(stack.undo(&mut executor), Ok(())); + } +} diff --git a/src/db.rs b/src/agdb/db.rs similarity index 73% rename from src/db.rs rename to src/agdb/db.rs index 9132713e..f10cbc15 100644 --- a/src/db.rs +++ b/src/agdb/db.rs @@ -1,4 +1,7 @@ -use crate::{Query, QueryError, QueryResult, Transaction}; +use crate::Query; +use crate::QueryError; +use crate::QueryResult; +use crate::Transaction; #[derive(Default)] pub struct Db {} diff --git a/src/agdb/db_error.rs b/src/agdb/db_error.rs new file mode 100644 index 00000000..ac1281da --- /dev/null +++ b/src/agdb/db_error.rs @@ -0,0 +1,163 @@ +use std::error::Error; +use std::fmt::Display; +use std::fmt::Formatter; +use std::fmt::Result as FMTResult; +use std::io::Error as IOError; +use std::panic::Location; +use std::string::FromUtf8Error; + +#[derive(Debug)] +pub struct DbError { + pub description: String, + pub cause: Option>, + pub source_location: Location<'static>, +} + +impl DbError { + pub fn caused_by(mut self, error: Self) -> Self { + self.cause = Some(Box::new(error)); + + self + } +} + +impl Display for DbError { + fn fmt(&self, f: &mut Formatter<'_>) -> FMTResult { + let location = self.source_location.to_string().replace('\\', "/"); + write!(f, "{} (at {})", self.description, location) + } +} + +impl Error for DbError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + if let Some(cause) = &self.cause { + return Some(cause); + } + + None + } +} + +impl From for DbError { + #[track_caller] + fn from(error: IOError) -> Self { + DbError::from(error.to_string()) + } +} + +impl From for DbError { + #[track_caller] + fn from(error: FromUtf8Error) -> Self { + DbError::from(error.to_string()) + } +} + +impl From<&str> for DbError { + #[track_caller] + fn from(description: &str) -> Self { + DbError::from(description.to_string()) + } +} + +impl From for DbError { + #[track_caller] + fn from(description: String) -> Self { + DbError { + description, + cause: None, + source_location: *Location::caller(), + } + } +} + +impl PartialEq for DbError { + fn eq(&self, other: &Self) -> bool { + self.description == other.description && self.cause == other.cause + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::ErrorKind; + + #[test] + fn caused_by() { + let error = DbError::from("file not found"); + let new_error = DbError::from("open error").caused_by(error); + + assert_eq!( + new_error.cause, + Some(Box::new(DbError::from("file not found"))) + ); + } + + #[test] + fn derived_from_debug() { + let error = DbError::from("error"); + + format!("{:?}", error); + } + + #[test] + fn derived_from_display() { + let file = file!(); + let col__ = column!(); + let line = line!(); + let error = DbError::from("file not found"); + + assert_eq!( + error.to_string(), + format!( + "file not found (at {}:{}:{})", + file.replace('\\', "/"), + line + 1, + col__ + ) + ); + } + + #[test] + fn derived_from_partial_eq() { + let left = DbError::from(IOError::from(ErrorKind::NotFound)); + let right = DbError::from(IOError::from(ErrorKind::NotFound)); + + assert_eq!(left, right); + } + + #[test] + fn derived_from_error() { + let file = file!(); + let col__ = column!(); + let line = line!(); + let error = DbError::from("file not found"); + let new_error = DbError::from("open error").caused_by(error); + + assert_eq!( + new_error.source().unwrap().to_string(), + format!( + "file not found (at {}:{}:{})", + file.replace('\\', "/"), + line + 1, + col__ + ) + ); + } + + #[test] + fn from_io_error() { + let _error = DbError::from(IOError::from(ErrorKind::NotFound)); + } + + #[test] + fn from_utf8_error() { + let _error = DbError::from(String::from_utf8(vec![0xdf, 0xff]).unwrap_err()); + } + + #[test] + fn source_none() { + let error = DbError::from("file not found"); + + assert!(error.source().is_none()); + } +} diff --git a/src/agdb/graph.rs b/src/agdb/graph.rs new file mode 100644 index 00000000..7f4e428b --- /dev/null +++ b/src/agdb/graph.rs @@ -0,0 +1,407 @@ +pub mod graph_data; +pub mod graph_impl; +pub mod graph_index; +pub mod storage_graph; + +mod graph_data_memory; +mod graph_data_storage; +mod graph_data_storage_indexes; +mod graph_edge; +mod graph_edge_iterator; +mod graph_edge_reverse_iterator; +mod graph_node; +mod graph_node_iterator; + +use self::graph_data_memory::GraphDataMemory; +use self::graph_impl::GraphImpl; + +pub type Graph = GraphImpl; + +impl Graph { + pub fn new() -> Graph { + Graph { + data: GraphDataMemory::default(), + } + } +} + +impl Default for Graph { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::db_error::DbError; + use crate::graph::graph_index::GraphIndex; + + #[test] + fn derived_from_default() { + let _graph = Graph::default(); + } + + #[test] + fn edge_from_index() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + assert_eq!(graph.edge(&index).unwrap().index(), index); + } + + #[test] + fn edge_from_index_missing() { + let graph = Graph::new(); + + assert!(graph.edge(&GraphIndex::from(-3)).is_none()); + } + + #[test] + fn edge_iteration() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node1, &node2).unwrap(); + + let mut actual = Vec::::new(); + + for edge in graph.node(&node1).unwrap().edge_iter_from() { + actual.push(edge.index()); + } + + assert_eq!(actual, vec![edge3, edge2, edge1]); + } + + #[test] + fn edge_iteration_reverse() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node1, &node2).unwrap(); + + let mut actual = Vec::::new(); + + for edge in graph.node(&node2).unwrap().edge_iter_to() { + actual.push(edge.index()); + } + + assert_eq!(actual, vec![edge3, edge2, edge1]); + } + + #[test] + fn index_from() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + + let index = graph.insert_edge(&node1, &node2).unwrap(); + + assert_eq!(graph.edge(&index).unwrap().index_from(), node1); + } + + #[test] + fn index_to() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + + let index = graph.insert_edge(&node1, &node2).unwrap(); + + assert_eq!(graph.edge(&index).unwrap().index_to(), node2); + } + + #[test] + fn insert_edge() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(GraphIndex::from(-3_i64))); + } + + #[test] + fn insert_edge_after_removed() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(index)); + } + + #[test] + fn insert_edge_after_several_removed() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index1).unwrap(); + graph.remove_edge(&index2).unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(index2)); + } + + #[test] + fn insert_edge_invalid_from() { + let mut graph = Graph::new(); + + assert_eq!( + graph.insert_edge(&GraphIndex::from(1), &GraphIndex::from(2)), + Err(DbError::from("'1' is invalid index")) + ); + } + + #[test] + fn insert_edge_invalid_to() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + + assert_eq!( + graph.insert_edge(&from, &GraphIndex::from(2)), + Err(DbError::from("'2' is invalid index")) + ); + } + + #[test] + fn insert_node() { + let mut graph = Graph::new(); + + assert_eq!(graph.insert_node(), Ok(GraphIndex::from(1))); + } + + #[test] + fn insert_node_after_removal() { + let mut graph = Graph::new(); + graph.insert_node().unwrap(); + let index = graph.insert_node().unwrap(); + graph.insert_node().unwrap(); + + graph.remove_node(&index).unwrap(); + + assert_eq!(graph.insert_node(), Ok(index)); + } + + #[test] + fn node_count() { + let mut graph = Graph::new(); + + assert_eq!(graph.node_count().unwrap(), 0); + + graph.insert_node().unwrap(); + let index = graph.insert_node().unwrap(); + graph.insert_node().unwrap(); + + assert_eq!(graph.node_count(), Ok(3)); + + graph.remove_node(&index).unwrap(); + + assert_eq!(graph.node_count(), Ok(2)); + } + + #[test] + fn node_from_index() { + let mut graph = Graph::new(); + let index = graph.insert_node().unwrap(); + + assert_eq!(graph.node(&index).unwrap().index(), index); + } + + #[test] + fn node_from_index_missing() { + let graph = Graph::new(); + + let node = graph.node(&GraphIndex::from(1)); + + assert!(node.is_none()); + } + + #[test] + fn node_iteration() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let expected = vec![node1, node2, node3]; + let mut nodes = Vec::::new(); + + for node in graph.node_iter() { + nodes.push(node.index()); + } + + assert_eq!(nodes, expected); + } + + #[test] + fn node_iteration_with_removed_nodes() { + let mut graph = Graph::new(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + let node5 = graph.insert_node().unwrap(); + + graph.remove_node(&node2).unwrap(); + graph.remove_node(&node5).unwrap(); + + let expected = vec![node1, node3, node4]; + let mut nodes = Vec::::new(); + + for node in graph.node_iter() { + nodes.push(node.index()); + } + + assert_eq!(nodes, expected); + } + + #[test] + fn remove_edge_circular() { + let mut graph = Graph::new(); + let node = graph.insert_node().unwrap(); + let index = graph.insert_edge(&node, &node).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert!(graph.edge(&index).is_none()); + } + + #[test] + fn remove_edge_first() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index3).unwrap(); + + assert!(graph.edge(&index1).is_some()); + assert!(graph.edge(&index2).is_some()); + assert!(graph.edge(&index3).is_none()); + } + + #[test] + fn remove_edge_last() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index1).unwrap(); + + assert!(graph.edge(&index1).is_none()); + assert!(graph.edge(&index2).is_some()); + assert!(graph.edge(&index3).is_some()); + } + + #[test] + fn remove_edge_middle() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index2).unwrap(); + + assert!(graph.edge(&index1).is_some()); + assert!(graph.edge(&index2).is_none()); + assert!(graph.edge(&index3).is_some()); + } + + #[test] + fn remove_edge_missing() { + let mut graph = Graph::new(); + graph.remove_edge(&GraphIndex::from(-3)).unwrap(); + } + + #[test] + fn remove_edge_only() { + let mut graph = Graph::new(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert!(graph.edge(&index).is_none()); + } + + #[test] + fn remove_node_circular_edge() { + let mut graph = Graph::new(); + let index = graph.insert_node().unwrap(); + let edge = graph.insert_edge(&index, &index).unwrap(); + + graph.remove_node(&index).unwrap(); + + assert!(graph.node(&index).is_none()); + assert!(graph.edge(&edge).is_none()); + } + + #[test] + fn remove_node_only() { + let mut graph = Graph::new(); + let index = graph.insert_node().unwrap(); + + graph.remove_node(&index).unwrap(); + + assert!(graph.node(&index).is_none()); + } + + #[test] + fn remove_node_missing() { + let mut graph = Graph::new(); + graph.remove_node(&GraphIndex::from(1)).unwrap(); + } + + #[test] + fn remove_nodes_with_edges() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node1).unwrap(); + let edge3 = graph.insert_edge(&node1, &node3).unwrap(); + let edge4 = graph.insert_edge(&node2, &node1).unwrap(); + let edge5 = graph.insert_edge(&node3, &node1).unwrap(); + + let edge6 = graph.insert_edge(&node3, &node2).unwrap(); + let edge7 = graph.insert_edge(&node2, &node3).unwrap(); + + graph.remove_node(&node1).unwrap(); + + assert!(graph.node(&node1).is_none()); + assert!(graph.edge(&edge1).is_none()); + assert!(graph.edge(&edge2).is_none()); + assert!(graph.edge(&edge3).is_none()); + assert!(graph.edge(&edge4).is_none()); + assert!(graph.edge(&edge5).is_none()); + + assert!(graph.node(&node2).is_some()); + assert!(graph.node(&node3).is_some()); + assert!(graph.edge(&edge6).is_some()); + assert!(graph.edge(&edge7).is_some()); + } +} diff --git a/crates/graph/src/graph_data.rs b/src/agdb/graph/graph_data.rs similarity index 93% rename from crates/graph/src/graph_data.rs rename to src/agdb/graph/graph_data.rs index dbabe2ce..8fab8fa6 100644 --- a/crates/graph/src/graph_data.rs +++ b/src/agdb/graph/graph_data.rs @@ -1,5 +1,5 @@ -use crate::graph_index::GraphIndex; -use agdb_db_error::DbError; +use super::graph_index::GraphIndex; +use crate::db_error::DbError; pub trait GraphData { fn capacity(&self) -> Result; diff --git a/crates/graph/src/graph_data_memory.rs b/src/agdb/graph/graph_data_memory.rs similarity index 86% rename from crates/graph/src/graph_data_memory.rs rename to src/agdb/graph/graph_data_memory.rs index fca25504..6d1a02fc 100644 --- a/crates/graph/src/graph_data_memory.rs +++ b/src/agdb/graph/graph_data_memory.rs @@ -1,6 +1,6 @@ -use crate::graph_data::GraphData; -use crate::graph_index::GraphIndex; -use agdb_db_error::DbError; +use super::graph_data::GraphData; +use super::graph_index::GraphIndex; +use crate::db_error::DbError; pub struct GraphDataMemory { pub(crate) from: Vec, @@ -83,3 +83,14 @@ impl GraphData for GraphDataMemory { fn transaction(&mut self) {} } + +impl Default for GraphDataMemory { + fn default() -> Self { + Self { + from: vec![0], + to: vec![0], + from_meta: vec![i64::MIN], + to_meta: vec![0], + } + } +} diff --git a/src/agdb/graph/graph_data_storage.rs b/src/agdb/graph/graph_data_storage.rs new file mode 100644 index 00000000..1ca8aca5 --- /dev/null +++ b/src/agdb/graph/graph_data_storage.rs @@ -0,0 +1,168 @@ +use super::graph_data::GraphData; +use super::graph_data_storage_indexes::GraphDataStorageIndexes; +use super::graph_index::GraphIndex; +use crate::collections::storage_vec::StorageVec; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct GraphDataStorage +where + Data: Storage, +{ + pub(crate) storage: Rc>, + #[allow(dead_code)] + pub(crate) storage_index: StorageIndex, + #[allow(dead_code)] + pub(crate) indexes: GraphDataStorageIndexes, + pub(crate) from: StorageVec, + pub(crate) to: StorageVec, + pub(crate) from_meta: StorageVec, + pub(crate) to_meta: StorageVec, +} + +impl GraphData for GraphDataStorage +where + Data: Storage, +{ + fn capacity(&self) -> Result { + Ok(self.from.len()) + } + + fn commit(&mut self) -> Result<(), DbError> { + self.storage.borrow_mut().commit() + } + + fn free_index(&self) -> Result { + self.from_meta.value(0) + } + + fn from(&self, index: &GraphIndex) -> Result { + self.from.value(index.as_u64()) + } + + fn from_meta(&self, index: &GraphIndex) -> Result { + self.from_meta.value(index.as_u64()) + } + + fn grow(&mut self) -> Result<(), DbError> { + self.from.push(&0)?; + self.to.push(&0)?; + self.from_meta.push(&0)?; + self.to_meta.push(&0) + } + + fn node_count(&self) -> Result { + Ok(self.to_meta.value(0)? as u64) + } + + fn set_from(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { + self.from.set_value(index.as_u64(), &value) + } + + fn set_from_meta(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { + self.from_meta.set_value(index.as_u64(), &value) + } + + fn set_node_count(&mut self, count: u64) -> Result<(), DbError> { + self.to_meta.set_value(0, &(count as i64)) + } + + fn set_to(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { + self.to.set_value(index.as_u64(), &value) + } + + fn set_to_meta(&mut self, index: &GraphIndex, value: i64) -> Result<(), DbError> { + self.to_meta.set_value(index.as_u64(), &value) + } + + fn to(&self, index: &GraphIndex) -> Result { + self.to.value(index.as_u64()) + } + + fn to_meta(&self, index: &GraphIndex) -> Result { + self.to_meta.value(index.as_u64()) + } + + fn transaction(&mut self) { + self.storage.borrow_mut().transaction() + } +} + +impl TryFrom>> for GraphDataStorage +where + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + let mut from = StorageVec::::try_from(storage.clone())?; + from.push(&0)?; + let mut to = StorageVec::::try_from(storage.clone())?; + to.push(&0)?; + let mut from_meta = StorageVec::::try_from(storage.clone())?; + from_meta.push(&i64::MIN)?; + let mut to_meta = StorageVec::::try_from(storage.clone())?; + to_meta.push(&0)?; + + let indexes = GraphDataStorageIndexes { + from: from.storage_index(), + to: to.storage_index(), + from_meta: from_meta.storage_index(), + to_meta: to_meta.storage_index(), + }; + + let index = storage.borrow_mut().insert(&indexes)?; + + Ok(GraphDataStorage:: { + storage, + storage_index: index, + indexes, + from, + to, + from_meta, + to_meta, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for GraphDataStorage { + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + let indexes = storage_with_index + .0 + .borrow_mut() + .value::(&storage_with_index.1)?; + + let from = StorageVec::::try_from(( + storage_with_index.0.clone(), + indexes.from.clone(), + ))?; + let to = + StorageVec::::try_from((storage_with_index.0.clone(), indexes.to.clone()))?; + let from_meta = StorageVec::::try_from(( + storage_with_index.0.clone(), + indexes.from_meta.clone(), + ))?; + let to_meta = StorageVec::::try_from(( + storage_with_index.0.clone(), + indexes.to_meta.clone(), + ))?; + + Ok(GraphDataStorage:: { + storage: storage_with_index.0, + storage_index: storage_with_index.1, + indexes, + from, + to, + from_meta, + to_meta, + }) + } +} diff --git a/crates/graph/src/graph_data_storage_indexes_serialize.rs b/src/agdb/graph/graph_data_storage_indexes.rs similarity index 74% rename from crates/graph/src/graph_data_storage_indexes_serialize.rs rename to src/agdb/graph/graph_data_storage_indexes.rs index 1ac32e44..d8ce1dba 100644 --- a/crates/graph/src/graph_data_storage_indexes_serialize.rs +++ b/src/agdb/graph/graph_data_storage_indexes.rs @@ -1,9 +1,15 @@ -use crate::graph_data_storage_indexes::GraphDataStorageIndexes; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage::StorageIndex; +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::utilities::serialize::Serialize; use std::mem::size_of; +pub(crate) struct GraphDataStorageIndexes { + pub(crate) from: StorageIndex, + pub(crate) to: StorageIndex, + pub(crate) from_meta: StorageIndex, + pub(crate) to_meta: StorageIndex, +} + impl Serialize for GraphDataStorageIndexes { fn deserialize(bytes: &[u8]) -> Result { Ok(GraphDataStorageIndexes { diff --git a/crates/graph/src/graph_edge.rs b/src/agdb/graph/graph_edge.rs similarity index 81% rename from crates/graph/src/graph_edge.rs rename to src/agdb/graph/graph_edge.rs index 51f59f02..10702c2f 100644 --- a/crates/graph/src/graph_edge.rs +++ b/src/agdb/graph/graph_edge.rs @@ -1,6 +1,4 @@ -use crate::graph_data::GraphData; -use crate::graph_impl::GraphImpl; -use crate::graph_index::GraphIndex; +use super::{graph_data::GraphData, graph_impl::GraphImpl, graph_index::GraphIndex}; pub struct GraphEdge<'a, Data> where diff --git a/crates/graph/src/graph_edge_iterator.rs b/src/agdb/graph/graph_edge_iterator.rs similarity index 82% rename from crates/graph/src/graph_edge_iterator.rs rename to src/agdb/graph/graph_edge_iterator.rs index be4978c9..93f004b9 100644 --- a/crates/graph/src/graph_edge_iterator.rs +++ b/src/agdb/graph/graph_edge_iterator.rs @@ -1,7 +1,7 @@ -use crate::graph_data::GraphData; -use crate::graph_edge::GraphEdge; -use crate::graph_impl::GraphImpl; -use crate::graph_index::GraphIndex; +use super::graph_data::GraphData; +use super::graph_edge::GraphEdge; +use super::graph_impl::GraphImpl; +use super::graph_index::GraphIndex; pub struct GraphEdgeIterator<'a, Data> where diff --git a/crates/graph/src/graph_edge_reverse_iterator.rs b/src/agdb/graph/graph_edge_reverse_iterator.rs similarity index 82% rename from crates/graph/src/graph_edge_reverse_iterator.rs rename to src/agdb/graph/graph_edge_reverse_iterator.rs index 29ab56be..51317d97 100644 --- a/crates/graph/src/graph_edge_reverse_iterator.rs +++ b/src/agdb/graph/graph_edge_reverse_iterator.rs @@ -1,7 +1,7 @@ -use crate::graph_data::GraphData; -use crate::graph_edge::GraphEdge; -use crate::graph_impl::GraphImpl; -use crate::graph_index::GraphIndex; +use super::graph_data::GraphData; +use super::graph_edge::GraphEdge; +use super::graph_impl::GraphImpl; +use super::graph_index::GraphIndex; pub struct GraphEdgeReverseIterator<'a, Data> where diff --git a/crates/graph/src/graph_impl.rs b/src/agdb/graph/graph_impl.rs similarity index 97% rename from crates/graph/src/graph_impl.rs rename to src/agdb/graph/graph_impl.rs index e479c27b..e896af79 100644 --- a/crates/graph/src/graph_impl.rs +++ b/src/agdb/graph/graph_impl.rs @@ -1,9 +1,9 @@ -use crate::graph_data::GraphData; -use crate::graph_edge::GraphEdge; -use crate::graph_index::GraphIndex; -use crate::graph_node::GraphNode; -use crate::graph_node_iterator::GraphNodeIterator; -use agdb_db_error::DbError; +use super::graph_data::GraphData; +use super::graph_edge::GraphEdge; +use super::graph_index::GraphIndex; +use super::graph_node::GraphNode; +use super::graph_node_iterator::GraphNodeIterator; +use crate::db_error::DbError; pub struct GraphImpl where @@ -12,6 +12,7 @@ where pub(crate) data: Data, } +#[allow(dead_code)] impl GraphImpl where Data: GraphData, diff --git a/src/agdb/graph/graph_index.rs b/src/agdb/graph/graph_index.rs new file mode 100644 index 00000000..8d5654b5 --- /dev/null +++ b/src/agdb/graph/graph_index.rs @@ -0,0 +1,99 @@ +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct GraphIndex { + pub(crate) index: i64, +} + +impl GraphIndex { + pub fn as_u64(&self) -> u64 { + if self.is_edge() { + (-self.value()) as u64 + } else { + self.value() as u64 + } + } + + pub fn as_usize(&self) -> usize { + if self.is_edge() { + (-self.value()) as usize + } else { + self.value() as usize + } + } + + pub fn is_edge(&self) -> bool { + self.index < 0 + } + + pub fn is_node(&self) -> bool { + 0 < self.index + } + + pub fn is_valid(&self) -> bool { + self.index != 0 + } + + pub fn value(&self) -> i64 { + self.index + } +} + +impl From for GraphIndex { + fn from(index: i64) -> Self { + Self { index } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cmp::Ordering; + + #[test] + fn derived_from_debug() { + let index = GraphIndex::default(); + + format!("{:?}", index); + } + + #[test] + fn derived_from_ord() { + let index = GraphIndex::default(); + assert_eq!(index.cmp(&index), Ordering::Equal); + } + + #[test] + fn is_edge() { + assert!(!GraphIndex::from(1).is_edge()); + assert!(!GraphIndex::default().is_edge()); + assert!(GraphIndex::from(-1).is_edge()); + } + + #[test] + fn is_node() { + assert!(GraphIndex::from(1).is_node()); + assert!(!GraphIndex::default().is_node()); + assert!(!GraphIndex::from(-1).is_node()); + } + + #[test] + fn ordering() { + let mut indexes = vec![ + GraphIndex::default(), + GraphIndex::from(100_i64), + GraphIndex::from(-1_i64), + GraphIndex::from(1_i64), + ]; + + indexes.sort(); + + assert_eq!( + indexes, + vec![ + GraphIndex::from(-1_i64), + GraphIndex::default(), + GraphIndex::from(1_i64), + GraphIndex::from(100_i64), + ] + ) + } +} diff --git a/crates/graph/src/graph_node.rs b/src/agdb/graph/graph_node.rs similarity index 75% rename from crates/graph/src/graph_node.rs rename to src/agdb/graph/graph_node.rs index 16d02932..404b3523 100644 --- a/crates/graph/src/graph_node.rs +++ b/src/agdb/graph/graph_node.rs @@ -1,8 +1,8 @@ -use crate::graph_data::GraphData; -use crate::graph_edge_iterator::GraphEdgeIterator; -use crate::graph_edge_reverse_iterator::GraphEdgeReverseIterator; -use crate::graph_impl::GraphImpl; -use crate::graph_index::GraphIndex; +use super::graph_data::GraphData; +use super::graph_edge_iterator::GraphEdgeIterator; +use super::graph_edge_reverse_iterator::GraphEdgeReverseIterator; +use super::graph_impl::GraphImpl; +use super::graph_index::GraphIndex; pub struct GraphNode<'a, Data> where @@ -12,6 +12,7 @@ where pub(crate) index: GraphIndex, } +#[allow(dead_code)] impl<'a, Data> GraphNode<'a, Data> where Data: GraphData, diff --git a/crates/graph/src/graph_node_iterator.rs b/src/agdb/graph/graph_node_iterator.rs similarity index 81% rename from crates/graph/src/graph_node_iterator.rs rename to src/agdb/graph/graph_node_iterator.rs index 64aa8e6a..2a17d795 100644 --- a/crates/graph/src/graph_node_iterator.rs +++ b/src/agdb/graph/graph_node_iterator.rs @@ -1,7 +1,7 @@ -use crate::graph_data::GraphData; -use crate::graph_impl::GraphImpl; -use crate::graph_index::GraphIndex; -use crate::graph_node::GraphNode; +use super::graph_data::GraphData; +use super::graph_impl::GraphImpl; +use super::graph_index::GraphIndex; +use super::graph_node::GraphNode; pub struct GraphNodeIterator<'a, Data> where diff --git a/src/agdb/graph/storage_graph.rs b/src/agdb/graph/storage_graph.rs new file mode 100644 index 00000000..92493a41 --- /dev/null +++ b/src/agdb/graph/storage_graph.rs @@ -0,0 +1,516 @@ +use super::graph_data_storage::GraphDataStorage; +use super::graph_impl::GraphImpl; +use crate::db_error::DbError; +use crate::storage::storage_file::StorageFile; +use crate::storage::storage_index::StorageIndex; +use crate::storage::Storage; +use std::cell::RefCell; +use std::rc::Rc; + +pub type StorageGraph = GraphImpl>; + +#[allow(dead_code)] +impl StorageGraph +where + Data: Storage, +{ + pub fn storage_index(&self) -> StorageIndex { + self.data.storage_index.clone() + } +} + +impl TryFrom>> for StorageGraph +where + Data: Storage, +{ + type Error = DbError; + + fn try_from(storage: Rc>) -> Result { + Ok(StorageGraph { + data: GraphDataStorage::::try_from(storage)?, + }) + } +} + +impl TryFrom<(Rc>, StorageIndex)> for StorageGraph { + type Error = DbError; + + fn try_from( + storage_with_index: (Rc>, StorageIndex), + ) -> Result { + Ok(StorageGraph { + data: GraphDataStorage::::try_from(storage_with_index)?, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::graph::graph_index::GraphIndex; + use crate::test_utilities::test_file::TestFile; + + #[test] + fn edge_from_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + assert_eq!(graph.edge(&index).unwrap().index(), index); + } + + #[test] + fn edge_from_index_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let graph = StorageGraph::try_from(storage).unwrap(); + + assert!(graph.edge(&GraphIndex::from(-3)).is_none()); + } + + #[test] + fn edge_iteration() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node1, &node2).unwrap(); + + let mut actual = Vec::::new(); + + for edge in graph.node(&node1).unwrap().edge_iter_from() { + actual.push(edge.index()); + } + + assert_eq!(actual, vec![edge3, edge2, edge1]); + } + + #[test] + fn insert_edge() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(GraphIndex::from(-3_i64))); + } + + #[test] + fn insert_edge_after_removed() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(index)); + } + + #[test] + fn insert_edge_after_several_removed() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index1).unwrap(); + graph.remove_edge(&index2).unwrap(); + + assert_eq!(graph.insert_edge(&from, &to), Ok(index2)); + } + + #[test] + fn insert_edge_invalid_from() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + + assert_eq!( + graph.insert_edge(&GraphIndex::from(1), &GraphIndex::from(2)), + Err(DbError::from("'1' is invalid index")) + ); + } + + #[test] + fn insert_edge_invalid_to() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + + assert_eq!( + graph.insert_edge(&from, &GraphIndex::from(2)), + Err(DbError::from("'2' is invalid index")) + ); + } + + #[test] + fn insert_node() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + + assert_eq!(graph.insert_node(), Ok(GraphIndex::from(1))); + } + + #[test] + fn insert_node_after_removal() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + graph.insert_node().unwrap(); + let index = graph.insert_node().unwrap(); + graph.insert_node().unwrap(); + + graph.remove_node(&index).unwrap(); + + assert_eq!(graph.insert_node().unwrap(), index); + } + + #[test] + fn node_count() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + + assert_eq!(graph.node_count().unwrap(), 0); + + graph.insert_node().unwrap(); + let index = graph.insert_node().unwrap(); + graph.insert_node().unwrap(); + + assert_eq!(graph.node_count().unwrap(), 3); + + graph.remove_node(&index).unwrap(); + + assert_eq!(graph.node_count().unwrap(), 2); + } + + #[test] + fn node_from_index() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let index = graph.insert_node().unwrap(); + + assert_eq!(graph.node(&index).unwrap().index(), index); + } + + #[test] + fn node_from_index_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let graph = StorageGraph::try_from(storage).unwrap(); + + let node = graph.node(&GraphIndex::from(1)); + + assert!(node.is_none()); + } + + #[test] + fn node_iteration() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let expected = vec![node1, node2, node3]; + let mut nodes = Vec::::new(); + + for node in graph.node_iter() { + nodes.push(node.index()); + } + + assert_eq!(nodes, expected); + } + + #[test] + fn node_iteration_with_removed_nodes() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + let node5 = graph.insert_node().unwrap(); + + graph.remove_node(&node2).unwrap(); + graph.remove_node(&node5).unwrap(); + + let expected = vec![node1, node3, node4]; + let mut nodes = Vec::::new(); + + for node in graph.node_iter() { + nodes.push(node.index()); + } + + assert_eq!(nodes, expected); + } + + #[test] + fn remove_edge_circular() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let node = graph.insert_node().unwrap(); + let index = graph.insert_edge(&node, &node).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert!(graph.edge(&index).is_none()); + } + + #[test] + fn remove_edge_first() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index3).unwrap(); + + assert!(graph.edge(&index1).is_some()); + assert!(graph.edge(&index2).is_some()); + assert!(graph.edge(&index3).is_none()); + } + + #[test] + fn remove_edge_last() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index1).unwrap(); + + assert!(graph.edge(&index1).is_none()); + assert!(graph.edge(&index2).is_some()); + assert!(graph.edge(&index3).is_some()); + } + + #[test] + fn remove_edge_middle() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index1 = graph.insert_edge(&from, &to).unwrap(); + let index2 = graph.insert_edge(&from, &to).unwrap(); + let index3 = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index2).unwrap(); + + assert!(graph.edge(&index1).is_some()); + assert!(graph.edge(&index2).is_none()); + assert!(graph.edge(&index3).is_some()); + } + + #[test] + fn remove_edge_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + graph.remove_edge(&GraphIndex::from(-3)).unwrap(); + } + + #[test] + fn remove_edge_only() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let from = graph.insert_node().unwrap(); + let to = graph.insert_node().unwrap(); + let index = graph.insert_edge(&from, &to).unwrap(); + + graph.remove_edge(&index).unwrap(); + + assert!(graph.edge(&index).is_none()); + } + + #[test] + fn remove_node_circular_edge() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let index = graph.insert_node().unwrap(); + let edge = graph.insert_edge(&index, &index).unwrap(); + + graph.remove_node(&index).unwrap(); + + assert!(graph.node(&index).is_none()); + assert!(graph.edge(&edge).is_none()); + } + + #[test] + fn remove_node_only() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + let index = graph.insert_node().unwrap(); + + graph.remove_node(&index).unwrap(); + + assert!(graph.node(&index).is_none()); + } + + #[test] + fn remove_node_missing() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + graph.remove_node(&GraphIndex::from(1)).unwrap(); + } + + #[test] + fn remove_nodes_with_edges() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + let mut graph = StorageGraph::try_from(storage).unwrap(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node1).unwrap(); + let edge3 = graph.insert_edge(&node1, &node3).unwrap(); + let edge4 = graph.insert_edge(&node2, &node1).unwrap(); + let edge5 = graph.insert_edge(&node3, &node1).unwrap(); + + let edge6 = graph.insert_edge(&node3, &node2).unwrap(); + let edge7 = graph.insert_edge(&node2, &node3).unwrap(); + + graph.remove_node(&node1).unwrap(); + + assert!(graph.node(&node1).is_none()); + assert!(graph.edge(&edge1).is_none()); + assert!(graph.edge(&edge2).is_none()); + assert!(graph.edge(&edge3).is_none()); + assert!(graph.edge(&edge4).is_none()); + assert!(graph.edge(&edge5).is_none()); + + assert!(graph.node(&node2).is_some()); + assert!(graph.node(&node3).is_some()); + assert!(graph.edge(&edge6).is_some()); + assert!(graph.edge(&edge7).is_some()); + } + + #[test] + fn restore_from_file() { + let test_file = TestFile::new(); + let storage = Rc::new(RefCell::new( + StorageFile::try_from(test_file.file_name().clone()).unwrap(), + )); + + let index; + + let node1; + let node2; + let node3; + + let edge1; + let edge2; + let edge3; + + { + let mut graph = StorageGraph::try_from(storage.clone()).unwrap(); + + index = graph.storage_index(); + + node1 = graph.insert_node().unwrap(); + node2 = graph.insert_node().unwrap(); + node3 = graph.insert_node().unwrap(); + + edge1 = graph.insert_edge(&node1, &node2).unwrap(); + edge2 = graph.insert_edge(&node2, &node3).unwrap(); + edge3 = graph.insert_edge(&node3, &node1).unwrap(); + } + + let graph = StorageGraph::try_from((storage, index)).unwrap(); + + assert!(graph.node(&node1).is_some()); + assert!(graph.node(&node2).is_some()); + assert!(graph.node(&node3).is_some()); + assert!(graph.edge(&edge1).is_some()); + assert!(graph.edge(&edge2).is_some()); + assert!(graph.edge(&edge3).is_some()); + } +} diff --git a/crates/graph_search/src/graph_search.rs b/src/agdb/graph_search.rs similarity index 66% rename from crates/graph_search/src/graph_search.rs rename to src/agdb/graph_search.rs index a9c66000..c56cb415 100644 --- a/crates/graph_search/src/graph_search.rs +++ b/src/agdb/graph_search.rs @@ -1,14 +1,28 @@ -use crate::breadth_first_search::BreadthFirstSearch; -use crate::breadth_first_search_reverse::BreadthFirstSearchReverse; -use crate::depth_first_search::DepthFirstSearch; -use crate::depth_first_search_reverse::DepthFirstSearchReverse; -use crate::path_search_impl::PathSearchImpl; -use crate::search_handler::SearchHandler; -use crate::search_impl::SearchImpl; -use crate::PathSearchHandler; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; +pub mod path_search_handler; +pub mod search_control; +pub mod search_handler; + +mod breadth_first_search; +mod breadth_first_search_reverse; +mod depth_first_search; +mod depth_first_search_reverse; +mod path; +mod path_search; +mod search_impl; +mod search_index; +mod search_iterator; + +use self::breadth_first_search::BreadthFirstSearch; +use self::breadth_first_search_reverse::BreadthFirstSearchReverse; +use self::depth_first_search::DepthFirstSearch; +use self::depth_first_search_reverse::DepthFirstSearchReverse; +use self::path_search::PathSearch; +use self::path_search_handler::PathSearchHandler; +use self::search_handler::SearchHandler; +use self::search_impl::SearchImpl; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; pub struct GraphSearch<'a, Data> where @@ -17,6 +31,7 @@ where pub(crate) graph: &'a GraphImpl, } +#[allow(dead_code)] impl<'a, Data> GraphSearch<'a, Data> where Data: GraphData, @@ -79,7 +94,7 @@ where handler: &'a Handler, ) -> Vec { if from != to && self.is_valid_node(from) && self.is_valid_node(to) { - PathSearchImpl::<'a, Data, Handler>::new(self.graph, from.clone(), to.clone(), handler) + PathSearch::<'a, Data, Handler>::new(self.graph, from.clone(), to.clone(), handler) .search() } else { vec![] @@ -94,3 +109,12 @@ where self.graph.node(index).is_some() } } + +impl<'a, Data> From<&'a GraphImpl> for GraphSearch<'a, Data> +where + Data: GraphData, +{ + fn from(graph: &'a GraphImpl) -> Self { + GraphSearch { graph } + } +} diff --git a/src/agdb/graph_search/breadth_first_search.rs b/src/agdb/graph_search/breadth_first_search.rs new file mode 100644 index 00000000..b8022bee --- /dev/null +++ b/src/agdb/graph_search/breadth_first_search.rs @@ -0,0 +1,238 @@ +use super::search_index::SearchIndex; +use super::search_iterator::SearchIterator; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; +use std::mem::swap; +use std::vec::IntoIter; + +pub(crate) struct BreadthFirstSearch { + stack_iterator: IntoIter, +} + +impl BreadthFirstSearch { + fn take_stack(stack: &mut Vec) -> Vec { + let mut res = Vec::::new(); + swap(&mut res, stack); + + res + } +} + +impl SearchIterator for BreadthFirstSearch { + fn expand_edge(index: &GraphIndex, graph: &GraphImpl) -> GraphIndex { + graph.edge(index).unwrap().index_to() + } + + fn expand_node( + index: &GraphIndex, + graph: &GraphImpl, + ) -> Vec { + graph + .node(index) + .expect("invalid index, expected a valid node index") + .edge_iter_from() + .map(|edge| edge.index()) + .collect() + } + + fn new(stack: &mut Vec) -> Self { + Self { + stack_iterator: Self::take_stack(stack).into_iter(), + } + } + + fn next(&mut self) -> Option { + self.stack_iterator.next() + } +} + +#[cfg(test)] +mod tests { + use super::super::search_control::SearchControl; + use super::super::search_handler::SearchHandler; + use super::*; + use crate::graph::Graph; + use crate::graph_search::GraphSearch; + + struct Handler { + pub processor: fn(&GraphIndex, &u64) -> SearchControl, + } + + impl Default for Handler { + fn default() -> Self { + Self { + processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), + } + } + } + + impl SearchHandler for Handler { + fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { + (self.processor)(index, distance) + } + } + + #[test] + fn empty_graph() { + let graph = Graph::new(); + + let result = GraphSearch::from(&graph) + .breadth_first_search(&GraphIndex::default(), &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn cyclic_graph() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node2, &node3).unwrap(); + let edge4 = graph.insert_edge(&node2, &node3).unwrap(); + let edge5 = graph.insert_edge(&node3, &node1).unwrap(); + let edge6 = graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); + + assert_eq!( + result, + vec![node1, edge2, edge1, node2, edge4, edge3, node3, edge6, edge5] + ); + } + + #[test] + fn full_search() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node3).unwrap(); + let edge3 = graph.insert_edge(&node1, &node4).unwrap(); + + let result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); + + assert_eq!( + result, + vec![node1, edge3, edge2, edge1, node4, node3, node2] + ); + } + + #[test] + fn filter_edges() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node1, &node3).unwrap(); + graph.insert_edge(&node1, &node4).unwrap(); + + let result = GraphSearch::from(&graph).breadth_first_search( + &node1, + &Handler { + processor: |index: &GraphIndex, _distance: &u64| { + SearchControl::Continue(index.is_node()) + }, + }, + ); + + assert_eq!(result, vec![node1, node4, node3, node2]); + } + + #[test] + fn finish_search() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node2, &node3).unwrap(); + graph.insert_edge(&node2, &node3).unwrap(); + graph.insert_edge(&node3, &node1).unwrap(); + graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).breadth_first_search( + &node1, + &Handler { + processor: |index: &GraphIndex, _distance: &u64| { + if index.value() == 2 { + SearchControl::Finish(true) + } else { + SearchControl::Continue(false) + } + }, + }, + ); + + assert_eq!(result, vec![node2]); + } + + #[test] + fn search_twice() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node3).unwrap(); + let edge3 = graph.insert_edge(&node1, &node4).unwrap(); + + let mut result = + GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); + let expected = vec![node1.clone(), edge3, edge2, edge1, node4, node3, node2]; + + assert_eq!(result, expected); + + result = GraphSearch::from(&graph).breadth_first_search(&node1, &Handler::default()); + + assert_eq!(result, expected); + } + + #[test] + fn stop_at_distance() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); + let _edge4 = graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).breadth_first_search( + &node1, + &Handler { + processor: |_index: &GraphIndex, distance: &u64| { + if *distance == 2 { + SearchControl::Stop(true) + } else { + SearchControl::Continue(true) + } + }, + }, + ); + + assert_eq!(result, vec![node1, edge2, edge1, node2]); + } +} diff --git a/src/agdb/graph_search/breadth_first_search_reverse.rs b/src/agdb/graph_search/breadth_first_search_reverse.rs new file mode 100644 index 00000000..a626485e --- /dev/null +++ b/src/agdb/graph_search/breadth_first_search_reverse.rs @@ -0,0 +1,105 @@ +use super::search_index::SearchIndex; +use super::search_iterator::SearchIterator; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; +use std::mem::swap; +use std::vec::IntoIter; + +pub(crate) struct BreadthFirstSearchReverse { + stack_iterator: IntoIter, +} + +impl BreadthFirstSearchReverse { + fn take_stack(stack: &mut Vec) -> Vec { + let mut res = Vec::::new(); + swap(&mut res, stack); + + res + } +} + +impl SearchIterator for BreadthFirstSearchReverse { + fn expand_edge(index: &GraphIndex, graph: &GraphImpl) -> GraphIndex { + graph.edge(index).unwrap().index_from() + } + + fn expand_node( + index: &GraphIndex, + graph: &GraphImpl, + ) -> Vec { + graph + .node(index) + .expect("invalid index, expected a valid node index") + .edge_iter_to() + .map(|edge| edge.index()) + .collect() + } + + fn new(stack: &mut Vec) -> Self { + Self { + stack_iterator: Self::take_stack(stack).into_iter(), + } + } + + fn next(&mut self) -> Option { + self.stack_iterator.next() + } +} + +#[cfg(test)] +mod tests { + use super::super::search_control::SearchControl; + use super::super::search_handler::SearchHandler; + use super::*; + use crate::graph::Graph; + use crate::graph_search::GraphSearch; + + struct Handler { + pub processor: fn(&GraphIndex, &u64) -> SearchControl, + } + + impl Default for Handler { + fn default() -> Self { + Self { + processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), + } + } + } + + impl SearchHandler for Handler { + fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { + (self.processor)(index, distance) + } + } + + #[test] + fn empty_graph_reverse() { + let graph = Graph::new(); + + let result = GraphSearch::from(&graph) + .breadth_first_search_reverse(&GraphIndex::default(), &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn search_in_reverse() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node2, &node3).unwrap(); + let edge3 = graph.insert_edge(&node3, &node4).unwrap(); + + let result = + GraphSearch::from(&graph).breadth_first_search_reverse(&node4, &Handler::default()); + let expected = vec![node4.clone(), edge3, node3, edge2, node2, edge1, node1]; + + assert_eq!(result, expected); + } +} diff --git a/src/agdb/graph_search/depth_first_search.rs b/src/agdb/graph_search/depth_first_search.rs new file mode 100644 index 00000000..75453f39 --- /dev/null +++ b/src/agdb/graph_search/depth_first_search.rs @@ -0,0 +1,227 @@ +use super::search_index::SearchIndex; +use super::search_iterator::SearchIterator; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; + +pub(crate) struct DepthFirstSearch { + index: Option, +} + +impl SearchIterator for DepthFirstSearch { + fn expand_edge(index: &GraphIndex, graph: &GraphImpl) -> GraphIndex { + graph + .edge(index) + .expect("invalid index, expected a valid edge index") + .index_to() + } + + fn expand_node( + index: &GraphIndex, + graph: &GraphImpl, + ) -> Vec { + graph + .node(index) + .expect("invalid index, expected a valid node index") + .edge_iter_from() + .map(|edge| edge.index()) + .collect() + } + + fn new(stack: &mut Vec) -> Self { + Self { index: stack.pop() } + } + + fn next(&mut self) -> Option { + self.index.take() + } +} + +#[cfg(test)] +mod tests { + use super::super::search_control::SearchControl; + use super::super::search_handler::SearchHandler; + use super::*; + use crate::graph::Graph; + use crate::graph_search::GraphSearch; + + struct Handler { + pub processor: fn(&GraphIndex, &u64) -> SearchControl, + } + + impl Default for Handler { + fn default() -> Self { + Self { + processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), + } + } + } + + impl SearchHandler for Handler { + fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { + (self.processor)(index, distance) + } + } + + #[test] + fn empty_graph() { + let graph = Graph::new(); + + let result = GraphSearch::from(&graph) + .depth_first_search(&GraphIndex::default(), &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn cyclic_graph() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node2, &node3).unwrap(); + let edge4 = graph.insert_edge(&node2, &node3).unwrap(); + let edge5 = graph.insert_edge(&node3, &node1).unwrap(); + let edge6 = graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); + + assert_eq!( + result, + vec![node1, edge1, node2, edge3, node3, edge5, edge6, edge4, edge2] + ); + } + + #[test] + fn full_search() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node3).unwrap(); + let edge3 = graph.insert_edge(&node1, &node4).unwrap(); + + let result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); + + assert_eq!( + result, + vec![node1, edge1, node2, edge2, node3, edge3, node4] + ); + } + + #[test] + fn filter_edges() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node1, &node3).unwrap(); + graph.insert_edge(&node1, &node4).unwrap(); + + let result = GraphSearch::from(&graph).depth_first_search( + &node1, + &Handler { + processor: |index: &GraphIndex, _distance: &u64| { + SearchControl::Continue(index.is_node()) + }, + }, + ); + + assert_eq!(result, vec![node1, node2, node3, node4]); + } + + #[test] + fn finish_search() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node1, &node2).unwrap(); + graph.insert_edge(&node2, &node3).unwrap(); + graph.insert_edge(&node2, &node3).unwrap(); + graph.insert_edge(&node3, &node1).unwrap(); + graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).depth_first_search( + &node1, + &Handler { + processor: |index: &GraphIndex, _distance: &u64| { + if index.value() == 2 { + SearchControl::Finish(true) + } else { + SearchControl::Continue(false) + } + }, + }, + ); + + assert_eq!(result, vec![node2]); + } + + #[test] + fn search_twice() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node3).unwrap(); + let edge3 = graph.insert_edge(&node1, &node4).unwrap(); + + let mut result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); + let expected = vec![node1.clone(), edge1, node2, edge2, node3, edge3, node4]; + + assert_eq!(result, expected); + + result = GraphSearch::from(&graph).depth_first_search(&node1, &Handler::default()); + + assert_eq!(result, expected); + } + + #[test] + fn stop_at_distance() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); + let _edge4 = graph.insert_edge(&node3, &node1).unwrap(); + + let result = GraphSearch::from(&graph).depth_first_search( + &node1, + &Handler { + processor: |_index: &GraphIndex, distance: &u64| { + if *distance == 2 { + SearchControl::Stop(true) + } else { + SearchControl::Continue(true) + } + }, + }, + ); + + assert_eq!(result, vec![node1, edge1, node2, edge2]); + } +} diff --git a/src/agdb/graph_search/depth_first_search_reverse.rs b/src/agdb/graph_search/depth_first_search_reverse.rs new file mode 100644 index 00000000..ee0034fa --- /dev/null +++ b/src/agdb/graph_search/depth_first_search_reverse.rs @@ -0,0 +1,95 @@ +use super::search_index::SearchIndex; +use super::search_iterator::SearchIterator; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; + +pub(crate) struct DepthFirstSearchReverse { + index: Option, +} + +impl SearchIterator for DepthFirstSearchReverse { + fn expand_edge(index: &GraphIndex, graph: &GraphImpl) -> GraphIndex { + graph + .edge(index) + .expect("invalid index, expected a valid edge index") + .index_from() + } + + fn expand_node( + index: &GraphIndex, + graph: &GraphImpl, + ) -> Vec { + graph + .node(index) + .expect("invalid index, expected a valid node index") + .edge_iter_to() + .map(|edge| edge.index()) + .collect() + } + + fn new(stack: &mut Vec) -> Self { + Self { index: stack.pop() } + } + + fn next(&mut self) -> Option { + self.index.take() + } +} + +#[cfg(test)] +mod tests { + use super::super::search_control::SearchControl; + use super::super::search_handler::SearchHandler; + use super::*; + use crate::graph::Graph; + use crate::graph_search::GraphSearch; + + struct Handler { + pub processor: fn(&GraphIndex, &u64) -> SearchControl, + } + + impl Default for Handler { + fn default() -> Self { + Self { + processor: |_index: &GraphIndex, _distance: &u64| SearchControl::Continue(true), + } + } + } + + impl SearchHandler for Handler { + fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl { + (self.processor)(index, distance) + } + } + + #[test] + fn empty_graph_reverse() { + let graph = Graph::new(); + + let result = GraphSearch::from(&graph) + .depth_first_search_reverse(&GraphIndex::default(), &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn search_in_reverse() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + let node4 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node2, &node3).unwrap(); + let edge3 = graph.insert_edge(&node3, &node4).unwrap(); + + let result = + GraphSearch::from(&graph).depth_first_search_reverse(&node4, &Handler::default()); + let expected = vec![node4.clone(), edge3, node3, edge2, node2, edge1, node1]; + + assert_eq!(result, expected); + } +} diff --git a/crates/graph_search/src/path.rs b/src/agdb/graph_search/path.rs similarity index 72% rename from crates/graph_search/src/path.rs rename to src/agdb/graph_search/path.rs index 20bfa942..0aed4077 100644 --- a/crates/graph_search/src/path.rs +++ b/src/agdb/graph_search/path.rs @@ -1,4 +1,4 @@ -use agdb_graph::GraphIndex; +use crate::graph::graph_index::GraphIndex; #[derive(Clone)] pub(crate) struct Path { diff --git a/src/agdb/graph_search/path_search.rs b/src/agdb/graph_search/path_search.rs new file mode 100644 index 00000000..bb9aebd6 --- /dev/null +++ b/src/agdb/graph_search/path_search.rs @@ -0,0 +1,294 @@ +use super::path::Path; +use super::path_search_handler::PathSearchHandler; +use crate::collections::bit_set::BitSet; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; +use std::cmp::Ordering; +use std::mem::swap; +use std::mem::take; + +pub(crate) struct PathSearch<'a, Data, Handler> +where + Data: GraphData, + Handler: PathSearchHandler, +{ + pub(crate) current_path: Path, + pub(crate) destination: GraphIndex, + pub(crate) graph: &'a GraphImpl, + pub(crate) handler: &'a Handler, + pub(crate) paths: Vec, + pub(crate) result: Vec, + pub(crate) visited: BitSet, +} + +impl<'a, Data, Handler> PathSearch<'a, Data, Handler> +where + Data: GraphData, + Handler: PathSearchHandler, +{ + pub(crate) fn new( + graph: &'a GraphImpl, + from: GraphIndex, + to: GraphIndex, + handler: &'a Handler, + ) -> Self { + Self { + current_path: Path { + elements: vec![], + cost: 0, + }, + destination: to, + graph, + handler, + paths: vec![Path { + elements: vec![from], + cost: 0, + }], + result: vec![], + visited: BitSet::new(), + } + } + + pub(crate) fn search(&mut self) -> Vec { + while !self.is_finished() { + self.sort_paths(); + self.process_last_path(); + } + + take(&mut self.result) + } + + fn expand_edge(&mut self, mut path: Path, index: &GraphIndex, node_index: &GraphIndex) { + let cost = self + .handler + .process(index, &(self.current_path.elements.len() as u64 + 1)); + + if cost != 0 && !self.visited.value(node_index.as_u64()) { + path.elements.push(index.clone()); + path.cost += cost; + self.expand_node(path, node_index); + } + } + + fn expand_node(&mut self, mut path: Path, index: &GraphIndex) { + let cost = self + .handler + .process(index, &(self.current_path.elements.len() as u64 + 1)); + + if cost != 0 { + path.elements.push(index.clone()); + path.cost += cost; + self.paths.push(path); + } + } + + fn expand(&mut self, index: &GraphIndex) { + let node = self + .graph + .node(index) + .expect("unexpected invalid node index"); + for edge in node.edge_iter_from() { + self.expand_edge(self.current_path.clone(), &edge.index(), &edge.index_to()); + } + } + + fn is_finished(&self) -> bool { + self.paths.is_empty() || !self.result.is_empty() + } + + fn process_path(&mut self) { + let index = self + .current_path + .elements + .last() + .map_or(GraphIndex::default(), |index| index.clone()); + self.process_index(&index); + } + + fn process_index(&mut self, index: &GraphIndex) { + if !self.visited.value(index.as_u64()) { + if index.value() == self.destination.value() { + swap(&mut self.result, &mut self.current_path.elements); + } else { + self.visited.insert(index.as_u64()); + self.expand(index); + } + } + } + + fn process_last_path(&mut self) { + self.current_path = self.paths.pop().unwrap_or(Path { + elements: vec![], + cost: 0, + }); + self.process_path(); + } + + fn sort_paths(&mut self) { + self.paths.sort_by(|left, right| { + let ordering = left.cost.cmp(&right.cost); + + if ordering == Ordering::Equal { + return left.elements.len().cmp(&right.elements.len()).reverse(); + } + + ordering.reverse() + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::graph::Graph; + use crate::graph_search::GraphSearch; + + struct Handler { + pub processor: fn(&GraphIndex, &u64) -> u64, + } + + impl Default for Handler { + fn default() -> Self { + Self { + processor: |_index: &GraphIndex, _distance: &u64| 1_u64, + } + } + } + + impl PathSearchHandler for Handler { + fn process(&self, index: &GraphIndex, distance: &u64) -> u64 { + (self.processor)(index, distance) + } + } + + #[test] + fn circular_path() { + let mut graph = Graph::new(); + let node = graph.insert_node().unwrap(); + let _edge = graph.insert_edge(&node, &node).unwrap(); + + let result = GraphSearch::from(&graph).path(&node, &node, &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn empty_graph() { + let graph = Graph::new(); + + let result = GraphSearch::from(&graph).path( + &GraphIndex::default(), + &GraphIndex::default(), + &Handler::default(), + ); + + assert_eq!(result, vec![]); + } + + #[test] + fn multi_edge_path() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let _edge2 = graph.insert_edge(&node1, &node2).unwrap(); + + let edge3 = graph.insert_edge(&node2, &node3).unwrap(); + let _edge4 = graph.insert_edge(&node2, &node3).unwrap(); + + let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); + + assert_eq!(result, vec![node1, edge1, node2, edge3, node3]); + } + + #[test] + fn origin_is_destination() { + let mut graph = Graph::new(); + let node = graph.insert_node().unwrap(); + + let result = GraphSearch::from(&graph).path(&node, &node, &Handler::default()); + + assert_eq!(result, vec![]); + } + + #[test] + fn short_circuit_path() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node3).unwrap(); + let _edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let _edge3 = graph.insert_edge(&node2, &node3).unwrap(); + + let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); + + assert_eq!(result, vec![node1, edge1, node3]); + } + + #[test] + fn single_path() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let edge1 = graph.insert_edge(&node1, &node2).unwrap(); + let edge2 = graph.insert_edge(&node2, &node3).unwrap(); + + let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); + + assert_eq!(result, vec![node1, edge1, node2, edge2, node3]); + } + + #[test] + fn skip_short_circuit_path() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let _edge1 = graph.insert_edge(&node1, &node3).unwrap(); + let edge2 = graph.insert_edge(&node1, &node2).unwrap(); + let edge3 = graph.insert_edge(&node2, &node3).unwrap(); + + let result = GraphSearch::from(&graph).path( + &node1, + &node3, + &Handler { + processor: |index: &GraphIndex, _distance: &u64| { + if index.value() == -4 { + return 0; + } + + 1 + }, + }, + ); + + assert_eq!(result, vec![node1, edge2, node2, edge3, node3]); + } + + #[test] + fn unconnected() { + let mut graph = Graph::new(); + + let node1 = graph.insert_node().unwrap(); + let node2 = graph.insert_node().unwrap(); + let node3 = graph.insert_node().unwrap(); + + let _edge1 = graph.insert_edge(&node1, &node2).unwrap(); + + let result = GraphSearch::from(&graph).path(&node1, &node3, &Handler::default()); + + assert_eq!(result, vec![]); + } +} diff --git a/crates/graph_search/src/path_search_handler.rs b/src/agdb/graph_search/path_search_handler.rs similarity index 69% rename from crates/graph_search/src/path_search_handler.rs rename to src/agdb/graph_search/path_search_handler.rs index e9d5358c..11828d5d 100644 --- a/crates/graph_search/src/path_search_handler.rs +++ b/src/agdb/graph_search/path_search_handler.rs @@ -1,4 +1,4 @@ -use agdb_graph::GraphIndex; +use crate::graph::graph_index::GraphIndex; pub trait PathSearchHandler { fn process(&self, index: &GraphIndex, distance: &u64) -> u64; diff --git a/crates/graph_search/src/search_control.rs b/src/agdb/graph_search/search_control.rs similarity index 80% rename from crates/graph_search/src/search_control.rs rename to src/agdb/graph_search/search_control.rs index 9725a6dc..87b209af 100644 --- a/crates/graph_search/src/search_control.rs +++ b/src/agdb/graph_search/search_control.rs @@ -1,3 +1,4 @@ +#[allow(dead_code)] pub enum SearchControl { Continue(bool), Finish(bool), diff --git a/crates/graph_search/src/search_handler.rs b/src/agdb/graph_search/search_handler.rs similarity index 55% rename from crates/graph_search/src/search_handler.rs rename to src/agdb/graph_search/search_handler.rs index e2648b58..17866fe7 100644 --- a/crates/graph_search/src/search_handler.rs +++ b/src/agdb/graph_search/search_handler.rs @@ -1,5 +1,5 @@ -use crate::search_control::SearchControl; -use agdb_graph::GraphIndex; +use super::search_control::SearchControl; +use crate::graph::graph_index::GraphIndex; pub trait SearchHandler { fn process(&self, index: &GraphIndex, distance: &u64) -> SearchControl; diff --git a/crates/graph_search/src/search_impl.rs b/src/agdb/graph_search/search_impl.rs similarity index 91% rename from crates/graph_search/src/search_impl.rs rename to src/agdb/graph_search/search_impl.rs index 20eb56b3..41cf2b71 100644 --- a/crates/graph_search/src/search_impl.rs +++ b/src/agdb/graph_search/search_impl.rs @@ -1,11 +1,11 @@ -use crate::search_index::SearchIndex; -use crate::search_iterator::SearchIterator; -use crate::SearchControl; -use crate::SearchHandler; -use agdb_bit_set::BitSet; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; +use super::search_control::SearchControl; +use super::search_handler::SearchHandler; +use super::search_index::SearchIndex; +use super::search_iterator::SearchIterator; +use crate::collections::bit_set::BitSet; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; use std::marker::PhantomData; use std::mem::swap; diff --git a/crates/graph_search/src/search_index.rs b/src/agdb/graph_search/search_index.rs similarity index 69% rename from crates/graph_search/src/search_index.rs rename to src/agdb/graph_search/search_index.rs index c15c12f1..78f42be9 100644 --- a/crates/graph_search/src/search_index.rs +++ b/src/agdb/graph_search/search_index.rs @@ -1,4 +1,4 @@ -use agdb_graph::GraphIndex; +use crate::graph::graph_index::GraphIndex; pub(crate) struct SearchIndex { pub(crate) index: GraphIndex, diff --git a/crates/graph_search/src/search_iterator.rs b/src/agdb/graph_search/search_iterator.rs similarity index 67% rename from crates/graph_search/src/search_iterator.rs rename to src/agdb/graph_search/search_iterator.rs index fcc068e5..5fd8a498 100644 --- a/crates/graph_search/src/search_iterator.rs +++ b/src/agdb/graph_search/search_iterator.rs @@ -1,7 +1,7 @@ -use crate::search_index::SearchIndex; -use agdb_graph::GraphData; -use agdb_graph::GraphImpl; -use agdb_graph::GraphIndex; +use super::search_index::SearchIndex; +use crate::graph::graph_data::GraphData; +use crate::graph::graph_impl::GraphImpl; +use crate::graph::graph_index::GraphIndex; pub(crate) trait SearchIterator { fn expand_edge(index: &GraphIndex, graph: &GraphImpl) -> GraphIndex; diff --git a/src/agdb/lib.rs b/src/agdb/lib.rs new file mode 100644 index 00000000..a056d9e0 --- /dev/null +++ b/src/agdb/lib.rs @@ -0,0 +1,20 @@ +mod collections; +mod commands; +mod db; +mod db_error; +mod graph; +mod graph_search; +mod query; +mod storage; +mod transaction; +mod utilities; + +#[cfg(test)] +mod test_utilities; + +pub use db::Db; +pub use db_error::DbError; +pub use query::Query; +pub use query::QueryError; +pub use query::QueryResult; +pub use transaction::Transaction; diff --git a/src/agdb/query.rs b/src/agdb/query.rs new file mode 100644 index 00000000..56327a98 --- /dev/null +++ b/src/agdb/query.rs @@ -0,0 +1,8 @@ +mod query_error; +mod query_result; + +pub use query_error::QueryError; +pub use query_result::QueryResult; + +#[derive(Default)] +pub struct Query {} diff --git a/src/query_error.rs b/src/agdb/query/query_error.rs similarity index 100% rename from src/query_error.rs rename to src/agdb/query/query_error.rs diff --git a/src/query_result.rs b/src/agdb/query/query_result.rs similarity index 100% rename from src/query_result.rs rename to src/agdb/query/query_result.rs diff --git a/crates/storage/src/storage.rs b/src/agdb/storage.rs similarity index 77% rename from crates/storage/src/storage.rs rename to src/agdb/storage.rs index a4e3029a..9427f70b 100644 --- a/crates/storage/src/storage.rs +++ b/src/agdb/storage.rs @@ -1,6 +1,16 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage_index::StorageIndex; +pub mod storage_file; +pub mod storage_index; + +mod storage_data; +mod storage_data_file; +mod storage_impl; +mod storage_record; +mod storage_records; +mod write_ahead_log; + +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::utilities::serialize::Serialize; pub trait Storage { fn commit(&mut self) -> Result<(), DbError>; diff --git a/crates/storage/src/storage_data.rs b/src/agdb/storage/storage_data.rs similarity index 84% rename from crates/storage/src/storage_data.rs rename to src/agdb/storage/storage_data.rs index 2caba4b7..9c9c7cc2 100644 --- a/crates/storage/src/storage_data.rs +++ b/src/agdb/storage/storage_data.rs @@ -1,7 +1,7 @@ -use agdb_db_error::DbError; -use agdb_storage_index::StorageIndex; -use agdb_storage_index::StorageRecord; -use agdb_write_ahead_log::WriteAheadLogRecord; +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::storage::storage_record::StorageRecord; +use crate::storage::write_ahead_log::WriteAheadLogRecord; use std::io::SeekFrom; pub trait StorageData { diff --git a/crates/storage/src/storage_data_file.rs b/src/agdb/storage/storage_data_file.rs similarity index 72% rename from crates/storage/src/storage_data_file.rs rename to src/agdb/storage/storage_data_file.rs index 2c88a69e..ce29a611 100644 --- a/crates/storage/src/storage_data_file.rs +++ b/src/agdb/storage/storage_data_file.rs @@ -1,10 +1,10 @@ -use crate::storage_data::StorageData; -use agdb_db_error::DbError; -use agdb_storage_index::StorageIndex; -use agdb_storage_index::StorageRecord; -use agdb_storage_index::StorageRecords; -use agdb_write_ahead_log::WriteAheadLog; -use agdb_write_ahead_log::WriteAheadLogRecord; +use crate::db_error::DbError; +use crate::storage::storage_data::StorageData; +use crate::storage::storage_index::StorageIndex; +use crate::storage::storage_record::StorageRecord; +use crate::storage::storage_records::StorageRecords; +use crate::storage::write_ahead_log::WriteAheadLog; +use crate::storage::write_ahead_log::WriteAheadLogRecord; use std::fs::File; use std::io::Read; use std::io::Seek; @@ -93,3 +93,22 @@ impl StorageData for StorageDataFile { Ok(Write::write_all(&mut self.file, bytes)?) } } + +impl TryFrom for StorageDataFile { + type Error = DbError; + + fn try_from(filename: String) -> Result { + Ok(StorageDataFile { + file: std::fs::OpenOptions::new() + .write(true) + .create(true) + .read(true) + .open(&filename)?, + filename: filename.clone(), + records: StorageRecords::default(), + wal: WriteAheadLog::try_from(&filename)?, + wal_filename: filename, + transactions: 0, + }) + } +} diff --git a/src/agdb/storage/storage_file.rs b/src/agdb/storage/storage_file.rs new file mode 100644 index 00000000..0a75f117 --- /dev/null +++ b/src/agdb/storage/storage_file.rs @@ -0,0 +1,749 @@ +use crate::db_error::DbError; +use crate::storage::storage_data_file::StorageDataFile; +use crate::storage::storage_impl::StorageImpl; + +pub type StorageFile = StorageImpl; + +impl TryFrom for StorageFile { + type Error = DbError; + + fn try_from(filename: String) -> Result { + let mut storage = StorageFile { + data: StorageDataFile::try_from(filename)?, + }; + + storage.apply_wal()?; + storage.read_records()?; + + Ok(storage) + } +} + +impl TryFrom<&str> for StorageFile { + type Error = DbError; + + fn try_from(filename: &str) -> Result { + Self::try_from(filename.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::storage_index::StorageIndex; + use crate::storage::Storage; + use crate::test_utilities::test_file::TestFile; + use crate::utilities::serialize::Serialize; + use std::fs::metadata; + + #[test] + fn bad_file() { + assert!(StorageFile::try_from("/a/").is_err()); + } + + #[test] + fn insert() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&10_i64); + + assert_eq!(index, Ok(StorageIndex::from(1_i64))); + } + + #[test] + fn insert_at() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size()) as u64; + storage.insert_at(&index, offset, &10_i64).unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 10_i64, 3_i64] + ); + } + + #[test] + fn insert_at_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + assert_eq!( + storage.insert_at(&StorageIndex::from(1_i64), 8, &1_i64), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn insert_at_value_end() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size() * 3) as u64; + storage.insert_at(&index, 0, &4_u64).unwrap(); + storage.insert_at(&index, offset, &10_i64).unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 2_i64, 3_i64, 10_i64] + ); + } + + #[test] + fn insert_at_beyond_end() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size() * 4) as u64; + storage.insert_at(&index, 0, &5_u64).unwrap(); + storage.insert_at(&index, offset, &10_i64).unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 2_i64, 3_i64, 0_i64, 10_i64] + ); + } + + #[test] + fn insert_at_bytes() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size()) as u64; + let size = i64::serialized_size() * 2; + + storage + .insert_at(&index, offset, &vec![0_u8; size as usize]) + .unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 0_i64, 0_i64] + ); + } + + #[test] + fn move_at() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset_from = (u64::serialized_size() + i64::serialized_size() * 2) as u64; + let offset_to = (u64::serialized_size() + i64::serialized_size()) as u64; + let size = u64::serialized_size(); + + storage + .move_at(&index, offset_from, offset_to, size) + .unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 3_i64, 0_i64] + ) + } + + #[test] + fn move_at_beyond_end() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset_from = (u64::serialized_size() + i64::serialized_size()) as u64; + let offset_to = (u64::serialized_size() + i64::serialized_size() * 4) as u64; + let size = u64::serialized_size(); + + storage + .move_at(&index, offset_from, offset_to, size) + .unwrap(); + + storage.insert_at(&index, 0, &5_u64).unwrap(); + + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 0_i64, 3_i64, 0_i64, 2_i64] + ) + } + + #[test] + fn move_at_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + assert_eq!( + storage.move_at(&StorageIndex::from(1_i64), 0, 1, 10), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn move_at_same_offset() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + + assert_eq!(storage.move_at(&index, 0, 0, 10), Ok(())); + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 2_i64, 3_i64] + ); + } + + #[test] + fn move_at_size_out_of_bounds() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + let offset_from = (u64::serialized_size() + i64::serialized_size() * 3) as u64; + let offset_to = (u64::serialized_size() + i64::serialized_size() * 2) as u64; + let size = (u64::serialized_size() * 10) as u64; + + assert_eq!( + storage.move_at(&index, offset_from, offset_to, size), + Err(DbError::from("move size out of bounds")) + ); + } + + #[test] + fn move_at_zero_size() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&vec![1_i64, 2_i64, 3_i64]).unwrap(); + + assert_eq!(storage.move_at(&index, 0, 1, 0), Ok(())); + assert_eq!( + storage.value::>(&index).unwrap(), + vec![1_i64, 2_i64, 3_i64] + ); + } + + #[test] + fn remove() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + let index = storage.insert(&1_i64).unwrap(); + storage.remove(&index).unwrap(); + + assert_eq!( + storage.value::(&index), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn remove_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + assert_eq!( + storage.remove(&StorageIndex::from(1_i64)), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn resize_at_end_does_not_move() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&1_i64).unwrap(); + let size = storage.size().unwrap(); + let value_size = storage.value_size(&index).unwrap(); + + storage.resize_value(&index, value_size + 8).unwrap(); + + assert_eq!(storage.size(), Ok(size + 8)); + } + + #[test] + fn resize_value_greater() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + let expected_size = i64::serialized_size(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + + storage.resize_value(&index, expected_size * 2).unwrap(); + + assert_eq!(storage.value_size(&index), Ok(expected_size * 2)); + } + + #[test] + fn resize_value_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!( + storage.resize_value(&StorageIndex::from(1_i64), 1), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn resize_value_same() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + let expected_size = i64::serialized_size(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + + storage.resize_value(&index, expected_size).unwrap(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + } + + #[test] + fn resize_value_smaller() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + let expected_size = i64::serialized_size(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + + storage.resize_value(&index, expected_size / 2).unwrap(); + + assert_eq!(storage.value_size(&index), Ok(expected_size / 2)); + } + + #[test] + fn resize_value_zero() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + let expected_size = i64::serialized_size(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + + assert_eq!( + storage.resize_value(&index, 0), + Err(DbError::from("value size cannot be 0")) + ); + } + + #[test] + fn resize_value_resizes_file() { + let test_file = TestFile::new(); + + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + let index = storage.insert(&3_i64).unwrap(); + let size = u64::serialized_size() + i64::serialized_size() * 3; + storage.resize_value(&index, size).unwrap(); + + assert_eq!(storage.value::>(&index), Ok(vec![0_i64; 3])); + } + + #[test] + fn resize_value_invalidates_original_position() { + let test_file = TestFile::new(); + + let index; + + { + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + index = storage.insert(&10_i64).unwrap(); + storage.insert(&5_i64).unwrap(); + storage.resize_value(&index, 1).unwrap(); + storage.remove(&index).unwrap(); + } + + let mut storage = StorageFile::try_from(test_file.file_name().as_str()).unwrap(); + + assert_eq!( + storage.value::(&index), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn restore_from_file() { + let test_file = TestFile::new(); + let value1 = vec![1_i64, 2_i64, 3_i64]; + let value2 = 64_u64; + let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; + let index1; + let index2; + let index3; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + index1 = storage.insert(&value1).unwrap(); + index2 = storage.insert(&value2).unwrap(); + index3 = storage.insert(&value3).unwrap(); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!(storage.value::>(&index1), Ok(value1)); + assert_eq!(storage.value::(&index2), Ok(value2)); + assert_eq!(storage.value::>(&index3), Ok(value3)); + } + + #[test] + fn restore_from_file_with_removed_index() { + let test_file = TestFile::new(); + let value1 = vec![1_i64, 2_i64, 3_i64]; + let value2 = 64_u64; + let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; + let index1; + let index2; + let index3; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + index1 = storage.insert(&value1).unwrap(); + index2 = storage.insert(&value2).unwrap(); + index3 = storage.insert(&value3).unwrap(); + storage.remove(&index2).unwrap(); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!(storage.value::>(&index1), Ok(value1)); + assert_eq!( + storage.value::(&StorageIndex::default()), + Err(DbError::from(format!("index '{}' not found", 0))) + ); + assert_eq!( + storage.value::(&index2), + Err(DbError::from(format!( + "index '{}' not found", + index2.value() + ))) + ); + assert_eq!(storage.value::>(&index3), Ok(value3)); + } + + #[test] + fn restore_from_file_with_all_indexes_removed() { + let test_file = TestFile::new(); + let value1 = vec![1_i64, 2_i64, 3_i64]; + let value2 = 64_u64; + let value3 = vec![4_i64, 5_i64, 6_i64, 7_i64, 8_i64, 9_i64, 10_i64]; + let index1; + let index2; + let index3; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + index1 = storage.insert(&value1).unwrap(); + index2 = storage.insert(&value2).unwrap(); + index3 = storage.insert(&value3).unwrap(); + storage.remove(&index1).unwrap(); + storage.remove(&index2).unwrap(); + storage.remove(&index3).unwrap(); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!( + storage.value::(&StorageIndex::default()), + Err(DbError::from(format!("index '{}' not found", 0))) + ); + assert_eq!( + storage.value::>(&index1), + Err(DbError::from(format!( + "index '{}' not found", + index1.value() + ))) + ); + assert_eq!( + storage.value::(&index2), + Err(DbError::from(format!( + "index '{}' not found", + index2.value() + ))) + ); + assert_eq!( + storage.value::>(&index3), + Err(DbError::from(format!( + "index '{}' not found", + index3.value() + ))) + ); + } + + #[test] + fn shrink_to_fit() { + let test_file = TestFile::new(); + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + let index1 = storage.insert(&1_i64).unwrap(); + let index2 = storage.insert(&2_i64).unwrap(); + let index3 = storage.insert(&3_i64).unwrap(); + storage.remove(&index2).unwrap(); + storage.shrink_to_fit().unwrap(); + + let actual_size = metadata(test_file.file_name()).unwrap().len(); + let expected_size = (u64::serialized_size() * 2) * 2 + i64::serialized_size() * 2; + + assert_eq!(actual_size, expected_size); + assert_eq!(storage.value(&index1), Ok(1_i64)); + assert_eq!(storage.value(&index3), Ok(3_i64)); + } + + #[test] + fn shrink_to_fit_no_change() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + let index1 = storage.insert(&1_i64).unwrap(); + let index2 = storage.insert(&2_i64).unwrap(); + let index3 = storage.insert(&3_i64).unwrap(); + + let actual_size = metadata(test_file.file_name()).unwrap().len(); + + storage.shrink_to_fit().unwrap(); + + assert_eq!(actual_size, metadata(test_file.file_name()).unwrap().len()); + assert_eq!(storage.value(&index1), Ok(1_i64)); + assert_eq!(storage.value(&index2), Ok(2_i64)); + assert_eq!(storage.value(&index3), Ok(3_i64)); + } + + #[test] + fn shrink_to_fit_uncommitted() { + let test_file = TestFile::new(); + + let expected_size; + let index1; + let index2; + let index3; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + index1 = storage.insert(&1_i64).unwrap(); + index2 = storage.insert(&2_i64).unwrap(); + index3 = storage.insert(&3_i64).unwrap(); + storage.remove(&index2).unwrap(); + + expected_size = metadata(test_file.file_name()).unwrap().len(); + + storage.transaction(); + storage.shrink_to_fit().unwrap(); + } + + let actual_size = metadata(test_file.file_name()).unwrap().len(); + assert_eq!(actual_size, expected_size); + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + assert_eq!(storage.value(&index1), Ok(1_i64)); + assert_eq!( + storage.value::(&index2), + Err(DbError::from(format!( + "index '{}' not found", + index2.value() + ))) + ); + assert_eq!(storage.value(&index3), Ok(3_i64)); + } + + #[test] + fn transaction_commit() { + let test_file = TestFile::from("file_storage-transaction_commit.agdb"); + let index; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + storage.transaction(); + index = storage.insert(&1_i64).unwrap(); + storage.commit().unwrap(); + assert_eq!(storage.value::(&index), Ok(1_i64)); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + assert_eq!(storage.value::(&index), Ok(1_i64)); + } + + #[test] + fn transaction_commit_no_transaction() { + let test_file = TestFile::from("file_storage-transaction_commit_no_transaction.agdb"); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + assert_eq!(storage.commit(), Ok(())); + } + + #[test] + fn transaction_unfinished() { + let test_file = TestFile::new(); + let index; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + storage.transaction(); + index = storage.insert(&1_i64).unwrap(); + assert_eq!(storage.value::(&index), Ok(1_i64)); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + assert_eq!( + storage.value::(&index), + Err(DbError::from(format!( + "index '{}' not found", + index.value() + ))) + ); + } + + #[test] + fn transaction_nested_unfinished() { + let test_file = TestFile::new(); + let index; + + { + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + storage.transaction(); + storage.transaction(); + index = storage.insert(&1_i64).unwrap(); + assert_eq!(storage.value::(&index), Ok(1_i64)); + storage.commit().unwrap(); + } + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + assert_eq!( + storage.value::(&index), + Err(DbError::from(format!( + "index '{}' not found", + index.value() + ))) + ); + } + + #[test] + fn value() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + let index = storage.insert(&10_i64).unwrap(); + + assert_eq!(storage.value::(&index), Ok(10_i64)); + } + + #[test] + fn value_at() { + let test_file = TestFile::new(); + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + let data = vec![1_i64, 2_i64, 3_i64]; + + let index = storage.insert(&data).unwrap(); + let offset = u64::serialized_size() + i64::serialized_size(); + + assert_eq!(storage.value_at::(&index, offset), Ok(2_i64)); + } + + #[test] + fn value_at_dynamic_size() { + let test_file = TestFile::new(); + + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + let data = vec![2_i64, 1_i64, 2_i64]; + + let index = storage.insert(&data).unwrap(); + let offset = u64::serialized_size(); + + assert_eq!( + storage.value_at::>(&index, offset), + Ok(vec![1_i64, 2_i64]) + ); + } + + #[test] + fn value_at_of_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!( + storage.value_at::(&StorageIndex::from(1_i64), 8), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn value_at_out_of_bounds() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let data = vec![1_i64, 2_i64]; + let index = storage.insert(&data).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size() * 2) as u64; + + assert_eq!( + storage.value_at::(&index, offset), + Err(DbError::from("deserialization error: value out of bounds")) + ); + } + + #[test] + fn value_at_offset_overflow() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let data = vec![1_i64, 2_i64]; + let index = storage.insert(&data).unwrap(); + let offset = (u64::serialized_size() + i64::serialized_size() * 3) as u64; + + assert_eq!( + storage.value_at::(&index, offset), + Err(DbError::from("deserialization error: offset out of bounds")) + ); + } + + #[test] + fn value_of_missing_index() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!( + storage.value::(&StorageIndex::from(1_i64)), + Err(DbError::from("index '1' not found")) + ); + } + + #[test] + fn value_out_of_bounds() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + + assert_eq!( + storage.value::>(&index), + Err(DbError::from("i64 deserialization error: out of bounds")) + ); + } + + #[test] + fn value_size() { + let test_file = TestFile::new(); + let mut storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + let index = storage.insert(&10_i64).unwrap(); + let expected_size = i64::serialized_size(); + + assert_eq!(storage.value_size(&index), Ok(expected_size)); + } + + #[test] + fn value_size_of_missing_index() { + let test_file = TestFile::new(); + let storage = StorageFile::try_from(test_file.file_name().clone()).unwrap(); + + assert_eq!( + storage.value_size(&StorageIndex::from(1_i64)), + Err(DbError::from("index '1' not found")) + ); + } +} diff --git a/crates/storage/src/storage_impl.rs b/src/agdb/storage/storage_impl.rs similarity index 68% rename from crates/storage/src/storage_impl.rs rename to src/agdb/storage/storage_impl.rs index 5dc20869..1c87c588 100644 --- a/crates/storage/src/storage_impl.rs +++ b/src/agdb/storage/storage_impl.rs @@ -1,9 +1,10 @@ -use crate::storage_data::StorageData; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use agdb_storage_index::StorageIndex; -use agdb_storage_index::StorageRecord; -use agdb_write_ahead_log::WriteAheadLogRecord; +use crate::db_error::DbError; +use crate::storage::storage_data::StorageData; +use crate::storage::storage_index::StorageIndex; +use crate::storage::storage_record::StorageRecord; +use crate::storage::write_ahead_log::WriteAheadLogRecord; +use crate::storage::Storage; +use crate::utilities::serialize::Serialize; use std::cmp::max; use std::cmp::min; use std::io::SeekFrom; @@ -311,3 +312,132 @@ impl StorageImpl { self.data.write_all(bytes) } } + +impl Drop for StorageImpl { + fn drop(&mut self) { + if self.apply_wal().is_ok() { + let _ = self.data.clear_wal(); + } + } +} + +impl Storage for StorageImpl { + fn commit(&mut self) -> Result<(), DbError> { + if self.data.end_transaction() { + self.data.clear_wal()?; + } + + Ok(()) + } + + fn insert(&mut self, value: &V) -> Result { + self.transaction(); + let position = self.size()?; + let bytes = value.serialize(); + let record = self.data.create_record(position, bytes.len() as u64); + + self.append(&record.serialize())?; + self.append(&bytes)?; + self.commit()?; + + Ok(record.index) + } + + fn insert_at( + &mut self, + index: &StorageIndex, + offset: u64, + value: &V, + ) -> Result<(), DbError> { + self.transaction(); + let mut record = self.data.record(index)?; + let bytes = V::serialize(value); + self.ensure_record_size(&mut record, index, offset, bytes.len() as u64)?; + self.write(Self::value_position(record.position, offset), &bytes)?; + self.commit() + } + + fn move_at( + &mut self, + index: &StorageIndex, + offset_from: u64, + offset_to: u64, + size: u64, + ) -> Result<(), DbError> { + if offset_from == offset_to || size == 0 { + return Ok(()); + } + + let mut record = self.data.record(index)?; + Self::validate_move_size(offset_from, size, record.size)?; + self.transaction(); + self.ensure_record_size(&mut record, index, offset_to, size)?; + self.move_bytes( + Self::value_position_u64(record.position, offset_from), + Self::value_position_u64(record.position, offset_to), + size, + )?; + self.commit()?; + + Ok(()) + } + + fn remove(&mut self, index: &StorageIndex) -> Result<(), DbError> { + self.transaction(); + let position = self.data.record(index)?.position; + self.invalidate_record(position)?; + self.data.remove_index(index); + self.commit() + } + + fn resize_value(&mut self, index: &StorageIndex, new_size: u64) -> Result<(), DbError> { + if new_size == 0 { + return Err(DbError::from("value size cannot be 0")); + } + + let mut record = self.data.record(index)?; + + if record.size != new_size { + self.transaction(); + self.resize_record(index, new_size, new_size, &mut record)?; + self.commit()?; + } + + Ok(()) + } + + fn shrink_to_fit(&mut self) -> Result<(), DbError> { + self.transaction(); + let indexes = self.data.indexes_by_position(); + let size = self.shrink_indexes(indexes)?; + self.truncate(size)?; + self.commit() + } + + fn size(&mut self) -> Result { + self.data.seek(SeekFrom::End(0)) + } + + fn transaction(&mut self) { + self.data.begin_transaction(); + } + + fn value(&mut self, index: &StorageIndex) -> Result { + let record = self.data.record(index)?; + V::deserialize(&self.read(Self::value_position(record.position, 0), record.size)?) + } + + fn value_at(&mut self, index: &StorageIndex, offset: u64) -> Result { + let record = self.data.record(index)?; + let bytes = self.read( + Self::value_position(record.position, offset), + Self::value_read_size::(record.size, offset)?, + ); + + V::deserialize(&bytes?) + } + + fn value_size(&self, index: &StorageIndex) -> Result { + Ok(self.data.record(index)?.size) + } +} diff --git a/src/agdb/storage/storage_index.rs b/src/agdb/storage/storage_index.rs new file mode 100644 index 00000000..4f907581 --- /dev/null +++ b/src/agdb/storage/storage_index.rs @@ -0,0 +1,104 @@ +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct StorageIndex { + pub(crate) index: i64, +} + +impl StorageIndex { + pub fn as_u64(&self) -> u64 { + self.value() as u64 + } + + pub fn as_usize(&self) -> usize { + self.value() as usize + } + + pub fn is_valid(&self) -> bool { + 0 < self.index + } + + pub fn value(&self) -> i64 { + self.index + } +} + +impl From for StorageIndex { + fn from(index: i64) -> Self { + Self { index } + } +} + +impl From for StorageIndex { + fn from(index: u64) -> Self { + Self { + index: index as i64, + } + } +} + +impl From for StorageIndex { + fn from(index: usize) -> Self { + Self { + index: index as i64, + } + } +} + +impl Serialize for StorageIndex { + fn deserialize(bytes: &[u8]) -> Result { + Ok(Self { + index: i64::deserialize(bytes)?, + }) + } + + fn serialize(&self) -> Vec { + self.index.serialize() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_valid() { + assert!(!StorageIndex::default().is_valid()); + assert!(!StorageIndex::from(-1_i64).is_valid()); + + assert!(StorageIndex::from(1_i64).is_valid()); + assert!(StorageIndex::from(100_i64).is_valid()); + } + + #[test] + fn ordering() { + let mut indexes = vec![ + StorageIndex::default(), + StorageIndex::from(100_i64), + StorageIndex::from(-1_i64), + StorageIndex::from(1_i64), + ]; + + indexes.sort(); + + assert_eq!( + indexes, + vec![ + StorageIndex::from(-1_i64), + StorageIndex::default(), + StorageIndex::from(1_i64), + StorageIndex::from(100_i64), + ] + ) + } + + #[test] + fn serialize() { + let index = StorageIndex::from(1_i64); + let bytes = index.serialize(); + let other = StorageIndex::deserialize(&bytes).unwrap(); + + assert_eq!(index, other); + } +} diff --git a/src/agdb/storage/storage_record.rs b/src/agdb/storage/storage_record.rs new file mode 100644 index 00000000..63e8400f --- /dev/null +++ b/src/agdb/storage/storage_record.rs @@ -0,0 +1,65 @@ +use crate::db_error::DbError; +use crate::storage::storage_index::StorageIndex; +use crate::utilities::serialize::Serialize; +use std::mem::size_of; + +#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] +pub struct StorageRecord { + pub index: StorageIndex, + pub position: u64, + pub size: u64, +} + +impl Serialize for StorageRecord { + fn deserialize(bytes: &[u8]) -> Result { + Ok(StorageRecord { + index: StorageIndex::deserialize(bytes)?, + position: 0, + size: u64::deserialize(&bytes[(StorageIndex::serialized_size() as usize)..])?, + }) + } + + fn serialize(&self) -> Vec { + let mut bytes = self.index.serialize(); + bytes.extend(self.size.serialize()); + + bytes + } + + fn serialized_size() -> u64 { + StorageIndex::serialized_size() + size_of::() as u64 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cmp::Ordering; + + #[test] + fn derived_from_debug() { + let record = StorageRecord::default(); + format!("{:?}", record); + } + + #[test] + fn derived_from_ord() { + let record = StorageRecord::default(); + assert_eq!(record.cmp(&record), Ordering::Equal); + } + + #[test] + fn serialize() { + let bytes = StorageRecord { + index: StorageIndex::from(1_i64), + position: 64, + size: 128, + } + .serialize(); + let record = StorageRecord::deserialize(&bytes).unwrap(); + + assert_eq!(record.index, StorageIndex::from(1_i64)); + assert_eq!(record.position, 0); + assert_eq!(record.size, 128); + } +} diff --git a/src/agdb/storage/storage_records.rs b/src/agdb/storage/storage_records.rs new file mode 100644 index 00000000..8200765c --- /dev/null +++ b/src/agdb/storage/storage_records.rs @@ -0,0 +1,379 @@ +use crate::storage::storage_index::StorageIndex; +use crate::storage::storage_record::StorageRecord; + +pub struct StorageRecords { + pub(crate) records: Vec, +} + +impl StorageRecords { + pub fn create(&mut self, position: u64, size: u64) -> StorageRecord { + let index; + + if let Some(free_index) = self.free_index() { + index = free_index.as_usize(); + self.records[index] = StorageRecord { + index: free_index, + position, + size, + }; + } else { + index = self.records.len(); + self.records.push(StorageRecord { + index: StorageIndex::from(index), + position, + size, + }); + } + + self.records[index].clone() + } + + pub fn get(&self, index: &StorageIndex) -> Option<&StorageRecord> { + if let Some(record) = self.records.get(index.as_usize()) { + if record.size != 0 { + return Some(record); + } + } + + None + } + + pub fn get_mut(&mut self, index: &StorageIndex) -> Option<&mut StorageRecord> { + if let Some(record) = self.records.get_mut(index.as_usize()) { + if record.size != 0 { + return Some(record); + } + } + + None + } + + pub fn indexes_by_position(&self) -> Vec { + let mut indexes = Vec::::new(); + + for index in 1..self.records.len() { + if self.records[index].size != 0 { + indexes.push(StorageIndex::from(index)); + } + } + + indexes.sort_by(|left, right| { + self.records[left.as_usize()] + .position + .cmp(&self.records[right.as_usize()].position) + }); + + indexes + } + + pub fn remove(&mut self, index: &StorageIndex) { + if let Some(_record) = self.get_mut(index) { + self.add_free_index(index); + } + } + + pub(crate) fn add_free_index(&mut self, index: &StorageIndex) { + self.records[index.as_usize()].position = self.records[0].position; + self.records[index.as_usize()].size = 0; + self.records[0].position = index.as_u64(); + } + + fn free_index(&mut self) -> Option { + let free = self.records[0].position; + + if free != 0 { + self.records[0].position = self.records[free as usize].position; + return Some(StorageIndex::from(free)); + } + + None + } +} + +impl Default for StorageRecords { + fn default() -> Self { + Self { + records: vec![StorageRecord::default()], + } + } +} + +impl From> for StorageRecords { + fn from(mut records: Vec) -> Self { + records.sort(); + + let mut file_records = StorageRecords::default(); + + if let Some(last) = records.last() { + if last.index.is_valid() { + file_records = StorageRecords { + records: vec![StorageRecord::default(); last.index.as_usize() + 1], + }; + } + } + + let mut last_index = 0; + + for record in records { + if !record.index.is_valid() { + continue; + } + + for index in (last_index + 1)..record.index.value() { + file_records.add_free_index(&StorageIndex::from(index)); + } + + file_records.records[record.index.as_usize()].position = record.position; + file_records.records[record.index.as_usize()].size = record.size; + last_index = record.index.value(); + } + + file_records + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn create() { + let mut file_records = StorageRecords::default(); + + let record = file_records.create(1, 4); + + assert_eq!( + record, + StorageRecord { + index: StorageIndex::from(1_i64), + position: 1, + size: 4 + } + ); + } + + #[test] + fn default_constructed() { + let _records = StorageRecords::default(); + } + + #[test] + fn from_records() { + let index1 = StorageIndex::from(2_i64); + let index2 = StorageIndex::from(1_i64); + let index3 = StorageIndex::from(3_i64); + + let file_records = StorageRecords::from(vec![ + StorageRecord { + index: index1.clone(), + position: 8, + size: 16, + }, + StorageRecord { + index: index2.clone(), + position: 24, + size: 16, + }, + StorageRecord { + index: index3.clone(), + position: 40, + size: 16, + }, + ]); + + assert_eq!( + file_records.get(&index1), + Some(&StorageRecord { + index: StorageIndex::default(), + position: 8, + size: 16 + }) + ); + assert_eq!( + file_records.get(&index2), + Some(&StorageRecord { + index: StorageIndex::default(), + position: 24, + size: 16 + }) + ); + assert_eq!( + file_records.get(&index3), + Some(&StorageRecord { + index: StorageIndex::default(), + position: 40, + size: 16 + }) + ); + } + + #[test] + fn from_records_with_index_gaps() { + let record1 = StorageRecord { + index: StorageIndex::from(5_i64), + position: 24, + size: 16, + }; + let record2 = StorageRecord { + index: StorageIndex::from(1_i64), + position: 40, + size: 16, + }; + let record3 = StorageRecord { + index: StorageIndex::from(2_i64), + position: 40, + size: 16, + }; + + let mut file_records = StorageRecords::from(vec![record1, record2, record3]); + + let record1 = file_records.create(2, 2); + let record2 = file_records.create(4, 4); + let record3 = file_records.create(6, 6); + + assert_eq!( + record1, + StorageRecord { + index: StorageIndex::from(4_i64), + position: 2, + size: 2 + } + ); + assert_eq!( + record2, + StorageRecord { + index: StorageIndex::from(3_i64), + position: 4, + size: 4 + } + ); + assert_eq!( + record3, + StorageRecord { + index: StorageIndex::from(6_i64), + position: 6, + size: 6 + } + ); + } + + #[test] + fn from_records_with_removed_index() { + let record1 = StorageRecord { + index: StorageIndex::from(1_i64), + position: 24, + size: 16, + }; + let record2 = StorageRecord { + index: StorageIndex::from(-2_i64), + position: 40, + size: 16, + }; + let record3 = StorageRecord { + index: StorageIndex::from(3_i64), + position: 40, + size: 16, + }; + + let file_records = StorageRecords::from(vec![record1, record2, record3]); + + assert_eq!(file_records.get(&StorageIndex::default()), None); + } + + #[test] + fn get() { + let mut file_records = StorageRecords::default(); + let position = 32_u64; + let size = 64_u64; + + let record = file_records.create(position, size); + let expected_record = StorageRecord { + index: StorageIndex::from(1_i64), + position, + size, + }; + + assert_eq!(file_records.get(&record.index), Some(&expected_record)); + } + + #[test] + fn get_mut() { + let mut file_records = StorageRecords::default(); + let position = 32_u64; + let size = 64_u64; + + let record = file_records.create(position, size); + let mut expected_record = StorageRecord { + index: StorageIndex::from(1_i64), + position, + size, + }; + + assert_eq!( + file_records.get_mut(&record.index), + Some(&mut expected_record) + ); + } + + #[test] + fn get_mut_invalid_index() { + let mut file_records = StorageRecords::default(); + + assert_eq!(file_records.get_mut(&StorageIndex::from(-1_i64)), None); + } + + #[test] + fn get_mut_zero_index() { + let mut file_records = StorageRecords::default(); + + assert_eq!(file_records.get_mut(&StorageIndex::default()), None); + } + + #[test] + fn get_zero_index() { + let file_records = StorageRecords::default(); + + assert_eq!(file_records.get(&StorageIndex::default()), None); + } + + #[test] + fn indexes_by_position() { + let mut file_records = StorageRecords::default(); + let index1 = file_records.create(30, 8).index; + let index2 = file_records.create(20, 8).index; + let index3 = file_records.create(10, 8).index; + file_records.remove(&index2); + + assert_eq!(file_records.indexes_by_position(), vec![index3, index1]); + } + + #[test] + fn remove() { + let mut file_records = StorageRecords::default(); + let record = file_records.create(8u64, 16u64); + + file_records.remove(&record.index); + + assert_eq!(file_records.get(&record.index), None); + } + + #[test] + fn remove_invalid_index() { + let mut file_records = StorageRecords::default(); + let record = file_records.create(16_u64, 48_u64); + + file_records.remove(&StorageIndex::from(-1_i64)); + + assert_eq!(file_records.get(&record.index), Some(&record)); + } + + #[test] + fn reuse_indexes() { + let mut file_records = StorageRecords::default(); + let record = file_records.create(8u64, 16u64); + file_records.remove(&record.index); + let other = file_records.create(16u64, 32u64); + + assert_eq!(record.index, other.index); + } +} diff --git a/src/agdb/storage/write_ahead_log.rs b/src/agdb/storage/write_ahead_log.rs new file mode 100644 index 00000000..5adda5c2 --- /dev/null +++ b/src/agdb/storage/write_ahead_log.rs @@ -0,0 +1,176 @@ +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct WriteAheadLogRecord { + pub position: u64, + pub bytes: Vec, +} + +pub struct WriteAheadLog { + file: File, +} + +impl WriteAheadLog { + pub fn clear(&mut self) -> Result<(), DbError> { + Ok(self.file.set_len(0)?) + } + + pub fn insert(&mut self, record: WriteAheadLogRecord) -> Result<(), DbError> { + self.file.seek(SeekFrom::End(0))?; + self.file.write_all(&record.position.serialize())?; + self.file + .write_all(&(record.bytes.len() as u64).serialize())?; + self.file.write_all(&record.bytes)?; + + Ok(()) + } + + pub fn records(&mut self) -> Result, DbError> { + let mut records = Vec::::new(); + let size = self.file.seek(SeekFrom::End(0))?; + self.file.seek(SeekFrom::Start(0))?; + + while self.file.seek(SeekFrom::Current(0))? < size { + records.push(Self::read_record(&mut self.file)?); + } + + Ok(records) + } + + fn read_exact(file: &mut File, size: u64) -> Result, DbError> { + let mut buffer = vec![0_u8; size as usize]; + file.read_exact(&mut buffer)?; + Ok(buffer) + } + + fn read_record(file: &mut File) -> Result { + let position = u64::deserialize(&Self::read_exact(file, u64::serialized_size())?)?; + let size = u64::deserialize(&Self::read_exact(file, u64::serialized_size())?)?; + + Ok(WriteAheadLogRecord { + position, + bytes: Self::read_exact(file, size)?, + }) + } + + pub(crate) fn wal_filename(filename: &str) -> String { + let pos; + + if let Some(slash) = filename.rfind('/') { + pos = slash + 1; + } else if let Some(backslash) = filename.rfind('\\') { + pos = backslash + 1 + } else { + pos = 0; + } + + let mut copy = filename.to_owned(); + copy.insert(pos, '.'); + copy + } +} + +impl TryFrom<&String> for WriteAheadLog { + type Error = DbError; + + fn try_from(filename: &String) -> Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(WriteAheadLog::wal_filename(filename))?; + + Ok(WriteAheadLog { file }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utilities::test_file::TestFile; + + #[test] + fn clear() { + let test_file = TestFile::new(); + + let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); + let record = WriteAheadLogRecord { + position: 1, + bytes: vec![1_u8; 5], + }; + + wal.insert(record).unwrap(); + wal.clear().unwrap(); + + assert_eq!(wal.records(), Ok(vec![])); + } + + #[test] + fn filename_constructed() { + let test_file = TestFile::new(); + WriteAheadLog::try_from(test_file.file_name()).unwrap(); + } + + #[test] + fn insert() { + let test_file = TestFile::from(".\\write_ahead_log_test.rs-insert.testfile"); + + let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); + let record = WriteAheadLogRecord { + position: 1, + bytes: vec![1_u8; 5], + }; + + wal.insert(record.clone()).unwrap(); + + assert_eq!(wal.records(), Ok(vec![record])); + } + + #[test] + fn insert_empty() { + let test_file = TestFile::from("./write_ahead_log_test.rs-insert_empty.testfile"); + + let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); + let record = WriteAheadLogRecord { + position: 16, + bytes: vec![], + }; + + wal.insert(record.clone()).unwrap(); + + assert_eq!(wal.records(), Ok(vec![record])); + } + + #[test] + fn record_derived_from_debug() { + let record = WriteAheadLogRecord::default(); + format!("{:?}", record); + } + + #[test] + fn records() { + let test_file = TestFile::from("write_ahead_log_test.rs-records.testfile"); + + let mut wal = WriteAheadLog::try_from(test_file.file_name()).unwrap(); + let record1 = WriteAheadLogRecord { + position: 1, + bytes: vec![1_u8; 5], + }; + let record2 = WriteAheadLogRecord { + position: 15, + bytes: vec![2_u8; 3], + }; + + wal.insert(record1.clone()).unwrap(); + wal.insert(record2.clone()).unwrap(); + + assert_eq!(wal.records(), Ok(vec![record1, record2])); + } +} diff --git a/src/agdb/test_utilities.rs b/src/agdb/test_utilities.rs new file mode 100644 index 00000000..ffe6b6e6 --- /dev/null +++ b/src/agdb/test_utilities.rs @@ -0,0 +1,4 @@ +#[cfg(test)] +pub mod collided_value; +#[cfg(test)] +pub mod test_file; diff --git a/src/agdb/test_utilities/collided_value.rs b/src/agdb/test_utilities/collided_value.rs new file mode 100644 index 00000000..cc1f108a --- /dev/null +++ b/src/agdb/test_utilities/collided_value.rs @@ -0,0 +1,71 @@ +use crate::db_error::DbError; +use crate::utilities::serialize::Serialize; +use crate::utilities::stable_hash::StableHash; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct CollidedValue { + pub value: T, +} + +impl CollidedValue { + pub fn new(value: T) -> Self { + CollidedValue { value } + } +} + +impl Serialize for CollidedValue +where + T: Serialize, +{ + fn deserialize(bytes: &[u8]) -> Result { + Ok(Self { + value: T::deserialize(bytes)?, + }) + } + + fn serialize(&self) -> Vec { + self.value.serialize() + } +} + +impl StableHash for CollidedValue { + fn stable_hash(&self) -> u64 { + 1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derived_from_clone() { + let value = CollidedValue::new(1_i64); + let other = value.clone(); + + assert_eq!(value, other); + } + + #[test] + fn derived_from_debug() { + let value = CollidedValue::new(1_i64); + + format!("{:?}", value); + } + + #[test] + fn serialize() { + let value = CollidedValue::new(1_i64); + let bytes = value.serialize(); + let other = CollidedValue::deserialize(&bytes).unwrap(); + + assert_eq!(value, other); + } + + #[test] + fn stable_hash() { + let value = CollidedValue::new(1_i64); + + assert_eq!(value.stable_hash(), 1_u64); + } +} diff --git a/src/agdb/test_utilities/test_file.rs b/src/agdb/test_utilities/test_file.rs new file mode 100644 index 00000000..fc4dc30b --- /dev/null +++ b/src/agdb/test_utilities/test_file.rs @@ -0,0 +1,185 @@ +use std::fs::remove_file; +use std::panic::Location; +use std::path::Path; + +pub struct TestFile { + pub(crate) filename: String, +} + +impl TestFile { + pub fn file_name(&self) -> &String { + &self.filename + } + + #[track_caller] + pub fn new() -> TestFile { + let caller = Location::caller(); + let file = format!( + "./{}.{}.{}.testfile", + Path::new(caller.file()) + .file_name() + .unwrap() + .to_str() + .unwrap(), + caller.line(), + caller.column() + ); + + TestFile::from(file) + } + + pub(crate) fn hidden_filename(filename: &String) -> String { + let path = Path::new(filename); + let name: String = path.file_name().unwrap().to_str().unwrap().to_string(); + let parent = path.parent().unwrap(); + + parent + .join(&Path::new(&(".".to_string() + &name))) + .to_str() + .unwrap() + .to_string() + } + + pub(crate) fn remove_file_if_exists(filename: &String) { + if Path::new(filename).exists() { + remove_file(filename).unwrap(); + } + } +} + +impl Default for TestFile { + #[track_caller] + fn default() -> Self { + Self::new() + } +} + +impl Drop for TestFile { + fn drop(&mut self) { + Self::remove_file_if_exists(&self.filename); + Self::remove_file_if_exists(&Self::hidden_filename(&self.filename)); + } +} + +impl From<&str> for TestFile { + fn from(filename: &str) -> Self { + TestFile::from(filename.to_string()) + } +} + +impl From for TestFile { + fn from(filename: String) -> Self { + Self::remove_file_if_exists(&filename); + Self::remove_file_if_exists(&Self::hidden_filename(&filename)); + + TestFile { filename } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::OpenOptions; + + fn ensure_file(filename: &str) { + OpenOptions::new() + .write(true) + .create_new(true) + .open(filename) + .unwrap(); + } + + #[test] + fn default() { + let caller = Location::caller(); + let current_source_file = Path::new(caller.file()) + .file_name() + .unwrap() + .to_str() + .unwrap(); + + let test_file = TestFile::default(); + assert!(!test_file.file_name().is_empty()); + assert!(test_file.file_name().contains(¤t_source_file)); + } + + #[test] + fn created_from_str_ref() { + let filename = "./test_file-created_from_str_ref"; + let _test_file = TestFile::from(filename); + } + + #[test] + fn created_from_string() { + let filename = "./test_file-created_from_string".to_string(); + let _test_file = TestFile::from(filename); + } + + #[test] + fn existing_file_is_deleted_on_construction() { + let filename = "./test_file-existing_file_is_deleted_on_construction"; + ensure_file(filename); + let _test_file = TestFile::from(filename); + assert!(!Path::new(filename).exists()); + } + + #[test] + fn file_is_deleted_on_destruction() { + let filename = "./test_file-file_is_deleted_on_destruction"; + + { + let _test_file = TestFile::from(filename); + ensure_file(filename); + } + + assert!(!Path::new(filename).exists()); + } + + #[test] + fn get_file_name() { + let filename = "./test_file-get_file_name"; + let test_file = TestFile::from(filename); + + assert_eq!(test_file.file_name(), filename); + } + + #[test] + fn hidden_file_is_deleted_on_construction() { + let filename = "./test_file-hidden_file_is_deleted_on_construction"; + let hidden_filename = "./.test_file-hidden_file_is_deleted_on_construction"; + ensure_file(filename); + ensure_file(hidden_filename); + let _test_file = TestFile::from(filename); + assert!(!Path::new(filename).exists()); + assert!(!Path::new(hidden_filename).exists()); + } + + #[test] + fn hidden_file_is_deleted_on_destruction() { + let filename = "test_file-hidden_file_is_deleted_on_destruction"; + let hidden_filename = ".test_file-hidden_file_is_deleted_on_destruction"; + + { + let _test_file = TestFile::from(filename); + ensure_file(filename); + ensure_file(hidden_filename); + } + + assert!(!Path::new(filename).exists()); + assert!(!Path::new(hidden_filename).exists()); + } + + #[test] + fn new() { + let caller = Location::caller(); + let current_source_file = Path::new(caller.file()) + .file_name() + .unwrap() + .to_str() + .unwrap(); + + let test_file = TestFile::new(); + assert!(!test_file.file_name().is_empty()); + assert!(test_file.file_name().contains(¤t_source_file)); + } +} diff --git a/src/transaction.rs b/src/agdb/transaction.rs similarity index 88% rename from src/transaction.rs rename to src/agdb/transaction.rs index e1f3198d..e24cb75f 100644 --- a/src/transaction.rs +++ b/src/agdb/transaction.rs @@ -1,4 +1,6 @@ -use crate::{Query, QueryError, QueryResult}; +use crate::Query; +use crate::QueryError; +use crate::QueryResult; #[derive(Default)] pub struct Transaction {} diff --git a/src/agdb/utilities.rs b/src/agdb/utilities.rs new file mode 100644 index 00000000..122d237c --- /dev/null +++ b/src/agdb/utilities.rs @@ -0,0 +1,2 @@ +pub mod serialize; +pub mod stable_hash; diff --git a/src/agdb/utilities/serialize.rs b/src/agdb/utilities/serialize.rs new file mode 100644 index 00000000..4f536468 --- /dev/null +++ b/src/agdb/utilities/serialize.rs @@ -0,0 +1,207 @@ +use crate::db_error::DbError; +use std::mem::size_of; + +pub trait Serialize: Sized { + fn deserialize(bytes: &[u8]) -> Result; + fn serialize(&self) -> Vec; + + fn serialized_size() -> u64 { + size_of::() as u64 + } +} + +impl Serialize for i64 { + fn deserialize(bytes: &[u8]) -> Result { + let buffer: [u8; size_of::()] = bytes + .get(0..size_of::()) + .ok_or_else(|| DbError::from("i64 deserialization error: out of bounds"))? + .try_into() + .unwrap(); + Ok(Self::from_le_bytes(buffer)) + } + + fn serialize(&self) -> Vec { + self.to_le_bytes().into() + } +} + +impl Serialize for String { + fn deserialize(bytes: &[u8]) -> Result { + Ok(String::from_utf8(bytes.to_vec())?) + } + + fn serialize(&self) -> Vec { + self.as_bytes().to_vec() + } + + fn serialized_size() -> u64 { + 0 + } +} + +impl Serialize for u64 { + fn deserialize(bytes: &[u8]) -> Result { + let buffer: [u8; size_of::()] = bytes + .get(0..size_of::()) + .ok_or_else(|| DbError::from("u64 deserialization error: out of bounds"))? + .try_into() + .unwrap(); + Ok(Self::from_le_bytes(buffer)) + } + + fn serialize(&self) -> Vec { + self.to_le_bytes().into() + } +} + +impl Serialize for Vec { + fn deserialize(bytes: &[u8]) -> Result { + const SIZE_OFFSET: usize = size_of::(); + let value_offset = T::serialized_size(); + let size = u64::deserialize(bytes)? as usize; + let mut data: Self = vec![]; + + data.reserve(size); + + for i in 0..size { + let offset = SIZE_OFFSET + value_offset as usize * i; + data.push(T::deserialize(&bytes[offset..])?); + } + + Ok(data) + } + + fn serialize(&self) -> Vec { + const SIZE_OFFSET: usize = size_of::(); + let value_offset: usize = size_of::(); + let mut bytes: Vec = vec![]; + + bytes.reserve(SIZE_OFFSET + value_offset * self.len()); + bytes.extend((self.len() as u64).serialize()); + + for value in self { + bytes.extend(value.serialize()); + } + + bytes + } + + fn serialized_size() -> u64 { + 0 + } +} + +impl Serialize for Vec { + fn deserialize(bytes: &[u8]) -> Result { + Ok(bytes.to_vec()) + } + + fn serialize(&self) -> Vec { + self.to_vec() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn i64() { + let number = -10_i64; + let bytes = number.serialize(); + let actual = i64::deserialize(&bytes); + + assert_eq!(actual, Ok(number)); + } + + #[test] + fn i64_out_of_bounds() { + let bytes = vec![0_u8; 4]; + + assert_eq!( + i64::deserialize(&bytes), + Err(DbError::from("i64 deserialization error: out of bounds")) + ); + } + + #[test] + fn serialized_size() { + assert_eq!(i64::serialized_size(), 8); + assert_eq!(u64::serialized_size(), 8); + assert_eq!(Vec::::serialized_size(), 0); + assert_eq!(String::serialized_size(), 0); + } + + #[test] + fn string() { + let value = "Hello, World!".to_string(); + let bytes = value.serialize(); + let actual = String::deserialize(&bytes); + + assert_eq!(actual, Ok(value)); + } + + #[test] + fn string_bad_bytes() { + let bad_bytes = vec![0xdf, 0xff]; + + assert!(String::deserialize(&bad_bytes).is_err()); + } + + #[test] + fn u64() { + let number = 10_u64; + let bytes = number.serialize(); + let actual = u64::deserialize(&bytes); + + assert_eq!(actual, Ok(number)); + } + + #[test] + fn u64_out_of_bounds() { + let bytes = vec![0_u8; 4]; + + assert_eq!( + u64::deserialize(&bytes), + Err(DbError::from("u64 deserialization error: out of bounds")) + ); + } + + #[test] + fn vec_i64() { + let data = vec![1_i64, 2_i64, 3_i64]; + let bytes = data.serialize(); + let actual = Vec::::deserialize(&bytes); + + assert_eq!(actual, Ok(data)); + } + + #[test] + fn vec_size_out_of_bounds() { + let bytes = vec![0_u8; 4]; + + assert_eq!( + Vec::::deserialize(&bytes), + Err(DbError::from("u64 deserialization error: out of bounds")) + ); + } + + #[test] + fn vec_u8() { + let data = vec![1_u8, 2_u8, 3_u8]; + let bytes = data.serialize(); + let actual = Vec::::deserialize(&bytes); + + assert_eq!(actual, Ok(data)); + } + + #[test] + fn vec_value_out_of_bounds() { + let bytes = 1_u64.serialize(); + + assert_eq!( + Vec::::deserialize(&bytes), + Err(DbError::from("i64 deserialization error: out of bounds")) + ); + } +} diff --git a/crates/utilities/src/stable_hash.rs b/src/agdb/utilities/stable_hash.rs similarity index 52% rename from crates/utilities/src/stable_hash.rs rename to src/agdb/utilities/stable_hash.rs index d86d6c4a..02b482a2 100644 --- a/crates/utilities/src/stable_hash.rs +++ b/src/agdb/utilities/stable_hash.rs @@ -13,3 +13,18 @@ impl StableHash for u64 { *self } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn i64() { + assert_eq!(10_i64.stable_hash(), 10_u64); + } + + #[test] + fn u64() { + assert_eq!(10_u64.stable_hash(), 10_u64); + } +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index b6bdca9e..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod db; -mod query; -mod query_error; -mod query_result; -mod transaction; - -pub use agdb_db_error::DbError; -pub use db::Db; -pub use query::Query; -pub use query_error::QueryError; -pub use query_result::QueryResult; -pub use transaction::Transaction; diff --git a/src/query.rs b/src/query.rs deleted file mode 100644 index 8064f962..00000000 --- a/src/query.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[derive(Default)] -pub struct Query {} diff --git a/tests/db_test.rs b/tests/db_test.rs index 7c939789..fca2ea13 100644 --- a/tests/db_test.rs +++ b/tests/db_test.rs @@ -1,13 +1,16 @@ -extern crate agdb; +use agdb::Db; +use agdb::Query; +use agdb::QueryError; +use agdb::QueryResult; #[test] fn db_is_public_type() { - let _db = agdb::Db::default(); + let _db = Db::default(); } #[test] fn exec_takes_query_returns_query_result() { - let db = agdb::Db::default(); - let query = agdb::Query::default(); - let _result: Result = db.exec(query); + let db = Db::default(); + let query = Query::default(); + let _result: Result = db.exec(query); } diff --git a/tests/query_error_test.rs b/tests/query_error_test.rs index e87faded..5b77c7eb 100644 --- a/tests/query_error_test.rs +++ b/tests/query_error_test.rs @@ -1,6 +1,6 @@ -extern crate agdb; +use agdb::QueryError; #[test] fn query_error_is_public_type() { - let _error = agdb::QueryError::default(); + let _error = QueryError::default(); } diff --git a/tests/query_result_test.rs b/tests/query_result_test.rs index 150e25c2..caa0214a 100644 --- a/tests/query_result_test.rs +++ b/tests/query_result_test.rs @@ -1,6 +1,6 @@ -extern crate agdb; +use agdb::QueryResult; #[test] fn query_result_is_public_type() { - let _result = agdb::QueryResult::default(); + let _result = QueryResult::default(); } diff --git a/tests/query_test.rs b/tests/query_test.rs index bdd8f980..1f0dc9a5 100644 --- a/tests/query_test.rs +++ b/tests/query_test.rs @@ -1,6 +1,6 @@ -extern crate agdb; +use agdb::Query; #[test] fn query_is_public_type() { - let _query = agdb::Query::default(); + let _query = Query::default(); } diff --git a/tests/transaction_test.rs b/tests/transaction_test.rs index 64ee0197..b52050aa 100644 --- a/tests/transaction_test.rs +++ b/tests/transaction_test.rs @@ -1,41 +1,45 @@ -extern crate agdb; +use agdb::Db; +use agdb::Query; +use agdb::QueryError; +use agdb::QueryResult; +use agdb::Transaction; #[test] fn transaction_is_public_type() { - let _transaction = agdb::Transaction::default(); + let _transaction = Transaction::default(); } #[test] fn create_transaction_from_db() { - let db = agdb::Db::default(); - let _transaction: agdb::Transaction = db.transaction(); + let db = Db::default(); + let _transaction: Transaction = db.transaction(); } #[test] fn exec_takes_query_returns_query_result() { - let db = agdb::Db::default(); - let query = agdb::Query::default(); + let db = Db::default(); + let query = Query::default(); let transaction = db.transaction(); - let _result: Result = transaction.exec(query); + let _result: Result = transaction.exec(query); } #[test] fn create_transaction_from_transaction() { - let db = agdb::Db::default(); + let db = Db::default(); let transaction = db.transaction(); - let _nested_transaction: agdb::Transaction = transaction.transaction(); + let _nested_transaction: Transaction = transaction.transaction(); } #[test] fn transaction_commit() { - let db = agdb::Db::default(); + let db = Db::default(); let transaction = db.transaction(); - let _result: Result = transaction.commit(); + let _result: Result = transaction.commit(); } #[test] fn transaction_rollback() { - let db = agdb::Db::default(); + let db = Db::default(); let transaction = db.transaction(); - let _result: Result = transaction.rollback(); + let _result: Result = transaction.rollback(); }