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) {
|
||||
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<E: TableEntry> DisplacementSet<E> {
|
|||
&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<E: TableEntry> DisplacementSet<E> {
|
|||
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<E: TableEntry> DisplacementSet<E> {
|
|||
let mut free: Option<usize> = 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<K>(&mut self, index: K)
|
||||
where
|
||||
E: Equivalent<K>,
|
||||
|
|
@ -225,7 +229,7 @@ impl<E: TableEntry> DisplacementSet<E> {
|
|||
} 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<K: Hashes + PartialEq, V>(pub Option<(K, V)>);
|
|||
|
||||
impl<K: PartialEq + Hashes, V> Equivalent<K> for MapEntry<K, V> {
|
||||
fn matches(&self, other: &K) -> bool {
|
||||
&self.0.as_ref().unwrap().0 == other
|
||||
if let Some((ref key,_)) = self.0 {
|
||||
key == other
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -342,3 +350,51 @@ impl<K: Debug + Hashes + PartialEq, V: Debug> Debug for Map<K, V> {
|
|||
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)]
|
||||
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::map::MapEntry<&'static str,fn()>>] = &deimos::macros::map! {
|
||||
|
|
@ -15,4 +16,10 @@ mod tests {
|
|||
"greetings" => || println!("salutations"),
|
||||
};
|
||||
}
|
||||
|
||||
// Makes and destroys lots of elements
|
||||
#[test]
|
||||
fn maker() {
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue