1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use core::cell::UnsafeCell;
use anyhow::Result;
use hashbrown::HashMap;
use zone_alloc::ElementRefMut;
use crate::{
battle::{
CoreBattle,
Mon,
MonHandle,
MoveHandle,
},
error::WrapOptionError,
moves::Move,
};
/// Cache of resources borrowed by a [`Context`][`crate::battle::Context`] chain.
///
/// Resources are borrowed for the lifetime of the context chain. For instance, if a
/// [`MonContext`][`crate::battle::MonContext`] borrows a [`Mon`], that [`Mon`] will stay borrowed
/// as long as the parent context lives, even if the original
/// [`MonContext`][`crate::battle::MonContext`] is dropped.
///
/// Borrowing resources at the root of the chain allows multiple contexts to borrow the same
/// resource at different parts in the chain.
pub struct ContextCache<'borrow> {
mons: UnsafeCell<HashMap<MonHandle, ElementRefMut<'borrow, Mon>>>,
active_moves: UnsafeCell<HashMap<MoveHandle, ElementRefMut<'borrow, Move>>>,
}
impl<'borrow> ContextCache<'borrow> {
/// Creates a new context cache.
pub fn new() -> Self {
Self {
mons: UnsafeCell::new(HashMap::default()),
active_moves: UnsafeCell::new(HashMap::default()),
}
}
/// Clears all borrowed references.
pub fn clear(&mut self) {
self.mons.get_mut().clear();
self.active_moves.get_mut().clear();
}
/// Borrows a new [`Mon`] by handle.
pub fn mon(&self, battle: &CoreBattle, mon_handle: MonHandle) -> Result<&mut Mon> {
// SAFETY: This is the only method that accesses this map.
let mons = unsafe { &mut *self.mons.get() };
// Multiple map look ups because the borrow checker cannot handle otherwise.
if mons.contains_key(&mon_handle) {
return mons
.get_mut(&mon_handle)
.wrap_expectation_with_format(format_args!(
"expected Mon {mon_handle} to exist in cache"
))
.map(|mon| mon.as_mut());
}
// SAFETY: This should always succeed, assuming that a Mon was not incorrectly borrowed
// outside of this context.
let mon = unsafe { battle.mon_mut(mon_handle)? };
// SAFETY: We extend the lifetime of the ElementRef (dynamic borrow checking) so that the
// borrow lives for the lifetime of the context chain.
//
// SAFETY: This is safe across multiple insertions because ElementRef does not own any data.
// The underlying reference is not invalidated.
let mon = unsafe {
core::mem::transmute::<ElementRefMut<'_, Mon>, ElementRefMut<'borrow, Mon>>(mon)
};
mons.insert(mon_handle, mon);
let mon = mons
.get_mut(&mon_handle)
.wrap_expectation_with_format(format_args!(
"expected Mon {mon_handle} to have been inserted"
))?;
Ok(mon.as_mut())
}
/// Borrows a new [`Move`] by handle.
pub fn active_move(&self, battle: &CoreBattle, move_handle: MoveHandle) -> Result<&mut Move> {
// SAFETY: This is the only method that accesses this map.
let moves = unsafe { &mut *self.active_moves.get() };
// Multiple map look ups because the borrow checker cannot handle otherwise.
if moves.contains_key(&move_handle) {
return moves
.get_mut(&move_handle)
.wrap_expectation_with_format(format_args!(
"expected active move {move_handle} to exist in cache"
))
.map(|mov| mov.as_mut());
}
// SAFETY: This should always succeed, assuming that a move was not incorrectly borrowed
// outside of this context.
let mov = unsafe { battle.active_move_mut(move_handle)? };
// SAFETY: We extend the lifetime of the ElementRef (dynamic borrow checking) so that the
// borrow lives for the lifetime of the context chain.
//
// SAFETY: This is safe across multiple insertions because ElementRef does not own any data.
// The underlying reference is not invalidated.
let mov = unsafe {
core::mem::transmute::<ElementRefMut<'_, Move>, ElementRefMut<'borrow, Move>>(mov)
};
moves.insert(move_handle, mov);
let mov = moves
.get_mut(&move_handle)
.wrap_expectation_with_format(format_args!(
"expected active move {move_handle} to have been inserted"
))?;
Ok(mov.as_mut())
}
}