1use bytemuck::{Pod, Zeroable};
2use wowlab_buffer_contract::SLOT_ALIGNMENT;
3use wowlab_types::{
4 combat::ResourceType,
5 sim::{AuraKey, AuraProjectionKey, SpellIdx},
6};
7
8use super::{BufferOffsets, HistoryIterMut};
9use crate::rotation::buffer::{
10 AuraSlot, ByteOffset, CombatSlot, CooldownSlot, DefaultValue, EquipmentSlot, HeroTreeSlot,
11 HistorySlot, ItemSlot, PetSlot, PlayerSlot, ResourceSlot, SetBonusSlot, SpellSlot,
12 StandaloneDefault, SwingSlot, TalentSlot, UnitSlot, WeaponImbueSlot,
13};
14
15#[derive(Clone, Copy, Debug)]
17#[repr(C, align(8))]
18struct SlotChunk([u8; SLOT_ALIGNMENT]);
19
20unsafe impl Zeroable for SlotChunk {}
22unsafe impl Pod for SlotChunk {}
24
25const _: () = assert!(
26 size_of::<SlotChunk>() == SLOT_ALIGNMENT,
27 "SlotChunk must be exactly SLOT_ALIGNMENT bytes wide"
28);
29
30const _: () = assert!(
31 align_of::<SlotChunk>() == SLOT_ALIGNMENT,
32 "SlotChunk must be SLOT_ALIGNMENT-aligned"
33);
34
35#[inline]
36fn chunks_for_bytes(byte_len: usize) -> usize {
37 byte_len.div_ceil(SLOT_ALIGNMENT)
38}
39
40macro_rules! singleton_slot {
41 ($read:ident, $write:ident, $Slot:ty, $field:ident) => {
42 #[inline(always)]
43 pub fn $read(&self) -> &$Slot {
44 self.slot_ref::<$Slot>(self.offsets.$field)
45 }
46 #[inline(always)]
47 pub fn $write(&mut self) -> &mut $Slot {
48 self.slot_mut::<$Slot>(self.offsets.$field)
49 }
50 };
51}
52
53macro_rules! keyed_slot {
54 ($read:ident, $write:ident, $Slot:ty, $Key:ty, $map:ident) => {
55 #[inline]
56 pub fn $read(&self, key: $Key) -> Option<&$Slot> {
57 let &base = self.offsets.slots.$map.get(&key)?;
58 Some(self.slot_ref::<$Slot>(base))
59 }
60 #[inline]
61 pub fn $write(&mut self, key: $Key) -> Option<&mut $Slot> {
62 let &base = self.offsets.slots.$map.get(&key)?;
63 Some(self.slot_mut::<$Slot>(base))
64 }
65 };
66}
67
68macro_rules! keyed_slot_str {
69 ($read:ident, $write:ident, $Slot:ty, $map:ident) => {
70 #[inline]
71 pub fn $read(&self, key: &str) -> Option<&$Slot> {
72 let &base = self.offsets.slots.$map.get(key)?;
73 Some(self.slot_ref::<$Slot>(base))
74 }
75 #[inline]
76 pub fn $write(&mut self, key: &str) -> Option<&mut $Slot> {
77 let &base = self.offsets.slots.$map.get(key)?;
78 Some(self.slot_mut::<$Slot>(base))
79 }
80 };
81}
82
83macro_rules! ensure_slot {
84 ($fn:ident, $Slot:ty, $map:ident, $key:ident : $Key:ty, $insert:expr, $lookup:expr) => {
85 #[doc = concat!(
86 "Ensure a `", stringify!($Slot), "` exists for `", stringify!($key),
87 "`, returning `true` if it already existed."
88 )]
89 pub fn $fn(&mut self, $key: $Key) -> bool {
90 if self.offsets.slots.$map.contains_key($lookup) {
91 return true;
92 }
93 let base = self.align_to_slot();
94 self.resize_bytes(base + <$Slot>::SIZE);
95 self.offsets.slots.$map.insert($insert, ByteOffset::new(base));
96 false
97 }
98 };
99}
100
101#[derive(Debug)]
103pub struct DenseBuffer {
104 chunks: Vec<SlotChunk>,
105 byte_len: usize,
106 pub(super) offsets: BufferOffsets,
107 standalone_defaults: Vec<(ByteOffset, &'static DefaultValue)>,
108}
109
110impl DenseBuffer {
111 #[must_use]
112 pub fn new(offsets: BufferOffsets, size: usize) -> Self {
113 let mut standalone_defaults = Vec::with_capacity(offsets.standalone.len());
114
115 for (&descriptor_id, &offset) in &offsets.standalone {
116 if let Some(default) = inventory::iter::<StandaloneDefault>
117 .into_iter()
118 .find(|d| (d.matches)(descriptor_id))
119 {
120 standalone_defaults.push((offset, &default.value));
121 }
122 }
123
124 Self {
125 chunks: vec![SlotChunk([0u8; SLOT_ALIGNMENT]); chunks_for_bytes(size)],
126 byte_len: size,
127 offsets,
128 standalone_defaults,
129 }
130 }
131
132 pub fn aura_keys(&self) -> impl Iterator<Item = AuraKey> + '_ {
134 self.offsets.slots.auras.keys().copied()
135 }
136 pub fn aura_projection_keys(&self) -> impl Iterator<Item = AuraProjectionKey> + '_ {
138 self.offsets.slots.aura_projections.keys().copied()
139 }
140
141 #[inline]
142 #[must_use]
143 pub fn as_ptr(&self) -> *const u8 {
144 self.chunks.as_ptr().cast::<u8>()
145 }
146
147 #[inline]
148 pub fn as_mut_ptr(&mut self) -> *mut u8 {
149 self.chunks.as_mut_ptr().cast::<u8>()
150 }
151
152 #[inline]
154 #[must_use]
155 pub fn bytes(&self) -> &[u8] {
156 &bytemuck::cast_slice::<SlotChunk, u8>(&self.chunks)[..self.byte_len]
158 }
159
160 #[inline]
162 pub fn bytes_mut(&mut self) -> &mut [u8] {
163 let len = self.byte_len;
164
165 &mut bytemuck::cast_slice_mut::<SlotChunk, u8>(&mut self.chunks)[..len]
167 }
168
169 pub fn history_iter_mut(&mut self) -> HistoryIterMut<'_> {
171 let mut offsets: Vec<usize> = self
172 .offsets
173 .slots
174 .history
175 .values()
176 .map(ByteOffset::as_usize)
177 .collect();
178
179 offsets.sort_unstable();
180
181 HistoryIterMut {
182 rest: self.bytes_mut(),
183 offsets: offsets.into_iter(),
184 consumed: 0,
185 }
186 }
187 pub fn clear_history(&mut self) {
189 for h in self.history_iter_mut() {
190 h.prev_gcd = 0;
191 h.prev_off_gcd = 0;
192 }
193 }
194 pub fn reset_and_defaults(&mut self) {
196 self.bytes_mut().fill(0);
197 self.populate_standalone_defaults();
198 }
199 #[inline]
200 fn slot_ref<T>(&self, base: ByteOffset) -> &T
201 where
202 T: Pod,
203 {
204 let base = base.as_usize();
205
206 bytemuck::from_bytes(&self.bytes()[base..base + size_of::<T>()])
208 }
209 #[inline]
210 fn slot_mut<T>(&mut self, base: ByteOffset) -> &mut T
211 where
212 T: Pod,
213 {
214 let base = base.as_usize();
215
216 bytemuck::from_bytes_mut(&mut self.bytes_mut()[base..base + size_of::<T>()])
218 }
219 fn resize_bytes(&mut self, new_byte_len: usize) {
220 let needed_chunks = chunks_for_bytes(new_byte_len);
221
222 if needed_chunks > self.chunks.len() {
223 self.chunks
224 .resize(needed_chunks, SlotChunk([0u8; SLOT_ALIGNMENT]));
225 }
226
227 let prev_len = self.byte_len;
228
229 if new_byte_len > prev_len {
230 let bytes = bytemuck::cast_slice_mut::<SlotChunk, u8>(&mut self.chunks);
231
232 for byte in &mut bytes[prev_len..new_byte_len] {
234 *byte = 0;
235 }
236 }
237
238 self.byte_len = new_byte_len;
239 }
240 fn align_to_slot(&mut self) -> usize {
241 let cur = self.byte_len;
242
243 if cur % SLOT_ALIGNMENT != 0 {
244 self.resize_bytes(cur + (SLOT_ALIGNMENT - (cur % SLOT_ALIGNMENT)));
245 }
246
247 self.byte_len
248 }
249 fn populate_standalone_defaults(&mut self) {
250 let byte_len = self.byte_len;
251 let bytes = &mut bytemuck::cast_slice_mut::<SlotChunk, u8>(&mut self.chunks)[..byte_len];
253
254 for &(offset, value) in &self.standalone_defaults {
255 value.write_to(bytes, offset);
256 }
257 }
258 singleton_slot!(player, player_mut, PlayerSlot, player);
259 singleton_slot!(combat, combat_mut, CombatSlot, combat);
260 singleton_slot!(pet, pet_mut, PetSlot, pet);
261 keyed_slot!(cooldown, cooldown_mut, CooldownSlot, SpellIdx, cooldowns);
262 keyed_slot!(aura, aura_mut, AuraSlot, AuraKey, auras);
263 keyed_slot!(
264 aura_projection,
265 aura_projection_mut,
266 AuraSlot,
267 AuraProjectionKey,
268 aura_projections
269 );
270 keyed_slot!(
271 resource,
272 resource_mut,
273 ResourceSlot,
274 ResourceType,
275 resources
276 );
277
278 keyed_slot!(spell, spell_mut, SpellSlot, SpellIdx, spells);
279
280 keyed_slot!(history, history_mut, HistorySlot, SpellIdx, history);
281
282 keyed_slot_str!(talent, talent_mut, TalentSlot, talents);
283 keyed_slot_str!(hero_tree, hero_tree_mut, HeroTreeSlot, hero_trees);
284 keyed_slot_str!(item, item_mut, ItemSlot, items);
285 keyed_slot_str!(swing, swing_mut, SwingSlot, swings);
286 keyed_slot_str!(equipment, equipment_mut, EquipmentSlot, equipment);
287 keyed_slot_str!(set_bonus, set_bonus_mut, SetBonusSlot, set_bonuses);
288 keyed_slot_str!(unit, unit_mut, UnitSlot, units);
289 keyed_slot_str!(
290 weapon_imbue,
291 weapon_imbue_mut,
292 WeaponImbueSlot,
293 weapon_imbues
294 );
295
296 ensure_slot!(ensure_swing_slot, SwingSlot, swings, key: &str, key.to_string(), key);
297
298 ensure_slot!(ensure_unit_slot, UnitSlot, units, key: &str, key.to_string(), key);
299
300 ensure_slot!(
301 ensure_weapon_imbue_slot,
302 WeaponImbueSlot,
303 weapon_imbues,
304 key: &str,
305 key.to_string(),
306 key
307 );
308
309 ensure_slot!(ensure_aura_slot, AuraSlot, auras, key: AuraKey, key, &key);
310
311 ensure_slot!(
312 ensure_aura_projection_slot,
313 AuraSlot,
314 aura_projections,
315 key: AuraProjectionKey,
316 key,
317 &key
318 );
319
320 ensure_slot!(
321 ensure_spell_slot,
322 SpellSlot,
323 spells,
324 spell: SpellIdx,
325 spell,
326 &spell
327 );
328
329 ensure_slot!(
330 ensure_cooldown_slot,
331 CooldownSlot,
332 cooldowns,
333 spell: SpellIdx,
334 spell,
335 &spell
336 );
337
338 ensure_slot!(
339 ensure_resource_slot,
340 ResourceSlot,
341 resources,
342 resource: ResourceType,
343 resource,
344 &resource
345 );
346}