solana_accounts_db/
account_info.rs1use {
6 crate::{
7 accounts_db::AccountsFileId,
8 accounts_file::ALIGN_BOUNDARY_OFFSET,
9 accounts_index::{IsCached, ZeroLamport},
10 },
11 modular_bitfield::prelude::*,
12};
13
14pub type Offset = usize;
16
17pub type StoredSize = u32;
20
21#[derive(Debug, PartialEq, Eq)]
23pub enum StorageLocation {
24 AppendVec(AccountsFileId, Offset),
25 Cached,
26}
27
28impl StorageLocation {
29 pub fn is_offset_equal(&self, other: &StorageLocation) -> bool {
30 match self {
31 StorageLocation::Cached => {
32 matches!(other, StorageLocation::Cached) }
34 StorageLocation::AppendVec(_, offset) => {
35 match other {
36 StorageLocation::Cached => {
37 false }
39 StorageLocation::AppendVec(_, other_offset) => other_offset == offset,
40 }
41 }
42 }
43 }
44 pub fn is_store_id_equal(&self, other: &StorageLocation) -> bool {
45 match self {
46 StorageLocation::Cached => {
47 matches!(other, StorageLocation::Cached) }
49 StorageLocation::AppendVec(store_id, _) => {
50 match other {
51 StorageLocation::Cached => {
52 false }
54 StorageLocation::AppendVec(other_store_id, _) => other_store_id == store_id,
55 }
56 }
57 }
58 }
59}
60
61pub type OffsetReduced = u32;
65
66const CACHED_OFFSET: OffsetReduced = (1 << (OffsetReduced::BITS - 1)) - 1;
74
75#[bitfield(bits = 32)]
76#[repr(C)]
77#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
78pub struct PackedOffsetAndFlags {
79 offset_reduced: B31,
81 is_zero_lamport: bool,
83}
84
85#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
86pub struct AccountInfo {
87 store_id: AccountsFileId,
89
90 account_offset_and_flags: AccountOffsetAndFlags,
91}
92
93#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
94pub struct AccountOffsetAndFlags {
95 packed_offset_and_flags: PackedOffsetAndFlags,
98}
99
100impl ZeroLamport for AccountInfo {
101 fn is_zero_lamport(&self) -> bool {
102 self.account_offset_and_flags
103 .packed_offset_and_flags
104 .is_zero_lamport()
105 }
106}
107
108impl IsCached for AccountInfo {
109 fn is_cached(&self) -> bool {
110 self.account_offset_and_flags
111 .packed_offset_and_flags
112 .offset_reduced()
113 == CACHED_OFFSET
114 }
115}
116
117impl IsCached for StorageLocation {
118 fn is_cached(&self) -> bool {
119 matches!(self, StorageLocation::Cached)
120 }
121}
122
123const CACHE_VIRTUAL_STORAGE_ID: AccountsFileId = AccountsFileId::MAX;
125
126impl AccountInfo {
127 pub fn new(storage_location: StorageLocation, lamports: u64) -> Self {
128 let mut packed_offset_and_flags = PackedOffsetAndFlags::default();
129 let store_id = match storage_location {
130 StorageLocation::AppendVec(store_id, offset) => {
131 let reduced_offset = Self::get_reduced_offset(offset);
132 assert_ne!(
133 CACHED_OFFSET, reduced_offset,
134 "illegal offset for non-cached item"
135 );
136 packed_offset_and_flags.set_offset_reduced(Self::get_reduced_offset(offset));
137 assert_eq!(
138 Self::reduced_offset_to_offset(packed_offset_and_flags.offset_reduced()),
139 offset,
140 "illegal offset"
141 );
142 store_id
143 }
144 StorageLocation::Cached => {
145 packed_offset_and_flags.set_offset_reduced(CACHED_OFFSET);
146 CACHE_VIRTUAL_STORAGE_ID
147 }
148 };
149 packed_offset_and_flags.set_is_zero_lamport(lamports == 0);
150 let account_offset_and_flags = AccountOffsetAndFlags {
151 packed_offset_and_flags,
152 };
153 Self {
154 store_id,
155 account_offset_and_flags,
156 }
157 }
158
159 pub fn get_reduced_offset(offset: usize) -> OffsetReduced {
160 (offset / ALIGN_BOUNDARY_OFFSET) as OffsetReduced
161 }
162
163 pub fn store_id(&self) -> AccountsFileId {
164 assert!(!self.is_cached());
166 self.store_id
167 }
168
169 pub fn offset(&self) -> Offset {
170 Self::reduced_offset_to_offset(
171 self.account_offset_and_flags
172 .packed_offset_and_flags
173 .offset_reduced(),
174 )
175 }
176
177 pub fn reduced_offset_to_offset(reduced_offset: OffsetReduced) -> Offset {
178 (reduced_offset as Offset) * ALIGN_BOUNDARY_OFFSET
179 }
180
181 pub fn storage_location(&self) -> StorageLocation {
182 if self.is_cached() {
183 StorageLocation::Cached
184 } else {
185 StorageLocation::AppendVec(self.store_id, self.offset())
186 }
187 }
188}
189#[cfg(test)]
190mod test {
191 use {super::*, crate::append_vec::MAXIMUM_APPEND_VEC_FILE_SIZE};
192
193 #[test]
194 fn test_limits() {
195 for offset in [
196 (MAXIMUM_APPEND_VEC_FILE_SIZE - 2 * (ALIGN_BOUNDARY_OFFSET as u64)) as Offset,
201 0,
202 ALIGN_BOUNDARY_OFFSET,
203 4 * ALIGN_BOUNDARY_OFFSET,
204 ] {
205 let info = AccountInfo::new(StorageLocation::AppendVec(0, offset), 0);
206 assert!(info.offset() == offset);
207 }
208 }
209
210 #[test]
211 #[should_panic(expected = "illegal offset")]
212 fn test_illegal_offset() {
213 let offset = (MAXIMUM_APPEND_VEC_FILE_SIZE - (ALIGN_BOUNDARY_OFFSET as u64)) as Offset;
214 AccountInfo::new(StorageLocation::AppendVec(0, offset), 0);
215 }
216
217 #[test]
218 #[should_panic(expected = "illegal offset")]
219 fn test_alignment() {
220 let offset = 1; AccountInfo::new(StorageLocation::AppendVec(0, offset), 0);
222 }
223}