hopper_native/borrow.rs
1//! Deterministic borrow guards for account data.
2//!
3//! `Ref` and `RefMut` provide RAII borrow tracking on the `borrow_state`
4//! field of `RuntimeAccount`. When dropped, they restore the borrow
5//! state, preventing use-after-free and double-mutable-borrow bugs.
6//!
7//! These replace `core::cell::RefCell` without requiring alloc.
8
9use crate::NOT_BORROWED;
10
11/// Shared (immutable) borrow guard for account data.
12///
13/// On drop, decrements the borrow count in `RuntimeAccount.borrow_state`.
14pub struct Ref<'a, T: ?Sized> {
15 value: &'a T,
16 state: *mut u8,
17}
18
19impl<'a, T: ?Sized> Ref<'a, T> {
20 /// Create a new shared borrow guard.
21 ///
22 /// The caller must have already incremented `*state` to reflect
23 /// the new shared borrow.
24 #[inline(always)]
25 pub(crate) fn new(value: &'a T, state: *mut u8) -> Self {
26 Self { value, state }
27 }
28
29 /// Create a shared borrow guard from raw parts.
30 ///
31 /// # Safety
32 ///
33 /// The caller must ensure:
34 /// - The borrow state at `state` was already incremented
35 /// - `value` is valid for lifetime `'a`
36 /// - `state` points to a valid `RuntimeAccount.borrow_state`
37 #[inline(always)]
38 pub unsafe fn from_raw_parts(value: &'a T, state: *mut u8) -> Self {
39 Self { value, state }
40 }
41
42 /// Decompose into raw parts without running the destructor.
43 ///
44 /// The caller takes responsibility for eventually releasing the
45 /// borrow (decrementing `*state`).
46 #[inline(always)]
47 pub fn into_raw_parts(self) -> (&'a T, *mut u8) {
48 let value = self.value;
49 let state = self.state;
50 core::mem::forget(self);
51 (value, state)
52 }
53}
54
55impl<T: ?Sized> core::ops::Deref for Ref<'_, T> {
56 type Target = T;
57
58 #[inline(always)]
59 fn deref(&self) -> &T {
60 self.value
61 }
62}
63
64impl<T: ?Sized> Drop for Ref<'_, T> {
65 fn drop(&mut self) {
66 // SAFETY: state points to RuntimeAccount.borrow_state in the
67 // BPF input buffer. We decrement the shared borrow count,
68 // restoring NOT_BORROWED when the last shared borrow is released.
69 unsafe {
70 let current = *self.state;
71 if current == 1 {
72 *self.state = NOT_BORROWED;
73 } else {
74 *self.state = current - 1;
75 }
76 }
77 }
78}
79
80/// Exclusive (mutable) borrow guard for account data.
81///
82/// On drop, restores `RuntimeAccount.borrow_state` to `NOT_BORROWED`.
83pub struct RefMut<'a, T: ?Sized> {
84 value: &'a mut T,
85 state: *mut u8,
86}
87
88impl<'a, T: ?Sized> RefMut<'a, T> {
89 /// Create a new exclusive borrow guard.
90 ///
91 /// The caller must have already set `*state = 0` to indicate
92 /// exclusive borrow.
93 #[inline(always)]
94 pub(crate) fn new(value: &'a mut T, state: *mut u8) -> Self {
95 Self { value, state }
96 }
97
98 /// Create an exclusive borrow guard from raw parts.
99 ///
100 /// # Safety
101 ///
102 /// The caller must ensure:
103 /// - The borrow state at `state` was set to 0 (exclusive)
104 /// - `value` is valid and unique for lifetime `'a`
105 /// - `state` points to a valid `RuntimeAccount.borrow_state`
106 #[inline(always)]
107 pub unsafe fn from_raw_parts(value: &'a mut T, state: *mut u8) -> Self {
108 Self { value, state }
109 }
110
111 /// Decompose into raw parts without running the destructor.
112 ///
113 /// The caller takes responsibility for eventually releasing the
114 /// borrow (restoring `*state` to `NOT_BORROWED`).
115 #[inline(always)]
116 pub fn into_raw_parts(self) -> (&'a mut T, *mut u8) {
117 let manual = core::mem::ManuallyDrop::new(self);
118 let value = unsafe { core::ptr::read(&manual.value) };
119 let state = manual.state;
120 (value, state)
121 }
122}
123
124impl<T: ?Sized> core::ops::Deref for RefMut<'_, T> {
125 type Target = T;
126
127 #[inline(always)]
128 fn deref(&self) -> &T {
129 self.value
130 }
131}
132
133impl<T: ?Sized> core::ops::DerefMut for RefMut<'_, T> {
134 #[inline(always)]
135 fn deref_mut(&mut self) -> &mut T {
136 self.value
137 }
138}
139
140impl<T: ?Sized> Drop for RefMut<'_, T> {
141 fn drop(&mut self) {
142 // SAFETY: state points to RuntimeAccount.borrow_state.
143 // Restore to NOT_BORROWED when the exclusive borrow is released.
144 unsafe {
145 *self.state = NOT_BORROWED;
146 }
147 }
148}