Tests for map lib, small and all passing.

This commit is contained in:
Christian Lincoln 2026-10-10 01:18:56 +01:00
parent 76a9c2259e
commit 6e9c1c1213
2 changed files with 96 additions and 33 deletions

View file

@ -94,7 +94,7 @@ pub fn get<E: TableEntry + Equivalent<K> ,K: Hashes>(map: &[Entry<E>], index: K)
if home.entry.matches(&index) { if home.entry.matches(&index) {
Some(&home.entry) Some(&home.entry)
} else { } else {
for i in 1..home.displacement as usize { for i in 1..=home.displacement as usize {
let neighbour = &map[(location + i) % map.len()]; let neighbour = &map[(location + i) % map.len()];
if neighbour.entry.matches(&index) { if neighbour.entry.matches(&index) {
return Some(&neighbour.entry); return Some(&neighbour.entry);
@ -110,10 +110,11 @@ impl<E: TableEntry> DisplacementSet<E> {
&mut self.map, &mut self.map,
(0..len).map(|_| Entry::VACANT).collect(), // todo: I don't like that this may go over all memory (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(); self.map_bounds = self.lower()..self.upper();
for Entry { entry, .. } in old { for Entry { entry, .. } in old {
if !entry.is_vacant() { if !entry.is_vacant() {
self.set(entry) self.rawset(entry)
} }
} }
} }
@ -136,47 +137,45 @@ impl<E: TableEntry> DisplacementSet<E> {
self.exchange(self.map.len() / 2) self.exchange(self.map.len() / 2)
} }
} }
pub fn set(&mut self, entry: E) { fn rawset(&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();
}
let range = self.map.len(); let range = self.map.len();
let home = entry.hashed() as usize % range; 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() { if self.map[home].entry.is_vacant() {
// attempt to place it directly in an empty space // attempt to place it directly in an empty space
// INSERT insert!(self.map[home]);
self.map[home].home = home;
self.map[home].entry = entry;
self.map[home].displacement = self.map[home].displacement.max(0);
self.map_count += 1;
} else if self.map[home].entry.matches(&entry) { } else if self.map[home].entry.matches(&entry) {
// attempt to replace it directly // attempt to replace it directly
// REPLACE replace!(self.map[home]);
self.map[home].entry = entry; // home.item.soft_drop()
} else { } else {
// attempt to replace it in a collided neighbour location // attempt to replace it in a collided neighbour location
for i in 1..self.map[home].displacement + 1 { for i in 1..self.map[home].displacement + 1 {
let neighbour = &mut self.map[(home + i as usize) % range]; // todo: is mod expensive? let neighbour = &mut self.map[(home + i as usize) % range]; // todo: is mod expensive?
if neighbour.entry.matches(&entry) { if neighbour.entry.matches(&entry) {
// found where it was displaced to // found where it was displaced to
// REPLACE replace!(neighbour);
neighbour.entry = entry;
return; return;
} }
} }
// could not find a space!
// at this point it must be added, probe to place in an empty 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 { for j in self.map[home].displacement + 1..Displacement::MAX - 1 {
let neighbour = &mut self.map[(home + j as usize) % range]; let neighbour = &mut self.map[(home + j as usize) % range];
if neighbour.entry.is_vacant() { if neighbour.entry.is_vacant() {
// new empty slot hooray! // new empty slot hooray!
// INSERT insert!(neighbour);
neighbour.entry = entry;
neighbour.home = home;
self.map[home].displacement = j; self.map[home].displacement = j;
self.map_count += 1;
return; return;
} }
} }
@ -186,29 +185,34 @@ impl<E: TableEntry> DisplacementSet<E> {
let mut free: Option<usize> = None; let mut free: Option<usize> = None;
for k in 0..self.map.len() { for k in 0..self.map.len() {
let neighbour = &mut self.map[k]; let neighbour = &mut self.map[k];
if neighbour.entry.is_vacant() && free.is_none() { if neighbour.entry.is_vacant() {
free = Some(k); free = Some(k);
} else if neighbour.entry.matches(&entry) { } else if neighbour.entry.matches(&entry) {
// REPLACE replace!(neighbour);
neighbour.entry = entry;
return; return;
} }
} }
if let Some(k) = free { if let Some(k) = free {
// INSERT insert!(self.map[k]);
self.map[k].home = home;
self.map[k].entry = entry;
self.map[home].displacement = Displacement::MAX; self.map[home].displacement = Displacement::MAX;
self.map_count += 1;
return; return;
} }
// must resize // must resize (also impossible)
#[cfg(feature = "messages")] #[cfg(feature = "messages")]
eprintln!("\n\tResizing..."); eprintln!("\n\tResizing...");
self.resize((self.map.len() + 4) * 2); self.resize((self.map.len() + 4) * 2);
self.set(entry); 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<K>(&mut self, index: K) pub fn remove<K>(&mut self, index: K)
where where
E: Equivalent<K>, E: Equivalent<K>,
@ -225,7 +229,7 @@ impl<E: TableEntry> DisplacementSet<E> {
} else { } else {
if self.map[home].displacement != Displacement::MAX { if self.map[home].displacement != Displacement::MAX {
let mut largest = 0; 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 let neighbour = &mut self.map[(home + i as usize) % range]; // todo: maybe mod slows this down
if neighbour.entry.matches(&index) { if neighbour.entry.matches(&index) {
neighbour.entry = E::VACANT; neighbour.entry = E::VACANT;
@ -290,7 +294,11 @@ pub struct MapEntry<K: Hashes + PartialEq, V>(pub Option<(K, V)>);
impl<K: PartialEq + Hashes, V> Equivalent<K> for MapEntry<K, V> { impl<K: PartialEq + Hashes, V> Equivalent<K> for MapEntry<K, V> {
fn matches(&self, other: &K) -> bool { 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<K: Debug + Hashes + PartialEq, V: Debug> Debug for Map<K, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("Map")) // todo: debug and print the entire thing? 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<String,String> = 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<String,i32> = 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);
}
}
} }

View file

@ -4,9 +4,10 @@ pub use map;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use map::{TableEntry,MapEntry,Entry}; use map::{Entry, MapEntry};
extern crate self as deimos; extern crate self as deimos;
// Makes a constant map
#[test] #[test]
fn simple() { fn simple() {
static MAP: &[Entry<deimos::map::MapEntry<&'static str,fn()>>] = &deimos::macros::map! { static MAP: &[Entry<deimos::map::MapEntry<&'static str,fn()>>] = &deimos::macros::map! {
@ -15,4 +16,10 @@ mod tests {
"greetings" => || println!("salutations"), "greetings" => || println!("salutations"),
}; };
} }
// Makes and destroys lots of elements
#[test]
fn maker() {
}
} }