1use std::sync::Arc;
2
3use bytemuck::{Pod, Zeroable};
4use wowlab_buffer_contract::SLOT_ALIGNMENT;
5#[cfg(any(test, feature = "test-support"))]
6use wowlab_types::{
7 combat::ResourceType,
8 sim::{AuraKey, AuraProjectionKey, SpellIdx},
9};
10
11use crate::rotation::buffer::{
12 AuraProjectionSlotHandle, AuraSlot, ByteOffset, CombatSlot, CooldownSlot, CooldownSlotHandle,
13 EquipmentSlot, EquipmentSlotHandle, FrozenBufferLayout, HeroTreeSlot, HeroTreeSlotHandle,
14 HistorySlot, HistorySlotHandle, ItemSlot, ItemSlotHandle, LayoutFingerprint, PetSlot,
15 PlayerSlot, ResourceSlot, ResourceSlotHandle, SetBonusSlot, SetBonusSlotHandle, SpellSlot,
16 SpellSlotHandle, SwingSlot, SwingSlotHandle, TalentSlot, TalentSlotHandle, UnitSlot,
17 UnitSlotHandle, WeaponImbueSlot, WeaponImbueSlotHandle,
18};
19
20#[path = "aura_ops.rs"]
21mod aura_ops;
22
23#[derive(Clone, Copy, Debug)]
25#[repr(C, align(8))]
26struct SlotChunk([u8; SLOT_ALIGNMENT]);
27
28unsafe impl Zeroable for SlotChunk {}
30unsafe impl Pod for SlotChunk {}
32
33const _: () = assert!(
34 size_of::<SlotChunk>() == SLOT_ALIGNMENT,
35 "SlotChunk must be exactly SLOT_ALIGNMENT bytes wide"
36);
37
38const _: () = assert!(
39 align_of::<SlotChunk>() == SLOT_ALIGNMENT,
40 "SlotChunk must be SLOT_ALIGNMENT-aligned"
41);
42
43#[inline]
44fn chunks_for_bytes(byte_len: usize) -> usize {
45 byte_len.div_ceil(SLOT_ALIGNMENT)
46}
47
48macro_rules! singleton_slot {
49 ($read:ident, $write:ident, $Slot:ty, $field:ident) => {
50 #[inline(always)]
51 pub(crate) fn $read(&self) -> &$Slot {
52 self.slot_ref::<$Slot>(self.layout.offsets.$field)
53 }
54 #[inline(always)]
55 pub(crate) fn $write(&mut self) -> &mut $Slot {
56 self.slot_mut::<$Slot>(self.layout.offsets.$field)
57 }
58 };
59}
60
61macro_rules! keyed_slot {
62 ($read:ident, $write:ident, $Slot:ty, $Key:ty, $map:ident) => {
63 #[cfg(any(test, feature = "test-support"))]
64 #[inline]
65 pub fn $read(&self, key: $Key) -> Option<&$Slot> {
66 wowlab_types::bench_metrics::record_keyed_lookup();
67 let &base = self.layout.offsets.slots.$map.get(&key)?;
68 Some(self.slot_ref::<$Slot>(base))
69 }
70 #[cfg(any(test, feature = "test-support"))]
71 #[inline]
72 pub fn $write(&mut self, key: $Key) -> Option<&mut $Slot> {
73 wowlab_types::bench_metrics::record_keyed_lookup();
74 let &base = self.layout.offsets.slots.$map.get(&key)?;
75 Some(self.slot_mut::<$Slot>(base))
76 }
77 };
78}
79
80macro_rules! keyed_read_slot {
81 ($read:ident, $Slot:ty, $Key:ty, $map:ident) => {
82 #[cfg(any(test, feature = "test-support"))]
83 #[inline]
84 pub fn $read(&self, key: $Key) -> Option<&$Slot> {
85 wowlab_types::bench_metrics::record_keyed_lookup();
86 let &base = self.layout.offsets.slots.$map.get(&key)?;
87 Some(self.slot_ref::<$Slot>(base))
88 }
89 };
90}
91
92macro_rules! keyed_slot_str {
93 ($read:ident, $write:ident, $Slot:ty, $map:ident) => {
94 #[cfg(any(test, feature = "test-support"))]
95 #[inline]
96 pub fn $read(&self, key: &str) -> Option<&$Slot> {
97 wowlab_types::bench_metrics::record_keyed_lookup();
98 let &base = self.layout.offsets.slots.$map.get(key)?;
99 Some(self.slot_ref::<$Slot>(base))
100 }
101 #[cfg(any(test, feature = "test-support"))]
102 #[inline]
103 pub fn $write(&mut self, key: &str) -> Option<&mut $Slot> {
104 wowlab_types::bench_metrics::record_keyed_lookup();
105 let &base = self.layout.offsets.slots.$map.get(key)?;
106 Some(self.slot_mut::<$Slot>(base))
107 }
108 };
109}
110
111macro_rules! handled_slot {
112 ($read:ident, $write:ident, $Slot:ty, $Handle:ty) => {
113 #[inline(always)]
114 pub(crate) fn $read(&self, handle: $Handle) -> &$Slot {
115 self.slot_ref::<$Slot>(handle.offset())
116 }
117
118 #[inline(always)]
119 pub(crate) fn $write(&mut self, handle: $Handle) -> &mut $Slot {
120 self.slot_mut::<$Slot>(handle.offset())
121 }
122 };
123}
124
125#[derive(Debug)]
127pub struct DenseBuffer {
128 chunks: Vec<SlotChunk>,
129 pub(super) layout: Arc<FrozenBufferLayout>,
130}
131
132impl DenseBuffer {
133 #[must_use]
134 pub(crate) fn new(layout: Arc<FrozenBufferLayout>) -> Self {
136 Self {
137 chunks: vec![SlotChunk([0u8; SLOT_ALIGNMENT]); chunks_for_bytes(layout.byte_len())],
138 layout,
139 }
140 }
141
142 pub fn layout_fingerprint(&self) -> LayoutFingerprint {
143 self.layout.fingerprint()
144 }
145
146 pub fn clear_history(&mut self) {
148 for index in 0..self.layout.history_handles().len() {
149 let handle = self.layout.history_handles()[index];
151 let h = self.history_mut_at(handle);
152
153 h.prev_gcd = 0;
154 h.prev_off_gcd = 0;
155 }
156 }
157
158 pub fn reset_and_defaults(&mut self) {
160 self.bytes_mut().fill(0);
161 self.populate_standalone_defaults();
162 }
163
164 #[cfg(test)]
165 pub(crate) fn layout(&self) -> &Arc<FrozenBufferLayout> {
166 &self.layout
167 }
168
169 #[inline]
170 pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 {
171 self.chunks.as_mut_ptr().cast::<u8>()
172 }
173
174 #[inline]
176 #[must_use]
177 pub(crate) fn bytes(&self) -> &[u8] {
178 &bytemuck::cast_slice::<SlotChunk, u8>(&self.chunks)[..self.layout.byte_len()]
180 }
181 #[inline]
183 pub(crate) fn bytes_mut(&mut self) -> &mut [u8] {
184 let len = self.layout.byte_len();
185
186 &mut bytemuck::cast_slice_mut::<SlotChunk, u8>(&mut self.chunks)[..len]
188 }
189 #[inline]
190 fn slot_ref<T>(&self, base: ByteOffset) -> &T
191 where
192 T: Pod,
193 {
194 let base = base.as_usize();
195
196 bytemuck::from_bytes(&self.bytes()[base..base + size_of::<T>()])
198 }
199 #[inline]
200 fn slot_mut<T>(&mut self, base: ByteOffset) -> &mut T
201 where
202 T: Pod,
203 {
204 let base = base.as_usize();
205
206 bytemuck::from_bytes_mut(&mut self.bytes_mut()[base..base + size_of::<T>()])
208 }
209 fn populate_standalone_defaults(&mut self) {
210 let byte_len = self.layout.byte_len();
211 let defaults = self.layout.standalone_defaults();
212 let bytes = &mut bytemuck::cast_slice_mut::<SlotChunk, u8>(&mut self.chunks)[..byte_len];
214
215 for &(offset, value) in defaults {
216 value.write_to(bytes, offset);
217 }
218 }
219 singleton_slot!(player, player_mut, PlayerSlot, player);
220 singleton_slot!(combat, combat_mut, CombatSlot, combat);
221 singleton_slot!(pet, pet_mut, PetSlot, pet);
222 handled_slot!(spell_at, spell_mut_at, SpellSlot, SpellSlotHandle);
223 handled_slot!(
224 aura_projection_at,
225 aura_projection_mut_at,
226 AuraSlot,
227 AuraProjectionSlotHandle
228 );
229 handled_slot!(
230 cooldown_at,
231 cooldown_mut_at,
232 CooldownSlot,
233 CooldownSlotHandle
234 );
235 handled_slot!(history_at, history_mut_at, HistorySlot, HistorySlotHandle);
236 handled_slot!(
237 resource_at,
238 resource_mut_at,
239 ResourceSlot,
240 ResourceSlotHandle
241 );
242 handled_slot!(unit_at, unit_mut_at, UnitSlot, UnitSlotHandle);
243 handled_slot!(talent_at, talent_mut_at, TalentSlot, TalentSlotHandle);
244 handled_slot!(
245 hero_tree_at,
246 hero_tree_mut_at,
247 HeroTreeSlot,
248 HeroTreeSlotHandle
249 );
250 handled_slot!(item_at, item_mut_at, ItemSlot, ItemSlotHandle);
251 handled_slot!(swing_at, swing_mut_at, SwingSlot, SwingSlotHandle);
252 handled_slot!(
253 equipment_at,
254 equipment_mut_at,
255 EquipmentSlot,
256 EquipmentSlotHandle
257 );
258 handled_slot!(
259 set_bonus_at,
260 set_bonus_mut_at,
261 SetBonusSlot,
262 SetBonusSlotHandle
263 );
264 handled_slot!(
265 weapon_imbue_at,
266 weapon_imbue_mut_at,
267 WeaponImbueSlot,
268 WeaponImbueSlotHandle
269 );
270 keyed_slot!(cooldown, cooldown_mut, CooldownSlot, SpellIdx, cooldowns);
271 keyed_read_slot!(aura, AuraSlot, AuraKey, auras);
272 keyed_slot!(
273 aura_projection,
274 aura_projection_mut,
275 AuraSlot,
276 AuraProjectionKey,
277 aura_projections
278 );
279 keyed_slot!(
280 resource,
281 resource_mut,
282 ResourceSlot,
283 ResourceType,
284 resources
285 );
286
287 keyed_slot!(spell, spell_mut, SpellSlot, SpellIdx, spells);
288
289 keyed_slot!(history, history_mut, HistorySlot, SpellIdx, history);
290
291 keyed_slot_str!(talent, talent_mut, TalentSlot, talents);
292 keyed_slot_str!(hero_tree, hero_tree_mut, HeroTreeSlot, hero_trees);
293 keyed_slot_str!(item, item_mut, ItemSlot, items);
294 keyed_slot_str!(swing, swing_mut, SwingSlot, swings);
295 keyed_slot_str!(equipment, equipment_mut, EquipmentSlot, equipment);
296 keyed_slot_str!(set_bonus, set_bonus_mut, SetBonusSlot, set_bonuses);
297 keyed_slot_str!(unit, unit_mut, UnitSlot, units);
298 keyed_slot_str!(
299 weapon_imbue,
300 weapon_imbue_mut,
301 WeaponImbueSlot,
302 weapon_imbues
303 );
304}
305
306#[derive(Debug)]
311pub struct BoundDenseBuffer {
312 inner: DenseBuffer,
313}
314
315impl BoundDenseBuffer {
316 pub(crate) const fn from_schema(inner: DenseBuffer) -> Self {
317 Self { inner }
318 }
319
320 #[inline]
321 #[must_use]
322 pub fn player(&self) -> &PlayerSlot {
323 self.inner.player()
324 }
325
326 #[inline]
327 pub fn player_mut(&mut self) -> &mut PlayerSlot {
328 self.inner.player_mut()
329 }
330
331 #[inline]
332 #[must_use]
333 pub fn combat(&self) -> &CombatSlot {
334 self.inner.combat()
335 }
336
337 #[inline]
338 pub fn combat_mut(&mut self) -> &mut CombatSlot {
339 self.inner.combat_mut()
340 }
341
342 #[inline]
343 #[must_use]
344 pub fn pet(&self) -> &PetSlot {
345 self.inner.pet()
346 }
347
348 #[inline]
349 pub fn pet_mut(&mut self) -> &mut PetSlot {
350 self.inner.pet_mut()
351 }
352}
353
354macro_rules! bound_handled_slot {
355 ($read:ident, $write:ident, $Slot:ty, $Handle:ty) => {
356 impl BoundDenseBuffer {
357 #[inline(always)]
358 #[must_use]
359 pub fn $read(&self, handle: $Handle) -> &$Slot {
360 self.inner.$read(handle)
361 }
362
363 #[inline(always)]
364 pub fn $write(&mut self, handle: $Handle) -> &mut $Slot {
365 self.inner.$write(handle)
366 }
367 }
368 };
369}
370
371bound_handled_slot!(spell_at, spell_mut_at, SpellSlot, SpellSlotHandle);
372bound_handled_slot!(
373 aura_projection_at,
374 aura_projection_mut_at,
375 AuraSlot,
376 AuraProjectionSlotHandle
377);
378bound_handled_slot!(
379 cooldown_at,
380 cooldown_mut_at,
381 CooldownSlot,
382 CooldownSlotHandle
383);
384bound_handled_slot!(history_at, history_mut_at, HistorySlot, HistorySlotHandle);
385bound_handled_slot!(
386 resource_at,
387 resource_mut_at,
388 ResourceSlot,
389 ResourceSlotHandle
390);
391bound_handled_slot!(unit_at, unit_mut_at, UnitSlot, UnitSlotHandle);
392bound_handled_slot!(talent_at, talent_mut_at, TalentSlot, TalentSlotHandle);
393bound_handled_slot!(
394 hero_tree_at,
395 hero_tree_mut_at,
396 HeroTreeSlot,
397 HeroTreeSlotHandle
398);
399bound_handled_slot!(item_at, item_mut_at, ItemSlot, ItemSlotHandle);
400bound_handled_slot!(swing_at, swing_mut_at, SwingSlot, SwingSlotHandle);
401bound_handled_slot!(
402 equipment_at,
403 equipment_mut_at,
404 EquipmentSlot,
405 EquipmentSlotHandle
406);
407bound_handled_slot!(
408 set_bonus_at,
409 set_bonus_mut_at,
410 SetBonusSlot,
411 SetBonusSlotHandle
412);
413bound_handled_slot!(
414 weapon_imbue_at,
415 weapon_imbue_mut_at,
416 WeaponImbueSlot,
417 WeaponImbueSlotHandle
418);
419
420impl std::ops::Deref for BoundDenseBuffer {
421 type Target = DenseBuffer;
422
423 fn deref(&self) -> &Self::Target {
424 &self.inner
425 }
426}
427
428impl std::ops::DerefMut for BoundDenseBuffer {
429 fn deref_mut(&mut self) -> &mut Self::Target {
430 &mut self.inner
431 }
432}