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::*;