This commit is contained in:
nora 2023-12-30 15:38:58 +01:00
parent 3d036ff039
commit 9e4255f239
4 changed files with 213 additions and 8 deletions

16
Cargo.lock generated
View file

@ -13,27 +13,27 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.49"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.107"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
@ -42,9 +42,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.6"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "uwu"

203
src/hashmap.rs Normal file
View file

@ -0,0 +1,203 @@
use std::{
hash::{BuildHasher, Hash, RandomState},
vec,
};
type Entry<K, V> = Option<(K, V)>;
pub struct HashMap<K, V, S = RandomState> {
buckets: Vec<Entry<K, V>>,
filled: usize,
s: S,
}
impl<K: Eq + Hash, V> HashMap<K, V, RandomState> {
pub fn new() -> Self {
Self::with_hasher(RandomState::new())
}
}
impl<K: Eq + Hash, V, S: BuildHasher> HashMap<K, V, S> {
pub fn with_hasher(state: S) -> Self {
Self {
buckets: Vec::new(),
filled: 0,
s: state,
}
}
pub fn len(&self) -> usize {
self.filled
}
pub fn is_empty(&self) -> bool {
self.buckets.len() == 0
}
pub fn get(&self, key: &K) -> Option<&V> {
if self.is_empty() {
return None;
}
let bucket = self.bucket_of_elem(&key);
let result = self.buckets[bucket..]
.iter()
.take_while(|elem| elem.is_some())
.find(|elem| matches!(elem, Some((elem_key, _)) if elem_key == key));
if let Some(Some((_, value))) = result {
Some(value)
} else {
None
}
}
pub fn insert(&mut self, key: K, value: V) {
if self.filled >= self.buckets.len() {
self.grow();
}
loop {
let bucket = self.bucket_of_elem(&key);
let bucket = self.buckets[bucket..].iter_mut().find(|bucket| {
bucket.is_none() || matches!(bucket, Some((elem_key, _)) if *elem_key == key)
});
if let Some(bucket) = bucket {
if bucket.is_none() {
self.filled += 1;
}
*bucket = Some((key, value));
return;
} else {
self.grow();
}
}
}
fn bucket_of_elem(&self, key: &K) -> usize {
assert_ne!(self.buckets.len(), 0, "cannot compute bucket of empty map");
let hash = self.s.hash_one(&key) as usize;
hash % self.buckets.len()
}
fn grow(&mut self) {
let len = self.buckets.len();
let new = if len == 0 { 8 } else { len * 2 };
let old = IntoIter::new(std::mem::take(&mut self.buckets));
let new_buckets = (0..new).map(|_| None).collect();
self.buckets = new_buckets;
self.extend(old);
}
}
impl<K: Eq + Hash, V, S: BuildHasher> Extend<(K, V)> for HashMap<K, V, S> {
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
iter.into_iter()
.for_each(|(key, value)| self.insert(key, value));
}
}
pub struct IntoIter<K, V> {
buckets: std::iter::FilterMap<vec::IntoIter<Entry<K, V>>, fn(Entry<K, V>) -> Option<(K, V)>>,
}
impl<K, V> IntoIter<K, V> {
fn new(buckets: Vec<Entry<K, V>>) -> Self {
IntoIter {
buckets: buckets.into_iter().filter_map(std::convert::identity),
}
}
}
impl<K, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.buckets.next()
}
}
impl<K, V, S> IntoIterator for HashMap<K, V, S> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self.buckets)
}
}
#[cfg(test)]
mod tests {
use std::hash::{BuildHasher, BuildHasherDefault, Hasher, RandomState};
use super::HashMap;
#[test]
fn get_empty() {
let m = HashMap::<&str, ()>::new();
assert_eq!(m.get(&"uwu"), None);
assert_eq!(m.get(&"uwu"), None);
}
#[test]
fn insert() {
let mut m = HashMap::new();
m.insert("hello", "world");
assert_eq!(m.get(&"hello"), Some(&"world"));
assert_eq!(m.len(), 1);
m.insert("aaa", "yes");
assert_eq!(m.get(&"hello"), Some(&"world"));
assert_eq!(m.get(&"aaa"), Some(&"yes"));
assert_eq!(m.len(), 2);
}
#[test]
fn overriding() {
let mut m = HashMap::new();
m.insert("hello", "world");
assert_eq!(m.get(&"hello"), Some(&"world"));
assert_eq!(m.len(), 1);
m.insert("hello", "no");
assert_eq!(m.get(&"hello"), Some(&"no"));
assert_eq!(m.len(), 1);
}
#[derive(Default)]
struct CollidingHasher;
impl Hasher for CollidingHasher {
fn finish(&self) -> u64 {
0
}
fn write(&mut self, _bytes: &[u8]) {}
}
fn test_many<H: BuildHasher>(count: usize, h: H) {
let mut m = HashMap::with_hasher(h);
for i in 0..count {
m.insert(i, i);
}
let mut found = vec![false; count];
for (k, v) in m.into_iter() {
assert_eq!(k, v);
assert!(!found[k], "duplicate element");
found[k] = true;
}
for (i, found) in found.iter().enumerate() {
assert!(found, "element {i} was lost");
}
}
#[test]
fn many_elements() {
for count in [1, 10, 100, 1000, 10_000, 100_000] {
test_many(count, RandomState::new());
}
}
#[test]
fn many_many_collisions() {
test_many(5000, BuildHasherDefault::<CollidingHasher>::default());
}
}

View file

@ -5,6 +5,7 @@
#![feature(strict_provenance)]
pub mod cfg_match;
pub mod hashmap;
pub mod innocent_linked_list;
pub mod scratch;
pub mod sendsync;

View file

@ -1,4 +1,5 @@
#![cfg_attr(not(test), allow(unused))]
#![allow(dropping_copy_types)]
use std::{
cell::{Cell, UnsafeCell},