1#![no_std]
2
3extern crate alloc;
4
5use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
6use ab_core_primitives::address::Address;
7use alloc::boxed::Box;
8use replace_with::replace_with_or_abort;
9use smallvec::SmallVec;
10use tracing::debug;
11
12const INLINE_SIZE: usize = 8;
17const NEW_CONTRACTS_INLINE: usize = 2;
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub struct SlotKey {
23 pub owner: Address,
25 pub contract: Address,
27}
28
29#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31pub struct SlotIndex(usize);
32
33impl From<SlotIndex> for usize {
34 #[inline(always)]
35 fn from(value: SlotIndex) -> Self {
36 value.0
37 }
38}
39
40#[derive(Debug, Clone)]
41pub enum Slot {
42 ReadOnly {
43 key: SlotKey,
44 buffer: SharedAlignedBuffer,
45 },
46 ReadWrite {
47 key: SlotKey,
48 buffer: SharedAlignedBuffer,
49 },
50}
51
52impl Slot {
53 fn is_null_contract(&self) -> bool {
54 let slot_key = match self {
55 Slot::ReadOnly { key, .. } => key,
56 Slot::ReadWrite { key, .. } => key,
57 };
58
59 slot_key.contract == Address::NULL
60 }
61}
62
63#[derive(Debug, Clone)]
64enum SlotState {
65 Original(SharedAlignedBuffer),
67 OriginalReadOnly(SharedAlignedBuffer),
69 Modified(SharedAlignedBuffer),
71 ModifiedReadOnly(SharedAlignedBuffer),
73 OriginalReadWrite {
75 buffer: OwnedAlignedBuffer,
76 previous: SharedAlignedBuffer,
78 },
79 ModifiedReadWrite {
81 buffer: OwnedAlignedBuffer,
82 previous: SharedAlignedBuffer,
84 },
85}
86
87#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
88struct SlotAccess {
89 slot_index: SlotIndex,
90 read_write: bool,
92}
93
94#[derive(Debug, Clone)]
95struct Inner {
96 slots: SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
97 slot_access: SmallVec<[SlotAccess; INLINE_SIZE]>,
98 new_contracts: SmallVec<[Address; NEW_CONTRACTS_INLINE]>,
104}
105
106#[derive(Debug, Clone)]
108pub struct Slots(Box<Inner>);
109
110impl Slots {
111 #[inline(always)]
118 pub fn new<I>(slots: I) -> Self
119 where
120 I: IntoIterator<Item = Slot>,
121 {
122 let slots = slots
123 .into_iter()
124 .filter_map(|slot| {
125 if slot.is_null_contract() {
129 return None;
130 }
131
132 Some(match slot {
133 Slot::ReadOnly { key, buffer } => {
134 (key, SlotState::OriginalReadOnly(buffer))
136 }
137 Slot::ReadWrite { key, buffer } => (key, SlotState::Original(buffer)),
138 })
139 })
140 .collect();
141
142 let inner = Inner {
143 slots,
144 slot_access: SmallVec::new(),
145 new_contracts: SmallVec::new(),
146 };
147
148 Self(Box::new(inner))
149 }
150
151 #[inline(always)]
156 pub fn new_nested_rw(&mut self) -> NestedSlots<'_> {
157 NestedSlots(NestedSlotsInner::ReadWrite {
158 inner: &mut self.0,
159 parent_slot_access_len: 0,
160 original_parent: true,
161 })
162 }
163
164 #[inline(always)]
166 pub fn new_nested_ro(&self) -> NestedSlots<'_> {
167 NestedSlots(NestedSlotsInner::ReadOnly { inner: &self.0 })
168 }
169
170 #[must_use]
178 #[inline(always)]
179 pub fn add_new_contract(&mut self, owner: Address) -> bool {
180 let new_contracts = &mut self.0.new_contracts;
181
182 if new_contracts.contains(&owner) {
183 debug!(?owner, "Not adding a new contract duplicate");
184 return false;
185 }
186
187 new_contracts.push(owner);
188 true
189 }
190
191 #[inline]
193 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
194 self.0.slots.iter().map(|(slot_key, slot)| match slot {
195 SlotState::Original(buffer) => (slot_key, buffer),
196 SlotState::OriginalReadOnly(_) => {
197 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
198 }
199 SlotState::Modified(buffer) => (slot_key, buffer),
200 SlotState::ModifiedReadOnly(_) => {
201 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
202 }
203 SlotState::OriginalReadWrite { .. } => {
204 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
205 }
206 SlotState::ModifiedReadWrite { .. } => {
207 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
208 }
209 })
210 }
211
212 #[inline]
214 pub fn iter_modified(&self) -> impl Iterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
215 self.0
216 .slots
217 .iter()
218 .filter_map(|(slot_key, slot)| match slot {
219 SlotState::Original(_) => None,
220 SlotState::OriginalReadOnly(_) => unreachable!(
221 "Only original and modified slots can exist at the `Slots` level; qed"
222 ),
223 SlotState::Modified(buffer) => Some((slot_key, buffer)),
224 SlotState::ModifiedReadOnly(_) => unreachable!(
225 "Only original and modified slots can exist at the `Slots` level; qed"
226 ),
227 SlotState::OriginalReadWrite { .. } => unreachable!(
228 "Only original and modified slots can exist at the `Slots` level; qed"
229 ),
230 SlotState::ModifiedReadWrite { .. } => unreachable!(
231 "Only original and modified slots can exist at the `Slots` level; qed"
232 ),
233 })
234 }
235
236 #[inline]
238 pub fn into_slots(self) -> impl ExactSizeIterator<Item = (SlotKey, SharedAlignedBuffer)> {
239 self.0.slots.into_iter().map(|(slot_key, slot)| match slot {
240 SlotState::Original(buffer) => (slot_key, buffer),
241 SlotState::OriginalReadOnly(_) => {
242 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
243 }
244 SlotState::Modified(buffer) => (slot_key, buffer),
245 SlotState::ModifiedReadOnly(_) => {
246 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
247 }
248 SlotState::OriginalReadWrite { .. } => {
249 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
250 }
251 SlotState::ModifiedReadWrite { .. } => {
252 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
253 }
254 })
255 }
256}
257
258#[derive(Debug)]
260enum NestedSlotsInner<'a> {
261 ReadWrite {
263 inner: &'a mut Inner,
264 parent_slot_access_len: usize,
265 original_parent: bool,
266 },
267 ReadOnly { inner: &'a Inner },
269}
270
271#[derive(Debug)]
272pub struct NestedSlots<'a>(NestedSlotsInner<'a>);
273
274impl<'a> Drop for NestedSlots<'a> {
275 #[inline(always)]
276 fn drop(&mut self) {
277 let (inner, parent_slot_access_len, original_parent) = match &mut self.0 {
278 NestedSlotsInner::ReadWrite {
279 inner,
280 parent_slot_access_len,
281 original_parent,
282 } => (&mut **inner, *parent_slot_access_len, *original_parent),
283 NestedSlotsInner::ReadOnly { .. } => {
284 return;
286 }
287 };
288
289 let slots = &mut inner.slots;
290 let slot_access = &mut inner.slot_access;
291
292 for slot_access in slot_access.drain(parent_slot_access_len..) {
294 let slot = &mut slots
295 .get_mut(usize::from(slot_access.slot_index))
296 .expect("Accessed slot exists; qed")
297 .1;
298
299 replace_with_or_abort(slot, |slot| match slot {
300 SlotState::Original(_buffer) => {
301 unreachable!("Slot can't be in `Original` state after being accessed; qed")
302 }
303 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
304 SlotState::Modified(buffer) => SlotState::Modified(buffer),
305 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
306 SlotState::OriginalReadWrite { buffer, .. }
307 | SlotState::ModifiedReadWrite { buffer, .. } => {
308 SlotState::Modified(buffer.into_shared())
309 }
310 })
311 }
312
313 if original_parent {
314 inner
318 .slots
319 .retain(|(slot_key, _slot)| slot_key.contract != Address::NULL);
320 }
321 }
322}
323
324impl<'a> NestedSlots<'a> {
325 #[inline(always)]
326 fn inner_ro(&self) -> &Inner {
327 match &self.0 {
328 NestedSlotsInner::ReadWrite { inner, .. } => inner,
329 NestedSlotsInner::ReadOnly { inner } => inner,
330 }
331 }
332
333 #[inline(always)]
334 fn inner_rw(&mut self) -> Option<&mut Inner> {
335 match &mut self.0 {
336 NestedSlotsInner::ReadWrite { inner, .. } => Some(inner),
337 NestedSlotsInner::ReadOnly { .. } => None,
338 }
339 }
340
341 #[inline(always)]
348 pub fn new_nested_rw<'b>(&'b mut self) -> Option<NestedSlots<'b>>
349 where
350 'a: 'b,
351 {
352 let inner = match &mut self.0 {
353 NestedSlotsInner::ReadWrite { inner, .. } => &mut **inner,
354 NestedSlotsInner::ReadOnly { .. } => {
355 return None;
356 }
357 };
358
359 let parent_slot_access_len = inner.slot_access.len();
360
361 Some(NestedSlots(NestedSlotsInner::ReadWrite {
362 inner,
363 parent_slot_access_len,
364 original_parent: false,
365 }))
366 }
367
368 #[inline(always)]
370 pub fn new_nested_ro<'b>(&'b self) -> NestedSlots<'b>
371 where
372 'a: 'b,
373 {
374 let inner = match &self.0 {
375 NestedSlotsInner::ReadWrite { inner, .. } => &**inner,
376 NestedSlotsInner::ReadOnly { inner } => &**inner,
377 };
378
379 NestedSlots(NestedSlotsInner::ReadOnly { inner })
380 }
381
382 #[must_use]
390 #[inline(always)]
391 pub fn add_new_contract(&mut self, owner: Address) -> bool {
392 let Some(inner) = self.inner_rw() else {
393 debug!(?owner, "`add_new_contract` access violation");
394 return false;
395 };
396
397 let new_contracts = &mut inner.new_contracts;
398
399 if new_contracts.contains(&owner) {
400 debug!(?owner, "Not adding a new contract duplicate");
401 return false;
402 }
403
404 new_contracts.push(owner);
405 true
406 }
407
408 #[inline(always)]
415 pub fn get_code(&self, owner: Address) -> Option<SharedAlignedBuffer> {
416 let result = self.get_code_internal(owner);
417
418 if result.is_none() {
419 debug!(?owner, "`get_code` access violation");
420 }
421
422 result
423 }
424
425 #[inline(always)]
426 fn get_code_internal(&self, owner: Address) -> Option<SharedAlignedBuffer> {
427 let inner = self.inner_ro();
428 let slots = &inner.slots;
429 let slot_access = &inner.slot_access;
430
431 let contract = Address::SYSTEM_CODE;
432
433 let slot_index = slots.iter().position(|(slot_key, _slot)| {
434 slot_key.owner == owner && slot_key.contract == contract
435 })?;
436 let slot_index = SlotIndex(slot_index);
437
438 if slot_access
440 .iter()
441 .any(|slot_access| slot_access.slot_index == slot_index && slot_access.read_write)
442 {
443 return None;
444 }
445
446 let buffer = match &slots
447 .get(usize::from(slot_index))
448 .expect("Just found; qed")
449 .1
450 {
451 SlotState::Original(buffer)
452 | SlotState::OriginalReadOnly(buffer)
453 | SlotState::Modified(buffer)
454 | SlotState::ModifiedReadOnly(buffer) => buffer,
455 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => {
456 return None;
457 }
458 };
459
460 Some(buffer.clone())
461 }
462
463 #[inline(always)]
467 pub fn use_ro(&mut self, slot_key: SlotKey) -> Option<&SharedAlignedBuffer> {
468 let inner_rw = match &mut self.0 {
469 NestedSlotsInner::ReadWrite { inner, .. } => &mut **inner,
470 NestedSlotsInner::ReadOnly { inner } => {
471 let result = Self::use_ro_internal_read_only(
473 slot_key,
474 &inner.slots,
475 &inner.slot_access,
476 &inner.new_contracts,
477 );
478
479 if result.is_none() {
480 debug!(?slot_key, "`use_ro` access violation");
481 }
482
483 return result;
484 }
485 };
486
487 let result = Self::use_ro_internal(
488 slot_key,
489 &mut inner_rw.slots,
490 &mut inner_rw.slot_access,
491 &inner_rw.new_contracts,
492 );
493
494 if result.is_none() {
495 debug!(?slot_key, "`use_ro` access violation");
496 }
497
498 result
499 }
500
501 #[inline(always)]
502 fn use_ro_internal<'b>(
503 slot_key: SlotKey,
504 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
505 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
506 new_contracts: &[Address],
507 ) -> Option<&'b SharedAlignedBuffer> {
508 let maybe_slot_index = slots
509 .iter()
510 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
511 .map(SlotIndex);
512
513 if let Some(slot_index) = maybe_slot_index {
514 if let Some(read_write) = slot_access.iter().find_map(|slot_access| {
516 (slot_access.slot_index == slot_index).then_some(slot_access.read_write)
517 }) {
518 if read_write {
519 return None;
520 }
521 } else {
522 slot_access.push(SlotAccess {
523 slot_index,
524 read_write: false,
525 });
526 }
527
528 let slot = &mut slots
529 .get_mut(usize::from(slot_index))
530 .expect("Just found; qed")
531 .1;
532
533 match slot {
535 SlotState::Original(buffer) => {
536 let buffer = buffer.clone();
537 *slot = SlotState::OriginalReadOnly(buffer);
538 let SlotState::OriginalReadOnly(buffer) = slot else {
539 unreachable!("Just inserted; qed");
540 };
541 Some(buffer)
542 }
543 SlotState::OriginalReadOnly(buffer) | SlotState::ModifiedReadOnly(buffer) => {
544 Some(buffer)
545 }
546 SlotState::Modified(buffer) => {
547 let buffer = buffer.clone();
548 *slot = SlotState::ModifiedReadOnly(buffer);
549 let SlotState::ModifiedReadOnly(buffer) = slot else {
550 unreachable!("Just inserted; qed");
551 };
552 Some(buffer)
553 }
554 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => None,
555 }
556 } else {
557 if !(slot_key.contract == Address::NULL
561 || new_contracts
562 .iter()
563 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
564 {
565 return None;
566 }
567
568 slot_access.push(SlotAccess {
569 slot_index: SlotIndex(slots.len()),
570 read_write: false,
571 });
572
573 let slot = SlotState::OriginalReadOnly(SharedAlignedBuffer::default());
574 slots.push((slot_key, slot));
575 let slot = &slots.last().expect("Just inserted; qed").1;
576 let SlotState::OriginalReadOnly(buffer) = slot else {
577 unreachable!("Just inserted; qed");
578 };
579
580 Some(buffer)
581 }
582 }
583
584 #[inline(always)]
586 fn use_ro_internal_read_only<'b>(
587 slot_key: SlotKey,
588 slots: &'b SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
589 slot_access: &SmallVec<[SlotAccess; INLINE_SIZE]>,
590 new_contracts: &[Address],
591 ) -> Option<&'b SharedAlignedBuffer> {
592 let maybe_slot_index = slots
593 .iter()
594 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
595 .map(SlotIndex);
596
597 if let Some(slot_index) = maybe_slot_index {
598 if let Some(read_write) = slot_access.iter().find_map(|slot_access| {
600 (slot_access.slot_index == slot_index).then_some(slot_access.read_write)
601 }) && read_write
602 {
603 return None;
604 }
605
606 let slot = &slots
607 .get(usize::from(slot_index))
608 .expect("Just found; qed")
609 .1;
610
611 match slot {
613 SlotState::Original(buffer)
614 | SlotState::OriginalReadOnly(buffer)
615 | SlotState::ModifiedReadOnly(buffer)
616 | SlotState::Modified(buffer) => Some(buffer),
617 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => None,
618 }
619 } else {
620 if !(slot_key.contract == Address::NULL
624 || new_contracts
625 .iter()
626 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
627 {
628 return None;
629 }
630
631 Some(SharedAlignedBuffer::empty_ref())
632 }
633 }
634
635 #[inline(always)]
644 pub fn use_rw(
645 &mut self,
646 slot_key: SlotKey,
647 capacity: u32,
648 ) -> Option<(SlotIndex, &mut OwnedAlignedBuffer)> {
649 let inner = self.inner_rw()?;
650 let slots = &mut inner.slots;
651 let slot_access = &mut inner.slot_access;
652 let new_contracts = &inner.new_contracts;
653
654 let result = Self::use_rw_internal(slot_key, capacity, slots, slot_access, new_contracts);
655
656 if result.is_none() {
657 debug!(?slot_key, "`use_rw` access violation");
658 }
659
660 result
661 }
662
663 #[inline(always)]
664 fn use_rw_internal<'b>(
665 slot_key: SlotKey,
666 capacity: u32,
667 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
668 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
669 new_contracts: &[Address],
670 ) -> Option<(SlotIndex, &'b mut OwnedAlignedBuffer)> {
671 let maybe_slot_index = slots
672 .iter()
673 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
674 .map(SlotIndex);
675
676 if let Some(slot_index) = maybe_slot_index {
677 if slot_access
679 .iter()
680 .any(|slot_access| slot_access.slot_index == slot_index)
681 {
682 return None;
683 }
684
685 slot_access.push(SlotAccess {
686 slot_index,
687 read_write: true,
688 });
689
690 let slot = &mut slots
691 .get_mut(usize::from(slot_index))
692 .expect("Just found; qed")
693 .1;
694
695 let buffer = match slot {
697 SlotState::OriginalReadOnly(_buffer) | SlotState::ModifiedReadOnly(_buffer) => {
698 return None;
699 }
700 SlotState::Original(buffer) => {
701 let mut new_buffer =
702 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
703 new_buffer.copy_from_slice(buffer.as_slice());
704
705 *slot = SlotState::OriginalReadWrite {
706 buffer: new_buffer,
707 previous: buffer.clone(),
708 };
709 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
710 unreachable!("Just inserted; qed");
711 };
712 buffer
713 }
714 SlotState::Modified(buffer) => {
715 let mut new_buffer =
716 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
717 new_buffer.copy_from_slice(buffer.as_slice());
718
719 *slot = SlotState::ModifiedReadWrite {
720 buffer: new_buffer,
721 previous: buffer.clone(),
722 };
723 let SlotState::ModifiedReadWrite { buffer, .. } = slot else {
724 unreachable!("Just inserted; qed");
725 };
726 buffer
727 }
728 SlotState::OriginalReadWrite { buffer, .. }
729 | SlotState::ModifiedReadWrite { buffer, .. } => {
730 buffer.ensure_capacity(capacity);
731 buffer
732 }
733 };
734
735 Some((slot_index, buffer))
736 } else {
737 if !(slot_key.contract == Address::NULL
741 || new_contracts
742 .iter()
743 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
744 {
745 return None;
746 }
747
748 let slot_index = SlotIndex(slots.len());
749 slot_access.push(SlotAccess {
750 slot_index,
751 read_write: true,
752 });
753
754 let slot = SlotState::OriginalReadWrite {
755 buffer: OwnedAlignedBuffer::with_capacity(capacity),
756 previous: SharedAlignedBuffer::default(),
757 };
758 slots.push((slot_key, slot));
759 let slot = &mut slots.last_mut().expect("Just inserted; qed").1;
760 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
761 unreachable!("Just inserted; qed");
762 };
763
764 Some((slot_index, buffer))
765 }
766 }
767
768 pub fn access_used_rw(&mut self, slot_index: SlotIndex) -> Option<&mut OwnedAlignedBuffer> {
776 let maybe_slot = self
777 .inner_rw()?
778 .slots
779 .get_mut(usize::from(slot_index))
780 .map(|(_slot_key, slot)| slot);
781
782 let Some(slot) = maybe_slot else {
783 debug!(?slot_index, "`access_used_rw` access violation (not found)");
784 return None;
785 };
786
787 match slot {
789 SlotState::Original(_buffer)
790 | SlotState::OriginalReadOnly(_buffer)
791 | SlotState::Modified(_buffer)
792 | SlotState::ModifiedReadOnly(_buffer) => {
793 debug!(?slot_index, "`access_used_rw` access violation (read only)");
794 None
795 }
796 SlotState::OriginalReadWrite { buffer, .. }
797 | SlotState::ModifiedReadWrite { buffer, .. } => Some(buffer),
798 }
799 }
800
801 #[cold]
803 pub fn reset(&mut self) {
804 let (inner, parent_slot_access_len) = match &mut self.0 {
805 NestedSlotsInner::ReadWrite {
806 inner,
807 parent_slot_access_len,
808 original_parent: _,
809 } => (&mut **inner, parent_slot_access_len),
810 NestedSlotsInner::ReadOnly { .. } => {
811 return;
813 }
814 };
815
816 let slots = &mut inner.slots;
817 let slot_access = &mut inner.slot_access;
818
819 for slot_access in slot_access.drain(*parent_slot_access_len..) {
821 let slot = &mut slots
822 .get_mut(usize::from(slot_access.slot_index))
823 .expect("Accessed slot exists; qed")
824 .1;
825 replace_with_or_abort(slot, |slot| match slot {
826 SlotState::Original(_buffer) => {
827 unreachable!("Slot can't be in `Original` state after being accessed; qed")
828 }
829 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
830 SlotState::Modified(buffer) => SlotState::Modified(buffer),
831 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
832 SlotState::OriginalReadWrite { previous, .. } => SlotState::Original(previous),
833 SlotState::ModifiedReadWrite { previous, .. } => SlotState::Modified(previous),
834 });
835 }
836
837 *parent_slot_access_len = 0;
838 }
839}