solana_account_view/lib.rs
1//! Data structures to represent account information.
2
3#![no_std]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![allow(clippy::arithmetic_side_effects)]
6
7use {
8 core::{
9 marker::PhantomData,
10 mem::{size_of, ManuallyDrop},
11 ops::{Deref, DerefMut},
12 ptr::{write, write_bytes, NonNull},
13 slice::{from_raw_parts, from_raw_parts_mut},
14 },
15 solana_address::Address,
16 solana_program_error::{ProgramError, ProgramResult},
17};
18
19/// Maximum number of bytes a program may add to an account during a
20/// single top-level instruction.
21pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
22
23/// Value to indicate that an account is not borrowed.
24///
25/// This value is the same as `solana_program_entrypoint::NON_DUP_MARKER`.
26pub const NOT_BORROWED: u8 = u8::MAX;
27
28/// Raw account data.
29///
30/// This struct is wrapped by [`AccountView`], which provides safe access
31/// to account information. At runtime, the account's data is serialized
32/// directly after the `Account` struct in memory, with its size specified
33/// by [`RuntimeAccount::data_len`].
34#[repr(C)]
35#[cfg_attr(feature = "copy", derive(Copy))]
36#[derive(Clone, Default)]
37pub struct RuntimeAccount {
38 /// Borrow state for account data.
39 ///
40 /// This reuses the memory reserved for the duplicate flag in the
41 /// account to track data borrows. It represents the numbers of
42 /// borrows available. The value `0` indicates that the account
43 /// data is mutably borrowed, while values between `2` and `255`
44 /// indicate the number of immutable borrows that can still be
45 /// allocated. An account's data can only be mutably borrowed
46 /// when there are no other active borrows, i.e., when this value
47 /// is equal to [`NOT_BORROWED`].
48 pub borrow_state: u8,
49
50 /// Indicates whether the transaction was signed by this account.
51 pub is_signer: u8,
52
53 /// Indicates whether the account is writable.
54 pub is_writable: u8,
55
56 /// Indicates whether this account represents a program.
57 pub executable: u8,
58
59 /// Difference between the original data length and the current
60 /// data length.
61 ///
62 /// This is used to track the original data length of the account
63 /// when the account is resized. The runtime guarantees that this
64 /// value is zero at the start of the instruction.
65 pub resize_delta: i32,
66
67 /// Address of the account.
68 pub address: Address,
69
70 /// Program that owns this account. Modifiable by programs.
71 pub owner: Address,
72
73 /// The lamports in the account. Modifiable by programs.
74 pub lamports: u64,
75
76 /// Length of the data. Modifiable by programs.
77 pub data_len: u64,
78}
79
80/// Wrapper struct for a `RuntimeAccount`.
81///
82/// This struct provides safe access to the data in a `RuntimeAccount`.
83/// It is also used to track borrows of the account data, given that
84/// an account can be "shared" across multiple `AccountView` instances.
85///
86/// # Invariants
87///
88/// - The `raw` pointer must be valid and point to memory containing a
89/// `RuntimeAccount` struct, immediately followed by the account's data
90/// region.
91/// - The length of the account data must exactly match the value stored in
92/// `RuntimeAccount::data_len`.
93///
94/// These conditions must always hold for any `AccountView` created from
95/// a raw pointer.
96#[repr(C)]
97#[cfg_attr(feature = "copy", derive(Copy))]
98#[derive(Clone, PartialEq, Eq, Debug)]
99pub struct AccountView {
100 /// Raw (pointer to) account data.
101 ///
102 /// Note that this is a pointer can be shared across multiple `AccountView`.
103 raw: *mut RuntimeAccount,
104}
105
106impl AccountView {
107 /// Creates a new [`AccountView`] for a given raw account pointer.
108 ///
109 /// # Safety
110 ///
111 /// The caller must ensure that the `raw` pointer is valid and points
112 /// to memory containing a `RuntimeAccount` struct, immediately followed by
113 /// the account's data region.
114 #[inline(always)]
115 pub unsafe fn new_unchecked(raw: *mut RuntimeAccount) -> Self {
116 Self { raw }
117 }
118
119 /// Address of the account.
120 #[inline(always)]
121 pub fn address(&self) -> &Address {
122 // SAFETY: The `raw` pointer is guaranteed to be valid.
123 unsafe { &(*self.raw).address }
124 }
125
126 /// Return a reference to the address of the program that owns this account.
127 ///
128 /// For ownership checks, use the safe `owned_by` method instead.
129 ///
130 /// # Safety
131 ///
132 /// This method is unsafe because it returns a reference to the owner field,
133 /// which can be modified by `assign` and `close` methods. It is undefined
134 /// behavior to use this reference after the account owner has been modified.
135 #[inline(always)]
136 pub unsafe fn owner(&self) -> &Address {
137 // SAFETY: The `raw` pointer is guaranteed to be valid.
138 unsafe { &(*self.raw).owner }
139 }
140
141 /// Indicate whether the transaction was signed by this account.
142 #[inline(always)]
143 pub fn is_signer(&self) -> bool {
144 // SAFETY: The `raw` pointer is guaranteed to be valid.
145 unsafe { (*self.raw).is_signer != 0 }
146 }
147
148 /// Indicate whether the account is writable or not.
149 #[inline(always)]
150 pub fn is_writable(&self) -> bool {
151 // SAFETY: The `raw` pointer is guaranteed to be valid.
152 unsafe { (*self.raw).is_writable != 0 }
153 }
154
155 /// Indicate whether this account represents an executable program
156 /// or not.
157 #[inline(always)]
158 pub fn executable(&self) -> bool {
159 // SAFETY: The `raw` pointer is guaranteed to be valid.
160 unsafe { (*self.raw).executable != 0 }
161 }
162
163 /// Return the size of the account data.
164 #[inline(always)]
165 pub fn data_len(&self) -> usize {
166 // SAFETY: The `raw` pointer is guaranteed to be valid.
167 unsafe { (*self.raw).data_len as usize }
168 }
169
170 /// Return the delta between the original data length and the current
171 /// data length.
172 ///
173 /// This value will be different than zero if the account has been
174 /// resized during the current instruction.
175 #[inline(always)]
176 pub fn resize_delta(&self) -> i32 {
177 // SAFETY: The `raw` pointer is guaranteed to be valid.
178 unsafe { (*self.raw).resize_delta }
179 }
180
181 /// Return the lamports in the account.
182 #[inline(always)]
183 pub fn lamports(&self) -> u64 {
184 // SAFETY: The `raw` pointer is guaranteed to be valid.
185 unsafe { (*self.raw).lamports }
186 }
187
188 /// Set the lamports in the account.
189 #[inline(always)]
190 pub fn set_lamports(&self, lamports: u64) {
191 // SAFETY: The `raw` pointer is guaranteed to be valid.
192 unsafe {
193 (*self.raw).lamports = lamports;
194 }
195 }
196
197 /// Indicates whether the account data is empty or not.
198 ///
199 /// An account is considered empty if the data length is zero.
200 #[inline(always)]
201 pub fn is_data_empty(&self) -> bool {
202 // SAFETY: The `raw` pointer is guaranteed to be valid.
203 self.data_len() == 0
204 }
205
206 /// Checks if the account is owned by the given program.
207 #[inline(always)]
208 pub fn owned_by(&self, program: &Address) -> bool {
209 // SAFETY: The `raw` pointer is guaranteed to be valid.
210 unsafe { self.owner() == program }
211 }
212
213 /// Changes the owner of the account.
214 ///
215 /// # Safety
216 ///
217 /// It is undefined behavior to use this method while there is an active reference
218 /// to the `owner` returned by [`Self::owner`].
219 #[allow(clippy::clone_on_copy)]
220 #[inline(always)]
221 pub unsafe fn assign(&self, new_owner: &Address) {
222 write(&mut (*self.raw).owner, new_owner.clone());
223 }
224
225 /// Return `true` if the account data is borrowed in any form.
226 #[inline(always)]
227 pub fn is_borrowed(&self) -> bool {
228 unsafe { (*self.raw).borrow_state != NOT_BORROWED }
229 }
230
231 /// Return `true` if the account data is mutably borrowed.
232 #[inline(always)]
233 pub fn is_borrowed_mut(&self) -> bool {
234 unsafe { (*self.raw).borrow_state == 0 }
235 }
236
237 /// Returns an immutable reference to the data in the account.
238 ///
239 /// # Safety
240 ///
241 /// This method is unsafe because it does not return a `Ref`, thus leaving the borrow
242 /// flag untouched. Useful when an instruction has verified non-duplicate accounts.
243 #[inline(always)]
244 pub unsafe fn borrow_unchecked(&self) -> &[u8] {
245 from_raw_parts(self.data_ptr(), self.data_len())
246 }
247
248 /// Returns a mutable reference to the data in the account.
249 ///
250 /// # Safety
251 ///
252 /// This method is unsafe because it does not return a `RefMut`, thus leaving the borrow
253 /// flag untouched. Useful when an instruction has verified non-duplicate accounts.
254 #[allow(clippy::mut_from_ref)]
255 #[inline(always)]
256 pub unsafe fn borrow_unchecked_mut(&self) -> &mut [u8] {
257 from_raw_parts_mut(self.data_ptr(), self.data_len())
258 }
259
260 /// Tries to get an immutable reference to the account data, failing if the account
261 /// is already mutably borrowed.
262 pub fn try_borrow(&self) -> Result<Ref<'_, [u8]>, ProgramError> {
263 // check if the account data can be borrowed
264 self.check_borrow()?;
265
266 let borrow_state = self.raw as *mut u8;
267 // Use one immutable borrow for data by subtracting `1` from the data
268 // borrow counter bits; we are guaranteed that there is at least one
269 // immutable borrow available.
270 //
271 // SAFETY: The `borrow_state` is a mutable pointer to the borrow state
272 // of the account, which is guaranteed to be valid.
273 unsafe { *borrow_state -= 1 };
274
275 // return the reference to data
276 Ok(Ref {
277 value: unsafe { NonNull::from(from_raw_parts(self.data_ptr(), self.data_len())) },
278 state: unsafe { NonNull::new_unchecked(borrow_state) },
279 marker: PhantomData,
280 })
281 }
282
283 /// Tries to get a mutable reference to the account data, failing if the account
284 /// is already borrowed in any form.
285 pub fn try_borrow_mut(&self) -> Result<RefMut<'_, [u8]>, ProgramError> {
286 // check if the account data can be mutably borrowed
287 self.check_borrow_mut()?;
288
289 let borrow_state = self.raw as *mut u8;
290 // Set the mutable data borrow bit to `0`; we are guaranteed that account
291 // data is not already borrowed in any form.
292 //
293 // SAFETY: The `borrow_state` is a mutable pointer to the borrow state
294 // of the account, which is guaranteed to be valid.
295 unsafe { *borrow_state = 0 };
296
297 // return the mutable reference to data
298 Ok(RefMut {
299 value: unsafe { NonNull::from(from_raw_parts_mut(self.data_ptr(), self.data_len())) },
300 state: unsafe { NonNull::new_unchecked(borrow_state) },
301 marker: PhantomData,
302 })
303 }
304
305 /// Check if it is possible to get an immutable reference to the account data,
306 /// failing if the account is already mutably borrowed or there are not enough
307 /// immutable borrows available.
308 #[inline(always)]
309 pub fn check_borrow(&self) -> Result<(), ProgramError> {
310 // There must be at least one immutable borrow available.
311 //
312 // SAFETY: The `raw` pointer is guaranteed to be valid.
313 if unsafe { (*self.raw).borrow_state } < 2 {
314 return Err(ProgramError::AccountBorrowFailed);
315 }
316
317 Ok(())
318 }
319
320 /// Checks if it is possible to get a mutable reference to the account data,
321 /// failing if the account is already borrowed in any form.
322 #[inline(always)]
323 pub fn check_borrow_mut(&self) -> Result<(), ProgramError> {
324 // SAFETY: The `raw` pointer is guaranteed to be valid.
325 if unsafe { (*self.raw).borrow_state } != NOT_BORROWED {
326 return Err(ProgramError::AccountBorrowFailed);
327 }
328
329 Ok(())
330 }
331
332 /// Resize (either truncating or zero extending) the account's data.
333 ///
334 /// The account data can be increased by up to [`MAX_PERMITTED_DATA_INCREASE`] bytes
335 /// within an instruction.
336 ///
337 /// # Important
338 ///
339 /// This method makes assumptions about the layout and location of memory
340 /// referenced by `RuntimeAccount` fields. It should only be called for
341 /// instances of `AccountView` that were created by the runtime and received
342 /// in the `process_instruction` entrypoint of a program.
343 #[inline]
344 pub fn resize(&self, new_len: usize) -> Result<(), ProgramError> {
345 // Check whether the account data is already borrowed.
346 self.check_borrow_mut()?;
347
348 // SAFETY: We are checking if the account data is already borrowed, so
349 // we are safe to call.
350 unsafe { self.resize_unchecked(new_len) }
351 }
352
353 /// Resize (either truncating or zero extending) the account's data.
354 ///
355 /// The account data can be increased by up to [`MAX_PERMITTED_DATA_INCREASE`] bytes
356 ///
357 /// # Safety
358 ///
359 /// This method is unsafe because it does not check if the account data is already
360 /// borrowed. The caller must guarantee that there are no active borrows to the account
361 /// data.
362 #[inline(always)]
363 pub unsafe fn resize_unchecked(&self, new_len: usize) -> Result<(), ProgramError> {
364 // Account length is always `< i32::MAX`...
365 let current_len = self.data_len() as i32;
366 // ...so the new length must fit in an `i32`.
367 let new_len = i32::try_from(new_len).map_err(|_| ProgramError::InvalidRealloc)?;
368
369 // Return early if length hasn't changed.
370 if new_len == current_len {
371 return Ok(());
372 }
373
374 let difference = new_len - current_len;
375 let accumulated_resize_delta = self.resize_delta() + difference;
376
377 // Return an error when the length increase from the original serialized data
378 // length is too large and would result in an out of bounds allocation
379 if accumulated_resize_delta > MAX_PERMITTED_DATA_INCREASE as i32 {
380 return Err(ProgramError::InvalidRealloc);
381 }
382
383 unsafe {
384 (*self.raw).data_len = new_len as u64;
385 (*self.raw).resize_delta = accumulated_resize_delta;
386 }
387
388 if difference > 0 {
389 unsafe {
390 write_bytes(
391 self.data_ptr().add(current_len as usize),
392 0,
393 difference as usize,
394 );
395 }
396 }
397
398 Ok(())
399 }
400
401 /// Zero out the the account's data length, lamports and owner fields, effectively
402 /// closing the account.
403 ///
404 /// Note: This does not zero the account data. The account data will be zeroed by
405 /// the runtime at the end of the instruction where the account was closed or at the
406 /// next CPI call.
407 ///
408 /// # Important
409 ///
410 /// The lamports must be moved from the account prior to closing it to prevent
411 /// an unbalanced instruction error. Any existing reference to the account owner
412 /// will be invalidated after calling this method.
413 #[inline]
414 pub fn close(&self) -> ProgramResult {
415 // Make sure the account is not borrowed since we are about to
416 // resize the data to zero.
417 if self.is_borrowed() {
418 return Err(ProgramError::AccountBorrowFailed);
419 }
420
421 // SAFETY: The are no active borrows on the account data or lamports.
422 unsafe {
423 // Update the resize delta since closing an account will set its data length
424 // to zero (account length is always `< i32::MAX`).
425 (*self.raw).resize_delta = self.resize_delta() - self.data_len() as i32;
426
427 self.close_unchecked();
428 }
429
430 Ok(())
431 }
432
433 /// Zero out the the account's data length, lamports and owner fields, effectively
434 /// closing the account.
435 ///
436 /// Note: This does not zero the account data. The account data will be zeroed by
437 /// the runtime at the end of the instruction where the account was closed or at the
438 /// next CPI call.
439 ///
440 /// # Important
441 ///
442 /// The lamports must be moved from the account prior to closing it to prevent
443 /// an unbalanced instruction error.
444 ///
445 /// If [`Self::resize`] is called after closing the account, it might incorrectly
446 /// return an error for going over the limit if the account previously had space
447 /// allocated since this method does not update the [`Self::resize_delta`] value.
448 ///
449 /// # Safety
450 ///
451 /// This method is unsafe because it does not check if the account data is already
452 /// borrowed. It should only be called when the account is not being used.
453 ///
454 /// It also makes assumptions about the layout and location of memory
455 /// referenced by `RuntimeAccount` fields. It should only be called for
456 /// instances of `AccountView` that were created by the runtime and received
457 /// in the `process_instruction` entrypoint of a program.
458 #[inline(always)]
459 pub unsafe fn close_unchecked(&self) {
460 // We take advantage that the 48 bytes before the account data are:
461 // - 32 bytes for the owner
462 // - 8 bytes for the lamports
463 // - 8 bytes for the data_len
464 //
465 // So we can zero out them directly.
466 write_bytes(self.data_ptr().sub(48), 0, 48);
467 }
468
469 /// Returns the raw pointer to the `Account` struct.
470 pub const fn account_ptr(&self) -> *const RuntimeAccount {
471 self.raw
472 }
473
474 /// Returns the memory address of the account data.
475 ///
476 /// # Important
477 ///
478 /// Obtaining the raw pointer itself is safe, but de-referencing it requires
479 /// the caller to uphold Rust's aliasing rules. It is undefined behavior to
480 /// de-reference the pointer or write through it while any safe reference
481 /// (e.g., from any of `borrow` or `borrow_mut` methods) to the same data
482 /// is still alive.
483 #[inline(always)]
484 pub fn data_ptr(&self) -> *mut u8 {
485 // SAFETY: The `raw` pointer is guaranteed to be valid.
486 unsafe { (self.raw as *mut u8).add(size_of::<RuntimeAccount>()) }
487 }
488}
489
490/// Reference to account data with checked borrow rules.
491#[derive(Debug)]
492pub struct Ref<'a, T: ?Sized> {
493 value: NonNull<T>,
494 state: NonNull<u8>,
495 /// The `value` raw pointer is only valid while the `&'a T` lives so we claim
496 /// to hold a reference to it.
497 marker: PhantomData<&'a T>,
498}
499
500impl<'a, T: ?Sized> Ref<'a, T> {
501 /// Maps a reference to a new type.
502 #[inline]
503 pub fn map<U: ?Sized, F>(orig: Ref<'a, T>, f: F) -> Ref<'a, U>
504 where
505 F: FnOnce(&T) -> &U,
506 {
507 // Avoid decrementing the borrow flag on drop.
508 let orig = ManuallyDrop::new(orig);
509 Ref {
510 value: NonNull::from(f(&*orig)),
511 state: orig.state,
512 marker: PhantomData,
513 }
514 }
515
516 /// Tries to makes a new `Ref` for a component of the borrowed data.
517 ///
518 /// On failure, the original guard is returned alongside with the error
519 /// returned by the closure.
520 #[inline]
521 pub fn try_map<U: ?Sized, E>(
522 orig: Ref<'a, T>,
523 f: impl FnOnce(&T) -> Result<&U, E>,
524 ) -> Result<Ref<'a, U>, (Self, E)> {
525 // Avoid decrementing the borrow flag on Drop.
526 let orig = ManuallyDrop::new(orig);
527 match f(&*orig) {
528 Ok(value) => Ok(Ref {
529 value: NonNull::from(value),
530 state: orig.state,
531 marker: PhantomData,
532 }),
533 Err(e) => Err((ManuallyDrop::into_inner(orig), e)),
534 }
535 }
536
537 /// Filters and maps a reference to a new type.
538 ///
539 /// On failure, the original guard is returned.
540 #[inline]
541 pub fn filter_map<U: ?Sized, F>(orig: Ref<'a, T>, f: F) -> Result<Ref<'a, U>, Self>
542 where
543 F: FnOnce(&T) -> Option<&U>,
544 {
545 // Avoid decrementing the borrow flag on drop.
546 let orig = ManuallyDrop::new(orig);
547
548 match f(&*orig) {
549 Some(value) => Ok(Ref {
550 value: NonNull::from(value),
551 state: orig.state,
552 marker: PhantomData,
553 }),
554 None => Err(ManuallyDrop::into_inner(orig)),
555 }
556 }
557}
558
559impl<T: ?Sized> Deref for Ref<'_, T> {
560 type Target = T;
561 fn deref(&self) -> &Self::Target {
562 unsafe { self.value.as_ref() }
563 }
564}
565
566impl<T: ?Sized> Drop for Ref<'_, T> {
567 fn drop(&mut self) {
568 // Increment the available borrow count.
569 unsafe { *self.state.as_mut() += 1 };
570 }
571}
572
573/// Mutable reference to account data with checked borrow rules.
574#[derive(Debug)]
575pub struct RefMut<'a, T: ?Sized> {
576 value: NonNull<T>,
577 state: NonNull<u8>,
578 /// The `value` raw pointer is only valid while the `&'a T` lives so we claim
579 /// to hold a reference to it.
580 marker: PhantomData<&'a mut T>,
581}
582
583impl<'a, T: ?Sized> RefMut<'a, T> {
584 /// Maps a mutable reference to a new type.
585 #[inline]
586 pub fn map<U: ?Sized, F>(orig: RefMut<'a, T>, f: F) -> RefMut<'a, U>
587 where
588 F: FnOnce(&mut T) -> &mut U,
589 {
590 // Avoid decrementing the borrow flag on Drop.
591 let mut orig = ManuallyDrop::new(orig);
592 RefMut {
593 value: NonNull::from(f(&mut *orig)),
594 state: orig.state,
595 marker: PhantomData,
596 }
597 }
598
599 /// Tries to makes a new `RefMut` for a component of the borrowed data.
600 ///
601 /// On failure, the original guard is returned alongside with the error
602 /// returned by the closure.
603 #[inline]
604 pub fn try_map<U: ?Sized, E>(
605 orig: RefMut<'a, T>,
606 f: impl FnOnce(&mut T) -> Result<&mut U, E>,
607 ) -> Result<RefMut<'a, U>, (Self, E)> {
608 // Avoid decrementing the borrow flag on Drop.
609 let mut orig = ManuallyDrop::new(orig);
610 match f(&mut *orig) {
611 Ok(value) => Ok(RefMut {
612 value: NonNull::from(value),
613 state: orig.state,
614 marker: PhantomData,
615 }),
616 Err(e) => Err((ManuallyDrop::into_inner(orig), e)),
617 }
618 }
619
620 /// Filters and maps a mutable reference to a new type.
621 ///
622 /// On failure, the original guard is returned alongside with the error
623 /// returned by the closure.
624 #[inline]
625 pub fn filter_map<U: ?Sized, F>(orig: RefMut<'a, T>, f: F) -> Result<RefMut<'a, U>, Self>
626 where
627 F: FnOnce(&mut T) -> Option<&mut U>,
628 {
629 // Avoid decrementing the mutable borrow flag on Drop.
630 let mut orig = ManuallyDrop::new(orig);
631 match f(&mut *orig) {
632 Some(value) => Ok(RefMut {
633 value: NonNull::from(value),
634 state: orig.state,
635 marker: PhantomData,
636 }),
637 None => Err(ManuallyDrop::into_inner(orig)),
638 }
639 }
640}
641
642impl<T: ?Sized> Deref for RefMut<'_, T> {
643 type Target = T;
644 fn deref(&self) -> &Self::Target {
645 unsafe { self.value.as_ref() }
646 }
647}
648impl<T: ?Sized> DerefMut for RefMut<'_, T> {
649 fn deref_mut(&mut self) -> &mut <Self as core::ops::Deref>::Target {
650 unsafe { self.value.as_mut() }
651 }
652}
653
654impl<T: ?Sized> Drop for RefMut<'_, T> {
655 fn drop(&mut self) {
656 // Reset the borrow state.
657 unsafe { *self.state.as_mut() = NOT_BORROWED };
658 }
659}
660
661#[cfg(test)]
662mod tests {
663 use {
664 super::*,
665 core::mem::{size_of, MaybeUninit},
666 };
667
668 #[test]
669 fn test_ref() {
670 let data: [u8; 4] = [0, 1, 2, 3];
671 let mut state = NOT_BORROWED - 1;
672
673 let ref_data = Ref {
674 value: NonNull::from(&data),
675 // borrow state must be a mutable reference
676 state: NonNull::from(&mut state),
677 marker: PhantomData,
678 };
679
680 let new_ref = Ref::map(ref_data, |data| &data[1]);
681
682 assert_eq!(state, NOT_BORROWED - 1);
683 assert_eq!(*new_ref, 1);
684
685 let Ok(new_ref) = Ref::filter_map(new_ref, |_| Some(&3)) else {
686 unreachable!()
687 };
688
689 assert_eq!(state, NOT_BORROWED - 1);
690 assert_eq!(*new_ref, 3);
691
692 let Ok(new_ref) = Ref::try_map::<_, u8>(new_ref, |_| Ok(&4)) else {
693 unreachable!()
694 };
695
696 assert_eq!(state, NOT_BORROWED - 1);
697 assert_eq!(*new_ref, 4);
698
699 let (new_ref, err) = Ref::try_map::<u8, u8>(new_ref, |_| Err(5)).unwrap_err();
700 assert_eq!(state, NOT_BORROWED - 1);
701 assert_eq!(err, 5);
702 // Unchanged
703 assert_eq!(*new_ref, 4);
704
705 let new_ref = Ref::filter_map(new_ref, |_| Option::<&u8>::None);
706
707 assert_eq!(state, NOT_BORROWED - 1);
708 assert!(new_ref.is_err());
709
710 drop(new_ref);
711
712 assert_eq!(state, NOT_BORROWED);
713 }
714
715 #[test]
716 fn test_ref_mut() {
717 let mut data: [u8; 4] = [0, 1, 2, 3];
718 let mut state = 0;
719
720 let ref_data = RefMut {
721 value: NonNull::from(&mut data),
722 // borrow state must be a mutable reference
723 state: NonNull::from(&mut state),
724 marker: PhantomData,
725 };
726
727 let Ok(mut new_ref) = RefMut::filter_map(ref_data, |data| data.get_mut(0)) else {
728 unreachable!()
729 };
730
731 *new_ref = 4;
732
733 assert_eq!(state, 0);
734 assert_eq!(*new_ref, 4);
735
736 drop(new_ref);
737
738 assert_eq!(data, [4, 1, 2, 3]);
739 assert_eq!(state, NOT_BORROWED);
740 }
741
742 #[test]
743 fn test_borrow() {
744 // 8-bytes aligned account data + 8 bytes of trailing data.
745 let mut data = [0u64; size_of::<RuntimeAccount>() / size_of::<u64>() + 1];
746 data[0] = NOT_BORROWED as u64;
747
748 let account = data.as_mut_ptr() as *mut RuntimeAccount;
749 unsafe { (*account).data_len = 8 };
750
751 let account_view = AccountView { raw: account };
752
753 // Check that we can borrow data and lamports.
754 assert!(account_view.check_borrow().is_ok());
755 assert!(account_view.check_borrow_mut().is_ok());
756
757 // It should be sound to mutate the data through the data pointer
758 // while no other borrows exist.
759 let data_ptr = account_view.data_ptr();
760 unsafe {
761 // There are 8 bytes of trailing data.
762 let data = from_raw_parts_mut(data_ptr, 8);
763 data[0] = 1;
764 }
765
766 // Borrow multiple immutable data references (254 immutable borrows
767 // available).
768 const ACCOUNT_REF: MaybeUninit<Ref<[u8]>> = MaybeUninit::<Ref<[u8]>>::uninit();
769 let mut refs = [ACCOUNT_REF; (NOT_BORROWED as usize) - 1];
770
771 refs.iter_mut().for_each(|r| {
772 let Ok(data_ref) = account_view.try_borrow() else {
773 panic!("Failed to borrow data");
774 };
775 // Sanity check: the data pointer should see the change.
776 assert!(data_ref[0] == 1);
777 r.write(data_ref);
778 });
779
780 // Check that we cannot borrow the data anymore.
781 assert!(account_view.check_borrow().is_err());
782 assert!(account_view.try_borrow().is_err());
783 assert!(account_view.check_borrow_mut().is_err());
784 assert!(account_view.try_borrow_mut().is_err());
785
786 // Drop the immutable borrows.
787 refs.iter_mut().for_each(|r| {
788 let r = unsafe { r.assume_init_read() };
789 drop(r);
790 });
791
792 // We should be able to borrow the data again.
793 assert!(account_view.check_borrow().is_ok());
794 assert!(account_view.check_borrow_mut().is_ok());
795
796 // Borrow mutable data.
797 let ref_mut = account_view.try_borrow_mut().unwrap();
798 // It should be sound to get the data pointer while the data is borrowed
799 // as long as we don't use it.
800 let _data_ptr = account_view.data_ptr();
801
802 // Check that we cannot borrow the data anymore.
803 assert!(account_view.check_borrow().is_err());
804 assert!(account_view.try_borrow().is_err());
805 assert!(account_view.check_borrow_mut().is_err());
806 assert!(account_view.try_borrow_mut().is_err());
807
808 drop(ref_mut);
809
810 // We should be able to borrow the data again.
811 assert!(account_view.check_borrow().is_ok());
812 assert!(account_view.check_borrow_mut().is_ok());
813
814 let borrow_state = unsafe { (*account_view.raw).borrow_state };
815 assert!(borrow_state == NOT_BORROWED);
816 }
817
818 #[test]
819 fn test_resize() {
820 // 8-bytes aligned account data.
821 let mut data = [0u64; 100 * size_of::<u64>()];
822
823 // Set the borrow state.
824 data[0] = NOT_BORROWED as u64;
825 // Set the initial data length to 100.
826 // - index `10` is equal to offset `10 * size_of::<u64>() = 80` bytes.
827 data[10] = 100;
828
829 let account = AccountView {
830 raw: data.as_mut_ptr() as *const _ as *mut RuntimeAccount,
831 };
832
833 assert_eq!(account.data_len(), 100);
834 assert_eq!(account.resize_delta(), 0);
835
836 // We should be able to get the data pointer whenever as long as we don't use it while the data is borrowed
837 let data_ptr_before = account.data_ptr();
838
839 // increase the size.
840
841 account.resize(200).unwrap();
842
843 let data_ptr_after = account.data_ptr();
844 // The data pointer should point to the same address regardless of the reallocation
845 assert_eq!(data_ptr_before, data_ptr_after);
846
847 assert_eq!(account.data_len(), 200);
848 assert_eq!(account.resize_delta(), 100);
849
850 // decrease the size.
851
852 account.resize(0).unwrap();
853
854 assert_eq!(account.data_len(), 0);
855 assert_eq!(account.resize_delta(), -100);
856
857 // Invalid reallocation.
858
859 let invalid_realloc = account.resize(10_000_000_001);
860 assert!(invalid_realloc.is_err());
861
862 // Reset to its original size.
863
864 account.resize(100).unwrap();
865
866 assert_eq!(account.data_len(), 100);
867 assert_eq!(account.resize_delta(), 0);
868
869 // Consecutive resizes.
870
871 account.resize(200).unwrap();
872 account.resize(50).unwrap();
873 account.resize(500).unwrap();
874
875 assert_eq!(account.data_len(), 500);
876 assert_eq!(account.resize_delta(), 400);
877
878 let data = account.try_borrow().unwrap();
879 assert_eq!(data.len(), 500);
880 }
881}