mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-27 09:36:39 +11:00
Really basic hash map which you can only add stuff to
This commit is contained in:
parent
82922abddb
commit
019872b8cc
2 changed files with 168 additions and 0 deletions
166
agb/src/hash_map.rs
Normal file
166
agb/src/hash_map.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use alloc::{vec, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
|
||||||
|
mem,
|
||||||
|
};
|
||||||
|
|
||||||
|
use rustc_hash::FxHasher;
|
||||||
|
|
||||||
|
type HashType = u32;
|
||||||
|
|
||||||
|
struct Node<K: Sized, V: Sized> {
|
||||||
|
hash: HashType,
|
||||||
|
distance_to_initial_bucket: u32,
|
||||||
|
key: K,
|
||||||
|
value: V,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Node<K, V>
|
||||||
|
where
|
||||||
|
K: Sized,
|
||||||
|
V: Sized,
|
||||||
|
{
|
||||||
|
fn with_new_key_value(&self, new_key: K, new_value: V) -> Self {
|
||||||
|
Self {
|
||||||
|
hash: self.hash,
|
||||||
|
distance_to_initial_bucket: self.distance_to_initial_bucket,
|
||||||
|
key: new_key,
|
||||||
|
value: new_value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HashMap<K, V> {
|
||||||
|
number_of_elements: usize,
|
||||||
|
max_distance_to_initial_bucket: u32,
|
||||||
|
nodes: Vec<Option<Node<K, V>>>,
|
||||||
|
|
||||||
|
hasher: BuildHasherDefault<FxHasher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> HashMap<K, V> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
number_of_elements: 0,
|
||||||
|
max_distance_to_initial_bucket: 0,
|
||||||
|
nodes: vec![
|
||||||
|
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
||||||
|
None, None,
|
||||||
|
],
|
||||||
|
hasher: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fast_mod(len: usize, hash: HashType) -> usize {
|
||||||
|
debug_assert!(len.is_power_of_two(), "Length must be a power of 2");
|
||||||
|
(hash as usize) & (len - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> HashMap<K, V>
|
||||||
|
where
|
||||||
|
K: Eq,
|
||||||
|
{
|
||||||
|
fn get_location(&self, key: &K, hash: HashType) -> Option<usize> {
|
||||||
|
for distance_to_initial_bucket in 0..=self.max_distance_to_initial_bucket + 1 {
|
||||||
|
let location = fast_mod(self.nodes.len(), hash + distance_to_initial_bucket);
|
||||||
|
|
||||||
|
let node = &self.nodes[location];
|
||||||
|
if let Some(node) = node {
|
||||||
|
if &node.key == key {
|
||||||
|
return Some(location);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> HashMap<K, V>
|
||||||
|
where
|
||||||
|
K: Eq + Hash,
|
||||||
|
{
|
||||||
|
pub fn put(&mut self, key: K, value: V) {
|
||||||
|
let mut hasher = self.hasher.build_hasher();
|
||||||
|
key.hash(&mut hasher);
|
||||||
|
let hash = hasher.finish() as HashType;
|
||||||
|
|
||||||
|
if let Some(location) = self.get_location(&key, hash) {
|
||||||
|
let old_node = self.nodes[location].as_ref().unwrap();
|
||||||
|
self.nodes[location] = Some(old_node.with_new_key_value(key, value));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.insert_new(key, value, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, key: &K) -> Option<&V> {
|
||||||
|
let mut hasher = self.hasher.build_hasher();
|
||||||
|
key.hash(&mut hasher);
|
||||||
|
let hash = hasher.finish() as HashType;
|
||||||
|
|
||||||
|
self.get_location(&key, hash)
|
||||||
|
.map(|location| &self.nodes[location].as_ref().unwrap().value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> HashMap<K, V> {
|
||||||
|
fn insert_new(&mut self, key: K, value: V, hash: HashType) {
|
||||||
|
// if we need to resize
|
||||||
|
if self.nodes.len() * 85 / 100 < self.number_of_elements {
|
||||||
|
todo!("resize not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_node = Node {
|
||||||
|
hash,
|
||||||
|
distance_to_initial_bucket: 0,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let location = fast_mod(self.nodes.len(), hash + new_node.distance_to_initial_bucket);
|
||||||
|
let current_node = self.nodes[location].as_mut();
|
||||||
|
|
||||||
|
if let Some(current_node) = current_node {
|
||||||
|
if current_node.distance_to_initial_bucket <= new_node.distance_to_initial_bucket {
|
||||||
|
self.max_distance_to_initial_bucket = new_node
|
||||||
|
.distance_to_initial_bucket
|
||||||
|
.max(self.max_distance_to_initial_bucket);
|
||||||
|
|
||||||
|
mem::swap(&mut new_node, current_node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.nodes[location] = Some(new_node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_node.distance_to_initial_bucket += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.number_of_elements += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::Gba;
|
||||||
|
|
||||||
|
#[test_case]
|
||||||
|
fn can_store_up_to_initial_capacity_elements(_gba: &mut Gba) {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
map.put(i, i % 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
assert_eq!(map.get(&i), Some(&(i % 4)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -160,6 +160,8 @@ pub mod syscall;
|
||||||
/// Interactions with the internal timers
|
/// Interactions with the internal timers
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
|
||||||
|
mod hash_map;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue