mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Extract agb hashmap to its own crate
This commit is contained in:
parent
06db23d574
commit
d80e3f876e
3
.vscode/agb.code-workspace
vendored
3
.vscode/agb.code-workspace
vendored
|
@ -38,6 +38,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../tools"
|
"path": "../tools"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../agb-hashmap"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
13
agb-hashmap/Cargo.toml
Normal file
13
agb-hashmap/Cargo.toml
Normal 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"] }
|
|
@ -1,6 +1,12 @@
|
||||||
#![deny(missing_docs)]
|
|
||||||
//! A lot of the documentation for this module was copied straight out of the rust
|
//! A lot of the documentation for this module was copied straight out of the rust
|
||||||
//! standard library. The implementation however is not.
|
//! 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 alloc::{alloc::Global, vec::Vec};
|
||||||
use core::{
|
use core::{
|
||||||
alloc::Allocator,
|
alloc::Allocator,
|
||||||
|
@ -993,10 +999,9 @@ mod test {
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{rng::RandomNumberGenerator, Gba};
|
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn can_store_and_retrieve_8_elements(_gba: &mut Gba) {
|
fn can_store_and_retrieve_8_elements() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
|
@ -1008,8 +1013,8 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn can_get_the_length(_gba: &mut Gba) {
|
fn can_get_the_length() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
|
@ -1019,8 +1024,8 @@ mod test {
|
||||||
assert_eq!(map.len(), 4);
|
assert_eq!(map.len(), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn returns_none_if_element_does_not_exist(_gba: &mut Gba) {
|
fn returns_none_if_element_does_not_exist() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
|
@ -1030,8 +1035,8 @@ mod test {
|
||||||
assert_eq!(map.get(&12), None);
|
assert_eq!(map.get(&12), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn can_delete_entries(_gba: &mut Gba) {
|
fn can_delete_entries() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
|
@ -1047,8 +1052,8 @@ mod test {
|
||||||
assert_eq!(map.get(&7), Some(&1));
|
assert_eq!(map.get(&7), Some(&1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn can_iterate_through_all_entries(_gba: &mut Gba) {
|
fn can_iterate_through_all_entries() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..8 {
|
for i in 0..8 {
|
||||||
|
@ -1067,8 +1072,8 @@ mod test {
|
||||||
assert_eq!(max_found, 7);
|
assert_eq!(max_found, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn can_insert_more_than_initial_capacity(_gba: &mut Gba) {
|
fn can_insert_more_than_initial_capacity() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..65 {
|
for i in 0..65 {
|
||||||
|
@ -1115,17 +1120,32 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
trait RngNextI32 {
|
||||||
fn extreme_case(_gba: &mut Gba) {
|
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 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];
|
let mut answers: [Option<i32>; 128] = [None; 128];
|
||||||
|
|
||||||
for _ in 0..5_000 {
|
for _ in 0..5_000 {
|
||||||
let command = rng.gen().rem_euclid(2);
|
let command = rng.next_i32().rem_euclid(2);
|
||||||
let key = rng.gen().rem_euclid(answers.len() as i32);
|
let key = rng.next_i32().rem_euclid(answers.len() as i32);
|
||||||
let value = rng.gen();
|
let value = rng.next_i32();
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
0 => {
|
0 => {
|
||||||
|
@ -1212,8 +1232,8 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn correctly_drops_on_remove_and_overall_drop(_gba: &mut Gba) {
|
fn correctly_drops_on_remove_and_overall_drop() {
|
||||||
let drop_registry = DropRegistry::new();
|
let drop_registry = DropRegistry::new();
|
||||||
|
|
||||||
let droppable1 = drop_registry.new_droppable();
|
let droppable1 = drop_registry.new_droppable();
|
||||||
|
@ -1239,8 +1259,8 @@ mod test {
|
||||||
drop_registry.assert_dropped_once(id2);
|
drop_registry.assert_dropped_once(id2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn correctly_drop_on_override(_gba: &mut Gba) {
|
fn correctly_drop_on_override() {
|
||||||
let drop_registry = DropRegistry::new();
|
let drop_registry = DropRegistry::new();
|
||||||
|
|
||||||
let droppable1 = drop_registry.new_droppable();
|
let droppable1 = drop_registry.new_droppable();
|
||||||
|
@ -1263,8 +1283,8 @@ mod test {
|
||||||
drop_registry.assert_dropped_once(id2);
|
drop_registry.assert_dropped_once(id2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn correctly_drops_key_on_override(_gba: &mut Gba) {
|
fn correctly_drops_key_on_override() {
|
||||||
let drop_registry = DropRegistry::new();
|
let drop_registry = DropRegistry::new();
|
||||||
|
|
||||||
let droppable1 = drop_registry.new_droppable();
|
let droppable1 = drop_registry.new_droppable();
|
||||||
|
@ -1285,8 +1305,8 @@ mod test {
|
||||||
drop_registry.assert_dropped_n_times(id1, 2);
|
drop_registry.assert_dropped_n_times(id1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_retain(_gba: &mut Gba) {
|
fn test_retain() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
|
@ -1301,8 +1321,8 @@ mod test {
|
||||||
assert_eq!(map.iter().count(), 50); // force full iteration
|
assert_eq!(map.iter().count(), 50); // force full iteration
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_size_hint_iter(_gba: &mut Gba) {
|
fn test_size_hint_iter() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
|
@ -1317,8 +1337,8 @@ mod test {
|
||||||
assert_eq!(iter.size_hint(), (99, Some(99)));
|
assert_eq!(iter.size_hint(), (99, Some(99)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_size_hint_into_iter(_gba: &mut Gba) {
|
fn test_size_hint_into_iter() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
|
@ -1336,13 +1356,10 @@ mod test {
|
||||||
// Following test cases copied from the rust source
|
// Following test cases copied from the rust source
|
||||||
// https://github.com/rust-lang/rust/blob/master/library/std/src/collections/hash/map/tests.rs
|
// https://github.com/rust-lang/rust/blob/master/library/std/src/collections/hash/map/tests.rs
|
||||||
mod rust_std_tests {
|
mod rust_std_tests {
|
||||||
use crate::{
|
use crate::{Entry::*, HashMap};
|
||||||
hash_map::{Entry::*, HashMap},
|
|
||||||
Gba,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_entry(_gba: &mut Gba) {
|
fn test_entry() {
|
||||||
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
||||||
|
|
||||||
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
||||||
|
@ -1391,8 +1408,8 @@ mod test {
|
||||||
assert_eq!(map.len(), 6);
|
assert_eq!(map.len(), 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_occupied_entry_key(_gba: &mut Gba) {
|
fn test_occupied_entry_key() {
|
||||||
let mut a = HashMap::new();
|
let mut a = HashMap::new();
|
||||||
let key = "hello there";
|
let key = "hello there";
|
||||||
let value = "value goes here";
|
let value = "value goes here";
|
||||||
|
@ -1409,8 +1426,8 @@ mod test {
|
||||||
assert_eq!(a[key], value);
|
assert_eq!(a[key], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_vacant_entry_key(_gba: &mut Gba) {
|
fn test_vacant_entry_key() {
|
||||||
let mut a = HashMap::new();
|
let mut a = HashMap::new();
|
||||||
let key = "hello there";
|
let key = "hello there";
|
||||||
let value = "value goes here";
|
let value = "value goes here";
|
||||||
|
@ -1427,8 +1444,8 @@ mod test {
|
||||||
assert_eq!(a[key], value);
|
assert_eq!(a[key], value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test]
|
||||||
fn test_index(_gba: &mut Gba) {
|
fn test_index() {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
map.insert(1, 2);
|
map.insert(1, 2);
|
||||||
|
@ -1438,111 +1455,4 @@ mod test {
|
||||||
assert_eq!(map[&2], 1);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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_sound_converter = { version = "0.14.0", path = "../agb-sound-converter" }
|
||||||
agb_macros = { version = "0.14.0", path = "../agb-macros" }
|
agb_macros = { version = "0.14.0", path = "../agb-macros" }
|
||||||
agb_fixnum = { version = "0.14.0", path = "../agb-fixnum" }
|
agb_fixnum = { version = "0.14.0", path = "../agb-fixnum" }
|
||||||
|
agb_hashmap = { version = "0.14.0", path = "../agb-hashmap" }
|
||||||
bare-metal = "1"
|
bare-metal = "1"
|
||||||
modular-bitfield = "0.11"
|
modular-bitfield = "0.11"
|
||||||
rustc-hash = { version = "1", default-features = false }
|
rustc-hash = { version = "1", default-features = false }
|
||||||
|
|
|
@ -152,7 +152,7 @@ pub mod mgba;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use agb_fixnum as fixnum;
|
pub use agb_fixnum as fixnum;
|
||||||
/// Contains an implementation of a hashmap which suits the gameboy advance's hardware.
|
/// 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
|
/// Simple random number generator
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
pub mod save;
|
pub mod save;
|
||||||
|
|
Loading…
Reference in a new issue