From 5d89e2fe6a91bcc4f5765969f0312d037130e42e Mon Sep 17 00:00:00 2001 From: "bruce.li" <127000969+binary-bruce@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:41:59 +0000 Subject: [PATCH 1/2] inital naive implementation of HashMap --- apps/memtest/src/main.rs | 10 +-- ulib/axstd/src/collections/hashmap.rs | 102 ++++++++++++++++++++++++++ ulib/axstd/src/collections/mod.rs | 1 + ulib/axstd/src/lib.rs | 3 +- 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 ulib/axstd/src/collections/hashmap.rs create mode 100644 ulib/axstd/src/collections/mod.rs diff --git a/apps/memtest/src/main.rs b/apps/memtest/src/main.rs index e23e95e723..df5a3c1fb1 100644 --- a/apps/memtest/src/main.rs +++ b/apps/memtest/src/main.rs @@ -6,7 +6,7 @@ extern crate axstd as std; use rand::{rngs::SmallRng, RngCore, SeedableRng}; -use std::collections::BTreeMap; +use std::collections::hashmap::HashMap; use std::vec::Vec; fn test_vec(rng: &mut impl RngCore) { @@ -22,9 +22,9 @@ fn test_vec(rng: &mut impl RngCore) { println!("test_vec() OK!"); } -fn test_btree_map(rng: &mut impl RngCore) { +fn test_hashmap_map(rng: &mut impl RngCore) { const N: usize = 50_000; - let mut m = BTreeMap::new(); + let mut m = HashMap::new(); for _ in 0..N { let value = rng.next_u32(); let key = format!("key_{value}"); @@ -35,7 +35,7 @@ fn test_btree_map(rng: &mut impl RngCore) { assert_eq!(k.parse::().unwrap(), *v); } } - println!("test_btree_map() OK!"); + println!("test_hashmap_map() OK!"); } #[cfg_attr(feature = "axstd", no_mangle)] @@ -44,7 +44,7 @@ fn main() { let mut rng = SmallRng::seed_from_u64(0xdead_beef); test_vec(&mut rng); - test_btree_map(&mut rng); + test_hashmap_map(&mut rng); println!("Memory tests run OK!"); } diff --git a/ulib/axstd/src/collections/hashmap.rs b/ulib/axstd/src/collections/hashmap.rs new file mode 100644 index 0000000000..094788f65e --- /dev/null +++ b/ulib/axstd/src/collections/hashmap.rs @@ -0,0 +1,102 @@ +extern crate alloc; + +const N: usize = 128; + +struct Entry { + key: K, + value: V, +} + +// As a Rust newbie, I found it's very hard to me to port the hash map implementation from std library which contains too many things +// I generated the initial code by ChatGPT, and fixed the code issues with the help of ChatGPT prompts and compiler hints +// I am glad to pass the unit tests in the end +pub struct HashMap { + storage: [Option>; N], +} + +pub struct HashMapIter<'a, K, V> { + hashmap: &'a HashMap, + index: usize, +} + +impl HashMap +where + K: PartialEq, +{ + const ARRAY_REPEAT_VALUE: Option> = None; + + pub fn new() -> Self { + HashMap { + storage: [Self::ARRAY_REPEAT_VALUE; N], + } + } + + pub fn insert(&mut self, key: K, value: V) -> Option { + let index = self.hash(&key) % N; + for i in 0..N { + let idx = (index + i) % N; + match self.storage[idx] { + Some(ref entry) if entry.key == key => { + let old_value = + core::mem::replace(&mut self.storage[idx].as_mut().unwrap().value, value); + return Some(old_value); + } + None => { + self.storage[idx] = Some(Entry { key, value }); + return None; + } + _ => continue, + } + } + None + } + + pub fn get(&self, key: K) -> Option<&V> { + let index = self.hash(&key) % N; + for i in 0..N { + let idx = (index + i) % N; + match self.storage[idx] { + Some(ref entry) if entry.key == key => return Some(&entry.value), + None => return None, + _ => continue, + } + } + None + } + + // A very simple hash function for demonstration purposes. + // You should use a better hash function for real applications. + fn hash(&self, key: &K) -> usize { + let key_ptr = key as *const K as usize; + key_ptr % N + } + + pub fn iter(&self) -> HashMapIter { + HashMapIter { + hashmap: self, + index: 0, + } + } +} + +impl<'a, K, V> Iterator for HashMapIter<'a, K, V> +where + K: PartialEq, +{ + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + while self.index < N { + match self.hashmap.storage[self.index] { + Some(ref entry) => { + self.index += 1; // Move to the next index for the next call to next() + return Some((&entry.key, &entry.value)); + } + None => { + self.index += 1; // Current index is empty, move to the next one + } + } + } + None // All entries have been traversed + } +} diff --git a/ulib/axstd/src/collections/mod.rs b/ulib/axstd/src/collections/mod.rs new file mode 100644 index 0000000000..7504d0add8 --- /dev/null +++ b/ulib/axstd/src/collections/mod.rs @@ -0,0 +1 @@ +pub mod hashmap; diff --git a/ulib/axstd/src/lib.rs b/ulib/axstd/src/lib.rs index d256cd527f..26608e6d2d 100644 --- a/ulib/axstd/src/lib.rs +++ b/ulib/axstd/src/lib.rs @@ -55,7 +55,7 @@ extern crate alloc; #[cfg(feature = "alloc")] #[doc(no_inline)] -pub use alloc::{boxed, collections, format, string, vec}; +pub use alloc::{boxed, format, string, vec}; #[doc(no_inline)] pub use core::{arch, cell, cmp, hint, marker, mem, ops, ptr, slice, str}; @@ -63,6 +63,7 @@ pub use core::{arch, cell, cmp, hint, marker, mem, ops, ptr, slice, str}; #[macro_use] mod macros; +pub mod collections; pub mod env; pub mod io; pub mod os; From 6f0d7c125998d06348486b46871161376a2c0467 Mon Sep 17 00:00:00 2001 From: "binary.bruce" Date: Thu, 29 Feb 2024 12:46:28 +0000 Subject: [PATCH 2/2] use same path with std to publish HashMap --- apps/memtest/src/main.rs | 2 +- ulib/axstd/src/collections/mod.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/memtest/src/main.rs b/apps/memtest/src/main.rs index df5a3c1fb1..c05bc5cc1b 100644 --- a/apps/memtest/src/main.rs +++ b/apps/memtest/src/main.rs @@ -6,7 +6,7 @@ extern crate axstd as std; use rand::{rngs::SmallRng, RngCore, SeedableRng}; -use std::collections::hashmap::HashMap; +use std::collections::HashMap; use std::vec::Vec; fn test_vec(rng: &mut impl RngCore) { diff --git a/ulib/axstd/src/collections/mod.rs b/ulib/axstd/src/collections/mod.rs index 7504d0add8..c3d908d715 100644 --- a/ulib/axstd/src/collections/mod.rs +++ b/ulib/axstd/src/collections/mod.rs @@ -1 +1,2 @@ pub mod hashmap; +pub use self::hashmap::HashMap;