1use crate::aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
2use ab_contracts_common::Address;
3use alloc::boxed::Box;
4use smallvec::SmallVec;
5use tracing::debug;
6
7const INLINE_SIZE: usize = 8;
12const NEW_CONTRACTS_INLINE: usize = 2;
14
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
16pub struct SlotKey {
17 pub owner: Address,
18 pub contract: Address,
19}
20
21#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub struct SlotIndex(usize);
23
24impl From<SlotIndex> for usize {
25 #[inline(always)]
26 fn from(value: SlotIndex) -> Self {
27 value.0
28 }
29}
30
31#[derive(Debug, Clone)]
32enum Slot {
33 Original(SharedAlignedBuffer),
35 OriginalAccessed(SharedAlignedBuffer),
37 Modified(SharedAlignedBuffer),
39 ModifiedAccessed(SharedAlignedBuffer),
41 ReadWriteOriginal {
43 buffer: OwnedAlignedBuffer,
44 previous: SharedAlignedBuffer,
46 },
47 ReadWriteModified {
49 buffer: OwnedAlignedBuffer,
50 previous: SharedAlignedBuffer,
52 },
53}
54
55#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
56struct SlotAccess {
57 slot_index: SlotIndex,
58 read_write: bool,
60}
61
62#[derive(Debug)]
63struct Inner {
64 slots: SmallVec<[(SlotKey, Slot); INLINE_SIZE]>,
65 slot_access: SmallVec<[SlotAccess; INLINE_SIZE]>,
66 new_contracts: SmallVec<[Address; NEW_CONTRACTS_INLINE]>,
72}
73
74#[derive(Debug)]
76enum SlotsInner<'a> {
77 Original { inner: Box<Inner> },
80 ReadWrite {
82 inner: &'a mut Inner,
83 parent_slot_access_len: usize,
84 },
85 ReadOnly { inner: &'a Inner },
87}
88
89#[derive(Debug)]
91pub struct Slots<'a>(SlotsInner<'a>);
92
93impl<'a> Drop for Slots<'a> {
94 #[inline(always)]
95 fn drop(&mut self) {
96 let (inner, parent_slot_access_len) = match &mut self.0 {
97 SlotsInner::Original { .. } | SlotsInner::ReadOnly { .. } => {
98 return;
100 }
101 SlotsInner::ReadWrite {
102 inner,
103 parent_slot_access_len,
104 } => (&mut **inner, *parent_slot_access_len),
105 };
106
107 let slots = &mut inner.slots;
108 let slot_access = &mut inner.slot_access;
109
110 for slot_access in slot_access.drain(parent_slot_access_len..) {
112 let slot = &mut slots
113 .get_mut(usize::from(slot_access.slot_index))
114 .expect("Accessed slot exists; qed")
115 .1;
116
117 take_mut::take(slot, |slot| match slot {
118 Slot::Original(_buffer) => {
119 unreachable!("Slot can't be in Original state after being accessed")
120 }
121 Slot::OriginalAccessed(buffer) => Slot::Original(buffer),
122 Slot::Modified(buffer) => Slot::Modified(buffer),
123 Slot::ModifiedAccessed(buffer) => Slot::Modified(buffer),
124 Slot::ReadWriteOriginal { buffer, .. } | Slot::ReadWriteModified { buffer, .. } => {
125 Slot::Modified(buffer.into_shared())
126 }
127 })
128 }
129 }
130}
131
132impl<'a> Slots<'a> {
134 #[inline(always)]
141 pub fn new<I>(slots: I) -> Self
142 where
143 I: IntoIterator<Item = (SlotKey, SharedAlignedBuffer)>,
144 {
145 let slots = slots
146 .into_iter()
147 .filter_map(|(slot_key, slot)| {
148 if slot_key.contract == Address::NULL {
152 return None;
153 }
154
155 Some((slot_key, Slot::Original(slot)))
156 })
157 .collect();
158
159 let inner = Inner {
160 slots,
161 slot_access: SmallVec::new(),
162 new_contracts: SmallVec::new(),
163 };
164
165 Self(SlotsInner::Original {
166 inner: Box::new(inner),
167 })
168 }
169
170 #[inline(always)]
171 fn inner_ro(&self) -> &Inner {
172 match &self.0 {
173 SlotsInner::Original { inner } => inner,
174 SlotsInner::ReadWrite { inner, .. } => inner,
175 SlotsInner::ReadOnly { inner } => inner,
176 }
177 }
178
179 #[inline(always)]
180 fn inner_rw(&mut self) -> Option<&mut Inner> {
181 match &mut self.0 {
182 SlotsInner::Original { inner } => Some(inner),
183 SlotsInner::ReadWrite { inner, .. } => Some(inner),
184 SlotsInner::ReadOnly { .. } => None,
185 }
186 }
187
188 #[inline(always)]
195 pub fn new_nested_rw<'b>(&'b mut self) -> Option<Slots<'b>>
196 where
197 'a: 'b,
198 {
199 let inner = match &mut self.0 {
200 SlotsInner::Original { inner } => inner.as_mut(),
201 SlotsInner::ReadWrite { inner, .. } => inner,
202 SlotsInner::ReadOnly { .. } => {
203 return None;
204 }
205 };
206
207 let parent_slot_access_len = inner.slot_access.len();
208
209 Some(Slots(SlotsInner::ReadWrite {
210 inner,
211 parent_slot_access_len,
212 }))
213 }
214
215 #[inline(always)]
217 pub fn new_nested_ro<'b>(&'b self) -> Slots<'b>
218 where
219 'a: 'b,
220 {
221 let inner = match &self.0 {
222 SlotsInner::Original { inner } => inner.as_ref(),
223 SlotsInner::ReadWrite { inner, .. } => inner,
224 SlotsInner::ReadOnly { inner } => inner,
225 };
226
227 Slots(SlotsInner::ReadOnly { inner })
228 }
229
230 #[must_use]
238 #[inline(always)]
239 pub fn add_new_contract(&mut self, owner: Address) -> bool {
240 let Some(inner) = self.inner_rw() else {
241 debug!(%owner, "`add_new_contract` access violation");
242 return false;
243 };
244
245 let new_contracts = &mut inner.new_contracts;
246
247 if new_contracts.contains(&owner) {
248 debug!(%owner, "Not adding new contract duplicate");
249 return false;
250 }
251
252 new_contracts.push(owner);
253 true
254 }
255
256 #[inline(always)]
263 pub fn get_code(&self, owner: Address) -> Option<SharedAlignedBuffer> {
264 let result = self.get_code_internal(owner);
265
266 if result.is_none() {
267 debug!(%owner, "`get_code` access violation");
268 }
269
270 result
271 }
272
273 #[inline(always)]
274 fn get_code_internal(&self, owner: Address) -> Option<SharedAlignedBuffer> {
275 let inner = self.inner_ro();
276 let slots = &inner.slots;
277 let slot_access = &inner.slot_access;
278
279 let contract = Address::SYSTEM_CODE;
280
281 let slot_index = slots.iter().position(|(slot_key, _slot)| {
282 slot_key.owner == owner && slot_key.contract == contract
283 })?;
284 let slot_index = SlotIndex(slot_index);
285
286 if slot_access
288 .iter()
289 .any(|slot_access| slot_access.slot_index == slot_index && slot_access.read_write)
290 {
291 return None;
292 }
293
294 let buffer = match &slots
295 .get(usize::from(slot_index))
296 .expect("Just found; qed")
297 .1
298 {
299 Slot::Original(buffer)
300 | Slot::OriginalAccessed(buffer)
301 | Slot::Modified(buffer)
302 | Slot::ModifiedAccessed(buffer) => buffer,
303 Slot::ReadWriteOriginal { .. } | Slot::ReadWriteModified { .. } => {
304 return None;
305 }
306 };
307
308 Some(buffer.clone())
309 }
310
311 #[inline(always)]
315 pub fn use_ro(&mut self, slot_key: SlotKey) -> Option<&SharedAlignedBuffer> {
316 let inner_rw = match &mut self.0 {
317 SlotsInner::Original { inner, .. } => inner.as_mut(),
318 SlotsInner::ReadWrite { inner, .. } => inner,
319 SlotsInner::ReadOnly { inner } => {
320 let result = Self::use_ro_internal_read_only(
322 slot_key,
323 &inner.slots,
324 &inner.slot_access,
325 &inner.new_contracts,
326 );
327
328 if result.is_none() {
329 debug!(?slot_key, "`use_ro` access violation");
330 }
331
332 return result;
333 }
334 };
335
336 let result = Self::use_ro_internal(
337 slot_key,
338 &mut inner_rw.slots,
339 &mut inner_rw.slot_access,
340 &inner_rw.new_contracts,
341 );
342
343 if result.is_none() {
344 debug!(?slot_key, "`use_ro` access violation");
345 }
346
347 result
348 }
349
350 #[inline(always)]
351 fn use_ro_internal<'b>(
352 slot_key: SlotKey,
353 slots: &'b mut SmallVec<[(SlotKey, Slot); INLINE_SIZE]>,
354 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
355 new_contracts: &[Address],
356 ) -> Option<&'b SharedAlignedBuffer> {
357 let maybe_slot_index = slots
358 .iter()
359 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
360 .map(SlotIndex);
361
362 if let Some(slot_index) = maybe_slot_index {
363 if let Some(read_write) = slot_access.iter().find_map(|slot_access| {
365 (slot_access.slot_index == slot_index).then_some(slot_access.read_write)
366 }) {
367 if read_write {
368 return None;
369 }
370 } else {
371 slot_access.push(SlotAccess {
372 slot_index,
373 read_write: false,
374 });
375 }
376
377 let slot = &mut slots
378 .get_mut(usize::from(slot_index))
379 .expect("Just found; qed")
380 .1;
381
382 match slot {
384 Slot::Original(buffer) => {
385 let buffer = buffer.clone();
386 *slot = Slot::OriginalAccessed(buffer);
387 let Slot::OriginalAccessed(buffer) = slot else {
388 unreachable!("Just inserted; qed");
389 };
390 Some(buffer)
391 }
392 Slot::OriginalAccessed(buffer) | Slot::ModifiedAccessed(buffer) => Some(buffer),
393 Slot::Modified(buffer) => {
394 let buffer = buffer.clone();
395 *slot = Slot::ModifiedAccessed(buffer);
396 let Slot::ModifiedAccessed(buffer) = slot else {
397 unreachable!("Just inserted; qed");
398 };
399 Some(buffer)
400 }
401 Slot::ReadWriteOriginal { .. } | Slot::ReadWriteModified { .. } => None,
402 }
403 } else {
404 if !(slot_key.contract == Address::NULL
408 || new_contracts
409 .iter()
410 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
411 {
412 return None;
413 }
414
415 slot_access.push(SlotAccess {
416 slot_index: SlotIndex(slots.len()),
417 read_write: false,
418 });
419
420 let slot = Slot::OriginalAccessed(SharedAlignedBuffer::default());
421 slots.push((slot_key, slot));
422 let slot = &slots.last().expect("Just inserted; qed").1;
423 let Slot::OriginalAccessed(buffer) = slot else {
424 unreachable!("Just inserted; qed");
425 };
426
427 Some(buffer)
428 }
429 }
430
431 #[inline(always)]
433 fn use_ro_internal_read_only<'b>(
434 slot_key: SlotKey,
435 slots: &'b SmallVec<[(SlotKey, Slot); INLINE_SIZE]>,
436 slot_access: &SmallVec<[SlotAccess; INLINE_SIZE]>,
437 new_contracts: &[Address],
438 ) -> Option<&'b SharedAlignedBuffer> {
439 let maybe_slot_index = slots
440 .iter()
441 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
442 .map(SlotIndex);
443
444 if let Some(slot_index) = maybe_slot_index {
445 if let Some(read_write) = slot_access.iter().find_map(|slot_access| {
447 (slot_access.slot_index == slot_index).then_some(slot_access.read_write)
448 }) {
449 if read_write {
450 return None;
451 }
452 }
453
454 let slot = &slots
455 .get(usize::from(slot_index))
456 .expect("Just found; qed")
457 .1;
458
459 match slot {
461 Slot::Original(buffer)
462 | Slot::OriginalAccessed(buffer)
463 | Slot::ModifiedAccessed(buffer)
464 | Slot::Modified(buffer) => Some(buffer),
465 Slot::ReadWriteOriginal { .. } | Slot::ReadWriteModified { .. } => None,
466 }
467 } else {
468 if !(slot_key.contract == Address::NULL
472 || new_contracts
473 .iter()
474 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
475 {
476 return None;
477 }
478
479 Some(SharedAlignedBuffer::empty_ref())
480 }
481 }
482
483 #[inline(always)]
491 pub fn use_rw(
492 &mut self,
493 slot_key: SlotKey,
494 capacity: u32,
495 ) -> Option<(SlotIndex, &mut OwnedAlignedBuffer)> {
496 let inner = self.inner_rw()?;
497 let slots = &mut inner.slots;
498 let slot_access = &mut inner.slot_access;
499 let new_contracts = &inner.new_contracts;
500
501 let result = Self::use_rw_internal(slot_key, capacity, slots, slot_access, new_contracts);
502
503 if result.is_none() {
504 debug!(?slot_key, "`use_rw` access violation");
505 }
506
507 result
508 }
509
510 #[inline(always)]
511 fn use_rw_internal<'b>(
512 slot_key: SlotKey,
513 capacity: u32,
514 slots: &'b mut SmallVec<[(SlotKey, Slot); INLINE_SIZE]>,
515 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
516 new_contracts: &[Address],
517 ) -> Option<(SlotIndex, &'b mut OwnedAlignedBuffer)> {
518 let maybe_slot_index = slots
519 .iter()
520 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
521 .map(SlotIndex);
522
523 if let Some(slot_index) = maybe_slot_index {
524 if slot_access
526 .iter()
527 .any(|slot_access| slot_access.slot_index == slot_index)
528 {
529 return None;
530 }
531
532 slot_access.push(SlotAccess {
533 slot_index,
534 read_write: true,
535 });
536
537 let slot = &mut slots
538 .get_mut(usize::from(slot_index))
539 .expect("Just found; qed")
540 .1;
541
542 let buffer = match slot {
544 Slot::OriginalAccessed(_buffer) | Slot::ModifiedAccessed(_buffer) => {
545 return None;
546 }
547 Slot::Original(buffer) => {
548 let mut new_buffer =
549 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
550 new_buffer.copy_from_slice(buffer.as_slice());
551
552 *slot = Slot::ReadWriteOriginal {
553 buffer: new_buffer,
554 previous: buffer.clone(),
555 };
556 let Slot::ReadWriteOriginal { buffer, .. } = slot else {
557 unreachable!("Just inserted; qed");
558 };
559 buffer
560 }
561 Slot::Modified(buffer) => {
562 let mut new_buffer =
563 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
564 new_buffer.copy_from_slice(buffer.as_slice());
565
566 *slot = Slot::ReadWriteModified {
567 buffer: new_buffer,
568 previous: buffer.clone(),
569 };
570 let Slot::ReadWriteModified { buffer, .. } = slot else {
571 unreachable!("Just inserted; qed");
572 };
573 buffer
574 }
575 Slot::ReadWriteOriginal { buffer, .. } | Slot::ReadWriteModified { buffer, .. } => {
576 buffer.ensure_capacity(capacity);
577 buffer
578 }
579 };
580
581 Some((slot_index, buffer))
582 } else {
583 if !(slot_key.contract == Address::NULL
587 || new_contracts
588 .iter()
589 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
590 {
591 return None;
592 }
593
594 let slot_index = SlotIndex(slots.len());
595 slot_access.push(SlotAccess {
596 slot_index,
597 read_write: true,
598 });
599
600 let slot = Slot::ReadWriteOriginal {
601 buffer: OwnedAlignedBuffer::with_capacity(capacity),
602 previous: SharedAlignedBuffer::default(),
603 };
604 slots.push((slot_key, slot));
605 let slot = &mut slots.last_mut().expect("Just inserted; qed").1;
606 let Slot::ReadWriteOriginal { buffer, .. } = slot else {
607 unreachable!("Just inserted; qed");
608 };
609
610 Some((slot_index, buffer))
611 }
612 }
613
614 pub fn access_used_rw(&mut self, slot_index: SlotIndex) -> Option<&mut OwnedAlignedBuffer> {
622 let maybe_slot = self
623 .inner_rw()?
624 .slots
625 .get_mut(usize::from(slot_index))
626 .map(|(_slot_key, slot)| slot);
627
628 let Some(slot) = maybe_slot else {
629 debug!(?slot_index, "`access_used_rw` access violation (not found)");
630 return None;
631 };
632
633 match slot {
635 Slot::Original(_buffer)
636 | Slot::OriginalAccessed(_buffer)
637 | Slot::Modified(_buffer)
638 | Slot::ModifiedAccessed(_buffer) => {
639 debug!(?slot_index, "`access_used_rw` access violation (read only)");
640 None
641 }
642 Slot::ReadWriteOriginal { buffer, .. } | Slot::ReadWriteModified { buffer, .. } => {
643 Some(buffer)
644 }
645 }
646 }
647
648 #[cold]
650 pub fn reset(&mut self) {
651 let (inner, parent_slot_access_len) = match &mut self.0 {
652 SlotsInner::Original { .. } | SlotsInner::ReadOnly { .. } => {
653 return;
655 }
656 SlotsInner::ReadWrite {
657 inner,
658 parent_slot_access_len,
659 } => (&mut **inner, parent_slot_access_len),
660 };
661
662 let slots = &mut inner.slots;
663 let slot_access = &mut inner.slot_access;
664
665 for slot_access in slot_access.drain(*parent_slot_access_len..) {
667 let slot = &mut slots
668 .get_mut(usize::from(slot_access.slot_index))
669 .expect("Accessed slot exists; qed")
670 .1;
671 take_mut::take(slot, |slot| match slot {
672 Slot::Original(_buffer) => {
673 unreachable!("Slot can't be in Original state after being accessed")
674 }
675 Slot::OriginalAccessed(buffer) => Slot::Original(buffer),
676 Slot::Modified(buffer) => Slot::Modified(buffer),
677 Slot::ModifiedAccessed(buffer) => Slot::Modified(buffer),
678 Slot::ReadWriteOriginal { previous, .. } => Slot::Original(previous),
679 Slot::ReadWriteModified { previous, .. } => Slot::Modified(previous),
680 });
681 }
682
683 *parent_slot_access_len = 0;
684 }
685}