Extract agb hashmap to its own crate

This commit is contained in:
Gwilym Inzani 2023-04-23 17:39:00 +01:00
parent 06db23d574
commit d80e3f876e
5 changed files with 80 additions and 153 deletions

View file

@ -38,6 +38,9 @@
},
{
"path": "../tools"
},
{
"path": "../agb-hashmap"
}
]
}
}

13
agb-hashmap/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "agb_hashmap"
version = "0.14.0"
edition = "2021"
license = "MPL-2.0"
description = "A simple no_std hashmap implementation intended for use in the `agb` library"
repository = "https://github.com/agbrs/agb"
[dependencies]
rustc-hash = { version = "1", default-features = false }
[dev-dependencies]
rand = { version = "0.8", default-features = false, features = ["small_rng"] }

View file

@ -1,6 +1,12 @@
#![deny(missing_docs)]
//! A lot of the documentation for this module was copied straight out of the rust
//! standard library. The implementation however is not.
#![no_std]
#![feature(allocator_api)]
#![deny(clippy::all)]
#![deny(missing_docs)]
extern crate alloc;
use alloc::{alloc::Global, vec::Vec};
use core::{
alloc::Allocator,
@ -993,10 +999,9 @@ mod test {
use core::cell::RefCell;
use super::*;
use crate::{rng::RandomNumberGenerator, Gba};
#[test_case]
fn can_store_and_retrieve_8_elements(_gba: &mut Gba) {
#[test]
fn can_store_and_retrieve_8_elements() {
let mut map = HashMap::new();
for i in 0..8 {
@ -1008,8 +1013,8 @@ mod test {
}
}
#[test_case]
fn can_get_the_length(_gba: &mut Gba) {
#[test]
fn can_get_the_length() {
let mut map = HashMap::new();
for i in 0..8 {
@ -1019,8 +1024,8 @@ mod test {
assert_eq!(map.len(), 4);
}
#[test_case]
fn returns_none_if_element_does_not_exist(_gba: &mut Gba) {
#[test]
fn returns_none_if_element_does_not_exist() {
let mut map = HashMap::new();
for i in 0..8 {
@ -1030,8 +1035,8 @@ mod test {
assert_eq!(map.get(&12), None);
}
#[test_case]
fn can_delete_entries(_gba: &mut Gba) {
#[test]
fn can_delete_entries() {
let mut map = HashMap::new();
for i in 0..8 {
@ -1047,8 +1052,8 @@ mod test {
assert_eq!(map.get(&7), Some(&1));
}
#[test_case]
fn can_iterate_through_all_entries(_gba: &mut Gba) {
#[test]
fn can_iterate_through_all_entries() {
let mut map = HashMap::new();
for i in 0..8 {
@ -1067,8 +1072,8 @@ mod test {
assert_eq!(max_found, 7);
}
#[test_case]
fn can_insert_more_than_initial_capacity(_gba: &mut Gba) {
#[test]
fn can_insert_more_than_initial_capacity() {
let mut map = HashMap::new();
for i in 0..65 {
@ -1115,17 +1120,32 @@ mod test {
}
}
#[test_case]
fn extreme_case(_gba: &mut Gba) {
trait RngNextI32 {
fn next_i32(&mut self) -> i32;
}
impl<T> RngNextI32 for T
where
T: rand::RngCore,
{
fn next_i32(&mut self) -> i32 {
self.next_u32() as i32
}
}
#[test]
fn extreme_case() {
use rand::SeedableRng;
let mut map = HashMap::new();
let mut rng = RandomNumberGenerator::new();
let mut rng = rand::rngs::SmallRng::seed_from_u64(20);
let mut answers: [Option<i32>; 128] = [None; 128];
for _ in 0..5_000 {
let command = rng.gen().rem_euclid(2);
let key = rng.gen().rem_euclid(answers.len() as i32);
let value = rng.gen();
let command = rng.next_i32().rem_euclid(2);
let key = rng.next_i32().rem_euclid(answers.len() as i32);
let value = rng.next_i32();
match command {
0 => {
@ -1212,8 +1232,8 @@ mod test {
}
}
#[test_case]
fn correctly_drops_on_remove_and_overall_drop(_gba: &mut Gba) {
#[test]
fn correctly_drops_on_remove_and_overall_drop() {
let drop_registry = DropRegistry::new();
let droppable1 = drop_registry.new_droppable();
@ -1239,8 +1259,8 @@ mod test {
drop_registry.assert_dropped_once(id2);
}
#[test_case]
fn correctly_drop_on_override(_gba: &mut Gba) {
#[test]
fn correctly_drop_on_override() {
let drop_registry = DropRegistry::new();
let droppable1 = drop_registry.new_droppable();
@ -1263,8 +1283,8 @@ mod test {
drop_registry.assert_dropped_once(id2);
}
#[test_case]
fn correctly_drops_key_on_override(_gba: &mut Gba) {
#[test]
fn correctly_drops_key_on_override() {
let drop_registry = DropRegistry::new();
let droppable1 = drop_registry.new_droppable();
@ -1285,8 +1305,8 @@ mod test {
drop_registry.assert_dropped_n_times(id1, 2);
}
#[test_case]
fn test_retain(_gba: &mut Gba) {
#[test]
fn test_retain() {
let mut map = HashMap::new();
for i in 0..100 {
@ -1301,8 +1321,8 @@ mod test {
assert_eq!(map.iter().count(), 50); // force full iteration
}
#[test_case]
fn test_size_hint_iter(_gba: &mut Gba) {
#[test]
fn test_size_hint_iter() {
let mut map = HashMap::new();
for i in 0..100 {
@ -1317,8 +1337,8 @@ mod test {
assert_eq!(iter.size_hint(), (99, Some(99)));
}
#[test_case]
fn test_size_hint_into_iter(_gba: &mut Gba) {
#[test]
fn test_size_hint_into_iter() {
let mut map = HashMap::new();
for i in 0..100 {
@ -1336,13 +1356,10 @@ mod test {
// Following test cases copied from the rust source
// https://github.com/rust-lang/rust/blob/master/library/std/src/collections/hash/map/tests.rs
mod rust_std_tests {
use crate::{
hash_map::{Entry::*, HashMap},
Gba,
};
use crate::{Entry::*, HashMap};
#[test_case]
fn test_entry(_gba: &mut Gba) {
#[test]
fn test_entry() {
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
let mut map: HashMap<_, _> = xs.iter().copied().collect();
@ -1391,8 +1408,8 @@ mod test {
assert_eq!(map.len(), 6);
}
#[test_case]
fn test_occupied_entry_key(_gba: &mut Gba) {
#[test]
fn test_occupied_entry_key() {
let mut a = HashMap::new();
let key = "hello there";
let value = "value goes here";
@ -1409,8 +1426,8 @@ mod test {
assert_eq!(a[key], value);
}
#[test_case]
fn test_vacant_entry_key(_gba: &mut Gba) {
#[test]
fn test_vacant_entry_key() {
let mut a = HashMap::new();
let key = "hello there";
let value = "value goes here";
@ -1427,8 +1444,8 @@ mod test {
assert_eq!(a[key], value);
}
#[test_case]
fn test_index(_gba: &mut Gba) {
#[test]
fn test_index() {
let mut map = HashMap::new();
map.insert(1, 2);
@ -1438,111 +1455,4 @@ mod test {
assert_eq!(map[&2], 1);
}
}
mod rust_std_tests_custom_allocator {
use crate::{
hash_map::{Entry::*, HashMap},
Gba, InternalAllocator,
};
#[test_case]
fn test_entry(_gba: &mut Gba) {
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
let mut map = HashMap::new_in(InternalAllocator);
for (k, v) in xs {
map.insert(k, v);
}
// Existing key (insert)
match map.entry(1) {
Vacant(_) => unreachable!(),
Occupied(mut view) => {
assert_eq!(view.get(), &10);
assert_eq!(view.insert(100), 10);
}
}
assert_eq!(map.get(&1).unwrap(), &100);
assert_eq!(map.len(), 6);
// Existing key (update)
match map.entry(2) {
Vacant(_) => unreachable!(),
Occupied(mut view) => {
let v = view.get_mut();
let new_v = (*v) * 10;
*v = new_v;
}
}
assert_eq!(map.get(&2).unwrap(), &200);
assert_eq!(map.len(), 6);
// Existing key (take)
match map.entry(3) {
Vacant(_) => unreachable!(),
Occupied(view) => {
assert_eq!(view.remove(), 30);
}
}
assert_eq!(map.get(&3), None);
assert_eq!(map.len(), 5);
// Inexistent key (insert)
match map.entry(10) {
Occupied(_) => unreachable!(),
Vacant(view) => {
assert_eq!(*view.insert(1000), 1000);
}
}
assert_eq!(map.get(&10).unwrap(), &1000);
assert_eq!(map.len(), 6);
}
#[test_case]
fn test_occupied_entry_key(_gba: &mut Gba) {
let mut a = HashMap::new_in(InternalAllocator);
let key = "hello there";
let value = "value goes here";
assert!(a.is_empty());
a.insert(key, value);
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
match a.entry(key) {
Vacant(_) => panic!(),
Occupied(e) => assert_eq!(key, *e.key()),
}
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
}
#[test_case]
fn test_vacant_entry_key(_gba: &mut Gba) {
let mut a = HashMap::new_in(InternalAllocator);
let key = "hello there";
let value = "value goes here";
assert!(a.is_empty());
match a.entry(key) {
Occupied(_) => panic!(),
Vacant(e) => {
assert_eq!(key, *e.key());
e.insert(value);
}
}
assert_eq!(a.len(), 1);
assert_eq!(a[key], value);
}
#[test_case]
fn test_index(_gba: &mut Gba) {
let mut map = HashMap::new_in(InternalAllocator);
map.insert(1, 2);
map.insert(2, 1);
map.insert(3, 4);
assert_eq!(map[&2], 1);
}
}
}

View file

@ -27,6 +27,7 @@ agb_image_converter = { version = "0.14.0", path = "../agb-image-converter" }
agb_sound_converter = { version = "0.14.0", path = "../agb-sound-converter" }
agb_macros = { version = "0.14.0", path = "../agb-macros" }
agb_fixnum = { version = "0.14.0", path = "../agb-fixnum" }
agb_hashmap = { version = "0.14.0", path = "../agb-hashmap" }
bare-metal = "1"
modular-bitfield = "0.11"
rustc-hash = { version = "1", default-features = false }

View file

@ -152,7 +152,7 @@ pub mod mgba;
#[doc(inline)]
pub use agb_fixnum as fixnum;
/// Contains an implementation of a hashmap which suits the gameboy advance's hardware.
pub mod hash_map;
pub use agb_hashmap as hash_map;
/// Simple random number generator
pub mod rng;
pub mod save;