Tests for map lib, small and all passing.
This commit is contained in:
parent
76a9c2259e
commit
6e9c1c1213
2 changed files with 96 additions and 33 deletions
120
map/src/lib.rs
120
map/src/lib.rs
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue