use {
crate::{accounts_db::FoundStoredAccount, append_vec::AppendVec},
solana_sdk::clock::Slot,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StorageSelector {
Primary,
Overflow,
}
pub struct AccountsToStore<'a> {
accounts: &'a [&'a FoundStoredAccount<'a>],
index_first_item_overflow: usize,
pub slot: Slot,
}
impl<'a> AccountsToStore<'a> {
pub fn new(
mut available_bytes: u64,
accounts: &'a [&'a FoundStoredAccount<'a>],
alive_total_bytes: usize,
slot: Slot,
) -> Self {
let num_accounts = accounts.len();
let mut index_first_item_overflow = num_accounts; if alive_total_bytes > available_bytes as usize {
for (i, account) in accounts.iter().enumerate() {
let account_size = account.account.stored_size as u64;
if available_bytes >= account_size {
available_bytes = available_bytes.saturating_sub(account_size);
} else if index_first_item_overflow == num_accounts {
index_first_item_overflow = i;
break;
}
}
}
Self {
accounts,
index_first_item_overflow,
slot,
}
}
pub fn has_overflow(&self) -> bool {
self.index_first_item_overflow < self.accounts.len()
}
pub fn get(&self, storage: StorageSelector) -> &[&'a FoundStoredAccount<'a>] {
let range = match storage {
StorageSelector::Primary => 0..self.index_first_item_overflow,
StorageSelector::Overflow => self.index_first_item_overflow..self.accounts.len(),
};
&self.accounts[range]
}
}
pub fn get_ancient_append_vec_capacity() -> u64 {
use crate::append_vec::MAXIMUM_APPEND_VEC_FILE_SIZE;
MAXIMUM_APPEND_VEC_FILE_SIZE / 10 - 2048
}
pub fn is_ancient(storage: &AppendVec) -> bool {
storage.capacity() >= get_ancient_append_vec_capacity()
}
#[cfg(test)]
pub mod tests {
use {
super::*,
crate::{
accounts_db::{get_temp_accounts_paths, AppendVecId},
append_vec::{AccountMeta, StoredAccountMeta, StoredMeta},
},
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
hash::Hash,
pubkey::Pubkey,
},
};
#[test]
fn test_accounts_to_store_simple() {
let map = vec![];
let slot = 1;
let accounts_to_store = AccountsToStore::new(0, &map, 0, slot);
for selector in [StorageSelector::Primary, StorageSelector::Overflow] {
let accounts = accounts_to_store.get(selector);
assert!(accounts.is_empty());
}
assert!(!accounts_to_store.has_overflow());
}
#[test]
fn test_accounts_to_store_more() {
let pubkey = Pubkey::new(&[1; 32]);
let store_id = AppendVecId::default();
let account_size = 3;
let account = AccountSharedData::default();
let account_meta = AccountMeta {
lamports: 1,
owner: Pubkey::new(&[2; 32]),
executable: false,
rent_epoch: 0,
};
let offset = 3;
let hash = Hash::new(&[2; 32]);
let stored_meta = StoredMeta {
write_version_obsolete: 0,
pubkey,
data_len: 43,
};
let account = StoredAccountMeta {
meta: &stored_meta,
account_meta: &account_meta,
data: account.data(),
offset,
stored_size: account_size,
hash: &hash,
};
let found = FoundStoredAccount { account, store_id };
let map = vec![&found];
for (selector, available_bytes) in [
(StorageSelector::Primary, account_size),
(StorageSelector::Overflow, account_size - 1),
] {
let slot = 1;
let alive_total_bytes = account_size;
let accounts_to_store =
AccountsToStore::new(available_bytes as u64, &map, alive_total_bytes, slot);
let accounts = accounts_to_store.get(selector);
assert_eq!(
accounts.iter().map(|b| &b.account).collect::<Vec<_>>(),
map.iter().map(|b| &b.account).collect::<Vec<_>>(),
"mismatch"
);
let accounts = accounts_to_store.get(get_opposite(&selector));
assert_eq!(
selector == StorageSelector::Overflow,
accounts_to_store.has_overflow()
);
assert!(accounts.is_empty());
}
}
fn get_opposite(selector: &StorageSelector) -> StorageSelector {
match selector {
StorageSelector::Overflow => StorageSelector::Primary,
StorageSelector::Primary => StorageSelector::Overflow,
}
}
#[test]
fn test_get_ancient_append_vec_capacity() {
assert_eq!(
get_ancient_append_vec_capacity(),
crate::append_vec::MAXIMUM_APPEND_VEC_FILE_SIZE / 10 - 2048
);
}
#[test]
fn test_is_ancient() {
for (size, expected_ancient) in [
(get_ancient_append_vec_capacity() + 1, true),
(get_ancient_append_vec_capacity(), true),
(get_ancient_append_vec_capacity() - 1, false),
] {
let tf = crate::append_vec::test_utils::get_append_vec_path("test_is_ancient");
let (_temp_dirs, _paths) = get_temp_accounts_paths(1).unwrap();
let av = AppendVec::new(&tf.path, true, size as usize);
assert_eq!(expected_ancient, is_ancient(&av));
}
}
}