Skip to main content

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 {}