From 6e9c1c121351c8383f3dda657f97fda71478222f Mon Sep 17 00:00:00 2001 From: Christian Lincoln Date: Sat, 10 Oct 2026 01:18:56 +0100 Subject: [PATCH] Tests for map lib, small and all passing. --- map/src/lib.rs | 120 ++++++++++++++++++++++++++++++++++++------------- src/lib.rs | 9 +++- 2 files changed, 96 insertions(+), 33 deletions(-) diff --git a/map/src/lib.rs b/map/src/lib.rs index d6f5f03..c7f806c 100644 --- a/map/src/lib.rs +++ b/map/src/lib.rs @@ -94,7 +94,7 @@ pub fn get ,K: Hashes>(map: &[Entry], index: K) if home.entry.matches(&index) { Some(&home.entry) } else { - for i in 1..home.displacement as usize { + for i in 1..=home.displacement as usize { let neighbour = &map[(location + i) % map.len()]; if neighbour.entry.matches(&index) { return Some(&neighbour.entry); @@ -110,10 +110,11 @@ impl DisplacementSet { &mut self.map, (0..len).map(|_| Entry::VACANT).collect(), // todo: I don't like that this may go over all memory ); + self.map_count = 0; self.map_bounds = self.lower()..self.upper(); for Entry { entry, .. } in old { if !entry.is_vacant() { - self.set(entry) + self.rawset(entry) } } } @@ -136,47 +137,45 @@ impl DisplacementSet { self.exchange(self.map.len() / 2) } } - pub fn set(&mut self, entry: E) { - #[cfg(feature = "assertions")] - assert!(!entry.is_vacant()); - if !self.map_bounds.contains(&(self.map_count + 1)) { - // make sure the table is appropriately sized - self.ensure(); - } + fn rawset(&mut self, entry: E) { let range = self.map.len(); let home = entry.hashed() as usize % range; + macro_rules! insert { + ($entry:expr) => { + $entry.home = home; + $entry.entry = entry; + self.map_count += 1; + } + } + macro_rules! replace { + ($entry:expr) => { + $entry.entry = entry; + } + } if self.map[home].entry.is_vacant() { // attempt to place it directly in an empty space - // INSERT - self.map[home].home = home; - self.map[home].entry = entry; - self.map[home].displacement = self.map[home].displacement.max(0); - self.map_count += 1; + insert!(self.map[home]); } else if self.map[home].entry.matches(&entry) { // attempt to replace it directly - // REPLACE - self.map[home].entry = entry; // home.item.soft_drop() + replace!(self.map[home]); } else { // attempt to replace it in a collided neighbour location for i in 1..self.map[home].displacement + 1 { let neighbour = &mut self.map[(home + i as usize) % range]; // todo: is mod expensive? if neighbour.entry.matches(&entry) { // found where it was displaced to - // REPLACE - neighbour.entry = entry; + replace!(neighbour); return; } } + // could not find a space! // at this point it must be added, probe to place in an empty space for j in self.map[home].displacement + 1..Displacement::MAX - 1 { let neighbour = &mut self.map[(home + j as usize) % range]; if neighbour.entry.is_vacant() { // new empty slot hooray! - // INSERT - neighbour.entry = entry; - neighbour.home = home; + insert!(neighbour); self.map[home].displacement = j; - self.map_count += 1; return; } } @@ -186,29 +185,34 @@ impl DisplacementSet { let mut free: Option = None; for k in 0..self.map.len() { let neighbour = &mut self.map[k]; - if neighbour.entry.is_vacant() && free.is_none() { + if neighbour.entry.is_vacant() { free = Some(k); } else if neighbour.entry.matches(&entry) { - // REPLACE - neighbour.entry = entry; + replace!(neighbour); return; } } if let Some(k) = free { - // INSERT - self.map[k].home = home; - self.map[k].entry = entry; + insert!(self.map[k]); self.map[home].displacement = Displacement::MAX; - self.map_count += 1; return; } - // must resize + // must resize (also impossible) #[cfg(feature = "messages")] eprintln!("\n\tResizing..."); self.resize((self.map.len() + 4) * 2); self.set(entry); } } + pub fn set(&mut self, entry: E) { + #[cfg(feature = "assertions")] + assert!(!entry.is_vacant()); + if !self.map_bounds.contains(&(self.map_count + 1)) { + // make sure the table is appropriately sized + self.ensure(); + } + self.rawset(entry) + } pub fn remove(&mut self, index: K) where E: Equivalent, @@ -225,7 +229,7 @@ impl DisplacementSet { } else { if self.map[home].displacement != Displacement::MAX { let mut largest = 0; - for i in 1..self.map[home].displacement { + for i in 1..=self.map[home].displacement { let neighbour = &mut self.map[(home + i as usize) % range]; // todo: maybe mod slows this down if neighbour.entry.matches(&index) { neighbour.entry = E::VACANT; @@ -290,7 +294,11 @@ pub struct MapEntry(pub Option<(K, V)>); impl Equivalent for MapEntry { fn matches(&self, other: &K) -> bool { - &self.0.as_ref().unwrap().0 == other + if let Some((ref key,_)) = self.0 { + key == other + } else { + false + } } } @@ -341,4 +349,52 @@ impl Debug for Map { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("Map")) // todo: debug and print the entire thing? } +} + +#[cfg(test)] +mod tests { + use crate::Map; + + macro_rules! str { + ($string:expr) => { + $string.to_string() + }; + } + #[test] + fn simple() { + let mut map: Map = Map::new(); + map.set(str!("name"),str!("bob")); + map.set(str!("age"),str!("43")); + map.set(str!("species"),str!("unknown")); + assert_eq!(*map.get(str!("name")).unwrap(),str!("bob")); + assert_eq!(*map.get(str!("age")).unwrap(),str!("43")); + assert_eq!(*map.get(str!("species")).unwrap(),str!("unknown")); + assert!(map.get(str!("gender")).is_none()); + map.remove(str!("species")); + assert!(map.get(str!("species")).is_none()); + map.remove(str!("name")); + assert!(map.get(str!("name")).is_none()); + assert!(map.get(str!("age")).is_some()); + } + + // to flag up + #[test] + fn load() { + let mut map: Map = Map::new(); + let mut count = 0; + for i in 1..100000 { + let before = map.0.map_count; + map.set(i.to_string(),i); + assert_eq!(map.0.map_count,before+1); + count+=1; + } + for i in 1..100000 { + if i % 2 == 0 { + map.remove(i.to_string()); + } + } + for i in 1..100000 { + assert_eq!(map.get(i.to_string()).is_none(), i % 2 == 0); + } + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 88427c4..2ce8d2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,10 @@ pub use map; #[cfg(test)] mod tests { - use map::{TableEntry,MapEntry,Entry}; + use map::{Entry, MapEntry}; extern crate self as deimos; + // Makes a constant map #[test] fn simple() { static MAP: &[Entry>] = &deimos::macros::map! { @@ -15,4 +16,10 @@ mod tests { "greetings" => || println!("salutations"), }; } + + // Makes and destroys lots of elements + #[test] + fn maker() { + + } } \ No newline at end of file