2021-12-14 07:31:42 -08:00
|
|
|
//! AccountInfo represents a reference to AccountSharedData in either an AppendVec or the write cache.
|
|
|
|
//! AccountInfo is not persisted anywhere between program runs.
|
|
|
|
//! AccountInfo is purely runtime state.
|
|
|
|
//! Note that AccountInfo is saved to disk buckets during runtime, but disk buckets are recreated at startup.
|
2021-12-13 15:35:07 -08:00
|
|
|
use crate::{
|
2021-12-15 09:49:24 -08:00
|
|
|
accounts_db::{AppendVecId, CACHE_VIRTUAL_OFFSET},
|
2021-12-13 15:35:07 -08:00
|
|
|
accounts_index::{IsCached, ZeroLamport},
|
2021-12-15 13:41:11 -08:00
|
|
|
append_vec::ALIGN_BOUNDARY_OFFSET,
|
2021-12-13 15:35:07 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/// offset within an append vec to account data
|
|
|
|
pub type Offset = usize;
|
|
|
|
|
2021-12-14 11:08:49 -08:00
|
|
|
/// bytes used to store this account in append vec
|
2021-12-14 11:50:19 -08:00
|
|
|
/// Note this max needs to be big enough to handle max data len of 10MB, which is a const
|
|
|
|
pub type StoredSize = u32;
|
2021-12-14 11:08:49 -08:00
|
|
|
|
2021-12-13 15:35:07 -08:00
|
|
|
/// specify where account data is located
|
2021-12-14 08:27:17 -08:00
|
|
|
#[derive(Debug)]
|
2021-12-13 15:35:07 -08:00
|
|
|
pub enum StorageLocation {
|
|
|
|
AppendVec(AppendVecId, Offset),
|
|
|
|
Cached,
|
|
|
|
}
|
2021-12-11 09:47:05 -08:00
|
|
|
|
2021-12-14 08:27:17 -08:00
|
|
|
impl StorageLocation {
|
|
|
|
pub fn is_offset_equal(&self, other: &StorageLocation) -> bool {
|
|
|
|
match self {
|
|
|
|
StorageLocation::Cached => {
|
|
|
|
matches!(other, StorageLocation::Cached) // technically, 2 cached entries match in offset
|
|
|
|
}
|
|
|
|
StorageLocation::AppendVec(_, offset) => {
|
|
|
|
match other {
|
|
|
|
StorageLocation::Cached => {
|
|
|
|
false // 1 cached, 1 not
|
|
|
|
}
|
|
|
|
StorageLocation::AppendVec(_, other_offset) => other_offset == offset,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn is_store_id_equal(&self, other: &StorageLocation) -> bool {
|
|
|
|
match self {
|
|
|
|
StorageLocation::Cached => {
|
|
|
|
matches!(other, StorageLocation::Cached) // 2 cached entries are same store id
|
|
|
|
}
|
|
|
|
StorageLocation::AppendVec(store_id, _) => {
|
|
|
|
match other {
|
|
|
|
StorageLocation::Cached => {
|
|
|
|
false // 1 cached, 1 not
|
|
|
|
}
|
|
|
|
StorageLocation::AppendVec(other_store_id, _) => other_store_id == store_id,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-15 13:41:11 -08:00
|
|
|
/// how large the offset we store in AccountInfo is
|
|
|
|
/// Note this is a smaller datatype than 'Offset'
|
|
|
|
/// AppendVecs store accounts aligned to u64, so offset is always a multiple of 8 (sizeof(u64))
|
|
|
|
pub type OffsetReduced = u32;
|
|
|
|
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
|
2021-12-11 09:47:05 -08:00
|
|
|
pub struct AccountInfo {
|
|
|
|
/// index identifying the append storage
|
2021-12-13 19:35:30 -08:00
|
|
|
store_id: AppendVecId,
|
2021-12-11 09:47:05 -08:00
|
|
|
|
2021-12-15 13:41:11 -08:00
|
|
|
/// offset = 'reduced_offset' / ALIGN_BOUNDARY_OFFSET into the storage
|
|
|
|
/// Note this is a smaller type than 'Offset'
|
|
|
|
reduced_offset: OffsetReduced,
|
2021-12-11 09:47:05 -08:00
|
|
|
|
|
|
|
/// needed to track shrink candidacy in bytes. Used to update the number
|
|
|
|
/// of alive bytes in an AppendVec as newer slots purge outdated entries
|
2021-12-15 09:49:24 -08:00
|
|
|
/// Note that highest bits are used by ALL_FLAGS
|
|
|
|
/// So, 'stored_size' is 'stored_size_mask' with ALL_FLAGS masked out.
|
|
|
|
stored_size_mask: StoredSize,
|
2021-12-11 09:47:05 -08:00
|
|
|
}
|
|
|
|
|
2021-12-15 09:49:24 -08:00
|
|
|
/// These flags can be present in stored_size_mask to indicate additional info about the AccountInfo
|
|
|
|
|
|
|
|
/// presence of this flag in stored_size_mask indicates this account info references an account with zero lamports
|
|
|
|
const IS_ZERO_LAMPORT_FLAG: StoredSize = 1 << (StoredSize::BITS - 1);
|
|
|
|
/// presence of this flag in stored_size_mask indicates this account info references an account stored in the cache
|
|
|
|
const IS_CACHED_STORE_ID_FLAG: StoredSize = 1 << (StoredSize::BITS - 2);
|
|
|
|
const ALL_FLAGS: StoredSize = IS_ZERO_LAMPORT_FLAG | IS_CACHED_STORE_ID_FLAG;
|
2021-12-14 07:31:42 -08:00
|
|
|
|
2021-12-11 09:47:05 -08:00
|
|
|
impl ZeroLamport for AccountInfo {
|
|
|
|
fn is_zero_lamport(&self) -> bool {
|
2021-12-15 09:49:24 -08:00
|
|
|
self.stored_size_mask & IS_ZERO_LAMPORT_FLAG == IS_ZERO_LAMPORT_FLAG
|
2021-12-11 09:47:05 -08:00
|
|
|
}
|
|
|
|
}
|
2021-12-12 12:36:05 -08:00
|
|
|
|
2021-12-13 15:35:07 -08:00
|
|
|
impl IsCached for AccountInfo {
|
|
|
|
fn is_cached(&self) -> bool {
|
2021-12-15 09:49:24 -08:00
|
|
|
self.stored_size_mask & IS_CACHED_STORE_ID_FLAG == IS_CACHED_STORE_ID_FLAG
|
2021-12-13 15:35:07 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 08:27:17 -08:00
|
|
|
impl IsCached for StorageLocation {
|
|
|
|
fn is_cached(&self) -> bool {
|
|
|
|
matches!(self, StorageLocation::Cached)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-16 14:37:08 -08:00
|
|
|
/// We have to have SOME value for store_id when we are cached
|
|
|
|
const CACHE_VIRTUAL_STORAGE_ID: AppendVecId = AppendVecId::MAX;
|
|
|
|
|
2021-12-12 12:36:05 -08:00
|
|
|
impl AccountInfo {
|
2021-12-15 09:49:24 -08:00
|
|
|
pub fn new(storage_location: StorageLocation, stored_size: StoredSize, lamports: u64) -> Self {
|
|
|
|
assert_eq!(stored_size & ALL_FLAGS, 0);
|
|
|
|
let mut stored_size_mask = stored_size;
|
2021-12-15 13:41:11 -08:00
|
|
|
let (store_id, raw_offset) = match storage_location {
|
2021-12-13 15:35:07 -08:00
|
|
|
StorageLocation::AppendVec(store_id, offset) => (store_id, offset),
|
2021-12-15 09:49:24 -08:00
|
|
|
StorageLocation::Cached => {
|
|
|
|
stored_size_mask |= IS_CACHED_STORE_ID_FLAG;
|
|
|
|
(CACHE_VIRTUAL_STORAGE_ID, CACHE_VIRTUAL_OFFSET)
|
|
|
|
}
|
2021-12-13 15:35:07 -08:00
|
|
|
};
|
2021-12-14 07:31:42 -08:00
|
|
|
if lamports == 0 {
|
2021-12-15 09:49:24 -08:00
|
|
|
stored_size_mask |= IS_ZERO_LAMPORT_FLAG;
|
2021-12-14 07:31:42 -08:00
|
|
|
}
|
2021-12-15 13:41:11 -08:00
|
|
|
let reduced_offset: OffsetReduced = (raw_offset / ALIGN_BOUNDARY_OFFSET) as OffsetReduced;
|
|
|
|
let result = Self {
|
2021-12-12 12:36:05 -08:00
|
|
|
store_id,
|
2021-12-15 13:41:11 -08:00
|
|
|
reduced_offset,
|
2021-12-15 09:49:24 -08:00
|
|
|
stored_size_mask,
|
2021-12-15 13:41:11 -08:00
|
|
|
};
|
|
|
|
assert_eq!(result.offset(), raw_offset, "illegal offset");
|
|
|
|
result
|
2021-12-12 12:36:05 -08:00
|
|
|
}
|
2021-12-13 08:10:06 -08:00
|
|
|
|
2021-12-14 11:08:49 -08:00
|
|
|
pub fn store_id(&self) -> AppendVecId {
|
2022-02-15 13:22:36 -08:00
|
|
|
// if the account is in a cached store, the store_id is meaningless
|
|
|
|
assert!(!self.is_cached());
|
2021-12-13 19:35:30 -08:00
|
|
|
self.store_id
|
|
|
|
}
|
|
|
|
|
2021-12-14 11:08:49 -08:00
|
|
|
pub fn offset(&self) -> Offset {
|
2021-12-15 13:41:11 -08:00
|
|
|
(self.reduced_offset as Offset) * ALIGN_BOUNDARY_OFFSET
|
2021-12-13 12:43:26 -08:00
|
|
|
}
|
|
|
|
|
2021-12-14 11:08:49 -08:00
|
|
|
pub fn stored_size(&self) -> StoredSize {
|
2021-12-14 07:31:42 -08:00
|
|
|
// elminate the special bit that indicates the info references an account with zero lamports
|
2021-12-15 09:49:24 -08:00
|
|
|
self.stored_size_mask & !ALL_FLAGS
|
2021-12-13 08:10:06 -08:00
|
|
|
}
|
2021-12-14 08:27:17 -08:00
|
|
|
|
2021-12-16 14:37:08 -08:00
|
|
|
/// true iff store_id and offset match self AND self is not cached
|
|
|
|
/// If self is cached, then store_id and offset are meaningless.
|
|
|
|
pub fn matches_storage_location(&self, store_id: AppendVecId, offset: Offset) -> bool {
|
|
|
|
// order is set for best short circuit
|
|
|
|
self.store_id == store_id && self.offset() == offset && !self.is_cached()
|
|
|
|
}
|
|
|
|
|
2021-12-14 08:27:17 -08:00
|
|
|
pub fn storage_location(&self) -> StorageLocation {
|
|
|
|
if self.is_cached() {
|
|
|
|
StorageLocation::Cached
|
|
|
|
} else {
|
2021-12-15 13:41:11 -08:00
|
|
|
StorageLocation::AppendVec(self.store_id, self.offset())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use {super::*, crate::append_vec::MAXIMUM_APPEND_VEC_FILE_SIZE};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_limits() {
|
|
|
|
for offset in [
|
|
|
|
MAXIMUM_APPEND_VEC_FILE_SIZE as Offset,
|
|
|
|
0,
|
|
|
|
ALIGN_BOUNDARY_OFFSET,
|
|
|
|
4 * ALIGN_BOUNDARY_OFFSET,
|
|
|
|
] {
|
|
|
|
let info = AccountInfo::new(StorageLocation::AppendVec(0, offset), 0, 0);
|
|
|
|
assert!(info.offset() == offset);
|
2021-12-14 08:27:17 -08:00
|
|
|
}
|
|
|
|
}
|
2021-12-15 13:41:11 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_panic(expected = "illegal offset")]
|
|
|
|
fn test_alignment() {
|
|
|
|
let offset = 1; // not aligned
|
|
|
|
AccountInfo::new(StorageLocation::AppendVec(0, offset), 0, 0);
|
|
|
|
}
|
2021-12-16 14:37:08 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_matches_storage_location() {
|
|
|
|
let offset = 0;
|
|
|
|
let id = 0;
|
|
|
|
let info = AccountInfo::new(StorageLocation::AppendVec(id, offset), 0, 0);
|
|
|
|
assert!(info.matches_storage_location(id, offset));
|
|
|
|
|
|
|
|
// wrong offset
|
|
|
|
let offset = ALIGN_BOUNDARY_OFFSET;
|
|
|
|
assert!(!info.matches_storage_location(id, offset));
|
|
|
|
|
|
|
|
// wrong id
|
|
|
|
let offset = 0;
|
|
|
|
let id = 1;
|
|
|
|
assert!(!info.matches_storage_location(id, offset));
|
|
|
|
|
|
|
|
// is cached
|
|
|
|
let id = CACHE_VIRTUAL_STORAGE_ID;
|
|
|
|
let info = AccountInfo::new(StorageLocation::Cached, 0, 0);
|
|
|
|
assert!(!info.matches_storage_location(id, offset));
|
|
|
|
}
|
2021-12-12 12:36:05 -08:00
|
|
|
}
|