1#![expect(
2 clippy::rest_pattern_accessible_field,
3 reason = "Intentionally not needing other fields, too verbose otherwise"
4)]
5#![no_std]
6
7#[cfg(test)]
8mod tests;
9
10extern crate alloc;
11
12use ab_aligned_buffer::{OwnedAlignedBuffer, SharedAlignedBuffer};
13use ab_core_primitives::address::Address;
14use alloc::boxed::Box;
15use core::mem::ManuallyDrop;
16use replace_with::replace_with_or_abort;
17use smallvec::SmallVec;
18use tracing::debug;
19
20const INLINE_SIZE: usize = 8;
25const NEW_CONTRACTS_INLINE: usize = 2;
27const COMPACTION_THRESHOLD: u32 = 4096;
33
34#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
36pub struct SlotKey {
37 pub owner: Address,
39 pub contract: Address,
41}
42
43#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
45pub struct SlotIndex(usize);
46
47impl From<SlotIndex> for usize {
48 #[inline(always)]
49 fn from(value: SlotIndex) -> Self {
50 value.0
51 }
52}
53
54#[derive(Debug, Clone)]
55pub enum Slot {
56 ReadOnly {
57 key: SlotKey,
58 buffer: SharedAlignedBuffer,
59 },
60 ReadWrite {
61 key: SlotKey,
62 buffer: SharedAlignedBuffer,
63 },
64}
65
66impl Slot {
67 fn is_null_contract(&self) -> bool {
68 let slot_key = match self {
69 Slot::ReadOnly { key, .. } => key,
70 Slot::ReadWrite { key, .. } => key,
71 };
72
73 slot_key.contract == Address::NULL
74 }
75}
76
77#[derive(Debug, Clone)]
78enum SlotState {
79 ReadOnly(SharedAlignedBuffer),
83 Original(SharedAlignedBuffer),
85 OriginalReadOnly(SharedAlignedBuffer),
87 Modified(SharedAlignedBuffer),
89 ModifiedReadOnly(SharedAlignedBuffer),
91 OriginalReadWrite {
93 buffer: OwnedAlignedBuffer,
94 previous: SharedAlignedBuffer,
96 },
97 ModifiedReadWrite {
99 buffer: OwnedAlignedBuffer,
100 previous: SharedAlignedBuffer,
102 },
103}
104
105#[derive(Debug, Clone)]
106enum SlotAccessKind {
107 ReadOnly,
109 ReadWrite,
111 Integrated { previous: SlotState },
115}
116
117#[derive(Debug, Clone)]
118struct SlotAccess {
119 slot_index: SlotIndex,
120 kind: SlotAccessKind,
121}
122
123#[inline(always)]
127fn active_access(slot_access: &[SlotAccess], slot_index: SlotIndex) -> Option<bool> {
128 let slot_access = slot_access
129 .iter()
130 .rev()
131 .find(|slot_access| slot_access.slot_index == slot_index)?;
132
133 match slot_access.kind {
134 SlotAccessKind::ReadOnly => Some(false),
135 SlotAccessKind::ReadWrite => Some(true),
136 SlotAccessKind::Integrated { .. } => None,
137 }
138}
139
140#[derive(Debug, Clone)]
141struct Inner {
142 slots: SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
143 slot_access: SmallVec<[SlotAccess; INLINE_SIZE]>,
146 new_contracts: SmallVec<[Address; NEW_CONTRACTS_INLINE]>,
152}
153
154#[derive(Debug, Clone)]
156pub struct Slots(Box<Inner>);
157
158impl Slots {
159 #[inline(always)]
166 pub fn new<I>(slots: I) -> Self
167 where
168 I: IntoIterator<Item = Slot>,
169 {
170 let slots = slots
171 .into_iter()
172 .filter_map(|slot| {
173 if slot.is_null_contract() {
177 return None;
178 }
179
180 Some(match slot {
181 Slot::ReadOnly { key, buffer } => {
182 (key, SlotState::ReadOnly(buffer))
184 }
185 Slot::ReadWrite { key, buffer } => (key, SlotState::Original(buffer)),
186 })
187 })
188 .collect();
189
190 let inner = Inner {
191 slots,
192 slot_access: SmallVec::new(),
193 new_contracts: SmallVec::new(),
194 };
195
196 Self(Box::new(inner))
197 }
198
199 #[inline(always)]
204 pub fn new_nested_rw(&mut self) -> NestedSlots<'_> {
205 let parent_slots_len = self.0.slots.len();
206 let parent_new_contracts_len = self.0.new_contracts.len();
207
208 NestedSlots(NestedSlotsInner::ReadWrite {
209 inner: &mut self.0,
210 parent_slot_access_len: 0,
211 parent_slots_len,
212 parent_new_contracts_len,
213 parent_slot_access_start: None,
214 })
215 }
216
217 #[inline(always)]
219 pub fn new_nested_ro(&self) -> NestedSlots<'_> {
220 NestedSlots(NestedSlotsInner::ReadOnly { inner: &self.0 })
221 }
222
223 #[must_use = "Check if contract already exists in a map"]
231 #[inline(always)]
232 pub fn add_new_contract(&mut self, owner: Address) -> bool {
233 let new_contracts = &mut self.0.new_contracts;
234
235 if new_contracts.contains(&owner) {
236 debug!(?owner, "Not adding a new contract duplicate");
237 return false;
238 }
239
240 new_contracts.push(owner);
241 true
242 }
243
244 #[inline]
246 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
247 self.0.slots.iter().map(|(slot_key, slot)| match slot {
248 SlotState::ReadOnly(buffer) => (slot_key, buffer),
249 SlotState::Original(buffer) => (slot_key, buffer),
250 SlotState::OriginalReadOnly(_) => {
251 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
252 }
253 SlotState::Modified(buffer) => (slot_key, buffer),
254 SlotState::ModifiedReadOnly(_) => {
255 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
256 }
257 SlotState::OriginalReadWrite { .. } => {
258 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
259 }
260 SlotState::ModifiedReadWrite { .. } => {
261 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
262 }
263 })
264 }
265
266 #[inline]
268 pub fn iter_modified(&self) -> impl Iterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
269 self.0
270 .slots
271 .iter()
272 .filter_map(|(slot_key, slot)| match slot {
273 SlotState::ReadOnly(_) => None,
274 SlotState::Original(_) => None,
275 SlotState::OriginalReadOnly(_) => unreachable!(
276 "Only original and modified slots can exist at the `Slots` level; qed"
277 ),
278 SlotState::Modified(buffer) => Some((slot_key, buffer)),
279 SlotState::ModifiedReadOnly(_) => unreachable!(
280 "Only original and modified slots can exist at the `Slots` level; qed"
281 ),
282 SlotState::OriginalReadWrite { .. } => unreachable!(
283 "Only original and modified slots can exist at the `Slots` level; qed"
284 ),
285 SlotState::ModifiedReadWrite { .. } => unreachable!(
286 "Only original and modified slots can exist at the `Slots` level; qed"
287 ),
288 })
289 }
290
291 #[inline]
293 pub fn into_slots(self) -> impl ExactSizeIterator<Item = (SlotKey, SharedAlignedBuffer)> {
294 self.0.slots.into_iter().map(|(slot_key, slot)| match slot {
295 SlotState::ReadOnly(buffer) => (slot_key, buffer),
296 SlotState::Original(buffer) => (slot_key, buffer),
297 SlotState::OriginalReadOnly(_) => {
298 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
299 }
300 SlotState::Modified(buffer) => {
301 if buffer.capacity() - buffer.len() >= COMPACTION_THRESHOLD {
302 (slot_key, SharedAlignedBuffer::from_bytes(buffer.as_slice()))
305 } else {
306 (slot_key, buffer)
307 }
308 }
309 SlotState::ModifiedReadOnly(_) => {
310 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
311 }
312 SlotState::OriginalReadWrite { .. } => {
313 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
314 }
315 SlotState::ModifiedReadWrite { .. } => {
316 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
317 }
318 })
319 }
320}
321
322#[derive(Debug)]
324enum NestedSlotsInner<'a> {
325 ReadWrite {
327 inner: &'a mut Inner,
328 parent_slot_access_len: usize,
329 parent_slots_len: usize,
331 parent_new_contracts_len: usize,
333 parent_slot_access_start: Option<usize>,
335 },
336 ReadOnly { inner: &'a Inner },
338}
339
340#[derive(Debug)]
341pub struct NestedSlots<'a>(NestedSlotsInner<'a>);
342
343impl Drop for NestedSlots<'_> {
344 #[inline(always)]
345 fn drop(&mut self) {
346 if matches!(self.0, NestedSlotsInner::ReadWrite { .. }) {
349 self.reset();
350 }
351 }
352}
353
354impl<'a> NestedSlots<'a> {
355 #[inline(always)]
356 fn inner_ro(&self) -> &Inner {
357 match &self.0 {
358 NestedSlotsInner::ReadWrite { inner, .. } => inner,
359 NestedSlotsInner::ReadOnly { inner } => inner,
360 }
361 }
362
363 #[inline(always)]
364 fn inner_rw(&mut self) -> Option<&mut Inner> {
365 match &mut self.0 {
366 NestedSlotsInner::ReadWrite { inner, .. } => Some(inner),
367 NestedSlotsInner::ReadOnly { .. } => None,
368 }
369 }
370
371 #[inline(always)]
378 pub fn new_nested_rw<'b>(&'b mut self) -> Option<NestedSlots<'b>>
379 where
380 'a: 'b,
381 {
382 let (inner, parent_slot_access_start) = match &mut self.0 {
383 NestedSlotsInner::ReadWrite {
384 inner,
385 parent_slot_access_len,
386 ..
387 } => (&mut **inner, *parent_slot_access_len),
388 NestedSlotsInner::ReadOnly { .. } => {
389 return None;
390 }
391 };
392
393 let parent_slot_access_len = inner.slot_access.len();
394 let parent_slots_len = inner.slots.len();
395 let parent_new_contracts_len = inner.new_contracts.len();
396
397 Some(NestedSlots(NestedSlotsInner::ReadWrite {
398 inner,
399 parent_slot_access_len,
400 parent_slots_len,
401 parent_new_contracts_len,
402 parent_slot_access_start: Some(parent_slot_access_start),
403 }))
404 }
405
406 #[inline(always)]
408 pub fn new_nested_ro<'b>(&'b self) -> NestedSlots<'b>
409 where
410 'a: 'b,
411 {
412 let inner = match &self.0 {
413 NestedSlotsInner::ReadWrite { inner, .. } => &**inner,
414 NestedSlotsInner::ReadOnly { inner } => &**inner,
415 };
416
417 NestedSlots(NestedSlotsInner::ReadOnly { inner })
418 }
419
420 #[must_use = "Check if contract already exists in a map"]
428 #[inline(always)]
429 pub fn add_new_contract(&mut self, owner: Address) -> bool {
430 let Some(inner) = self.inner_rw() else {
431 debug!(?owner, "`add_new_contract` access violation");
432 return false;
433 };
434
435 let new_contracts = &mut inner.new_contracts;
436
437 if new_contracts.contains(&owner) {
438 debug!(?owner, "Not adding a new contract duplicate");
439 return false;
440 }
441
442 new_contracts.push(owner);
443 true
444 }
445
446 #[inline(always)]
453 pub fn get_code(&self, owner: Address) -> Option<SharedAlignedBuffer> {
454 let result = self.get_code_internal(owner);
455
456 if result.is_none() {
457 debug!(?owner, "`get_code` access violation");
458 }
459
460 result
461 }
462
463 #[inline(always)]
464 fn get_code_internal(&self, owner: Address) -> Option<SharedAlignedBuffer> {
465 let inner = self.inner_ro();
466 let slots = &inner.slots;
467 let slot_access = &inner.slot_access;
468
469 let contract = Address::SYSTEM_CODE;
470
471 let slot_index = slots.iter().position(|(slot_key, _slot)| {
472 slot_key.owner == owner && slot_key.contract == contract
473 })?;
474 let slot_index = SlotIndex(slot_index);
475
476 if active_access(slot_access, slot_index) == Some(true) {
478 return None;
479 }
480
481 let buffer = match &slots
482 .get(usize::from(slot_index))
483 .expect("Just found; qed")
484 .1
485 {
486 SlotState::ReadOnly(buffer)
487 | SlotState::Original(buffer)
488 | SlotState::OriginalReadOnly(buffer)
489 | SlotState::Modified(buffer)
490 | SlotState::ModifiedReadOnly(buffer) => buffer,
491 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => {
492 return None;
493 }
494 };
495
496 Some(buffer.clone())
497 }
498
499 #[inline(always)]
506 pub fn use_ro(&mut self, slot_key: SlotKey) -> Option<&SharedAlignedBuffer> {
507 let inner_rw = match &mut self.0 {
508 NestedSlotsInner::ReadWrite { inner, .. } => &mut **inner,
509 NestedSlotsInner::ReadOnly { inner } => {
510 let result = Self::use_ro_internal_read_only(
512 slot_key,
513 &inner.slots,
514 &inner.slot_access,
515 &inner.new_contracts,
516 );
517
518 if result.is_none() {
519 debug!(?slot_key, "`use_ro` access violation");
520 }
521
522 return result;
523 }
524 };
525
526 let result = Self::use_ro_internal(
527 slot_key,
528 &mut inner_rw.slots,
529 &mut inner_rw.slot_access,
530 &inner_rw.new_contracts,
531 );
532
533 if result.is_none() {
534 debug!(?slot_key, "`use_ro` access violation");
535 }
536
537 result
538 }
539
540 #[inline(always)]
541 fn use_ro_internal<'b>(
542 slot_key: SlotKey,
543 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
544 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
545 new_contracts: &[Address],
546 ) -> Option<&'b SharedAlignedBuffer> {
547 let maybe_slot_index = slots
548 .iter()
549 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
550 .map(SlotIndex);
551
552 if let Some(slot_index) = maybe_slot_index {
553 let maybe_read_write = active_access(slot_access, slot_index);
555 if maybe_read_write == Some(true) {
556 return None;
557 }
558
559 let slot = &mut slots
560 .get_mut(usize::from(slot_index))
561 .expect("Just found; qed")
562 .1;
563
564 let buffer = match slot {
566 SlotState::ReadOnly(buffer) => {
567 return Some(buffer);
569 }
570 SlotState::Original(buffer) => {
571 let buffer = buffer.clone();
572 *slot = SlotState::OriginalReadOnly(buffer);
573 let SlotState::OriginalReadOnly(buffer) = slot else {
574 unreachable!("Just inserted; qed");
575 };
576 buffer
577 }
578 SlotState::OriginalReadOnly(buffer) | SlotState::ModifiedReadOnly(buffer) => buffer,
579 SlotState::Modified(buffer) => {
580 let buffer = buffer.clone();
581 *slot = SlotState::ModifiedReadOnly(buffer);
582 let SlotState::ModifiedReadOnly(buffer) = slot else {
583 unreachable!("Just inserted; qed");
584 };
585 buffer
586 }
587 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => {
588 return None;
589 }
590 };
591
592 if maybe_read_write.is_none() {
593 slot_access.push(SlotAccess {
594 slot_index,
595 kind: SlotAccessKind::ReadOnly,
596 });
597 }
598
599 Some(buffer)
600 } else {
601 if !(slot_key.contract == Address::NULL
605 || new_contracts
606 .iter()
607 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
608 {
609 return None;
610 }
611
612 slot_access.push(SlotAccess {
613 slot_index: SlotIndex(slots.len()),
614 kind: SlotAccessKind::ReadOnly,
615 });
616
617 let slot = SlotState::OriginalReadOnly(SharedAlignedBuffer::default());
618 slots.push((slot_key, slot));
619 let slot = &slots.last().expect("Just inserted; qed").1;
620 let SlotState::OriginalReadOnly(buffer) = slot else {
621 unreachable!("Just inserted; qed");
622 };
623
624 Some(buffer)
625 }
626 }
627
628 #[inline(always)]
630 fn use_ro_internal_read_only<'b>(
631 slot_key: SlotKey,
632 slots: &'b SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
633 slot_access: &SmallVec<[SlotAccess; INLINE_SIZE]>,
634 new_contracts: &[Address],
635 ) -> Option<&'b SharedAlignedBuffer> {
636 let maybe_slot_index = slots
637 .iter()
638 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
639 .map(SlotIndex);
640
641 if let Some(slot_index) = maybe_slot_index {
642 if active_access(slot_access, slot_index) == Some(true) {
644 return None;
645 }
646
647 let slot = &slots
648 .get(usize::from(slot_index))
649 .expect("Just found; qed")
650 .1;
651
652 match slot {
654 SlotState::ReadOnly(buffer)
655 | SlotState::Original(buffer)
656 | SlotState::OriginalReadOnly(buffer)
657 | SlotState::ModifiedReadOnly(buffer)
658 | SlotState::Modified(buffer) => Some(buffer),
659 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => None,
660 }
661 } else {
662 if !(slot_key.contract == Address::NULL
666 || new_contracts
667 .iter()
668 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
669 {
670 return None;
671 }
672
673 Some(SharedAlignedBuffer::empty_ref())
674 }
675 }
676
677 #[inline(always)]
686 pub fn use_rw(
687 &mut self,
688 slot_key: SlotKey,
689 capacity: u32,
690 ) -> Option<(SlotIndex, &mut OwnedAlignedBuffer)> {
691 let inner = self.inner_rw()?;
692 let slots = &mut inner.slots;
693 let slot_access = &mut inner.slot_access;
694 let new_contracts = &inner.new_contracts;
695
696 let result = Self::use_rw_internal(slot_key, capacity, slots, slot_access, new_contracts);
697
698 if result.is_none() {
699 debug!(?slot_key, "`use_rw` access violation");
700 }
701
702 result
703 }
704
705 #[inline(always)]
706 fn use_rw_internal<'b>(
707 slot_key: SlotKey,
708 capacity: u32,
709 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
710 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
711 new_contracts: &[Address],
712 ) -> Option<(SlotIndex, &'b mut OwnedAlignedBuffer)> {
713 let maybe_slot_index = slots
714 .iter()
715 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
716 .map(SlotIndex);
717
718 if let Some(slot_index) = maybe_slot_index {
719 if active_access(slot_access, slot_index).is_some() {
721 return None;
722 }
723
724 let slot = &mut slots
725 .get_mut(usize::from(slot_index))
726 .expect("Just found; qed")
727 .1;
728
729 let buffer = match slot {
731 SlotState::ReadOnly(_buffer)
732 | SlotState::OriginalReadOnly(_buffer)
733 | SlotState::ModifiedReadOnly(_buffer) => {
734 return None;
735 }
736 SlotState::Original(buffer) => {
737 let mut new_buffer =
738 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
739 new_buffer.copy_from_slice(buffer.as_slice());
740
741 *slot = SlotState::OriginalReadWrite {
742 buffer: new_buffer,
743 previous: buffer.clone(),
744 };
745 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
746 unreachable!("Just inserted; qed");
747 };
748 buffer
749 }
750 SlotState::Modified(buffer) => {
751 let mut new_buffer =
752 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
753 new_buffer.copy_from_slice(buffer.as_slice());
754
755 *slot = SlotState::ModifiedReadWrite {
756 buffer: new_buffer,
757 previous: buffer.clone(),
758 };
759 let SlotState::ModifiedReadWrite { buffer, .. } = slot else {
760 unreachable!("Just inserted; qed");
761 };
762 buffer
763 }
764 SlotState::OriginalReadWrite { buffer, .. }
765 | SlotState::ModifiedReadWrite { buffer, .. } => {
766 buffer.ensure_capacity(capacity);
767 buffer
768 }
769 };
770
771 slot_access.push(SlotAccess {
772 slot_index,
773 kind: SlotAccessKind::ReadWrite,
774 });
775
776 Some((slot_index, buffer))
777 } else {
778 if !(slot_key.contract == Address::NULL
782 || new_contracts
783 .iter()
784 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
785 {
786 return None;
787 }
788
789 let slot_index = SlotIndex(slots.len());
790 slot_access.push(SlotAccess {
791 slot_index,
792 kind: SlotAccessKind::ReadWrite,
793 });
794
795 let slot = SlotState::OriginalReadWrite {
796 buffer: OwnedAlignedBuffer::with_capacity(capacity),
797 previous: SharedAlignedBuffer::default(),
798 };
799 slots.push((slot_key, slot));
800 let slot = &mut slots.last_mut().expect("Just inserted; qed").1;
801 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
802 unreachable!("Just inserted; qed");
803 };
804
805 Some((slot_index, buffer))
806 }
807 }
808
809 pub fn access_used_rw(&mut self, slot_index: SlotIndex) -> Option<&mut OwnedAlignedBuffer> {
817 let maybe_slot = self
818 .inner_rw()?
819 .slots
820 .get_mut(usize::from(slot_index))
821 .map(|(_slot_key, slot)| slot);
822
823 let Some(slot) = maybe_slot else {
824 debug!(?slot_index, "`access_used_rw` access violation (not found)");
825 return None;
826 };
827
828 match slot {
830 SlotState::ReadOnly(_buffer)
831 | SlotState::Original(_buffer)
832 | SlotState::OriginalReadOnly(_buffer)
833 | SlotState::Modified(_buffer)
834 | SlotState::ModifiedReadOnly(_buffer) => {
835 debug!(?slot_index, "`access_used_rw` access violation (read only)");
836 None
837 }
838 SlotState::OriginalReadWrite { buffer, .. }
839 | SlotState::ModifiedReadWrite { buffer, .. } => Some(buffer),
840 }
841 }
842
843 #[inline(always)]
847 pub fn commit(self) {
848 let mut this = ManuallyDrop::new(self);
850
851 let (inner, parent_slot_access_len, parent_slot_access_start) = match &mut this.0 {
852 NestedSlotsInner::ReadWrite {
853 inner,
854 parent_slot_access_len,
855 parent_slot_access_start,
856 ..
857 } => (
858 &mut **inner,
859 *parent_slot_access_len,
860 *parent_slot_access_start,
861 ),
862 NestedSlotsInner::ReadOnly { .. } => {
863 return;
865 }
866 };
867
868 let slots = &mut inner.slots;
869 let slot_access = &mut inner.slot_access;
870
871 for slot_access in slot_access.iter_mut().skip(parent_slot_access_len) {
875 if matches!(slot_access.kind, SlotAccessKind::Integrated { .. }) {
876 continue;
878 }
879
880 let slot = &mut slots
881 .get_mut(usize::from(slot_access.slot_index))
882 .expect("Accessed slot exists; qed")
883 .1;
884
885 let mut previous = None;
886 replace_with_or_abort(slot, |slot| match slot {
887 SlotState::ReadOnly(_buffer) => {
888 unreachable!("Accesses of read-only slots are not tracked; qed")
889 }
890 SlotState::Original(_buffer) => {
891 unreachable!("Slot can't be in `Original` state after being accessed; qed")
892 }
893 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
894 SlotState::Modified(buffer) => SlotState::Modified(buffer),
895 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
896 SlotState::OriginalReadWrite {
897 buffer,
898 previous: previous_buffer,
899 } => {
900 previous = Some(SlotState::Original(previous_buffer));
901 SlotState::Modified(buffer.into_shared())
902 }
903 SlotState::ModifiedReadWrite {
904 buffer,
905 previous: previous_buffer,
906 } => {
907 previous = Some(SlotState::Modified(previous_buffer));
908 SlotState::Modified(buffer.into_shared())
909 }
910 });
911 if let Some(previous) = previous {
912 slot_access.kind = SlotAccessKind::Integrated { previous };
913 }
914 }
915
916 let Some(parent_slot_access_start) = parent_slot_access_start else {
917 slot_access.clear();
919
920 slots.retain(|(slot_key, _slot)| slot_key.contract != Address::NULL);
924 return;
925 };
926
927 let accesses = slot_access
930 .drain(parent_slot_access_len..)
931 .collect::<SmallVec<[_; INLINE_SIZE]>>();
932 for access in accesses {
933 if !matches!(access.kind, SlotAccessKind::Integrated { .. }) {
934 continue;
936 }
937
938 if slot_access
939 .iter()
940 .skip(parent_slot_access_start)
941 .any(|slot_access| slot_access.slot_index == access.slot_index)
942 {
943 continue;
945 }
946
947 slot_access.push(access);
948 }
949 }
950
951 #[cold]
954 fn reset(&mut self) {
955 let (inner, parent_slot_access_len, parent_slots_len, parent_new_contracts_len) =
956 match &mut self.0 {
957 NestedSlotsInner::ReadWrite {
958 inner,
959 parent_slot_access_len,
960 parent_slots_len,
961 parent_new_contracts_len,
962 parent_slot_access_start: _,
963 } => (
964 &mut **inner,
965 *parent_slot_access_len,
966 *parent_slots_len,
967 *parent_new_contracts_len,
968 ),
969 NestedSlotsInner::ReadOnly { .. } => {
970 return;
972 }
973 };
974
975 let slots = &mut inner.slots;
976 let slot_access = &mut inner.slot_access;
977
978 for slot_access in slot_access.drain(parent_slot_access_len..).rev() {
981 let slot = &mut slots
982 .get_mut(usize::from(slot_access.slot_index))
983 .expect("Accessed slot exists; qed")
984 .1;
985
986 if let SlotAccessKind::Integrated { previous } = slot_access.kind {
987 *slot = previous;
988 continue;
989 }
990
991 replace_with_or_abort(slot, |slot| match slot {
992 SlotState::ReadOnly(_buffer) => {
993 unreachable!("Accesses of read-only slots are not tracked; qed");
994 }
995 SlotState::Original(_buffer) => {
996 unreachable!("Slot can't be in `Original` state after being accessed; qed");
997 }
998 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
999 SlotState::Modified(buffer) => SlotState::Modified(buffer),
1000 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
1001 SlotState::OriginalReadWrite { previous, .. } => SlotState::Original(previous),
1002 SlotState::ModifiedReadWrite { previous, .. } => SlotState::Modified(previous),
1003 });
1004 }
1005
1006 slots.truncate(parent_slots_len);
1008 inner.new_contracts.truncate(parent_new_contracts_len);
1009 }
1010}