diff --git a/Cargo.toml b/Cargo.toml index a3ba2f61..8eada103 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,13 @@ edition = "2021" [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_file = { path = "crates/test_file", version = "<=0.1.0" } diff --git a/crates/map_common/Cargo.toml b/crates/map_common/Cargo.toml new file mode 100644 index 00000000..8c2c0da8 --- /dev/null +++ b/crates/map_common/Cargo.toml @@ -0,0 +1,11 @@ +[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 new file mode 100644 index 00000000..0d50e72f --- /dev/null +++ b/crates/map_common/src/lib.rs @@ -0,0 +1,14 @@ +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.rs b/crates/map_common/src/map_common.rs new file mode 100644 index 00000000..f8964abd --- /dev/null +++ b/crates/map_common/src/map_common.rs @@ -0,0 +1,148 @@ +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; +use std::cmp::max; +use std::hash::Hash; +use std::marker::PhantomData; + +pub struct MapCommon +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: MapData, +{ + pub data: Data, + pub(crate) phantom_data: PhantomData<(K, T)>, +} + +impl MapCommon +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: MapData, +{ + pub fn capacity(&self) -> u64 { + self.data.capacity() + } + + pub fn count(&self) -> u64 { + self.data.count() + } + + pub fn iter(&self) -> MapIterator { + MapIterator:: { + pos: 0, + data: &self.data, + phantom_data: PhantomData, + } + } + + pub fn reserve(&mut self, new_capacity: u64) -> Result<(), DbError> { + if self.capacity() < new_capacity { + return self.rehash(new_capacity); + } + + Ok(()) + } + + pub fn value(&self, key: &K) -> Result, DbError> { + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + + loop { + let record = self.data.record(pos)?; + + match record.state { + MapValueState::Empty => return Ok(None), + MapValueState::Valid if record.key == *key => return Ok(Some(record.value)), + MapValueState::Valid | MapValueState::Deleted => { + pos = Self::next_pos(pos, self.capacity()) + } + } + } + } + + pub fn insert_value(&mut self, pos: u64, key: K, value: T) -> Result<(), DbError> { + self.data.set_value( + pos, + MapValue { + state: MapValueState::Valid, + key, + value, + }, + ) + } + + pub fn max_size(&self) -> u64 { + self.capacity() * 15 / 16 + } + + pub fn next_pos(pos: u64, capacity: u64) -> u64 { + if pos == capacity - 1 { + 0 + } else { + pos + 1 + } + } + + pub fn rehash(&mut self, mut new_capacity: u64) -> Result<(), DbError> { + new_capacity = max(new_capacity, 64); + + if new_capacity != self.capacity() { + let old_data = self.data.take_values()?; + self.data.transaction(); + self.data + .set_values(self.rehash_old_data(old_data, new_capacity))?; + self.data.commit()?; + } + + Ok(()) + } + + pub fn remove_record(&mut self, pos: u64) -> Result<(), DbError> { + self.data.set_meta_value(pos, MapValueState::Deleted)?; + self.data.set_count(self.data.count() - 1)?; + + if 0 != self.data.count() && (self.data.count() - 1) < self.min_size() { + self.rehash(self.capacity() / 2)?; + } + + Ok(()) + } + + fn min_size(&self) -> u64 { + self.capacity() * 7 / 16 + } + + fn place_new_record(&self, new_data: &mut Vec>, record: MapValue) { + let hash = record.key.stable_hash(); + let mut pos = hash % new_data.len() as u64; + + while new_data[pos as usize].state != MapValueState::Empty { + pos = Self::next_pos(pos, new_data.len() as u64); + } + + new_data[pos as usize] = record; + } + + fn rehash_old_data( + &self, + old_data: Vec>, + new_capacity: u64, + ) -> Vec> { + let mut new_data: Vec> = + vec![MapValue::::default(); new_capacity as usize]; + + for record in old_data { + if record.state == MapValueState::Valid { + self.place_new_record(&mut new_data, record); + } + } + + new_data + } +} diff --git a/crates/map_common/src/map_common_from.rs b/crates/map_common/src/map_common_from.rs new file mode 100644 index 00000000..8849dc8d --- /dev/null +++ b/crates/map_common/src/map_common_from.rs @@ -0,0 +1,20 @@ +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_data.rs b/crates/map_common/src/map_data.rs new file mode 100644 index 00000000..68a290f4 --- /dev/null +++ b/crates/map_common/src/map_data.rs @@ -0,0 +1,24 @@ +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 std::hash::Hash; + +pub trait MapData +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, +{ + fn capacity(&self) -> u64; + fn commit(&mut self) -> Result<(), DbError>; + fn count(&self) -> u64; + fn meta_value(&self, pos: u64) -> Result; + fn record(&self, pos: u64) -> Result, DbError>; + fn set_count(&mut self, new_count: u64) -> Result<(), DbError>; + fn set_meta_value(&mut self, pos: u64, meta_value: MapValueState) -> Result<(), DbError>; + fn set_value(&mut self, pos: u64, value: MapValue) -> Result<(), DbError>; + fn set_values(&mut self, values: Vec>) -> Result<(), DbError>; + fn take_values(&mut self) -> Result>, DbError>; + fn transaction(&mut self); +} diff --git a/src/storage/hash_map_iterator.rs b/crates/map_common/src/map_iterator.rs similarity index 57% rename from src/storage/hash_map_iterator.rs rename to crates/map_common/src/map_iterator.rs index ff9022db..0edfcddd 100644 --- a/src/storage/hash_map_iterator.rs +++ b/crates/map_common/src/map_iterator.rs @@ -1,34 +1,26 @@ -use super::hash_map_data::HashMapData; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; +use super::map_data::MapData; +use super::map_value_state::MapValueState; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; use std::hash::Hash; use std::marker::PhantomData; -pub(crate) struct HashMapIterator<'a, K, T, Data> +pub struct MapIterator<'a, K, T, Data> where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, T: Clone + Default + Serialize, - Data: HashMapData, + Data: MapData, { pub(super) pos: u64, pub(super) data: &'a Data, pub(super) phantom_data: PhantomData<(K, T)>, } -impl<'a, K, T, Data> HashMapIterator<'a, K, T, Data> +impl<'a, K, T, Data> Iterator for MapIterator<'a, K, T, Data> where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, T: Clone + Default + Serialize, - Data: HashMapData, -{ -} - -impl<'a, K, T, Data> Iterator for HashMapIterator<'a, K, T, Data> -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: HashMapData, + Data: MapData, { type Item = (K, T); @@ -38,7 +30,7 @@ where self.pos += 1; - if value.meta_value == HashMapMetaValue::Valid { + if value.state == MapValueState::Valid { return Some((value.key, value.value)); } } diff --git a/crates/map_common/src/map_value.rs b/crates/map_common/src/map_value.rs new file mode 100644 index 00000000..60ed7ad1 --- /dev/null +++ b/crates/map_common/src/map_value.rs @@ -0,0 +1,13 @@ +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 new file mode 100644 index 00000000..c2bbcb0c --- /dev/null +++ b/crates/map_common/src/map_value_serialize.rs @@ -0,0 +1,34 @@ +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 new file mode 100644 index 00000000..7be84195 --- /dev/null +++ b/crates/map_common/src/map_value_state.rs @@ -0,0 +1,7 @@ +#[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 new file mode 100644 index 00000000..90101134 --- /dev/null +++ b/crates/map_common/src/map_value_state_serialize.rs @@ -0,0 +1,27 @@ +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 new file mode 100644 index 00000000..68ecac0d --- /dev/null +++ b/crates/map_common/tests/map_value_state_test.rs @@ -0,0 +1,41 @@ +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 new file mode 100644 index 00000000..8db73a9c --- /dev/null +++ b/crates/map_common/tests/map_value_test.rs @@ -0,0 +1,54 @@ +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 new file mode 100644 index 00000000..6d031d75 --- /dev/null +++ b/crates/multi_map/Cargo.toml @@ -0,0 +1,17 @@ +[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_file = { path = "../test_file", version = "<=0.1.0" } diff --git a/crates/multi_map/src/lib.rs b/crates/multi_map/src/lib.rs new file mode 100644 index 00000000..643bf09f --- /dev/null +++ b/crates/multi_map/src/lib.rs @@ -0,0 +1,9 @@ +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.rs b/crates/multi_map/src/map_data_memory.rs new file mode 100644 index 00000000..ec35fbb6 --- /dev/null +++ b/crates/multi_map/src/map_data_memory.rs @@ -0,0 +1,73 @@ +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 std::hash::Hash; +use std::mem::take; + +pub struct MapDataMemory +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, +{ + pub(crate) data: Vec>, + pub(crate) count: u64, +} + +impl MapData for MapDataMemory +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, +{ + fn capacity(&self) -> u64 { + self.data.len() as u64 + } + + fn commit(&mut self) -> Result<(), DbError> { + Ok(()) + } + + fn count(&self) -> u64 { + self.count + } + + fn meta_value(&self, pos: u64) -> Result { + Ok(self.data[pos as usize].state.clone()) + } + + fn record(&self, pos: u64) -> Result, DbError> { + Ok(self.data[pos as usize].clone()) + } + + fn set_count(&mut self, new_count: u64) -> Result<(), DbError> { + self.count = new_count; + + Ok(()) + } + + fn set_meta_value(&mut self, pos: u64, meta_value: MapValueState) -> Result<(), DbError> { + self.data[pos as usize].state = meta_value; + + Ok(()) + } + + fn set_value(&mut self, pos: u64, value: MapValue) -> Result<(), DbError> { + self.data[pos as usize] = value; + + Ok(()) + } + + fn set_values(&mut self, values: Vec>) -> Result<(), DbError> { + self.data = values; + + Ok(()) + } + + fn take_values(&mut self) -> Result>, DbError> { + Ok(take(&mut self.data)) + } + + fn transaction(&mut self) {} +} diff --git a/crates/multi_map/src/map_data_memory_default.rs b/crates/multi_map/src/map_data_memory_default.rs new file mode 100644 index 00000000..9b513898 --- /dev/null +++ b/crates/multi_map/src/map_data_memory_default.rs @@ -0,0 +1,18 @@ +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 new file mode 100644 index 00000000..a652178b --- /dev/null +++ b/crates/multi_map/src/multi_map.rs @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000..b2c04fa8 --- /dev/null +++ b/crates/multi_map/src/multi_map_default.rs @@ -0,0 +1,14 @@ +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/multi_map_impl.rs b/crates/multi_map/src/multi_map_impl.rs new file mode 100644 index 00000000..21e3e02a --- /dev/null +++ b/crates/multi_map/src/multi_map_impl.rs @@ -0,0 +1,142 @@ +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 std::hash::Hash; + +pub struct MultiMapImpl +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, + Data: MapData, +{ + pub(crate) map_common: MapCommon, +} + +#[allow(dead_code)] +impl MultiMapImpl +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Eq + PartialEq + Serialize, + Data: MapData, +{ + pub fn capacity(&self) -> u64 { + self.map_common.capacity() + } + + pub fn count(&self) -> u64 { + self.map_common.count() + } + + pub fn insert(&mut self, key: K, value: T) -> Result<(), DbError> { + self.map_common.data.transaction(); + let free = self.find_free(&key)?; + self.map_common.insert_value(free, key, value)?; + self.map_common.data.set_count(self.count() + 1)?; + self.map_common.data.commit() + } + + pub fn iter(&self) -> MapIterator { + self.map_common.iter() + } + + pub fn remove_key(&mut self, key: &K) -> Result<(), DbError> { + let hash = key.stable_hash(); + let mut pos = hash % self.map_common.data.capacity(); + + self.map_common.data.transaction(); + + loop { + let record = self.map_common.data.record(pos)?; + + match record.state { + MapValueState::Empty => break, + MapValueState::Valid if record.key == *key => { + self.map_common.remove_record(pos)?; + } + MapValueState::Valid | MapValueState::Deleted => { + pos = MapCommon::::next_pos(pos, self.capacity()); + } + } + + pos = MapCommon::::next_pos(pos, self.capacity()); + } + + self.map_common.data.commit() + } + + pub fn remove_value(&mut self, key: &K, value: &T) -> Result<(), DbError> { + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + + loop { + let record = self.map_common.data.record(pos)?; + + match record.state { + MapValueState::Empty => break, + MapValueState::Valid if record.key == *key && record.value == *value => { + self.map_common.data.transaction(); + self.map_common.remove_record(pos)?; + self.map_common.data.commit()?; + break; + } + MapValueState::Valid | MapValueState::Deleted => { + pos = MapCommon::::next_pos(pos, self.capacity()); + } + } + } + + Ok(()) + } + + pub fn reserve(&mut self, new_capacity: u64) -> Result<(), DbError> { + self.map_common.reserve(new_capacity) + } + + pub fn value(&self, key: &K) -> Result, DbError> { + self.map_common.value(key) + } + + pub fn values(&self, key: &K) -> Result, DbError> { + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + let mut values: Vec = vec![]; + + loop { + let record = self.map_common.data.record(pos)?; + + match record.state { + MapValueState::Empty => break, + MapValueState::Valid if record.key == *key => values.push(record.value.clone()), + MapValueState::Valid | MapValueState::Deleted => {} + } + + pos = MapCommon::::next_pos(pos, self.capacity()); + } + + Ok(values) + } + + fn find_free(&mut self, key: &K) -> Result { + if self.map_common.max_size() < (self.count() + 1) { + self.map_common.rehash(self.capacity() * 2)?; + } + + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + + loop { + let meta_value = self.map_common.data.meta_value(pos)?; + + match meta_value { + MapValueState::Empty | MapValueState::Deleted => return Ok(pos), + MapValueState::Valid => { + pos = MapCommon::::next_pos(pos, self.capacity()); + } + } + } + } +} diff --git a/crates/multi_map/src/storage_multi_map.rs b/crates/multi_map/src/storage_multi_map.rs new file mode 100644 index 00000000..fbc4a783 --- /dev/null +++ b/crates/multi_map/src/storage_multi_map.rs @@ -0,0 +1,68 @@ +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 new file mode 100644 index 00000000..49fe97e4 --- /dev/null +++ b/crates/multi_map/tests/multi_map_insert_test.rs @@ -0,0 +1,61 @@ +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 new file mode 100644 index 00000000..d1a4d3e0 --- /dev/null +++ b/crates/multi_map/tests/multi_map_remove_key_test.rs @@ -0,0 +1,68 @@ +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 new file mode 100644 index 00000000..ac45b9db --- /dev/null +++ b/crates/multi_map/tests/multi_map_remove_value_test.rs @@ -0,0 +1,27 @@ +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 new file mode 100644 index 00000000..5f8af261 --- /dev/null +++ b/crates/multi_map/tests/multi_map_reserve_test.rs @@ -0,0 +1,45 @@ +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 new file mode 100644 index 00000000..9508fc0c --- /dev/null +++ b/crates/multi_map/tests/multi_map_test.rs @@ -0,0 +1,26 @@ +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 new file mode 100644 index 00000000..6ec4f639 --- /dev/null +++ b/crates/multi_map/tests/multi_map_value_test.rs @@ -0,0 +1,21 @@ +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 new file mode 100644 index 00000000..3ab21029 --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_insert_test.rs @@ -0,0 +1,81 @@ +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_test_file::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 new file mode 100644 index 00000000..a1bd6b17 --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_remove_key_test.rs @@ -0,0 +1,88 @@ +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_test_file::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 new file mode 100644 index 00000000..e8457aa5 --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_remove_value_test.rs @@ -0,0 +1,39 @@ +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_test_file::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 new file mode 100644 index 00000000..605f2ce8 --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_reserve_test.rs @@ -0,0 +1,64 @@ +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_test_file::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 new file mode 100644 index 00000000..2c59b0b5 --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_test.rs @@ -0,0 +1,107 @@ +use agdb_db_error::DbError; +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_storage::StorageIndex; +use agdb_test_file::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 new file mode 100644 index 00000000..05e91bbe --- /dev/null +++ b/crates/multi_map/tests/storage_multi_map_value_test.rs @@ -0,0 +1,33 @@ +use agdb_multi_map::StorageMultiMap; +use agdb_storage::StorageFile; +use agdb_test_file::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/storage_map/Cargo.toml b/crates/storage_map/Cargo.toml new file mode 100644 index 00000000..581477bd --- /dev/null +++ b/crates/storage_map/Cargo.toml @@ -0,0 +1,16 @@ +[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_file = { path = "../test_file", version = "<=0.1.0" } diff --git a/crates/storage_map/src/lib.rs b/crates/storage_map/src/lib.rs new file mode 100644 index 00000000..61278235 --- /dev/null +++ b/crates/storage_map/src/lib.rs @@ -0,0 +1,8 @@ +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/src/storage/hash_map_data_storage.rs b/crates/storage_map/src/map_data_storage.rs similarity index 54% rename from src/storage/hash_map_data_storage.rs rename to crates/storage_map/src/map_data_storage.rs index 43cb56e7..c338ba93 100644 --- a/src/storage/hash_map_data_storage.rs +++ b/crates/storage_map/src/map_data_storage.rs @@ -1,47 +1,55 @@ -use super::hash_map_data::HashMapData; -use super::hash_map_key_value::HashMapKeyValue; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; 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 std::cell::RefCell; use std::hash::Hash; use std::marker::PhantomData; use std::mem::size_of; use std::rc::Rc; -pub(crate) struct HashMapDataStorage +pub struct MapDataStorage where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, T: Clone + Default + Serialize, { - pub(super) storage: Rc>, - pub(super) storage_index: StorageIndex, - pub(super) count: u64, - pub(super) capacity: u64, - pub(super) phantom_data: PhantomData<(K, T)>, + pub(crate) storage: Rc>, + pub(crate) storage_index: StorageIndex, + pub(crate) count: u64, + pub(crate) capacity: u64, + pub(crate) phantom_data: PhantomData<(K, T)>, } -impl HashMapDataStorage +impl MapDataStorage where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, T: Clone + Default + Serialize, Data: Storage, { + pub fn count(&self) -> u64 { + self.count + } + fn record_offset(pos: u64) -> u64 { - u64::serialized_size() * 2 + HashMapKeyValue::::serialized_size() * pos + u64::serialized_size() * 2 + MapValue::::serialized_size() * pos + } + + pub fn storage_index(&self) -> StorageIndex { + self.storage_index.clone() } - pub(super) fn values(&self) -> Result>, DbError> { + pub fn values(&self) -> Result>, DbError> { self.storage .borrow_mut() - .value_at::>>(&self.storage_index, size_of::() as u64) + .value_at::>>(&self.storage_index, size_of::() as u64) } } -impl HashMapData for HashMapDataStorage +impl MapData for MapDataStorage where K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, T: Clone + Default + Serialize, @@ -59,16 +67,16 @@ where self.count } - fn meta_value(&self, pos: u64) -> Result { + fn meta_value(&self, pos: u64) -> Result { self.storage .borrow_mut() - .value_at::(&self.storage_index, Self::record_offset(pos)) + .value_at::(&self.storage_index, Self::record_offset(pos)) } - fn record(&self, pos: u64) -> Result, DbError> { + fn record(&self, pos: u64) -> Result, DbError> { self.storage .borrow_mut() - .value_at::>(&self.storage_index, Self::record_offset(pos)) + .value_at::>(&self.storage_index, Self::record_offset(pos)) } fn set_count(&mut self, new_count: u64) -> Result<(), DbError> { @@ -78,11 +86,7 @@ where .insert_at(&self.storage_index, 0, &self.count) } - fn set_meta_value( - &mut self, - pos: u64, - meta_value: HashMapMetaValue, - ) -> Result<(), crate::DbError> { + fn set_meta_value(&mut self, pos: u64, meta_value: MapValueState) -> Result<(), DbError> { self.storage.borrow_mut().insert_at( &self.storage_index, Self::record_offset(pos), @@ -90,20 +94,20 @@ where ) } - fn set_value(&mut self, pos: u64, value: HashMapKeyValue) -> Result<(), DbError> { + fn set_value(&mut self, pos: u64, value: MapValue) -> Result<(), DbError> { self.storage .borrow_mut() .insert_at(&self.storage_index, Self::record_offset(pos), &value) } - fn set_values(&mut self, values: Vec>) -> Result<(), DbError> { + fn set_values(&mut self, values: Vec>) -> Result<(), DbError> { self.capacity = values.len() as u64; self.storage .borrow_mut() .insert_at(&self.storage_index, size_of::() as u64, &values) } - fn take_values(&mut self) -> Result>, DbError> { + fn take_values(&mut self) -> Result>, DbError> { self.values() } diff --git a/crates/storage_map/src/map_data_storage_try_from.rs b/crates/storage_map/src/map_data_storage_try_from.rs new file mode 100644 index 00000000..de3f0b63 --- /dev/null +++ b/crates/storage_map/src/map_data_storage_try_from.rs @@ -0,0 +1,68 @@ +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/map_impl.rs b/crates/storage_map/src/map_impl.rs new file mode 100644 index 00000000..23b76a67 --- /dev/null +++ b/crates/storage_map/src/map_impl.rs @@ -0,0 +1,122 @@ +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 std::collections::HashMap; +use std::hash::Hash; +use std::marker::PhantomData; + +pub struct MapImpl +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: MapData, +{ + pub(crate) map_common: MapCommon, + pub(crate) phantom_data: PhantomData<(K, T)>, +} + +impl MapImpl +where + K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, + T: Clone + Default + Serialize, + Data: MapData, +{ + pub fn capacity(&self) -> u64 { + self.map_common.capacity() + } + + pub fn count(&self) -> u64 { + self.map_common.count() + } + + pub fn insert(&mut self, key: K, value: T) -> Result, DbError> { + self.map_common.data.transaction(); + let free = self.find_or_free(&key)?; + self.map_common.insert_value(free.0, key, value)?; + + if free.1.is_none() { + self.map_common + .data + .set_count(self.map_common.count() + 1)?; + } + + self.map_common.data.commit()?; + + Ok(free.1) + } + + pub fn iter(&self) -> MapIterator { + self.map_common.iter() + } + + pub fn remove(&mut self, key: &K) -> Result<(), DbError> { + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + + self.map_common.data.transaction(); + + loop { + let record = self.map_common.data.record(pos)?; + + match record.state { + MapValueState::Empty => break, + MapValueState::Valid if record.key == *key => { + self.map_common.remove_record(pos)?; + } + MapValueState::Valid | MapValueState::Deleted => { + pos = MapCommon::::next_pos(pos, self.capacity()); + } + } + } + + self.map_common.data.commit() + } + + pub fn reserve(&mut self, new_capacity: u64) -> Result<(), DbError> { + self.map_common.reserve(new_capacity) + } + + pub fn to_hash_map(&self) -> Result, DbError> { + let mut map = HashMap::::new(); + map.reserve(self.count() as usize); + + for i in 0..self.capacity() { + let record = self.map_common.data.record(i)?; + + if record.state == MapValueState::Valid { + map.insert(record.key, record.value); + } + } + + Ok(map) + } + + pub fn value(&self, key: &K) -> Result, DbError> { + self.map_common.value(key) + } + + fn find_or_free(&mut self, key: &K) -> Result<(u64, Option), DbError> { + if self.map_common.max_size() < (self.count() + 1) { + self.map_common.rehash(self.capacity() * 2)?; + } + + let hash = key.stable_hash(); + let mut pos = hash % self.capacity(); + + loop { + let record = self.map_common.data.record(pos)?; + + match record.state { + MapValueState::Empty | MapValueState::Deleted => return Ok((pos, None)), + MapValueState::Valid if record.key == *key => return Ok((pos, Some(record.value))), + MapValueState::Valid => { + pos = MapCommon::::next_pos(pos, self.capacity()) + } + } + } + } +} diff --git a/crates/storage_map/src/storage_map.rs b/crates/storage_map/src/storage_map.rs new file mode 100644 index 00000000..cf6a2211 --- /dev/null +++ b/crates/storage_map/src/storage_map.rs @@ -0,0 +1,21 @@ +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 new file mode 100644 index 00000000..bf50aa93 --- /dev/null +++ b/crates/storage_map/src/storage_map_try_from.rs @@ -0,0 +1,55 @@ +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 new file mode 100644 index 00000000..8a044dad --- /dev/null +++ b/crates/storage_map/tests/storage_map_insert_test.rs @@ -0,0 +1,84 @@ +use agdb_storage::StorageFile; +use agdb_storage_map::StorageMap; +use agdb_test_file::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 new file mode 100644 index 00000000..3f5beaae --- /dev/null +++ b/crates/storage_map/tests/storage_map_remove_test.rs @@ -0,0 +1,90 @@ +use agdb_storage::StorageFile; +use agdb_storage_map::StorageMap; +use agdb_test_file::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 new file mode 100644 index 00000000..4704d4de --- /dev/null +++ b/crates/storage_map/tests/storage_map_reserve_test.rs @@ -0,0 +1,64 @@ +use agdb_storage::StorageFile; +use agdb_storage_map::StorageMap; +use agdb_test_file::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 new file mode 100644 index 00000000..393c0a22 --- /dev/null +++ b/crates/storage_map/tests/storage_map_test.rs @@ -0,0 +1,107 @@ +use agdb_db_error::DbError; +use agdb_storage::StorageFile; +use agdb_storage::StorageIndex; +use agdb_storage_map::StorageMap; +use agdb_test_file::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 new file mode 100644 index 00000000..1c853754 --- /dev/null +++ b/crates/storage_map/tests/storage_map_value_test.rs @@ -0,0 +1,35 @@ +use agdb_storage::StorageFile; +use agdb_storage_map::StorageMap; +use agdb_test_file::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/utilities/Cargo.toml b/crates/utilities/Cargo.toml new file mode 100644 index 00000000..41ff4ec0 --- /dev/null +++ b/crates/utilities/Cargo.toml @@ -0,0 +1,8 @@ +[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 new file mode 100644 index 00000000..7a106ef0 --- /dev/null +++ b/crates/utilities/src/lib.rs @@ -0,0 +1,3 @@ +mod stable_hash; + +pub use stable_hash::StableHash; diff --git a/crates/utilities/src/stable_hash.rs b/crates/utilities/src/stable_hash.rs new file mode 100644 index 00000000..d86d6c4a --- /dev/null +++ b/crates/utilities/src/stable_hash.rs @@ -0,0 +1,15 @@ +pub trait StableHash { + fn stable_hash(&self) -> u64; +} + +impl StableHash for i64 { + fn stable_hash(&self) -> u64 { + *self as u64 + } +} + +impl StableHash for u64 { + fn stable_hash(&self) -> u64 { + *self + } +} diff --git a/crates/utilities/tests/stable_hash_test.rs b/crates/utilities/tests/stable_hash_test.rs new file mode 100644 index 00000000..0beb1aa3 --- /dev/null +++ b/crates/utilities/tests/stable_hash_test.rs @@ -0,0 +1,11 @@ +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/src/graph/dictionary.rs b/src/graph/dictionary.rs index ae525fa5..c8f7cebc 100644 --- a/src/graph/dictionary.rs +++ b/src/graph/dictionary.rs @@ -1,9 +1,9 @@ use super::dictionary_data_memory::DictionaryDataMemory; use super::dictionary_impl::DictionaryImpl; use super::dictionary_value::DictionaryValue; -use crate::storage::HashMultiMap; -use crate::storage::StableHash; +use agdb_multi_map::MultiMap; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; use std::marker::PhantomData; pub(crate) type Dictionary = DictionaryImpl>; @@ -16,7 +16,7 @@ where pub(crate) fn new() -> Dictionary { Dictionary { data: DictionaryDataMemory:: { - index: HashMultiMap::::new(), + index: MultiMap::::new(), values: vec![DictionaryValue:: { meta: 0, hash: 0, diff --git a/src/graph/dictionary_data.rs b/src/graph/dictionary_data.rs index f2845fcf..eb75be28 100644 --- a/src/graph/dictionary_data.rs +++ b/src/graph/dictionary_data.rs @@ -1,7 +1,7 @@ use super::dictionary_value::DictionaryValue; -use crate::storage::StableHash; use agdb_db_error::DbError; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; pub(crate) trait DictionaryData where diff --git a/src/graph/dictionary_data_memory.rs b/src/graph/dictionary_data_memory.rs index 56b0de01..2f7e2f0c 100644 --- a/src/graph/dictionary_data_memory.rs +++ b/src/graph/dictionary_data_memory.rs @@ -1,15 +1,15 @@ use super::dictionary_data::DictionaryData; use super::dictionary_value::DictionaryValue; -use crate::storage::HashMultiMap; -use crate::storage::StableHash; use agdb_db_error::DbError; +use agdb_multi_map::MultiMap; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; pub(crate) struct DictionaryDataMemory where T: Clone + Default + Eq + PartialEq + StableHash + Serialize, { - pub(super) index: HashMultiMap, + pub(super) index: MultiMap, pub(super) values: Vec>, } diff --git a/src/graph/dictionary_data_storage.rs b/src/graph/dictionary_data_storage.rs index 48308e5f..de589281 100644 --- a/src/graph/dictionary_data_storage.rs +++ b/src/graph/dictionary_data_storage.rs @@ -1,13 +1,13 @@ use super::dictionary_data::DictionaryData; use super::dictionary_value::DictionaryValue; -use crate::storage::StableHash; -use crate::storage::StorageHashMultiMap; 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 std::cell::RefCell; use std::rc::Rc; @@ -18,7 +18,7 @@ where { pub(super) storage: Rc>, pub(super) storage_index: StorageIndex, - pub(super) index: StorageHashMultiMap, + pub(super) index: StorageMultiMap, pub(super) values: StorageVec, Data>, } diff --git a/src/graph/dictionary_impl.rs b/src/graph/dictionary_impl.rs index 685680e9..c34525b4 100644 --- a/src/graph/dictionary_impl.rs +++ b/src/graph/dictionary_impl.rs @@ -1,8 +1,8 @@ use super::dictionary_data::DictionaryData; use super::dictionary_value::DictionaryValue; -use crate::storage::StableHash; use agdb_db_error::DbError; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; use std::marker::PhantomData; pub(crate) struct DictionaryImpl diff --git a/src/graph/dictionary_value.rs b/src/graph/dictionary_value.rs index aded18cf..f35a21b4 100644 --- a/src/graph/dictionary_value.rs +++ b/src/graph/dictionary_value.rs @@ -1,6 +1,6 @@ -use crate::storage::StableHash; use agdb_db_error::DbError; use agdb_serialize::Serialize; +use agdb_utilities::StableHash; #[derive(Clone, Default, PartialEq, Eq)] pub(crate) struct DictionaryValue diff --git a/src/graph/storage_dictionary.rs b/src/graph/storage_dictionary.rs index 9a5e41cb..86febb6a 100644 --- a/src/graph/storage_dictionary.rs +++ b/src/graph/storage_dictionary.rs @@ -2,14 +2,14 @@ use super::dictionary_data_storage::DictionaryDataStorage; use super::dictionary_data_storage_indexes::DictionaryDataStorageIndexes; use super::dictionary_impl::DictionaryImpl; use super::dictionary_value::DictionaryValue; -use crate::storage::StableHash; -use crate::storage::StorageHashMultiMap; 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 std::cell::RefCell; use std::marker::PhantomData; use std::rc::Rc; @@ -36,7 +36,7 @@ where type Error = DbError; fn try_from(storage: Rc>) -> Result { - let index = StorageHashMultiMap::::try_from(storage.clone())?; + let index = StorageMultiMap::::try_from(storage.clone())?; let mut values = StorageVec::, Data>::try_from(storage.clone())?; values.push(&DictionaryValue::default())?; @@ -71,7 +71,7 @@ where .0 .borrow_mut() .value::(&storage_with_index.1)?; - let index = StorageHashMultiMap::::try_from(( + let index = StorageMultiMap::::try_from(( storage_with_index.0.clone(), indexes.index, ))?; diff --git a/src/lib.rs b/src/lib.rs index 7a22788a..d4d8d1fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ mod graph; mod query; mod query_error; mod query_result; -mod storage; mod transaction; pub use agdb_db_error::DbError; diff --git a/src/storage.rs b/src/storage.rs deleted file mode 100644 index 1f1b03a6..00000000 --- a/src/storage.rs +++ /dev/null @@ -1,20 +0,0 @@ -mod hash_map_data; -mod hash_map_data_memory; -mod hash_map_data_storage; -mod hash_map_impl; -mod hash_map_iterator; -mod hash_map_key_value; -mod hash_map_meta_value; -mod hash_multi_map; -mod hash_multi_map_impl; -mod stable_hash; -mod storage_hash_map; -mod storage_hash_multi_map; - -#[allow(unused_imports)] -pub(crate) use hash_multi_map::HashMultiMap; -#[allow(unused_imports)] -pub(crate) use stable_hash::StableHash; -pub(crate) use storage_hash_map::StorageHashMap; -#[allow(unused_imports)] -pub(crate) use storage_hash_multi_map::StorageHashMultiMap; diff --git a/src/storage/hash_map_data.rs b/src/storage/hash_map_data.rs deleted file mode 100644 index 289870fd..00000000 --- a/src/storage/hash_map_data.rs +++ /dev/null @@ -1,25 +0,0 @@ -use agdb_db_error::DbError; - -use super::hash_map_key_value::HashMapKeyValue; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; -use agdb_serialize::Serialize; -use std::hash::Hash; - -pub(crate) trait HashMapData -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, -{ - fn capacity(&self) -> u64; - fn commit(&mut self) -> Result<(), DbError>; - fn count(&self) -> u64; - fn meta_value(&self, pos: u64) -> Result; - fn record(&self, pos: u64) -> Result, DbError>; - fn set_count(&mut self, new_count: u64) -> Result<(), DbError>; - fn set_meta_value(&mut self, pos: u64, meta_value: HashMapMetaValue) -> Result<(), DbError>; - fn set_value(&mut self, pos: u64, value: HashMapKeyValue) -> Result<(), DbError>; - fn set_values(&mut self, values: Vec>) -> Result<(), DbError>; - fn take_values(&mut self) -> Result>, DbError>; - fn transaction(&mut self); -} diff --git a/src/storage/hash_map_data_memory.rs b/src/storage/hash_map_data_memory.rs deleted file mode 100644 index 2fe3fe15..00000000 --- a/src/storage/hash_map_data_memory.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::hash_map_data::HashMapData; -use super::hash_map_key_value::HashMapKeyValue; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::hash::Hash; -use std::mem::take; - -pub(crate) struct HashMapDataMemory -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - pub(super) data: Vec>, - pub(super) count: u64, -} - -impl HashMapData for HashMapDataMemory -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - fn capacity(&self) -> u64 { - self.data.len() as u64 - } - - fn commit(&mut self) -> Result<(), DbError> { - Ok(()) - } - - fn count(&self) -> u64 { - self.count - } - - fn meta_value(&self, pos: u64) -> Result { - Ok(self.data[pos as usize].meta_value.clone()) - } - - fn record(&self, pos: u64) -> Result, DbError> { - Ok(self.data[pos as usize].clone()) - } - - fn set_count(&mut self, new_count: u64) -> Result<(), DbError> { - self.count = new_count; - - Ok(()) - } - - fn set_meta_value( - &mut self, - pos: u64, - meta_value: HashMapMetaValue, - ) -> Result<(), crate::DbError> { - self.data[pos as usize].meta_value = meta_value; - - Ok(()) - } - - fn set_value(&mut self, pos: u64, value: HashMapKeyValue) -> Result<(), DbError> { - self.data[pos as usize] = value; - - Ok(()) - } - - fn set_values(&mut self, values: Vec>) -> Result<(), DbError> { - self.data = values; - - Ok(()) - } - - fn take_values(&mut self) -> Result>, DbError> { - Ok(take(&mut self.data)) - } - - fn transaction(&mut self) {} -} diff --git a/src/storage/hash_map_impl.rs b/src/storage/hash_map_impl.rs deleted file mode 100644 index e6b9bdb8..00000000 --- a/src/storage/hash_map_impl.rs +++ /dev/null @@ -1,226 +0,0 @@ -use super::hash_map_data::HashMapData; -use super::hash_map_iterator::HashMapIterator; -use super::hash_map_key_value::HashMapKeyValue; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::cmp::max; -use std::hash::Hash; -use std::marker::PhantomData; - -pub(crate) struct HashMapImpl -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: HashMapData, -{ - pub(super) data: Data, - pub(super) phantom_data: PhantomData<(K, T)>, -} - -#[allow(dead_code)] -impl HashMapImpl -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: HashMapData, -{ - pub(crate) fn capacity(&self) -> u64 { - self.data.capacity() - } - - pub(crate) fn count(&self) -> u64 { - self.data.count() - } - - pub(crate) fn insert(&mut self, key: K, value: T) -> Result, DbError> { - self.data.transaction(); - let free = self.find_or_free(&key)?; - self.insert_value(free.0, key, value)?; - - if free.1.is_none() { - self.data.set_count(self.data.count() + 1)?; - } - - self.data.commit()?; - - Ok(free.1) - } - - pub(crate) fn iter(&self) -> HashMapIterator { - HashMapIterator:: { - pos: 0, - data: &self.data, - phantom_data: PhantomData, - } - } - - pub(crate) fn remove(&mut self, key: &K) -> Result<(), DbError> { - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - - self.data.transaction(); - - loop { - let record = self.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty => break, - HashMapMetaValue::Valid if record.key == *key => { - self.remove_record(pos)?; - } - HashMapMetaValue::Valid | HashMapMetaValue::Deleted => { - pos = Self::next_pos(pos, self.capacity()); - } - } - } - - self.data.commit() - } - - pub(crate) fn reserve(&mut self, new_capacity: u64) -> Result<(), DbError> { - if self.capacity() < new_capacity { - return self.rehash(new_capacity); - } - - Ok(()) - } - - pub(crate) fn to_hash_map(&self) -> Result, DbError> { - let mut map = std::collections::HashMap::::new(); - map.reserve(self.count() as usize); - - for i in 0..self.capacity() { - let record = self.data.record(i)?; - - if record.meta_value == HashMapMetaValue::Valid { - map.insert(record.key, record.value); - } - } - - Ok(map) - } - - pub(crate) fn value(&self, key: &K) -> Result, DbError> { - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - - loop { - let record = self.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty => return Ok(None), - HashMapMetaValue::Valid if record.key == *key => return Ok(Some(record.value)), - HashMapMetaValue::Valid | HashMapMetaValue::Deleted => { - pos = Self::next_pos(pos, self.capacity()) - } - } - } - } - - fn find_or_free(&mut self, key: &K) -> Result<(u64, Option), DbError> { - if self.max_size() < (self.data.count() + 1) { - self.rehash(self.capacity() * 2)?; - } - - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - - loop { - let record = self.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty | HashMapMetaValue::Deleted => return Ok((pos, None)), - HashMapMetaValue::Valid if record.key == *key => { - return Ok((pos, Some(record.value))) - } - HashMapMetaValue::Valid => pos = Self::next_pos(pos, self.capacity()), - } - } - } - - pub(super) fn insert_value(&mut self, pos: u64, key: K, value: T) -> Result<(), DbError> { - self.data.set_value( - pos, - HashMapKeyValue { - meta_value: HashMapMetaValue::Valid, - key, - value, - }, - ) - } - - pub(super) fn max_size(&self) -> u64 { - self.capacity() * 15 / 16 - } - - pub(super) fn min_size(&self) -> u64 { - self.capacity() * 7 / 16 - } - - pub(super) fn next_pos(pos: u64, capacity: u64) -> u64 { - if pos == capacity - 1 { - 0 - } else { - pos + 1 - } - } - - fn place_new_record( - &self, - new_data: &mut Vec>, - record: HashMapKeyValue, - ) { - let hash = record.key.stable_hash(); - let mut pos = hash % new_data.len() as u64; - - while new_data[pos as usize].meta_value != HashMapMetaValue::Empty { - pos = Self::next_pos(pos, new_data.len() as u64); - } - - new_data[pos as usize] = record; - } - - pub(super) fn rehash(&mut self, mut new_capacity: u64) -> Result<(), DbError> { - new_capacity = max(new_capacity, 64); - - if new_capacity != self.capacity() { - let old_data = self.data.take_values()?; - self.data.transaction(); - self.data - .set_values(self.rehash_old_data(old_data, new_capacity))?; - self.data.commit()?; - } - - Ok(()) - } - - fn rehash_old_data( - &self, - old_data: Vec>, - new_capacity: u64, - ) -> Vec> { - let mut new_data: Vec> = - vec![HashMapKeyValue::::default(); new_capacity as usize]; - - for record in old_data { - if record.meta_value == HashMapMetaValue::Valid { - self.place_new_record(&mut new_data, record); - } - } - - new_data - } - - pub(super) fn remove_record(&mut self, pos: u64) -> Result<(), DbError> { - self.data.set_meta_value(pos, HashMapMetaValue::Deleted)?; - self.data.set_count(self.data.count() - 1)?; - - if 0 != self.data.count() && (self.data.count() - 1) < self.min_size() { - self.rehash(self.capacity() / 2)?; - } - - Ok(()) - } -} diff --git a/src/storage/hash_map_key_value.rs b/src/storage/hash_map_key_value.rs deleted file mode 100644 index b08d5275..00000000 --- a/src/storage/hash_map_key_value.rs +++ /dev/null @@ -1,100 +0,0 @@ -use super::hash_map_meta_value::HashMapMetaValue; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; - -#[derive(Clone, Debug, Default, PartialEq)] -pub(crate) struct HashMapKeyValue -where - K: Clone + Default + Serialize, - T: Clone + Default + Serialize, -{ - pub(super) meta_value: HashMapMetaValue, - pub(super) key: K, - pub(super) value: T, -} - -impl Serialize for HashMapKeyValue -where - K: Clone + Default + Serialize, - T: Clone + Default + Serialize, -{ - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - meta_value: HashMapMetaValue::deserialize(bytes)?, - key: K::deserialize(&bytes[(HashMapMetaValue::serialized_size() as usize)..])?, - value: T::deserialize( - &bytes[((HashMapMetaValue::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.meta_value.serialize()); - data.extend(self.key.serialize()); - data.extend(self.value.serialize()); - - data - } - - fn serialized_size() -> u64 { - HashMapMetaValue::serialized_size() + K::serialized_size() + T::serialized_size() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn derived_from_debug() { - let key_value = HashMapKeyValue::::default(); - - format!("{:?}", key_value); - } - - #[test] - fn derived_from_default() { - let key_value = HashMapKeyValue::::default(); - - assert_eq!( - key_value, - HashMapKeyValue:: { - meta_value: HashMapMetaValue::Empty, - key: 0, - value: 0, - } - ) - } - - #[test] - fn i64_i64() { - let key_value = HashMapKeyValue { - meta_value: HashMapMetaValue::Valid, - key: 1_i64, - value: 10_i64, - }; - let bytes = key_value.serialize(); - let other = HashMapKeyValue::deserialize(&bytes); - - assert_eq!(other, Ok(key_value)); - } - - #[test] - fn out_of_bounds() { - let bytes = vec![0_u8; 16]; - - assert_eq!( - HashMapKeyValue::::deserialize(&bytes) - .unwrap_err() - .description, - "i64 deserialization error: out of bounds" - ); - } - - #[test] - fn serialized_size() { - assert_eq!(HashMapKeyValue::::serialized_size(), 17); - } -} diff --git a/src/storage/hash_map_meta_value.rs b/src/storage/hash_map_meta_value.rs deleted file mode 100644 index d0f13da7..00000000 --- a/src/storage/hash_map_meta_value.rs +++ /dev/null @@ -1,77 +0,0 @@ -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::mem::size_of; - -#[derive(Clone, Default, Debug, PartialEq)] -pub(crate) enum HashMapMetaValue { - #[default] - Empty, - Deleted, - Valid, -} - -impl Serialize for HashMapMetaValue { - fn deserialize(bytes: &[u8]) -> Result { - match bytes.first() { - Some(0) => Ok(HashMapMetaValue::Empty), - Some(1) => Ok(HashMapMetaValue::Valid), - Some(2) => Ok(HashMapMetaValue::Deleted), - _ => Err(DbError::from("value out of bounds")), - } - } - - fn serialize(&self) -> Vec { - match self { - HashMapMetaValue::Empty => vec![0_u8], - HashMapMetaValue::Deleted => vec![2_u8], - HashMapMetaValue::Valid => vec![1_u8], - } - } - - fn serialized_size() -> u64 { - size_of::() as u64 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bad_deserialization() { - assert_eq!( - HashMapMetaValue::deserialize(&[10_u8]), - Err(DbError::from("value out of bounds")) - ); - } - - #[test] - fn derived_from_default() { - assert_eq!(HashMapMetaValue::default(), HashMapMetaValue::Empty); - } - - #[test] - fn derived_from_debug() { - let value = HashMapMetaValue::Deleted; - - format!("{:?}", value); - } - - #[test] - fn serialize() { - let data = vec![ - HashMapMetaValue::Valid, - HashMapMetaValue::Empty, - HashMapMetaValue::Deleted, - ]; - let bytes = data.serialize(); - let other = Vec::::deserialize(&bytes).unwrap(); - - assert_eq!(data, other); - } - - #[test] - fn serialized_size() { - assert_eq!(HashMapMetaValue::serialized_size(), 1); - } -} diff --git a/src/storage/hash_multi_map.rs b/src/storage/hash_multi_map.rs deleted file mode 100644 index f4ba76ef..00000000 --- a/src/storage/hash_multi_map.rs +++ /dev/null @@ -1,278 +0,0 @@ -use super::hash_map_data_memory::HashMapDataMemory; -use super::hash_map_impl::HashMapImpl; -use super::hash_map_key_value::HashMapKeyValue; -use super::hash_multi_map_impl::HashMultiMapImpl; -use super::StableHash; -use agdb_serialize::Serialize; -use std::hash::Hash; -use std::marker::PhantomData; - -pub(crate) type HashMultiMap = HashMultiMapImpl>; - -#[allow(dead_code)] -impl HashMultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, -{ - pub(crate) fn new() -> HashMultiMap { - HashMultiMap:: { - map: HashMapImpl::> { - data: HashMapDataMemory:: { - data: vec![HashMapKeyValue::::default()], - count: 0, - }, - phantom_data: PhantomData, - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn insert() { - let mut map = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::new(); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 0); - } - - #[test] - fn remove_value() { - let mut map = HashMultiMap::::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 = HashMultiMap::::new(); - - map.remove_value(&5, &10).unwrap(); - - assert_eq!(map.count(), 0); - } - - #[test] - fn remove_missing() { - let mut map = HashMultiMap::::new(); - - assert_eq!(map.count(), 0); - assert_eq!(map.remove_key(&0), Ok(())); - assert_eq!(map.count(), 0); - } - - #[test] - fn remove_shrinks_capacity() { - let mut map = HashMultiMap::::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 reserve_larger() { - let mut map = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::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 = HashMultiMap::::new(); - - assert_eq!(map.value(&0), Ok(None)); - } - - #[test] - fn values_at_end() { - let mut map = HashMultiMap::::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/src/storage/hash_multi_map_impl.rs b/src/storage/hash_multi_map_impl.rs deleted file mode 100644 index 36dae4db..00000000 --- a/src/storage/hash_multi_map_impl.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::hash_map_data::HashMapData; -use super::hash_map_impl::HashMapImpl; -use super::hash_map_iterator::HashMapIterator; -use super::hash_map_meta_value::HashMapMetaValue; -use super::StableHash; -use agdb_db_error::DbError; -use agdb_serialize::Serialize; -use std::hash::Hash; - -pub(crate) struct HashMultiMapImpl -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: HashMapData, -{ - pub(super) map: HashMapImpl, -} - -#[allow(dead_code)] -impl HashMultiMapImpl -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: HashMapData, -{ - pub(crate) fn capacity(&self) -> u64 { - self.map.capacity() - } - - pub(crate) fn count(&self) -> u64 { - self.map.count() - } - - pub(crate) fn insert(&mut self, key: K, value: T) -> Result<(), DbError> { - self.map.data.transaction(); - let free = self.find_free(&key)?; - self.map.insert_value(free, key, value)?; - self.map.data.set_count(self.count() + 1)?; - self.map.data.commit() - } - - pub(crate) fn iter(&self) -> HashMapIterator { - self.map.iter() - } - - pub(crate) fn remove_key(&mut self, key: &K) -> Result<(), DbError> { - let hash = key.stable_hash(); - let mut pos = hash % self.map.data.capacity(); - - self.map.data.transaction(); - - loop { - let record = self.map.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty => break, - HashMapMetaValue::Valid if record.key == *key => { - self.map.remove_record(pos)?; - } - HashMapMetaValue::Valid | HashMapMetaValue::Deleted => { - pos = HashMapImpl::::next_pos(pos, self.capacity()); - } - } - - pos = HashMapImpl::::next_pos(pos, self.capacity()); - } - - self.map.data.commit() - } - - pub(crate) fn remove_value(&mut self, key: &K, value: &T) -> Result<(), DbError> { - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - - loop { - let record = self.map.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty => break, - HashMapMetaValue::Valid if record.key == *key && record.value == *value => { - self.map.data.transaction(); - self.map.remove_record(pos)?; - self.map.data.commit()?; - break; - } - HashMapMetaValue::Valid | HashMapMetaValue::Deleted => { - pos = HashMapImpl::::next_pos(pos, self.capacity()); - } - } - } - - Ok(()) - } - - pub(crate) fn reserve(&mut self, new_capacity: u64) -> Result<(), DbError> { - self.map.reserve(new_capacity) - } - - pub(crate) fn value(&self, key: &K) -> Result, DbError> { - self.map.value(key) - } - - pub(crate) fn values(&self, key: &K) -> Result, DbError> { - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - let mut values: Vec = vec![]; - - loop { - let record = self.map.data.record(pos)?; - - match record.meta_value { - HashMapMetaValue::Empty => break, - HashMapMetaValue::Valid if record.key == *key => values.push(record.value.clone()), - HashMapMetaValue::Valid | HashMapMetaValue::Deleted => {} - } - - pos = HashMapImpl::::next_pos(pos, self.capacity()); - } - - Ok(values) - } - - fn find_free(&mut self, key: &K) -> Result { - if self.map.max_size() < (self.count() + 1) { - self.map.rehash(self.capacity() * 2)?; - } - - let hash = key.stable_hash(); - let mut pos = hash % self.capacity(); - - loop { - let meta_value = self.map.data.meta_value(pos)?; - - match meta_value { - HashMapMetaValue::Empty | HashMapMetaValue::Deleted => return Ok(pos), - HashMapMetaValue::Valid => { - pos = HashMapImpl::::next_pos(pos, self.capacity()); - } - } - } - } -} diff --git a/src/storage/stable_hash.rs b/src/storage/stable_hash.rs deleted file mode 100644 index c9ca29a9..00000000 --- a/src/storage/stable_hash.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub(crate) trait StableHash { - fn stable_hash(&self) -> u64; -} - -impl StableHash for i64 { - fn stable_hash(&self) -> u64 { - *self as u64 - } -} - -impl StableHash for u64 { - fn stable_hash(&self) -> 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/storage/storage_hash_map.rs b/src/storage/storage_hash_map.rs deleted file mode 100644 index 33b97cbd..00000000 --- a/src/storage/storage_hash_map.rs +++ /dev/null @@ -1,449 +0,0 @@ -use super::hash_map_data_storage::HashMapDataStorage; -use super::hash_map_impl::HashMapImpl; -use super::hash_map_key_value::HashMapKeyValue; -use super::stable_hash::StableHash; -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::hash::Hash; -use std::marker::PhantomData; -use std::mem::size_of; -use std::rc::Rc; - -pub(crate) type StorageHashMap = - HashMapImpl>; - -#[allow(dead_code)] -impl StorageHashMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Serialize, - Data: Storage, -{ - pub(crate) fn storage_index(&self) -> StorageIndex { - self.data.storage_index.clone() - } -} - -impl TryFrom>> for StorageHashMap -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![HashMapKeyValue::::default()], - )?; - - Ok(Self { - data: HashMapDataStorage:: { - storage, - storage_index, - count: 0, - capacity: 1, - phantom_data: PhantomData, - }, - phantom_data: PhantomData, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageHashMap -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 { - data: HashMapDataStorage:: { - storage: storage_with_index.0, - storage_index: storage_with_index.1, - count, - capacity, - phantom_data: PhantomData, - }, - phantom_data: PhantomData, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use agdb_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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::try_from((storage, index)).unwrap(); - - let mut expected = std::collections::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!( - StorageHashMap::::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 = StorageHashMap::::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 = StorageHashMap::::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/storage/storage_hash_multi_map.rs b/src/storage/storage_hash_multi_map.rs deleted file mode 100644 index 8b734507..00000000 --- a/src/storage/storage_hash_multi_map.rs +++ /dev/null @@ -1,472 +0,0 @@ -use super::hash_map_data_memory::HashMapDataMemory; -use super::hash_map_data_storage::HashMapDataStorage; -use super::hash_map_impl::HashMapImpl; -use super::hash_multi_map::HashMultiMap; -use super::hash_multi_map_impl::HashMultiMapImpl; -use super::stable_hash::StableHash; -use super::StorageHashMap; -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::hash::Hash; -use std::marker::PhantomData; -use std::rc::Rc; - -pub(crate) type StorageHashMultiMap = - HashMultiMapImpl>; - -#[allow(dead_code)] -impl StorageHashMultiMap -where - K: Clone + Default + Eq + Hash + PartialEq + StableHash + Serialize, - T: Clone + Default + Eq + PartialEq + Serialize, - Data: Storage, -{ - pub(crate) fn storage_index(&self) -> StorageIndex { - self.map.data.storage_index.clone() - } - - pub(crate) fn to_hash_multi_map(&self) -> Result, DbError> { - Ok(HashMultiMap:: { - map: HashMapImpl::> { - data: HashMapDataMemory:: { - data: self.map.data.values()?, - count: self.map.data.count, - }, - phantom_data: PhantomData, - }, - }) - } -} - -impl TryFrom>> for StorageHashMultiMap -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: StorageHashMap::::try_from(storage)?, - }) - } -} - -impl TryFrom<(Rc>, StorageIndex)> for StorageHashMultiMap -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: StorageHashMap::::try_from(storage_with_index)?, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use agdb_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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::try_from(storage).unwrap(); - - map.remove_key(&5).unwrap(); - - assert_eq!(map.count(), 0); - } - - #[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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::try_from(storage).unwrap(); - - map.remove_value(&5, &10).unwrap(); - - assert_eq!(map.count(), 0); - } - - #[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 = StorageHashMultiMap::::try_from(storage).unwrap(); - - assert_eq!(map.count(), 0); - assert_eq!(map.remove_key(&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 = StorageHashMultiMap::::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 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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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_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 = StorageHashMultiMap::::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_hash_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_hash_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 = StorageHashMultiMap::::try_from(storage).unwrap(); - let other = map.to_hash_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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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!( - StorageHashMultiMap::::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 = StorageHashMultiMap::::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 = StorageHashMultiMap::::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))); - } -}