diff --git a/agdb/src/collections/bit_set.rs b/agdb/src/collections/bit_set.rs index e5a92d1e..2ba8399f 100644 --- a/agdb/src/collections/bit_set.rs +++ b/agdb/src/collections/bit_set.rs @@ -9,7 +9,7 @@ impl BitSet { self.data.clear(); } - pub fn insert(&mut self, value: u64) { + pub fn set(&mut self, value: u64) { let byte_index = value as usize / 8; let bit_index = value as usize % 8; @@ -24,8 +24,14 @@ impl BitSet { Self { data: vec![] } } + pub fn with_capacity(capacity: u64) -> Self { + Self { + data: Vec::with_capacity(capacity as usize / 8), + } + } + #[allow(dead_code)] - pub fn remove(&mut self, value: u64) { + pub fn unset(&mut self, value: u64) { let byte_index = value as usize / 8; if byte_index < self.data.len() { @@ -55,7 +61,7 @@ mod tests { fn clear() { let mut bitset = BitSet::new(); - bitset.insert(10_u64); + bitset.set(10_u64); bitset.clear(); assert!(!bitset.value(10_u64)); @@ -72,7 +78,7 @@ mod tests { assert!(!bitset.value(10_u64)); - bitset.insert(10_u64); + bitset.set(10_u64); assert!(bitset.value(10_u64)); } @@ -85,9 +91,9 @@ mod tests { assert!(!bitset.value(11_u64)); assert!(!bitset.value(2_u64)); - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); + bitset.set(10_u64); + bitset.set(11_u64); + bitset.set(2_u64); assert!(bitset.value(10_u64)); assert!(bitset.value(11_u64)); @@ -95,14 +101,14 @@ mod tests { } #[test] - fn remove() { + fn unset() { let mut bitset = BitSet::new(); - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); + bitset.set(10_u64); + bitset.set(11_u64); + bitset.set(2_u64); - bitset.remove(11_u64); + bitset.unset(11_u64); assert!(bitset.value(10_u64)); assert!(!bitset.value(11_u64)); @@ -113,11 +119,11 @@ mod tests { fn remove_unset() { let mut bitset = BitSet::new(); - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); + bitset.set(10_u64); + bitset.set(11_u64); + bitset.set(2_u64); - bitset.remove(9_u64); + bitset.unset(9_u64); assert!(bitset.value(10_u64)); assert!(bitset.value(11_u64)); @@ -128,11 +134,11 @@ mod tests { fn remove_beyond_length() { let mut bitset = BitSet::new(); - bitset.insert(10_u64); - bitset.insert(11_u64); - bitset.insert(2_u64); + bitset.set(10_u64); + bitset.set(11_u64); + bitset.set(2_u64); - bitset.remove(150_u64); + bitset.unset(150_u64); assert!(bitset.value(10_u64)); assert!(bitset.value(11_u64)); @@ -143,7 +149,7 @@ mod tests { fn value_missing() { let mut bitset = BitSet::new(); - bitset.insert(5_u64); + bitset.set(5_u64); assert!(!bitset.value(2_u64)); } diff --git a/agdb/src/collections/multi_map.rs b/agdb/src/collections/multi_map.rs index db26de5e..c8cabe00 100644 --- a/agdb/src/collections/multi_map.rs +++ b/agdb/src/collections/multi_map.rs @@ -1,3 +1,4 @@ +use crate::collections::bit_set::BitSet; use crate::collections::map::DbMapData; use crate::collections::map::MapData; use crate::collections::map::MapIterator; @@ -460,14 +461,20 @@ where storage: &mut Storage, i: &mut u64, new_capacity: u64, - empty_list: &mut [bool], + occupancy: &mut BitSet, ) -> Result<(), DbError> { + if *i < new_capacity && occupancy.value(*i) { + *i += 1; + return Ok(()); + } + let key = self.data.key(storage, *i)?; - let mut pos = key.stable_hash() % new_capacity; + let hash = key.stable_hash(); + let mut pos = hash % new_capacity; loop { - if empty_list[pos as usize] { - empty_list[pos as usize] = false; + if !occupancy.value(pos) { + occupancy.set(pos); self.data.swap(storage, *i, pos)?; if *i == pos { @@ -493,12 +500,12 @@ where state: MapValueState, i: &mut u64, new_capacity: u64, - empty_list: &mut [bool], + occupancy: &mut BitSet, ) -> Result<(), DbError> { match state { MapValueState::Empty => self.rehash_empty(i), MapValueState::Deleted => self.rehash_deleted(storage, i, new_capacity), - MapValueState::Valid => self.rehash_valid(storage, i, new_capacity, empty_list), + MapValueState::Valid => self.rehash_valid(storage, i, new_capacity, occupancy), } } @@ -510,10 +517,15 @@ where new_capacity: u64, ) -> Result<(), DbError> { let mut i = 0_u64; - let mut empty_list = vec![true; new_capacity as usize]; + let mut occupancy = BitSet::with_capacity(new_capacity); while i != current_capacity { - self.rehash_value(storage, self.data.state(storage, i)?, &mut i, new_capacity, &mut empty_list)?; + self.rehash_value( + storage, + self.data.state(storage, i)?, + &mut i, + new_capacity, + &mut occupancy)?; } Ok(()) @@ -814,4 +826,26 @@ mod tests { assert_eq!(storage.len(), 0) } + + #[test] + fn empty_pos_after_rehash() { + let mut storage: Storage = Storage::new("test").unwrap(); + let mut map = MultiMapStorage::::new(&mut storage).unwrap(); + let range = 1..200; + + let users: Vec<(String, String)> = range + .clone() + .rev() + .map(|i| (format!("db_user{i}"), i.to_string())) + .collect(); + + for (user, value) in users { + map.insert(&mut storage, &user, &value).unwrap(); + } + + for i in range { + let value = map.value(&storage, &format!("db_user{i}")).unwrap(); + assert_eq!(value, Some(i.to_string())); + } + } } diff --git a/agdb/src/db/db_index.rs b/agdb/src/db/db_index.rs index faba6267..dfdaaa91 100644 --- a/agdb/src/db/db_index.rs +++ b/agdb/src/db/db_index.rs @@ -17,7 +17,6 @@ pub struct DbIndexStorageIndex { ids_index: StorageIndex, } -#[allow(dead_code)] pub struct DbIndex where D: StorageData, @@ -27,7 +26,6 @@ where ids: MultiMapStorage, } -#[allow(dead_code)] pub struct DbIndexes where D: StorageData, @@ -36,7 +34,6 @@ where storage_indexes: DbVec, } -#[allow(dead_code)] impl DbIndex where D: StorageData, diff --git a/agdb/src/graph_search/path_search.rs b/agdb/src/graph_search/path_search.rs index c2db1985..2ab4dd90 100644 --- a/agdb/src/graph_search/path_search.rs +++ b/agdb/src/graph_search/path_search.rs @@ -138,7 +138,7 @@ where if index.0 == self.destination.0 { std::mem::swap(&mut self.result, &mut self.current_path.elements); } else { - self.visited.insert(index.as_u64()); + self.visited.set(index.as_u64()); self.expand(index)?; } } diff --git a/agdb/src/graph_search/search_impl.rs b/agdb/src/graph_search/search_impl.rs index 71becce4..21463c4e 100644 --- a/agdb/src/graph_search/search_impl.rs +++ b/agdb/src/graph_search/search_impl.rs @@ -162,7 +162,7 @@ where fn visit_index(&mut self, index: &SearchIndex) -> bool { let visited = self.visited.value(index.index.as_u64()); - self.visited.insert(index.index.as_u64()); + self.visited.set(index.index.as_u64()); visited } diff --git a/agdb/src/test_utilities.rs b/agdb/src/test_utilities.rs index 51c17bb1..c3798eb3 100644 --- a/agdb/src/test_utilities.rs +++ b/agdb/src/test_utilities.rs @@ -1,2 +1 @@ -pub mod collision_value; pub mod test_file; diff --git a/agdb/src/test_utilities/collision_value.rs b/agdb/src/test_utilities/collision_value.rs deleted file mode 100644 index 7a092286..00000000 --- a/agdb/src/test_utilities/collision_value.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::collections::vec::VecValue; -use crate::storage::Storage; -use crate::utilities::serialize::Serialize; -use crate::utilities::serialize::SerializeStatic; -use crate::utilities::stable_hash::StableHash; -use crate::DbError; -use crate::StorageData; - -#[derive(Clone, Debug, Eq, PartialEq)] -struct CollisionValue { - pub value: T, -} - -impl CollisionValue { - pub fn new(value: T) -> Self { - CollisionValue { value } - } -} - -impl StableHash for CollisionValue { - fn stable_hash(&self) -> u64 { - 1 - } -} - -impl Serialize for CollisionValue -where - T: Serialize, -{ - fn serialize(&self) -> Vec { - self.value.serialize() - } - - fn deserialize(bytes: &[u8]) -> Result { - Ok(Self { - value: T::deserialize(bytes)?, - }) - } - - fn serialized_size(&self) -> u64 { - self.value.serialized_size() - } -} - -impl SerializeStatic for CollisionValue where T: SerializeStatic {} - -impl VecValue for CollisionValue -where - T: VecValue, -{ - fn store(&self, storage: &mut Storage) -> Result, DbError> { - self.value.store(storage) - } - - fn load(storage: &Storage, bytes: &[u8]) -> Result { - Ok(Self { - value: T::load(storage, bytes)?, - }) - } - - fn remove(storage: &mut Storage, bytes: &[u8]) -> Result<(), DbError> { - T::remove(storage, bytes) - } - - fn storage_len() -> u64 { - T::storage_len() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{storage::file_storage::FileStorage, test_utilities::test_file::TestFile}; - - #[test] - #[allow(clippy::redundant_clone)] - fn derived_from_clone() { - let value = CollisionValue::new(1_i64); - let other = value.clone(); - assert_eq!(value, other); - } - - #[test] - fn derived_from_debug() { - let value = CollisionValue::new(1_i64); - format!("{value:?}"); - } - - #[test] - fn serialize() { - let value = CollisionValue::new(1_i64); - let bytes = value.serialize(); - - assert_eq!(bytes.len() as u64, value.serialized_size()); - - let other = CollisionValue::deserialize(&bytes).unwrap(); - - assert_eq!(value, other); - } - - #[test] - fn stable_hash() { - let value = CollisionValue::new(1_i64); - - assert_eq!(value.stable_hash(), 1_u64); - } - - #[test] - fn storage_value() { - let test_file = TestFile::new(); - let mut storage = Storage::::new(test_file.file_name()).unwrap(); - let value = CollisionValue::::new(1); - let bytes = value.store(&mut storage).unwrap(); - CollisionValue::::remove(&mut storage, &bytes).unwrap(); - let other = CollisionValue::::load(&storage, &bytes).unwrap(); - - assert_eq!(value, other); - - assert_eq!( - CollisionValue::::storage_len(), - i64::serialized_size_static() - ); - } -} diff --git a/agdb_server/openapi/schema.json b/agdb_server/openapi/schema.json index 5de14b34..f225feb1 100644 --- a/agdb_server/openapi/schema.json +++ b/agdb_server/openapi/schema.json @@ -6,7 +6,7 @@ "license": { "name": "Apache-2.0" }, - "version": "0.1.0" + "version": "0.6.4" }, "servers": [ { diff --git a/agdb_server/src/db_pool.rs b/agdb_server/src/db_pool.rs index aa500738..ecb9deb1 100644 --- a/agdb_server/src/db_pool.rs +++ b/agdb_server/src/db_pool.rs @@ -47,7 +47,7 @@ const SERVER_DB_NAME: &str = "mapped:agdb_server.agdb"; #[derive(UserValue)] pub(crate) struct ServerUser { pub(crate) db_id: Option, - pub(crate) name: String, + pub(crate) username: String, pub(crate) password: Vec, pub(crate) salt: Vec, pub(crate) token: String, @@ -83,6 +83,9 @@ impl DbPool { let admin_password = Password::create(&config.admin, &config.admin); db_pool.0.server_db.get_mut()?.transaction_mut(|t| { + t.exec_mut(&QueryBuilder::insert().index("username").query())?; + t.exec_mut(&QueryBuilder::insert().index("token").query())?; + t.exec_mut( &QueryBuilder::insert() .nodes() @@ -95,7 +98,7 @@ impl DbPool { .nodes() .values(&ServerUser { db_id: None, - name: config.admin.clone(), + username: config.admin.clone(), password: admin_password.password.to_vec(), salt: admin_password.user_salt.to_vec(), token: String::new(), @@ -282,7 +285,7 @@ impl DbPool { pub(crate) fn change_password(&self, mut user: ServerUser, new_password: &str) -> ServerResult { password::validate_password(new_password)?; - let pswd = Password::create(&user.name, new_password); + let pswd = Password::create(&user.username, new_password); user.password = pswd.password.to_vec(); user.salt = pswd.user_salt.to_vec(); self.save_user(user)?; @@ -475,14 +478,14 @@ impl DbPool { .db()? .exec( &QueryBuilder::select() - .values(vec!["name".into()]) + .values(vec!["username".into()]) .ids( QueryBuilder::search() .from("users") .where_() .distance(CountComparison::Equal(2)) .and() - .keys(vec!["name".into()]) + .keys(vec!["username".into()]) .query(), ) .query(), @@ -549,18 +552,7 @@ impl DbPool { pub(crate) fn find_user_id(&self, name: &str) -> ServerResult { Ok(self .db()? - .exec( - &QueryBuilder::search() - .depth_first() - .from("users") - .limit(1) - .where_() - .distance(CountComparison::Equal(2)) - .and() - .key("name") - .value(Comparison::Equal(name.into())) - .query(), - )? + .exec(&QueryBuilder::search().index("username").value(name).query())? .elements .first() .ok_or(user_not_found(name))? @@ -570,18 +562,7 @@ impl DbPool { pub(crate) fn find_user_id_by_token(&self, token: &str) -> ServerResult { Ok(self .db()? - .exec( - &QueryBuilder::search() - .depth_first() - .from("users") - .limit(1) - .where_() - .distance(CountComparison::Equal(2)) - .and() - .key("token") - .value(Comparison::Equal(token.into())) - .query(), - )? + .exec(&QueryBuilder::search().index("token").value(token).query())? .elements .first() .ok_or(format!("No user found for token '{token}'"))? @@ -887,7 +868,7 @@ impl DbPool { .db()? .exec( &QueryBuilder::select() - .values(vec!["name".into()]) + .values(vec!["username".into()]) .ids(id) .query(), )? @@ -915,7 +896,7 @@ impl DbPool { .where_() .distance(CountComparison::Equal(2)) .and() - .key("name") + .key("username") .value(Comparison::Equal(name.into())) .query(), )? diff --git a/agdb_server/src/routes/admin/user.rs b/agdb_server/src/routes/admin/user.rs index 2da4d594..7c3c8ec0 100644 --- a/agdb_server/src/routes/admin/user.rs +++ b/agdb_server/src/routes/admin/user.rs @@ -46,7 +46,7 @@ pub(crate) async fn add( db_pool.add_user(ServerUser { db_id: None, - name: username.clone(), + username: username.clone(), password: pswd.password.to_vec(), salt: pswd.user_salt.to_vec(), token: String::new(), diff --git a/agdb_server/src/routes/user.rs b/agdb_server/src/routes/user.rs index 5e93f044..e004cdd5 100644 --- a/agdb_server/src/routes/user.rs +++ b/agdb_server/src/routes/user.rs @@ -25,7 +25,7 @@ pub(crate) async fn login( let user = db_pool .find_user(&request.username) .map_err(|_| ServerError::new(StatusCode::UNAUTHORIZED, "unuauthorized"))?; - let pswd = Password::new(&user.name, &user.password, &user.salt)?; + let pswd = Password::new(&user.username, &user.password, &user.salt)?; if !pswd.verify_password(&request.password) { return Err(ServerError::new(StatusCode::UNAUTHORIZED, "unuauthorized")); @@ -71,7 +71,7 @@ pub(crate) async fn change_password( Json(request): Json, ) -> ServerResponse { let user = db_pool.get_user(user.0)?; - let old_pswd = Password::new(&user.name, &user.password, &user.salt)?; + let old_pswd = Password::new(&user.username, &user.password, &user.salt)?; if !old_pswd.verify_password(&request.password) { return Err(ServerError::new(StatusCode::UNAUTHORIZED, "unuauthorized"));