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;
27
28#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct SlotKey {
31 pub owner: Address,
33 pub contract: Address,
35}
36
37#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
39pub struct SlotIndex(usize);
40
41impl From<SlotIndex> for usize {
42 #[inline(always)]
43 fn from(value: SlotIndex) -> Self {
44 value.0
45 }
46}
47
48#[derive(Debug, Clone)]
49pub enum Slot {
50 ReadOnly {
51 key: SlotKey,
52 buffer: SharedAlignedBuffer,
53 },
54 ReadWrite {
55 key: SlotKey,
56 buffer: SharedAlignedBuffer,
57 },
58}
59
60impl Slot {
61 fn is_null_contract(&self) -> bool {
62 let slot_key = match self {
63 Slot::ReadOnly { key, .. } => key,
64 Slot::ReadWrite { key, .. } => key,
65 };
66
67 slot_key.contract == Address::NULL
68 }
69}
70
71#[derive(Debug, Clone)]
72enum SlotState {
73 ReadOnly(SharedAlignedBuffer),
77 Original(SharedAlignedBuffer),
79 OriginalReadOnly(SharedAlignedBuffer),
81 Modified(SharedAlignedBuffer),
83 ModifiedReadOnly(SharedAlignedBuffer),
85 OriginalReadWrite {
87 buffer: OwnedAlignedBuffer,
88 previous: SharedAlignedBuffer,
90 },
91 ModifiedReadWrite {
93 buffer: OwnedAlignedBuffer,
94 previous: SharedAlignedBuffer,
96 },
97}
98
99#[derive(Debug, Clone)]
100enum SlotAccessKind {
101 ReadOnly,
103 ReadWrite,
105 Integrated { previous: SlotState },
109}
110
111#[derive(Debug, Clone)]
112struct SlotAccess {
113 slot_index: SlotIndex,
114 kind: SlotAccessKind,
115}
116
117#[inline(always)]
121fn active_access(slot_access: &[SlotAccess], slot_index: SlotIndex) -> Option<bool> {
122 let slot_access = slot_access
123 .iter()
124 .rev()
125 .find(|slot_access| slot_access.slot_index == slot_index)?;
126
127 match slot_access.kind {
128 SlotAccessKind::ReadOnly => Some(false),
129 SlotAccessKind::ReadWrite => Some(true),
130 SlotAccessKind::Integrated { .. } => None,
131 }
132}
133
134#[derive(Debug, Clone)]
135struct Inner {
136 slots: SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
137 slot_access: SmallVec<[SlotAccess; INLINE_SIZE]>,
140 new_contracts: SmallVec<[Address; NEW_CONTRACTS_INLINE]>,
146}
147
148#[derive(Debug, Clone)]
150pub struct Slots(Box<Inner>);
151
152impl Slots {
153 #[inline(always)]
160 pub fn new<I>(slots: I) -> Self
161 where
162 I: IntoIterator<Item = Slot>,
163 {
164 let slots = slots
165 .into_iter()
166 .filter_map(|slot| {
167 if slot.is_null_contract() {
171 return None;
172 }
173
174 Some(match slot {
175 Slot::ReadOnly { key, buffer } => {
176 (key, SlotState::ReadOnly(buffer))
178 }
179 Slot::ReadWrite { key, buffer } => (key, SlotState::Original(buffer)),
180 })
181 })
182 .collect();
183
184 let inner = Inner {
185 slots,
186 slot_access: SmallVec::new(),
187 new_contracts: SmallVec::new(),
188 };
189
190 Self(Box::new(inner))
191 }
192
193 #[inline(always)]
198 pub fn new_nested_rw(&mut self) -> NestedSlots<'_> {
199 let parent_slots_len = self.0.slots.len();
200 let parent_new_contracts_len = self.0.new_contracts.len();
201
202 NestedSlots(NestedSlotsInner::ReadWrite {
203 inner: &mut self.0,
204 parent_slot_access_len: 0,
205 parent_slots_len,
206 parent_new_contracts_len,
207 parent_slot_access_start: None,
208 })
209 }
210
211 #[inline(always)]
213 pub fn new_nested_ro(&self) -> NestedSlots<'_> {
214 NestedSlots(NestedSlotsInner::ReadOnly { inner: &self.0 })
215 }
216
217 #[must_use]
225 #[inline(always)]
226 pub fn add_new_contract(&mut self, owner: Address) -> bool {
227 let new_contracts = &mut self.0.new_contracts;
228
229 if new_contracts.contains(&owner) {
230 debug!(?owner, "Not adding a new contract duplicate");
231 return false;
232 }
233
234 new_contracts.push(owner);
235 true
236 }
237
238 #[inline]
240 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
241 self.0.slots.iter().map(|(slot_key, slot)| match slot {
242 SlotState::ReadOnly(buffer) => (slot_key, buffer),
243 SlotState::Original(buffer) => (slot_key, buffer),
244 SlotState::OriginalReadOnly(_) => {
245 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
246 }
247 SlotState::Modified(buffer) => (slot_key, buffer),
248 SlotState::ModifiedReadOnly(_) => {
249 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
250 }
251 SlotState::OriginalReadWrite { .. } => {
252 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
253 }
254 SlotState::ModifiedReadWrite { .. } => {
255 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
256 }
257 })
258 }
259
260 #[inline]
262 pub fn iter_modified(&self) -> impl Iterator<Item = (&SlotKey, &SharedAlignedBuffer)> + '_ {
263 self.0
264 .slots
265 .iter()
266 .filter_map(|(slot_key, slot)| match slot {
267 SlotState::ReadOnly(_) => None,
268 SlotState::Original(_) => None,
269 SlotState::OriginalReadOnly(_) => unreachable!(
270 "Only original and modified slots can exist at the `Slots` level; qed"
271 ),
272 SlotState::Modified(buffer) => Some((slot_key, buffer)),
273 SlotState::ModifiedReadOnly(_) => unreachable!(
274 "Only original and modified slots can exist at the `Slots` level; qed"
275 ),
276 SlotState::OriginalReadWrite { .. } => unreachable!(
277 "Only original and modified slots can exist at the `Slots` level; qed"
278 ),
279 SlotState::ModifiedReadWrite { .. } => unreachable!(
280 "Only original and modified slots can exist at the `Slots` level; qed"
281 ),
282 })
283 }
284
285 #[inline]
287 pub fn into_slots(self) -> impl ExactSizeIterator<Item = (SlotKey, SharedAlignedBuffer)> {
288 self.0.slots.into_iter().map(|(slot_key, slot)| match slot {
289 SlotState::ReadOnly(buffer) => (slot_key, buffer),
290 SlotState::Original(buffer) => (slot_key, buffer),
291 SlotState::OriginalReadOnly(_) => {
292 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
293 }
294 SlotState::Modified(buffer) => (slot_key, buffer),
295 SlotState::ModifiedReadOnly(_) => {
296 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
297 }
298 SlotState::OriginalReadWrite { .. } => {
299 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
300 }
301 SlotState::ModifiedReadWrite { .. } => {
302 unreachable!("Only original and modified slots can exist at the `Slots` level; qed")
303 }
304 })
305 }
306}
307
308#[derive(Debug)]
310enum NestedSlotsInner<'a> {
311 ReadWrite {
313 inner: &'a mut Inner,
314 parent_slot_access_len: usize,
315 parent_slots_len: usize,
317 parent_new_contracts_len: usize,
319 parent_slot_access_start: Option<usize>,
321 },
322 ReadOnly { inner: &'a Inner },
324}
325
326#[derive(Debug)]
327pub struct NestedSlots<'a>(NestedSlotsInner<'a>);
328
329impl Drop for NestedSlots<'_> {
330 #[inline(always)]
331 fn drop(&mut self) {
332 if matches!(self.0, NestedSlotsInner::ReadWrite { .. }) {
335 self.reset();
336 }
337 }
338}
339
340impl<'a> NestedSlots<'a> {
341 #[inline(always)]
342 fn inner_ro(&self) -> &Inner {
343 match &self.0 {
344 NestedSlotsInner::ReadWrite { inner, .. } => inner,
345 NestedSlotsInner::ReadOnly { inner } => inner,
346 }
347 }
348
349 #[inline(always)]
350 fn inner_rw(&mut self) -> Option<&mut Inner> {
351 match &mut self.0 {
352 NestedSlotsInner::ReadWrite { inner, .. } => Some(inner),
353 NestedSlotsInner::ReadOnly { .. } => None,
354 }
355 }
356
357 #[inline(always)]
364 pub fn new_nested_rw<'b>(&'b mut self) -> Option<NestedSlots<'b>>
365 where
366 'a: 'b,
367 {
368 let (inner, parent_slot_access_start) = match &mut self.0 {
369 NestedSlotsInner::ReadWrite {
370 inner,
371 parent_slot_access_len,
372 ..
373 } => (&mut **inner, *parent_slot_access_len),
374 NestedSlotsInner::ReadOnly { .. } => {
375 return None;
376 }
377 };
378
379 let parent_slot_access_len = inner.slot_access.len();
380 let parent_slots_len = inner.slots.len();
381 let parent_new_contracts_len = inner.new_contracts.len();
382
383 Some(NestedSlots(NestedSlotsInner::ReadWrite {
384 inner,
385 parent_slot_access_len,
386 parent_slots_len,
387 parent_new_contracts_len,
388 parent_slot_access_start: Some(parent_slot_access_start),
389 }))
390 }
391
392 #[inline(always)]
394 pub fn new_nested_ro<'b>(&'b self) -> NestedSlots<'b>
395 where
396 'a: 'b,
397 {
398 let inner = match &self.0 {
399 NestedSlotsInner::ReadWrite { inner, .. } => &**inner,
400 NestedSlotsInner::ReadOnly { inner } => &**inner,
401 };
402
403 NestedSlots(NestedSlotsInner::ReadOnly { inner })
404 }
405
406 #[must_use]
414 #[inline(always)]
415 pub fn add_new_contract(&mut self, owner: Address) -> bool {
416 let Some(inner) = self.inner_rw() else {
417 debug!(?owner, "`add_new_contract` access violation");
418 return false;
419 };
420
421 let new_contracts = &mut inner.new_contracts;
422
423 if new_contracts.contains(&owner) {
424 debug!(?owner, "Not adding a new contract duplicate");
425 return false;
426 }
427
428 new_contracts.push(owner);
429 true
430 }
431
432 #[inline(always)]
439 pub fn get_code(&self, owner: Address) -> Option<SharedAlignedBuffer> {
440 let result = self.get_code_internal(owner);
441
442 if result.is_none() {
443 debug!(?owner, "`get_code` access violation");
444 }
445
446 result
447 }
448
449 #[inline(always)]
450 fn get_code_internal(&self, owner: Address) -> Option<SharedAlignedBuffer> {
451 let inner = self.inner_ro();
452 let slots = &inner.slots;
453 let slot_access = &inner.slot_access;
454
455 let contract = Address::SYSTEM_CODE;
456
457 let slot_index = slots.iter().position(|(slot_key, _slot)| {
458 slot_key.owner == owner && slot_key.contract == contract
459 })?;
460 let slot_index = SlotIndex(slot_index);
461
462 if active_access(slot_access, slot_index) == Some(true) {
464 return None;
465 }
466
467 let buffer = match &slots
468 .get(usize::from(slot_index))
469 .expect("Just found; qed")
470 .1
471 {
472 SlotState::ReadOnly(buffer)
473 | SlotState::Original(buffer)
474 | SlotState::OriginalReadOnly(buffer)
475 | SlotState::Modified(buffer)
476 | SlotState::ModifiedReadOnly(buffer) => buffer,
477 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => {
478 return None;
479 }
480 };
481
482 Some(buffer.clone())
483 }
484
485 #[inline(always)]
489 pub fn use_ro(&mut self, slot_key: SlotKey) -> Option<&SharedAlignedBuffer> {
490 let inner_rw = match &mut self.0 {
491 NestedSlotsInner::ReadWrite { inner, .. } => &mut **inner,
492 NestedSlotsInner::ReadOnly { inner } => {
493 let result = Self::use_ro_internal_read_only(
495 slot_key,
496 &inner.slots,
497 &inner.slot_access,
498 &inner.new_contracts,
499 );
500
501 if result.is_none() {
502 debug!(?slot_key, "`use_ro` access violation");
503 }
504
505 return result;
506 }
507 };
508
509 let result = Self::use_ro_internal(
510 slot_key,
511 &mut inner_rw.slots,
512 &mut inner_rw.slot_access,
513 &inner_rw.new_contracts,
514 );
515
516 if result.is_none() {
517 debug!(?slot_key, "`use_ro` access violation");
518 }
519
520 result
521 }
522
523 #[inline(always)]
524 fn use_ro_internal<'b>(
525 slot_key: SlotKey,
526 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
527 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
528 new_contracts: &[Address],
529 ) -> Option<&'b SharedAlignedBuffer> {
530 let maybe_slot_index = slots
531 .iter()
532 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
533 .map(SlotIndex);
534
535 if let Some(slot_index) = maybe_slot_index {
536 let maybe_read_write = active_access(slot_access, slot_index);
538 if maybe_read_write == Some(true) {
539 return None;
540 }
541
542 let slot = &mut slots
543 .get_mut(usize::from(slot_index))
544 .expect("Just found; qed")
545 .1;
546
547 let buffer = match slot {
549 SlotState::ReadOnly(buffer) => {
550 return Some(buffer);
552 }
553 SlotState::Original(buffer) => {
554 let buffer = buffer.clone();
555 *slot = SlotState::OriginalReadOnly(buffer);
556 let SlotState::OriginalReadOnly(buffer) = slot else {
557 unreachable!("Just inserted; qed");
558 };
559 buffer
560 }
561 SlotState::OriginalReadOnly(buffer) | SlotState::ModifiedReadOnly(buffer) => buffer,
562 SlotState::Modified(buffer) => {
563 let buffer = buffer.clone();
564 *slot = SlotState::ModifiedReadOnly(buffer);
565 let SlotState::ModifiedReadOnly(buffer) = slot else {
566 unreachable!("Just inserted; qed");
567 };
568 buffer
569 }
570 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => {
571 return None;
572 }
573 };
574
575 if maybe_read_write.is_none() {
576 slot_access.push(SlotAccess {
577 slot_index,
578 kind: SlotAccessKind::ReadOnly,
579 });
580 }
581
582 Some(buffer)
583 } else {
584 if !(slot_key.contract == Address::NULL
588 || new_contracts
589 .iter()
590 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
591 {
592 return None;
593 }
594
595 slot_access.push(SlotAccess {
596 slot_index: SlotIndex(slots.len()),
597 kind: SlotAccessKind::ReadOnly,
598 });
599
600 let slot = SlotState::OriginalReadOnly(SharedAlignedBuffer::default());
601 slots.push((slot_key, slot));
602 let slot = &slots.last().expect("Just inserted; qed").1;
603 let SlotState::OriginalReadOnly(buffer) = slot else {
604 unreachable!("Just inserted; qed");
605 };
606
607 Some(buffer)
608 }
609 }
610
611 #[inline(always)]
613 fn use_ro_internal_read_only<'b>(
614 slot_key: SlotKey,
615 slots: &'b SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
616 slot_access: &SmallVec<[SlotAccess; INLINE_SIZE]>,
617 new_contracts: &[Address],
618 ) -> Option<&'b SharedAlignedBuffer> {
619 let maybe_slot_index = slots
620 .iter()
621 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
622 .map(SlotIndex);
623
624 if let Some(slot_index) = maybe_slot_index {
625 if active_access(slot_access, slot_index) == Some(true) {
627 return None;
628 }
629
630 let slot = &slots
631 .get(usize::from(slot_index))
632 .expect("Just found; qed")
633 .1;
634
635 match slot {
637 SlotState::ReadOnly(buffer)
638 | SlotState::Original(buffer)
639 | SlotState::OriginalReadOnly(buffer)
640 | SlotState::ModifiedReadOnly(buffer)
641 | SlotState::Modified(buffer) => Some(buffer),
642 SlotState::OriginalReadWrite { .. } | SlotState::ModifiedReadWrite { .. } => None,
643 }
644 } else {
645 if !(slot_key.contract == Address::NULL
649 || new_contracts
650 .iter()
651 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
652 {
653 return None;
654 }
655
656 Some(SharedAlignedBuffer::empty_ref())
657 }
658 }
659
660 #[inline(always)]
669 pub fn use_rw(
670 &mut self,
671 slot_key: SlotKey,
672 capacity: u32,
673 ) -> Option<(SlotIndex, &mut OwnedAlignedBuffer)> {
674 let inner = self.inner_rw()?;
675 let slots = &mut inner.slots;
676 let slot_access = &mut inner.slot_access;
677 let new_contracts = &inner.new_contracts;
678
679 let result = Self::use_rw_internal(slot_key, capacity, slots, slot_access, new_contracts);
680
681 if result.is_none() {
682 debug!(?slot_key, "`use_rw` access violation");
683 }
684
685 result
686 }
687
688 #[inline(always)]
689 fn use_rw_internal<'b>(
690 slot_key: SlotKey,
691 capacity: u32,
692 slots: &'b mut SmallVec<[(SlotKey, SlotState); INLINE_SIZE]>,
693 slot_access: &mut SmallVec<[SlotAccess; INLINE_SIZE]>,
694 new_contracts: &[Address],
695 ) -> Option<(SlotIndex, &'b mut OwnedAlignedBuffer)> {
696 let maybe_slot_index = slots
697 .iter()
698 .position(|(slot_key_candidate, _slot)| slot_key_candidate == &slot_key)
699 .map(SlotIndex);
700
701 if let Some(slot_index) = maybe_slot_index {
702 if active_access(slot_access, slot_index).is_some() {
704 return None;
705 }
706
707 let slot = &mut slots
708 .get_mut(usize::from(slot_index))
709 .expect("Just found; qed")
710 .1;
711
712 let buffer = match slot {
714 SlotState::ReadOnly(_buffer)
715 | SlotState::OriginalReadOnly(_buffer)
716 | SlotState::ModifiedReadOnly(_buffer) => {
717 return None;
718 }
719 SlotState::Original(buffer) => {
720 let mut new_buffer =
721 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
722 new_buffer.copy_from_slice(buffer.as_slice());
723
724 *slot = SlotState::OriginalReadWrite {
725 buffer: new_buffer,
726 previous: buffer.clone(),
727 };
728 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
729 unreachable!("Just inserted; qed");
730 };
731 buffer
732 }
733 SlotState::Modified(buffer) => {
734 let mut new_buffer =
735 OwnedAlignedBuffer::with_capacity(capacity.max(buffer.len()));
736 new_buffer.copy_from_slice(buffer.as_slice());
737
738 *slot = SlotState::ModifiedReadWrite {
739 buffer: new_buffer,
740 previous: buffer.clone(),
741 };
742 let SlotState::ModifiedReadWrite { buffer, .. } = slot else {
743 unreachable!("Just inserted; qed");
744 };
745 buffer
746 }
747 SlotState::OriginalReadWrite { buffer, .. }
748 | SlotState::ModifiedReadWrite { buffer, .. } => {
749 buffer.ensure_capacity(capacity);
750 buffer
751 }
752 };
753
754 slot_access.push(SlotAccess {
755 slot_index,
756 kind: SlotAccessKind::ReadWrite,
757 });
758
759 Some((slot_index, buffer))
760 } else {
761 if !(slot_key.contract == Address::NULL
765 || new_contracts
766 .iter()
767 .any(|candidate| candidate == slot_key.owner || candidate == slot_key.contract))
768 {
769 return None;
770 }
771
772 let slot_index = SlotIndex(slots.len());
773 slot_access.push(SlotAccess {
774 slot_index,
775 kind: SlotAccessKind::ReadWrite,
776 });
777
778 let slot = SlotState::OriginalReadWrite {
779 buffer: OwnedAlignedBuffer::with_capacity(capacity),
780 previous: SharedAlignedBuffer::default(),
781 };
782 slots.push((slot_key, slot));
783 let slot = &mut slots.last_mut().expect("Just inserted; qed").1;
784 let SlotState::OriginalReadWrite { buffer, .. } = slot else {
785 unreachable!("Just inserted; qed");
786 };
787
788 Some((slot_index, buffer))
789 }
790 }
791
792 pub fn access_used_rw(&mut self, slot_index: SlotIndex) -> Option<&mut OwnedAlignedBuffer> {
800 let maybe_slot = self
801 .inner_rw()?
802 .slots
803 .get_mut(usize::from(slot_index))
804 .map(|(_slot_key, slot)| slot);
805
806 let Some(slot) = maybe_slot else {
807 debug!(?slot_index, "`access_used_rw` access violation (not found)");
808 return None;
809 };
810
811 match slot {
813 SlotState::ReadOnly(_buffer)
814 | SlotState::Original(_buffer)
815 | SlotState::OriginalReadOnly(_buffer)
816 | SlotState::Modified(_buffer)
817 | SlotState::ModifiedReadOnly(_buffer) => {
818 debug!(?slot_index, "`access_used_rw` access violation (read only)");
819 None
820 }
821 SlotState::OriginalReadWrite { buffer, .. }
822 | SlotState::ModifiedReadWrite { buffer, .. } => Some(buffer),
823 }
824 }
825
826 #[inline(always)]
830 pub fn commit(self) {
831 let mut this = ManuallyDrop::new(self);
833
834 let (inner, parent_slot_access_len, parent_slot_access_start) = match &mut this.0 {
835 NestedSlotsInner::ReadWrite {
836 inner,
837 parent_slot_access_len,
838 parent_slot_access_start,
839 ..
840 } => (
841 &mut **inner,
842 *parent_slot_access_len,
843 *parent_slot_access_start,
844 ),
845 NestedSlotsInner::ReadOnly { .. } => {
846 return;
848 }
849 };
850
851 let slots = &mut inner.slots;
852 let slot_access = &mut inner.slot_access;
853
854 for slot_access in slot_access.iter_mut().skip(parent_slot_access_len) {
858 if matches!(slot_access.kind, SlotAccessKind::Integrated { .. }) {
859 continue;
861 }
862
863 let slot = &mut slots
864 .get_mut(usize::from(slot_access.slot_index))
865 .expect("Accessed slot exists; qed")
866 .1;
867
868 let mut previous = None;
869 replace_with_or_abort(slot, |slot| match slot {
870 SlotState::ReadOnly(_buffer) => {
871 unreachable!("Accesses of read-only slots are not tracked; qed")
872 }
873 SlotState::Original(_buffer) => {
874 unreachable!("Slot can't be in `Original` state after being accessed; qed")
875 }
876 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
877 SlotState::Modified(buffer) => SlotState::Modified(buffer),
878 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
879 SlotState::OriginalReadWrite {
880 buffer,
881 previous: previous_buffer,
882 } => {
883 previous = Some(SlotState::Original(previous_buffer));
884 SlotState::Modified(buffer.into_shared())
885 }
886 SlotState::ModifiedReadWrite {
887 buffer,
888 previous: previous_buffer,
889 } => {
890 previous = Some(SlotState::Modified(previous_buffer));
891 SlotState::Modified(buffer.into_shared())
892 }
893 });
894 if let Some(previous) = previous {
895 slot_access.kind = SlotAccessKind::Integrated { previous };
896 }
897 }
898
899 let Some(parent_slot_access_start) = parent_slot_access_start else {
900 slot_access.clear();
902
903 slots.retain(|(slot_key, _slot)| slot_key.contract != Address::NULL);
907 return;
908 };
909
910 let accesses = slot_access
913 .drain(parent_slot_access_len..)
914 .collect::<SmallVec<[_; INLINE_SIZE]>>();
915 for access in accesses {
916 if !matches!(access.kind, SlotAccessKind::Integrated { .. }) {
917 continue;
919 }
920
921 if slot_access
922 .iter()
923 .skip(parent_slot_access_start)
924 .any(|slot_access| slot_access.slot_index == access.slot_index)
925 {
926 continue;
928 }
929
930 slot_access.push(access);
931 }
932 }
933
934 #[cold]
937 fn reset(&mut self) {
938 let (inner, parent_slot_access_len, parent_slots_len, parent_new_contracts_len) =
939 match &mut self.0 {
940 NestedSlotsInner::ReadWrite {
941 inner,
942 parent_slot_access_len,
943 parent_slots_len,
944 parent_new_contracts_len,
945 parent_slot_access_start: _,
946 } => (
947 &mut **inner,
948 *parent_slot_access_len,
949 *parent_slots_len,
950 *parent_new_contracts_len,
951 ),
952 NestedSlotsInner::ReadOnly { .. } => {
953 return;
955 }
956 };
957
958 let slots = &mut inner.slots;
959 let slot_access = &mut inner.slot_access;
960
961 for slot_access in slot_access.drain(parent_slot_access_len..).rev() {
964 let slot = &mut slots
965 .get_mut(usize::from(slot_access.slot_index))
966 .expect("Accessed slot exists; qed")
967 .1;
968
969 if let SlotAccessKind::Integrated { previous } = slot_access.kind {
970 *slot = previous;
971 continue;
972 }
973
974 replace_with_or_abort(slot, |slot| match slot {
975 SlotState::ReadOnly(_buffer) => {
976 unreachable!("Accesses of read-only slots are not tracked; qed");
977 }
978 SlotState::Original(_buffer) => {
979 unreachable!("Slot can't be in `Original` state after being accessed; qed");
980 }
981 SlotState::OriginalReadOnly(buffer) => SlotState::Original(buffer),
982 SlotState::Modified(buffer) => SlotState::Modified(buffer),
983 SlotState::ModifiedReadOnly(buffer) => SlotState::Modified(buffer),
984 SlotState::OriginalReadWrite { previous, .. } => SlotState::Original(previous),
985 SlotState::ModifiedReadWrite { previous, .. } => SlotState::Modified(previous),
986 });
987 }
988
989 slots.truncate(parent_slots_len);
991 inner.new_contracts.truncate(parent_new_contracts_len);
992 }
993}