From d73227b9073141fce3b4d819692261eca3fc6ce9 Mon Sep 17 00:00:00 2001 From: paladin Date: Mon, 28 Sep 2026 11:57:44 +0100 Subject: [PATCH] Refactoring for new types Map and Set, renaming of DisplacementSet and its methods. Builds, not tested. --- src/gc.rs | 4 + src/lib.rs | 10 +- src/map.rs | 568 ++++++++++++++++++++++++++++++++--------------------- 3 files changed, 353 insertions(+), 229 deletions(-) diff --git a/src/gc.rs b/src/gc.rs index 12e66a7..73b3068 100644 --- a/src/gc.rs +++ b/src/gc.rs @@ -197,6 +197,10 @@ impl Traverse for IdNode where C: Allocates { IdNode::traverse(next,context) } } + + fn collect(self) { + todo!() + } } #[derive(Clone)] diff --git a/src/lib.rs b/src/lib.rs index 938c419..c830934 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,10 @@ pub mod gc; -pub mod map; \ No newline at end of file +pub mod map; + +#[cfg(test)] +mod tests { + #[test] + fn simple() { + + } +} \ No newline at end of file diff --git a/src/map.rs b/src/map.rs index 5aae0ab..d5fe69d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -18,8 +18,9 @@ Notes: - Values would only need to be shuffled around closer to their 'home' during a resize. - It is unlikely the displacement would large value without a resize amending that problem. */ -use std::hash::{DefaultHasher, Hash, Hasher}; use crate::gc::{AnyId, Id}; +use std::fmt::Debug; +use std::hash::{DefaultHasher, Hash, Hasher}; type Displacement = u8; @@ -29,269 +30,380 @@ pub type Hashed = u64; pub type Hashed = u32; pub trait Equivalent: Sized { - fn matches(&self, other: &T) -> bool; + fn matches(&self, other: &T) -> bool; } impl Equivalent for T where - T: PartialEq, + T: PartialEq, { - fn matches(&self, other: &T) -> bool { - self == other - } + fn matches(&self, other: &T) -> bool { + self == other + } } pub trait Hashes { - fn hashed(&self) -> Hashed; + fn hashed(&self) -> Hashed; } impl Hashes for &str { - fn hashed(&self) -> Hashed { - let mut hasher = DefaultHasher::new(); - self[..self.len().min(6)].hash(&mut hasher); - hasher.finish() as Hashed - } + fn hashed(&self) -> Hashed { + let mut hasher = DefaultHasher::new(); + self[..self.len().min(6)].hash(&mut hasher); + hasher.finish() as Hashed + } } impl Hashes for Id { - fn hashed(&self) -> Hashed { - let mut hasher = DefaultHasher::new(); - self.index.hash(&mut hasher); - hasher.finish() as Hashed - } + fn hashed(&self) -> Hashed { + let mut hasher = DefaultHasher::new(); + self.index.hash(&mut hasher); + hasher.finish() as Hashed + } } impl Hashes for AnyId { - fn hashed(&self) -> Hashed { - let mut hasher = DefaultHasher::new(); - self.index.hash(&mut hasher); - hasher.finish() as Hashed - } + fn hashed(&self) -> Hashed { + let mut hasher = DefaultHasher::new(); + self.index.hash(&mut hasher); + hasher.finish() as Hashed + } } pub trait TableEntry: Equivalent + Hashes { - fn new_vacant() -> Self; - fn is_vacant(&self) -> bool; + const VACANT: Self; + fn is_vacant(&self) -> bool; } -#[derive(Debug, Clone)] -pub struct Entry { - pub entry: E, - home: usize, - displacement: Displacement, +#[derive(Debug)] +pub struct Entry { + pub entry: E, + home: usize, + displacement: Displacement, } -#[derive(Clone, Debug)] -pub struct Map { - // default representing vacancy! - pub map: Vec>, // todo: make this private and use an iterator - map_bounds: std::ops::Range, - map_count: usize, // number of elements in table +impl Entry { + const VACANT: Entry = Entry { + entry: E::VACANT, + home: 0, + displacement: 0, + }; } -impl Map { - fn exchange_table(&mut self, len: usize) { - let old = std::mem::replace( - &mut self.map, - vec![ - Entry { - entry: E::new_vacant(), - home: 0, - displacement: 0, - }; - len - ], - ); - self.map_bounds = self.table_lower()..self.table_upper(); - for Entry { entry, .. } in old { - if !entry.is_vacant() { - self.set_table(entry) - } - } - } - pub fn resize_table(&mut self, len: usize) { - self.exchange_table(len.max(self.map_count + (self.map_count / 3))); - self.map_bounds = 0..self.table_upper(); - } - fn table_upper(&self) -> usize { - (self.map.len() / 4) * 3 - } - fn table_lower(&self) -> usize { - self.map.len() / 3 - } - fn ensure_table(&mut self) { - if self.map_count + 1 > self.table_upper() { - // free space is short - self.exchange_table((self.map.len() + 4) * 2); - } else if self.map_count < self.table_lower() { - // too much free space - self.exchange_table(self.map.len() / 2) - } - } - pub fn set_table(&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_table(); - } - let range = self.map.len(); - let home = entry.hashed() as usize % range; - 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; - } else if self.map[home].entry.matches(&entry) { - // attempt to replace it directly - // REPLACE - self.map[home].entry = entry; // home.item.soft_drop() - } 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; - return; - } - } - // 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; - self.map[home].displacement = j; - self.map_count += 1; - return; - } - } - // impossible but this still needs to be complete - #[cfg(feature = "messages")] - eprintln!("Large table collision, are the hashes ok?\n\tBrute-force probing..."); - 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() { - free = Some(k); - } else if neighbour.entry.matches(&entry) { - // REPLACE - neighbour.entry = entry; - return; - } - } - if let Some(k) = free { - // INSERT - self.map[k].home = home; - self.map[k].entry = entry; - self.map[home].displacement = Displacement::MAX; - self.map_count += 1; - return; - } - // must resize - #[cfg(feature = "messages")] - eprintln!("\n\tResizing..."); - self.resize_table((self.map.len() + 4) * 2); - self.set_table(entry); - } - } - pub fn rem_table(&mut self, index: K) - where - E: Equivalent, - K: Hashes, - { - if self.map.len() == 0 { - return; - } - let range = self.map.len(); - let home = index.hashed() as usize % range; - if self.map[home].entry.matches(&index) { - self.map[home].entry = E::new_vacant(); - self.map_count -= 1; - } else { - if self.map[home].displacement != Displacement::MAX { - let mut largest = 0; - 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::new_vacant(); - self.map_count -= 1; - if i == self.map[home].displacement { - self.map[home].displacement = largest +#[derive(Debug)] +pub struct DisplacementSet { + // default representing vacancy! + pub map: Vec>, // todo: make this private and use an iterator + map_bounds: std::ops::Range, + map_count: usize, // number of elements in table +} + +impl DisplacementSet { + fn exchange(&mut self, len: usize) { + let old = std::mem::replace( + &mut self.map, + (0..len).map(|_| Entry::VACANT).collect(), // todo: I don't like that this may go over all memory + ); + self.map_bounds = self.lower()..self.upper(); + for Entry { entry, .. } in old { + if !entry.is_vacant() { + self.set(entry) } - return; - } else if !neighbour.entry.is_vacant() && neighbour.home == home { - largest = i; - } } - } else { - let mut largest = 0; + } + pub fn resize(&mut self, len: usize) { + self.exchange(len.max(self.map_count + (self.map_count / 3))); + self.map_bounds = 0..self.upper(); + } + fn upper(&self) -> usize { + (self.map.len() / 4) * 3 + } + fn lower(&self) -> usize { + self.map.len() / 3 + } + fn ensure(&mut self) { + if self.map_count + 1 > self.upper() { + // free space is short + self.exchange((self.map.len() + 4) * 2); + } else if self.map_count < self.lower() { + // too much free space + self.exchange(self.map.len() / 2) + } + } + pub fn set(&mut self, entry: E) { #[cfg(feature = "assertions")] - let mut finished = false; // what - for k in 0..self.map.len() { - let neighbour = &mut self.map[k]; - if neighbour.entry.matches(&index) { - neighbour.entry = E::new_vacant(); - self.map_count -= 1; - #[cfg(feature = "assertions")] - { - assert!(!finished); - finished = true; - } - } - if neighbour.home == home && !neighbour.entry.is_vacant() { - largest = largest.max(if k < home { - k + self.map.len() - home - 1 // ? - } else { - k - home - }) - } + assert!(!entry.is_vacant()); + if !self.map_bounds.contains(&(self.map_count + 1)) { + // make sure the table is appropriately sized + self.ensure(); } - self.map[home].displacement = if largest > Displacement::MAX as usize { - Displacement::MAX + let range = self.map.len(); + let home = entry.hashed() as usize % range; + 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; + } else if self.map[home].entry.matches(&entry) { + // attempt to replace it directly + // REPLACE + self.map[home].entry = entry; // home.item.soft_drop() } else { - largest as Displacement + // 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; + return; + } + } + // 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; + self.map[home].displacement = j; + self.map_count += 1; + return; + } + } + // impossible but this still needs to be complete + #[cfg(feature = "messages")] + eprintln!("Large table collision, are the hashes ok?\n\tBrute-force probing..."); + 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() { + free = Some(k); + } else if neighbour.entry.matches(&entry) { + // REPLACE + neighbour.entry = entry; + return; + } + } + if let Some(k) = free { + // INSERT + self.map[k].home = home; + self.map[k].entry = entry; + self.map[home].displacement = Displacement::MAX; + self.map_count += 1; + return; + } + // must resize + #[cfg(feature = "messages")] + eprintln!("\n\tResizing..."); + self.resize((self.map.len() + 4) * 2); + self.set(entry); } - } } - } - pub fn get_table(&mut self, index: K) -> Option<&E> - where - E: Equivalent, - K: Hashes, - { - if self.map.len() == 0 { - return None; - } - let location = index.hashed() as usize % self.map.len(); - let home = &self.map[location]; - if home.entry.matches(&index) { - Some(&home.entry) - } else { - for i in 1..home.displacement as usize { - let neighbour = &self.map[(location + i) % self.map.len()]; - if neighbour.entry.matches(&index) { - return Some(&neighbour.entry); + pub fn remove(&mut self, index: K) + where + E: Equivalent, + K: Hashes, + { + if self.map.len() == 0 { + return; + } + let range = self.map.len(); + let home = index.hashed() as usize % range; + if self.map[home].entry.matches(&index) { + self.map[home].entry = E::VACANT; + self.map_count -= 1; + } else { + if self.map[home].displacement != Displacement::MAX { + let mut largest = 0; + 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; + self.map_count -= 1; + if i == self.map[home].displacement { + self.map[home].displacement = largest + } + return; + } else if !neighbour.entry.is_vacant() && neighbour.home == home { + largest = i; + } + } + } else { + let mut largest = 0; + #[cfg(feature = "assertions")] + let mut finished = false; // what + for k in 0..self.map.len() { + let neighbour = &mut self.map[k]; + if neighbour.entry.matches(&index) { + neighbour.entry = E::VACANT; + self.map_count -= 1; + #[cfg(feature = "assertions")] + { + assert!(!finished); + finished = true; + } + } + if neighbour.home == home && !neighbour.entry.is_vacant() { + largest = largest.max(if k < home { + k + self.map.len() - home - 1 // ? + } else { + k - home + }) + } + } + self.map[home].displacement = if largest > Displacement::MAX as usize { + Displacement::MAX + } else { + largest as Displacement + } + } } - } - None } - } - pub fn new() -> Map { - Map { - map: Vec::new(), - map_count: 0, - map_bounds: (0..0).into(), + pub fn get(&mut self, index: K) -> Option<&E> + where + E: Equivalent, + K: Hashes, + { + if self.map.len() == 0 { + return None; + } + let location = index.hashed() as usize % self.map.len(); + let home = &self.map[location]; + if home.entry.matches(&index) { + Some(&home.entry) + } else { + for i in 1..home.displacement as usize { + let neighbour = &self.map[(location + i) % self.map.len()]; + if neighbour.entry.matches(&index) { + return Some(&neighbour.entry); + } + } + None + } + } + pub fn new() -> DisplacementSet { + DisplacementSet { + map: Vec::new(), + map_count: 0, + map_bounds: (0..0).into(), + } } - } } -pub struct TableEntry +#[derive(Clone)] +pub struct MapEntry(pub Option<(K, V)>); -pub type Table = Map<> \ No newline at end of file +impl Equivalent for MapEntry { + fn matches(&self, other: &K) -> bool { + &self.0.as_ref().unwrap().0 == other + } +} + +impl Hashes for MapEntry { + fn hashed(&self) -> Hashed { + self.0.as_ref().unwrap().0.hashed() + } +} + +impl PartialEq for MapEntry { + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().unwrap().0 == other.0.as_ref().unwrap().0 + } +} + +impl TableEntry for MapEntry { + const VACANT: Self = MapEntry(None); + fn is_vacant(&self) -> bool { + self.0.is_none() + } +} + +pub struct Map(pub DisplacementSet>); + +impl Map { + pub fn resize(&mut self, len: usize) { + self.0.resize(len) + } + pub fn set(&mut self, index: K, value: V) { + self.0.set(MapEntry(Some((index, value)))); + } + pub fn remove(&mut self, index: K) { + self.0.remove(index); + } + pub fn get(&mut self, index: K) -> Option<&V> { + if let Some(ref entry) = self.0.get(index) { + Some(&entry.0.as_ref().unwrap().1) + } else { + None + } + } + pub fn new() -> Map { + Map(DisplacementSet::new()) + } +} + +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? + } +} + +#[derive(PartialEq)] +pub struct SetEntry(Option); + +pub struct Set(DisplacementSet>); + +impl Hashes for SetEntry { + fn hashed(&self) -> Hashed { + todo!() + } +} + +impl TableEntry for SetEntry { + const VACANT: Self = SetEntry(None); + fn is_vacant(&self) -> bool { + self.0.is_none() + } +} + +impl Equivalent for SetEntry { + fn matches(&self, other: &E) -> bool { + self.0.as_ref().unwrap() == other + } +} + +impl> Equivalent for SetEntry { + fn matches(&self, other: &K) -> bool { + self.0.unwrap().matches(other) + } +} + +impl Set { + pub fn resize(&mut self, len: usize) { + self.0.resize(len) + } + pub fn set(&mut self, element: E) { + self.0.set(SetEntry(Some(element))); + } + pub fn remove(&mut self, element: E) { + self.0.remove(element); + } + pub fn get(&mut self, element: K) -> Option<&E> + where + E: Equivalent, + K: Hashes, + { + if let Some(ref entry) = self.0.get(element) { + Some(&entry.0.as_ref().unwrap()) + } else { + None + } + } + pub fn new() -> Set { + Set(DisplacementSet::new()) + } +}