Skip to main content

thruput_sdk/
cell.rs

1// Test environment: just alias `core::cell::Cell`.
2// Statics will work if placed in `contract_local!{}`
3#[cfg(not(any(doc, target_arch = "riscv32")))]
4mod _impl {
5    pub use core::cell::Cell as ContractCell;
6}
7
8// Test environment: alias `core::cell::RefCell`; `contract_local!` places the
9// static in thread-local storage and `with_ref` goes through `LocalKey`.
10#[cfg(all(not(any(doc, target_arch = "riscv32")), feature = "std"))]
11mod _ref_impl {
12    pub use core::cell::RefCell as ContractRefCell;
13
14    impl<T: 'static> super::ContractLocalRef<T>
15        for std::thread::LocalKey<ContractRefCell<T>>
16    {
17        fn with_ref<R>(
18            &'static self,
19            f: impl FnOnce(&core::cell::RefCell<T>) -> R,
20        ) -> R {
21            self.with(f)
22        }
23    }
24}
25
26// Guest environment: wrap `RefCell` so it can live in a plain static.
27#[cfg(all(any(doc, target_arch = "riscv32"), feature = "alloc"))]
28mod _ref_impl {
29    /// A wrapped [`core::cell::RefCell`] that implements [`Sync`] so it can
30    /// be used in statics — the by-reference companion to [`ContractCell`]
31    /// for state that must be borrowed in place (e.g. the SDK's fixed
32    /// buffers, which would cost a stack copy if moved through `Cell`).
33    /// Declare via [`contract_local!`](crate::contract_local); the SDK
34    /// accesses it through a crate-private checkout trait that works
35    /// identically against the guest static and the test env's
36    /// thread-local.
37    ///
38    /// [`ContractCell`]: super::ContractCell
39    pub struct ContractRefCell<T>(core::cell::RefCell<T>);
40
41    // SAFETY: single-threaded guest, no interrupts; the test environment
42    // uses the thread-local alias instead.
43    unsafe impl<T> Sync for ContractRefCell<T> {}
44
45    impl<T> ContractRefCell<T> {
46        pub const fn new(value: T) -> Self {
47            Self(core::cell::RefCell::new(value))
48        }
49    }
50
51    impl<T> super::ContractLocalRef<T> for ContractRefCell<T> {
52        fn with_ref<R>(
53            &'static self,
54            f: impl FnOnce(&core::cell::RefCell<T>) -> R,
55        ) -> R {
56            f(&self.0)
57        }
58    }
59}
60
61#[cfg(feature = "alloc")]
62#[allow(unused_imports)]
63pub use _ref_impl::*;
64
65/// Uniform access to a [`contract_local!`](crate::contract_local)
66/// [`ContractRefCell`] static: implemented by the guest static itself and by
67/// the test env's `LocalKey`, so `CELL.with_ref(|c| ...)` works in both.
68///
69/// Crate-private: contracts should not build on the SDK's internal cells.
70///
71/// `alloc`-only, because the SDK's one by-reference cell is the `alloc`
72/// scratch pool ([`call::with_call_scratch`](crate::call)). The no-alloc guest
73/// keeps its scratch on the stack — a `MaybeUninit` buffer is free to build,
74/// so there is nothing to cache — and so needs no cell, no borrow flag, and no
75/// re-entrancy fallback at all.
76#[cfg(feature = "alloc")]
77pub(crate) trait ContractLocalRef<T> {
78    fn with_ref<R>(
79        &'static self,
80        f: impl FnOnce(&core::cell::RefCell<T>) -> R,
81    ) -> R;
82}
83
84// Guest environment: wrap
85#[cfg(any(doc, target_arch = "riscv32"))]
86mod _impl {
87    // All we need to provide here is interior mutability. By also declaring
88    // our wrapper thread-safe, we can use it in a static.
89
90    use core::ops::Deref;
91
92    /// A wrapped [`core::cell::Cell`] that implements [`Sync`] so it can be used
93    /// in statics.  Methods of the inner `Cell` are available via this wrapper's [`Deref`]
94    /// implementation.  The wrapper adds no costs to `Cell`.
95    ///
96    /// Note: For static storage that works in the test environment,
97    /// use the [`contract_local!`](crate::contract_local) macro, e.g.:
98    ///
99    ///     contract_local! {
100    ///         static VAR: ContractCell<u32> = const { ContractCell::new(0) };
101    ///     }
102    ///
103    /// (This macro is a no-op in the guest environment.)
104    ///
105    pub struct ContractCell<T: ?Sized>(core::cell::Cell<T>);
106
107    // SAFETY: We are single-threaded and don't get interrupts
108    unsafe impl<T: ?Sized> Sync for ContractCell<T> {}
109
110    // Override * to remove the `Cell` wrapper, leaving
111    // the `core::cell::Cell`
112    impl<T: ?Sized> Deref for ContractCell<T> {
113        type Target = core::cell::Cell<T>;
114
115        fn deref(&self) -> &Self::Target {
116            &self.0
117        }
118    }
119
120    impl<T> ContractCell<T> {
121        pub const fn new(value: T) -> Self {
122            Self(core::cell::Cell::new(value))
123        }
124    }
125}
126
127pub use _impl::*;