mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
Merge remote-tracking branch 'upstream/master' into new-objects
This commit is contained in:
commit
3ec9bd2610
5
.github/workflows/build-and-test.yml
vendored
5
.github/workflows/build-and-test.yml
vendored
|
@ -18,6 +18,11 @@ jobs:
|
|||
steps:
|
||||
- name: Install build tools
|
||||
run: sudo apt-get update && sudo apt-get install build-essential binutils-arm-none-eabi libelf-dev zip -y
|
||||
- name: Install Miri
|
||||
run: |
|
||||
rustup toolchain install nightly --component miri
|
||||
rustup override set nightly
|
||||
cargo miri setup
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
|
|
5
.vscode/agb.code-workspace
vendored
5
.vscode/agb.code-workspace
vendored
|
@ -41,6 +41,9 @@
|
|||
},
|
||||
{
|
||||
"path": "../examples/combo"
|
||||
},
|
||||
{
|
||||
"path": "../agb-hashmap"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Changed
|
||||
- Importing background tiles has been improved. You no longer need to use `include_gfx!` with the toml file. Instead, use `include_background_gfx`. See the documentation for usage.
|
||||
- The hashmap implementation is now it its own crate, `agb-hashmap`. There is no change in API, but you can now use this for interop between non-agb code and agb code
|
||||
- Moved the existing object API to be the OamManaged API. The old names persist with deprecated notices on them.
|
||||
|
||||
## [0.14.0] - 2023/04/11
|
||||
|
|
|
@ -66,6 +66,8 @@ to just write games for the Game Boy Advance using this library:
|
|||
* Install with `cargo install just`
|
||||
* [mdbook](https://rust-lang.github.io/mdBook/index.html)
|
||||
* Install with `cargo install mdbook`
|
||||
* [miri](https://github.com/rust-lang/miri)
|
||||
* Some of the unsafe code is tested using miri, install with `rustup component add miri`
|
||||
|
||||
With all of this installed, you should be able to run a full build of agb using by running
|
||||
```sh
|
||||
|
@ -85,6 +87,8 @@ for performant decimals.
|
|||
|
||||
`agb-sound-converter` - a crate which converts wav files into a format supported by the game boy advance
|
||||
|
||||
`agb-hashmap` - an no_std hashmap implementation tuned for use on the game boy advance
|
||||
|
||||
`agb` - the main library code
|
||||
|
||||
`agb/examples` - basic examples often targeting 1 feature, you can run these using `just run-example <example-name>`
|
||||
|
|
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,9 +1,26 @@
|
|||
#![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(clippy::must_use_candidate)]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
#![deny(clippy::semicolon_if_nothing_returned)]
|
||||
#![deny(clippy::map_unwrap_or)]
|
||||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::redundant_closure_for_method_calls)]
|
||||
#![deny(clippy::cloned_instead_of_copied)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(rustdoc::private_intra_doc_links)]
|
||||
#![deny(rustdoc::invalid_html_tags)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{alloc::Global, vec::Vec};
|
||||
use core::{
|
||||
alloc::Allocator,
|
||||
borrow::Borrow,
|
||||
hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
|
||||
iter::FromIterator,
|
||||
mem::{self, MaybeUninit},
|
||||
|
@ -83,6 +100,38 @@ type HashType = u32;
|
|||
///
|
||||
/// [`Eq`]: https://doc.rust-lang.org/core/cmp/trait.Eq.html
|
||||
/// [`Hash`]: https://doc.rust-lang.org/core/hash/trait.Hash.html
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use agb_hashmap::HashMap;
|
||||
///
|
||||
/// // Type inference lets you omit the type signature (which would be HashMap<String, String> in this example)
|
||||
/// let mut game_reviews = HashMap::new();
|
||||
///
|
||||
/// // Review some games
|
||||
/// game_reviews.insert(
|
||||
/// "Pokemon Emerald".to_string(),
|
||||
/// "Best post-game battle experience of any generation.".to_string(),
|
||||
/// );
|
||||
/// game_reviews.insert(
|
||||
/// "Golden Sun".to_string(),
|
||||
/// "Some of the best music on the console".to_string(),
|
||||
/// );
|
||||
/// game_reviews.insert(
|
||||
/// "Super Dodge Ball Advance".to_string(),
|
||||
/// "Really great launch title".to_string(),
|
||||
/// );
|
||||
///
|
||||
/// // Check for a specific entry
|
||||
/// if !game_reviews.contains_key("Legend of Zelda: The Minish Cap") {
|
||||
/// println!("We've got {} reviews, but The Minish Cap ain't one", game_reviews.len());
|
||||
/// }
|
||||
///
|
||||
/// // Iterate over everything
|
||||
/// for (game, review) in &game_reviews {
|
||||
/// println!("{game}: \"{review}\"");
|
||||
/// }
|
||||
/// ```
|
||||
pub struct HashMap<K, V, ALLOCATOR: Allocator = Global> {
|
||||
nodes: NodeStorage<K, V, ALLOCATOR>,
|
||||
|
||||
|
@ -283,13 +332,21 @@ where
|
|||
}
|
||||
|
||||
/// Returns `true` if the map contains a value for the specified key.
|
||||
pub fn contains_key(&self, k: &K) -> bool {
|
||||
pub fn contains_key<Q>(&self, k: &Q) -> bool
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
let hash = self.hash(k);
|
||||
self.nodes.location(k, hash).is_some()
|
||||
}
|
||||
|
||||
/// Returns the key-value pair corresponding to the supplied key
|
||||
pub fn get_key_value(&self, key: &K) -> Option<(&K, &V)> {
|
||||
pub fn get_key_value<Q>(&self, key: &Q) -> Option<(&K, &V)>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
let hash = self.hash(key);
|
||||
|
||||
self.nodes
|
||||
|
@ -299,13 +356,45 @@ where
|
|||
|
||||
/// Returns a reference to the value corresponding to the key. Returns [`None`] if there is
|
||||
/// no element in the map with the given key.
|
||||
pub fn get(&self, key: &K) -> Option<&V> {
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use agb_hashmap::HashMap;
|
||||
///
|
||||
/// let mut map = HashMap::new();
|
||||
/// map.insert("a".to_string(), "A");
|
||||
/// assert_eq!(map.get("a"), Some(&"A"));
|
||||
/// assert_eq!(map.get("b"), None);
|
||||
/// ```
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<&V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
self.get_key_value(key).map(|(_, v)| v)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value corresponding to the key. Return [`None`] if
|
||||
/// there is no element in the map with the given key.
|
||||
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use agb_hashmap::HashMap;
|
||||
///
|
||||
/// let mut map = HashMap::new();
|
||||
/// map.insert("a".to_string(), "A");
|
||||
///
|
||||
/// if let Some(x) = map.get_mut("a") {
|
||||
/// *x = "b";
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(map["a"], "b");
|
||||
/// ```
|
||||
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
let hash = self.hash(key);
|
||||
|
||||
if let Some(location) = self.nodes.location(key, hash) {
|
||||
|
@ -317,7 +406,21 @@ where
|
|||
|
||||
/// Removes the given key from the map. Returns the current value if it existed, or [`None`]
|
||||
/// if it did not.
|
||||
pub fn remove(&mut self, key: &K) -> Option<V> {
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use agb_hashmap::HashMap;
|
||||
///
|
||||
/// let mut map = HashMap::new();
|
||||
/// map.insert(1, "a");
|
||||
/// assert_eq!(map.remove(&1), Some("a"));
|
||||
/// assert_eq!(map.remove(&1), None);
|
||||
/// ```
|
||||
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq + ?Sized,
|
||||
{
|
||||
let hash = self.hash(key);
|
||||
|
||||
self.nodes
|
||||
|
@ -330,7 +433,11 @@ impl<K, V, ALLOCATOR: ClonableAllocator> HashMap<K, V, ALLOCATOR>
|
|||
where
|
||||
K: Hash,
|
||||
{
|
||||
fn hash(&self, key: &K) -> HashType {
|
||||
fn hash<Q>(&self, key: &Q) -> HashType
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + ?Sized,
|
||||
{
|
||||
let mut hasher = self.hasher.build_hasher();
|
||||
key.hash(&mut hasher);
|
||||
hasher.finish() as HashType
|
||||
|
@ -657,28 +764,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<K, V, ALLOCATOR: ClonableAllocator> Index<&K> for HashMap<K, V, ALLOCATOR>
|
||||
impl<K, V, Q, ALLOCATOR: ClonableAllocator> Index<&Q> for HashMap<K, V, ALLOCATOR>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
K: Eq + Hash + Borrow<Q>,
|
||||
Q: Eq + Hash + ?Sized,
|
||||
{
|
||||
type Output = V;
|
||||
|
||||
fn index(&self, key: &K) -> &V {
|
||||
fn index(&self, key: &Q) -> &V {
|
||||
self.get(key).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, ALLOCATOR: ClonableAllocator> Index<K> for HashMap<K, V, ALLOCATOR>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
type Output = V;
|
||||
|
||||
fn index(&self, key: K) -> &V {
|
||||
self.get(&key).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
const fn number_before_resize(capacity: usize) -> usize {
|
||||
capacity * 85 / 100
|
||||
}
|
||||
|
@ -812,9 +909,10 @@ impl<K, V, ALLOCATOR: ClonableAllocator> NodeStorage<K, V, ALLOCATOR> {
|
|||
}
|
||||
}
|
||||
|
||||
fn location(&self, key: &K, hash: HashType) -> Option<usize>
|
||||
fn location<Q>(&self, key: &Q, hash: HashType) -> Option<usize>
|
||||
where
|
||||
K: Eq,
|
||||
K: Borrow<Q>,
|
||||
Q: Eq + ?Sized,
|
||||
{
|
||||
for distance_to_initial_bucket in 0..(self.max_distance_to_initial_bucket + 1) {
|
||||
let location = fast_mod(
|
||||
|
@ -824,7 +922,7 @@ impl<K, V, ALLOCATOR: ClonableAllocator> NodeStorage<K, V, ALLOCATOR> {
|
|||
|
||||
let node = &self.nodes[location];
|
||||
if let Some(node_key_ref) = node.key_ref() {
|
||||
if node_key_ref == key {
|
||||
if node_key_ref.borrow() == key {
|
||||
return Some(location);
|
||||
}
|
||||
} else {
|
||||
|
@ -993,10 +1091,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 +1105,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 +1116,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 +1127,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 +1144,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 +1164,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 {
|
||||
|
@ -1086,6 +1183,7 @@ mod test {
|
|||
}
|
||||
|
||||
impl NoisyDrop {
|
||||
#[cfg(not(miri))]
|
||||
fn new(i: i32) -> Self {
|
||||
Self { i, dropped: false }
|
||||
}
|
||||
|
@ -1115,17 +1213,33 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // takes way too long to run under miri
|
||||
#[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 +1326,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 +1353,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 +1377,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 +1399,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 +1415,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 +1431,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 +1450,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 +1502,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 +1520,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 +1538,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 +1549,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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
|
|
|
@ -151,7 +151,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;
|
||||
|
|
8
book/games/pong/Cargo.lock
generated
8
book/games/pong/Cargo.lock
generated
|
@ -19,6 +19,7 @@ name = "agb"
|
|||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
|
@ -35,6 +36,13 @@ dependencies = [
|
|||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
|
|
8
examples/combo/Cargo.lock
generated
8
examples/combo/Cargo.lock
generated
|
@ -19,6 +19,7 @@ name = "agb"
|
|||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
|
@ -35,6 +36,13 @@ dependencies = [
|
|||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
|
|
8
examples/hyperspace-roll/Cargo.lock
generated
8
examples/hyperspace-roll/Cargo.lock
generated
|
@ -19,6 +19,7 @@ name = "agb"
|
|||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
|
@ -35,6 +36,13 @@ dependencies = [
|
|||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
|
|
8
examples/the-hat-chooses-the-wizard/Cargo.lock
generated
8
examples/the-hat-chooses-the-wizard/Cargo.lock
generated
|
@ -19,6 +19,7 @@ name = "agb"
|
|||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
|
@ -35,6 +36,13 @@ dependencies = [
|
|||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
|
|
8
examples/the-purple-night/Cargo.lock
generated
8
examples/the-purple-night/Cargo.lock
generated
|
@ -19,6 +19,7 @@ name = "agb"
|
|||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"agb_fixnum",
|
||||
"agb_hashmap",
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
|
@ -35,6 +36,13 @@ dependencies = [
|
|||
"agb_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_hashmap"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_image_converter"
|
||||
version = "0.14.0"
|
||||
|
|
7
justfile
7
justfile
|
@ -14,6 +14,7 @@ clippy:
|
|||
test:
|
||||
just _test-debug agb
|
||||
just _test-debug agb-fixnum
|
||||
just _test-debug agb-hashmap
|
||||
just _test-debug-arm agb
|
||||
just _test-debug tools
|
||||
|
||||
|
@ -27,6 +28,7 @@ doctest-agb:
|
|||
check-docs:
|
||||
(cd agb && cargo doc --target=thumbv6m-none-eabi --no-deps)
|
||||
just _build_docs agb-fixnum
|
||||
just _build_docs agb-hashmap
|
||||
|
||||
_build_docs crate:
|
||||
(cd "{{crate}}" && cargo doc --no-deps)
|
||||
|
@ -59,7 +61,7 @@ check-linker-script-consistency:
|
|||
find -type f -name gba.ld -print0 | xargs -0 -n1 cmp -- agb/gba.ld
|
||||
find -type f -name gba_mb.ld -print0 | xargs -0 -n1 cmp -- agb/gba_mb.ld
|
||||
|
||||
ci: check-linker-script-consistency build-debug clippy fmt-check test build-release test-release doctest-agb build-roms build-book check-docs
|
||||
ci: check-linker-script-consistency build-debug clippy fmt-check test miri build-release test-release doctest-agb build-roms build-book check-docs
|
||||
|
||||
build-roms:
|
||||
just _build-rom "examples/the-purple-night" "PURPLENIGHT"
|
||||
|
@ -85,6 +87,9 @@ publish *args: (_run-tool "publish" args)
|
|||
|
||||
release +args: (_run-tool "release" args)
|
||||
|
||||
miri:
|
||||
(cd agb-hashmap && cargo miri test)
|
||||
|
||||
_run-tool +tool:
|
||||
(cd tools && cargo build)
|
||||
"$CARGO_TARGET_DIR/debug/tools" {{tool}}
|
||||
|
|
|
@ -170,7 +170,8 @@ mod test {
|
|||
"agb-image-converter",
|
||||
"agb-sound-converter",
|
||||
"agb-macros",
|
||||
"agb-fixnum"
|
||||
"agb-fixnum",
|
||||
"agb-hashmap",
|
||||
]
|
||||
);
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue