magicblock_account/cow/borrowed.rs
1//! Raw layout used by the borrowed zero-copy account view.
2//!
3//! The buffer is 8-byte aligned and contains a header followed by two images.
4//! `AccountHeader::sequence` selects the active image; `translate` copies it to the shadow
5//! image, `reset` repoints the view to the active image, `commit` publishes the shadow image,
6//! and `rollback` undoes that publication by decrementing the sequence counter.
7
8#![allow(unsafe_op_in_unsafe_fn)]
9
10use std::{
11 ops::{Deref, DerefMut},
12 ptr::NonNull,
13 slice,
14 sync::atomic::{AtomicU32, Ordering::*},
15};
16
17use solana_pubkey::Pubkey;
18
19use super::owned::OwnedAccount;
20use super::{ALIGNMENT, AccountCore, STORAGE_UNIT, StorageUnit};
21
22/// Fixed bytes in one image after the shared pubkey prefix: core and data header.
23pub(super) const STATIC_SIZE: usize = size_of::<AccountCore>() + size_of::<DataHeader>();
24/// Storage-unit offset from the header to the first image payload, including the pubkey prefix.
25pub(super) const IMAGE_OFFSET: usize =
26 (size_of::<AccountHeader>() + size_of::<Pubkey>()) / STORAGE_UNIT;
27
28/// Header that prefixes a double-allocation borrowed account buffer.
29#[repr(C, align(8))]
30pub(crate) struct AccountHeader {
31 /// Sequence counter; parity selects the active image.
32 pub(crate) sequence: AtomicU32,
33 /// Image size measured in `AccountHeader` units.
34 pub(crate) space: u32,
35}
36
37impl AccountHeader {
38 /// Creates a header for one image size in storage units.
39 pub(crate) fn new(space: u32) -> Self {
40 // `space` stays in storage units so the active
41 // image can be indexed with one multiply.
42 Self { sequence: 0.into(), space }
43 }
44}
45
46/// Pointer arithmetic relies on these size and alignment invariants.
47const _: () = assert!(size_of::<AccountHeader>() == ALIGNMENT);
48const _: () = assert!(size_of::<AccountHeader>() == STORAGE_UNIT);
49const _: () = assert!((size_of::<Pubkey>() + STORAGE_UNIT) / ALIGNMENT == IMAGE_OFFSET);
50
51/// Borrowed zero-copy account view into an aligned external buffer.
52#[derive(Eq, PartialEq)]
53pub struct BorrowedAccount {
54 /// Header pointer for the borrowed buffer.
55 pub(crate) header: NonNull<AccountHeader>,
56 /// Pointer to the active image's account core.
57 pub(crate) core: NonNull<AccountCore>,
58 /// Borrowed data bytes for the active image.
59 pub(crate) data: DataSlice,
60 /// Sequence used to select this view's image.
61 pub(crate) version: u32,
62}
63
64/// Returns the byte offset for the active or shadow image.
65#[inline]
66fn offset(space: u32, sequence: u32, active: bool) -> usize {
67 // Even sequence => image A is active, odd sequence => image B is active.
68 let even = sequence.is_multiple_of(2);
69 // Flip to the shadow image when `active` does not match the current parity.
70 let step = (active ^ even) as u32;
71 (step * space) as usize + IMAGE_OFFSET
72}
73
74impl BorrowedAccount {
75 /// Returns the sequence value that selects the active image.
76 pub(crate) fn sequence(&self) -> u32 {
77 // SAFETY: borrowed account headers live for the account view.
78 unsafe { self.header.as_ref() }.sequence.load(Acquire)
79 }
80 /// Returns the total borrowed span in `StorageUnit`s.
81 ///
82 /// # Safety
83 ///
84 /// `ptr` must point to a valid borrowed buffer created by
85 /// [`OwnedAccount::serialize`].
86 pub unsafe fn span(ptr: NonNull<StorageUnit>) -> u32 {
87 let space = ptr.cast::<AccountHeader>().as_ref().space;
88 space * 2 + IMAGE_OFFSET as u32
89 }
90
91 /// Reads the account's pubkey stored in the image prefix.
92 ///
93 /// # Safety
94 ///
95 /// `ptr` must point to a valid borrowed buffer created by
96 /// [`OwnedAccount::serialize`].
97 pub unsafe fn pubkey(ptr: NonNull<StorageUnit>) -> Pubkey {
98 *ptr.add(1).cast().as_ref()
99 }
100
101 /// Builds a borrowed account view from an aligned account buffer.
102 ///
103 /// # Safety
104 ///
105 /// `buffer` must be 8-byte aligned and point to a valid borrowed account
106 /// buffer whose first bytes are the account header, followed by two
107 /// image-sized payloads. The active image is selected from the header
108 /// sequence parity.
109 pub unsafe fn init(buffer: NonNull<StorageUnit>) -> Self {
110 let header = buffer.cast::<AccountHeader>();
111 let version = header.as_ref().sequence.load(Acquire);
112 let offset = offset(header.as_ref().space, version, true);
113
114 let core = header.add(offset).cast();
115 let data = DataSlice::init(core.add(1).cast());
116
117 Self { header, core, data, version }
118 }
119
120 /// Copies the active image into the shadow image and switches to it.
121 ///
122 /// # Safety
123 ///
124 /// The borrowed image must still be the one selected by `init`.
125 pub unsafe fn translate(&mut self) {
126 let offset = offset(self.header.as_ref().space, self.version, false);
127
128 // Copy bytes in bulk from active image to the shadow
129 let dst = self.header.add(offset).cast();
130 let src = self.core.cast::<StorageUnit>();
131 if src == dst {
132 return;
133 }
134 let count = self.header.as_ref().space as usize;
135 dst.copy_from_nonoverlapping(src, count);
136 // Switch the pointers to the shadow view
137 self.core = dst.cast();
138 self.data = DataSlice::init(self.core.add(1).cast());
139 }
140
141 /// Publishes the shadow image if it was prepared against the current sequence.
142 pub fn commit(&self) {
143 // SAFETY: the header is part of the borrowed buffer for the lifetime of `self`.
144 let header = unsafe { self.header.as_ref() };
145 let shadow = unsafe {
146 self.header.add(offset(header.space, self.version, false)).cast::<AccountCore>()
147 };
148 if self.core != shadow {
149 return;
150 }
151 let next = self.version.wrapping_add(1);
152 let _ = header.sequence.compare_exchange(self.version, next, Release, Relaxed);
153 }
154
155 /// Repoints this view to the currently active image without copying data.
156 ///
157 /// # Safety
158 ///
159 /// The header must remain live, and `self` must be a view previously produced
160 /// by [`Self::init`] or [`Self::translate`] for that borrowed buffer.
161 pub unsafe fn reset(&mut self) {
162 self.version = self.header.as_ref().sequence.load(Acquire);
163 let offset = offset(self.header.as_ref().space, self.version, true);
164 self.core = self.header.add(offset).cast();
165 self.data = DataSlice::init(self.core.add(1).cast());
166 }
167
168 /// Undoes the latest commit, by adjusting the sequence counter
169 ///
170 /// # Safety
171 ///
172 /// Call this only after `commit` to avoid data corruption;
173 pub unsafe fn rollback(&self) {
174 // SAFETY: the header is part of the borrowed buffer for the lifetime of `self`.
175 unsafe { self.header.as_ref().sequence.fetch_sub(1, Release) };
176 }
177
178 /// Returns the owner pubkey from the active image.
179 pub fn owner(&self) -> Pubkey {
180 // SAFETY: `core` points at a live `AccountCore` inside the borrowed buffer.
181 unsafe { self.core.as_ref() }.owner
182 }
183
184 /// Returns the serialized active image bytes that define account state.
185 ///
186 /// The slice starts at `AccountCore`, includes the `DataHeader`, and stops
187 /// after initialized data. It excludes the shared header, pubkey prefix,
188 /// inactive shadow image, and spare data capacity.
189 pub fn storage(&self) -> &[u8] {
190 let len = STATIC_SIZE + self.data.len();
191 // SAFETY: `core` points at the active image and `len` only covers its
192 // initialized state bytes: core, data header, and initialized data.
193 unsafe { slice::from_raw_parts(self.core.as_ptr().cast(), len) }
194 }
195}
196
197impl From<&BorrowedAccount> for OwnedAccount {
198 fn from(value: &BorrowedAccount) -> Self {
199 Self {
200 // SAFETY: `BorrowedAccount` guarantees `core` points at a live account
201 // header inside the borrowed buffer for the lifetime of the borrow.
202 core: *unsafe { value.core.as_ref() },
203 data: value.data.deref().to_vec().into(),
204 }
205 }
206}
207
208/// Mutable byte slice backed by a borrowed account buffer.
209#[derive(Clone, Eq, PartialEq)]
210pub(crate) struct DataSlice {
211 /// Header carrying length and capacity.
212 header: NonNull<DataHeader>,
213 /// Pointer to the first data byte.
214 ptr: NonNull<u8>,
215}
216
217/// Data header stored immediately before the raw byte slice.
218#[repr(C)]
219pub(crate) struct DataHeader {
220 /// Initialized data length.
221 len: u32,
222 /// Total writable capacity.
223 cap: u32,
224}
225
226impl DataHeader {
227 /// Creates a data header for one image.
228 pub(crate) fn new(len: u32, allocation: u32) -> Self {
229 // `cap` is the writable tail after `AccountCore` and `DataHeader`.
230 let cap = (allocation as usize * STORAGE_UNIT - STATIC_SIZE) as u32;
231 Self { len, cap }
232 }
233}
234
235impl DataSlice {
236 /// Builds a borrowed slice from a data header.
237 ///
238 /// # Safety
239 ///
240 /// `header` must point at a valid `DataHeader` followed by initialized data.
241 unsafe fn init(header: NonNull<DataHeader>) -> Self {
242 let ptr = header.add(1).cast();
243 Self { header, ptr }
244 }
245
246 /// Returns the initialized byte length.
247 pub(crate) fn len(&self) -> usize {
248 // SAFETY: `header` points at the live data header for this borrowed slice.
249 let header = unsafe { self.header.as_ref() };
250 header.len.min(header.cap) as usize
251 }
252
253 /// Returns the total writable capacity.
254 pub(crate) fn capacity(&self) -> usize {
255 // SAFETY: `header` points at the live data header for this borrowed slice.
256 let header = unsafe { self.header.as_ref() };
257 header.cap as usize
258 }
259
260 /// Returns the remaining writable capacity.
261 pub(crate) fn spare(&self) -> usize {
262 self.capacity() - self.len()
263 }
264
265 /// Resizes the initialized range in place.
266 ///
267 /// # Safety
268 ///
269 /// `len` must not exceed the borrowed capacity.
270 pub(crate) unsafe fn resize(&mut self, len: usize, val: u8) {
271 let prev = self.len();
272 debug_assert!(prev <= self.capacity());
273 debug_assert!(len <= self.capacity());
274 let delta = len.saturating_sub(prev);
275 if delta > 0 {
276 self.ptr.as_ptr().add(prev).write_bytes(val, delta);
277 }
278 self.header.as_mut().len = len as u32;
279 }
280
281 /// Appends bytes in place.
282 ///
283 /// # Safety
284 ///
285 /// `data` must fit in the remaining borrowed capacity and not overlap.
286 pub(crate) unsafe fn extend(&mut self, data: &[u8]) {
287 let len = self.len();
288 let dst = self.ptr.as_ptr().add(len);
289 dst.copy_from_nonoverlapping(data.as_ptr(), data.len());
290 self.header.as_mut().len += data.len() as u32;
291 }
292
293 /// Replaces the initialized bytes in place.
294 ///
295 /// # Safety
296 ///
297 /// `data` must fit in the borrowed capacity and not overlap.
298 pub(crate) unsafe fn set(&mut self, data: &[u8]) {
299 self.ptr.as_ptr().copy_from_nonoverlapping(data.as_ptr(), data.len());
300 self.header.as_mut().len = data.len() as u32;
301 }
302}
303
304impl Deref for DataSlice {
305 type Target = [u8];
306
307 fn deref(&self) -> &Self::Target {
308 // SAFETY: `len` bytes from `ptr` are initialized account data owned by
309 // the borrowed buffer described by this `DataSlice`.
310 unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
311 }
312}
313
314impl DerefMut for DataSlice {
315 fn deref_mut(&mut self) -> &mut Self::Target {
316 // SAFETY: the borrowed buffer grants unique mutable access through this borrow.
317 unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len()) }
318 }
319}
320
321// SAFETY: `BorrowedAccount` points into external storage and only exposes
322// shared reads unless the caller holds `&mut self`; moving the view to another
323// thread does not weaken the buffer lifetime and aliasing requirements.
324unsafe impl Send for BorrowedAccount {}