From affcb5ec43e971531146c01b42c1f497efa2da22 Mon Sep 17 00:00:00 2001 From: Rob Walker Date: Sat, 7 Sep 2019 10:33:06 -0700 Subject: [PATCH] remove hashmap from stake_history (#5834) --- sdk/src/sysvar/slot_hashes.rs | 31 +++++++++---------- sdk/src/sysvar/stake_history.rs | 53 +++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/sdk/src/sysvar/slot_hashes.rs b/sdk/src/sysvar/slot_hashes.rs index e6d50ee79b..5e7b7a24a5 100644 --- a/sdk/src/sysvar/slot_hashes.rs +++ b/sdk/src/sysvar/slot_hashes.rs @@ -20,10 +20,7 @@ crate::solana_name_id!(ID, "SysvarS1otHashes111111111111111111111111111"); pub const MAX_SLOT_HASHES: usize = 512; // 512 slots to get your vote in #[derive(Serialize, Deserialize, PartialEq, Debug)] -pub struct SlotHashes { - // non-pub to keep control of size - inner: Vec<(Slot, Hash)>, -} +pub struct SlotHashes(Vec<(Slot, Hash)>); impl SlotHashes { pub fn from(account: &Account) -> Option { @@ -34,26 +31,30 @@ impl SlotHashes { } pub fn size_of() -> usize { - serialized_size(&SlotHashes { - inner: vec![(0, Hash::default()); MAX_SLOT_HASHES], - }) - .unwrap() as usize + serialized_size(&SlotHashes(vec![(0, Hash::default()); MAX_SLOT_HASHES])).unwrap() as usize } pub fn add(&mut self, slot: Slot, hash: Hash) { - self.inner.insert(0, (slot, hash)); - self.inner.truncate(MAX_SLOT_HASHES); + match self.binary_search_by(|probe| slot.cmp(&probe.0)) { + Ok(index) => (self.0)[index] = (slot, hash), + Err(index) => (self.0).insert(index, (slot, hash)), + } + (self.0).truncate(MAX_SLOT_HASHES); + } + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn get(&self, slot: &Slot) -> Option<&Hash> { + self.binary_search_by(|probe| slot.cmp(&probe.0)) + .ok() + .map(|index| &self[index].1) } pub fn new(slot_hashes: &[(Slot, Hash)]) -> Self { - Self { - inner: slot_hashes.to_vec(), - } + Self(slot_hashes.to_vec()) } } impl Deref for SlotHashes { type Target = Vec<(u64, Hash)>; fn deref(&self) -> &Self::Target { - &self.inner + &self.0 } } @@ -83,7 +84,7 @@ mod tests { let account = create_account(lamports, &[]); assert_eq!(account.data.len(), SlotHashes::size_of()); let slot_hashes = SlotHashes::from(&account); - assert_eq!(slot_hashes, Some(SlotHashes { inner: vec![] })); + assert_eq!(slot_hashes, Some(SlotHashes(vec![]))); let mut slot_hashes = slot_hashes.unwrap(); for i in 0..MAX_SLOT_HASHES + 1 { slot_hashes.add( diff --git a/sdk/src/sysvar/stake_history.rs b/sdk/src/sysvar/stake_history.rs index 505992c78a..1949502b99 100644 --- a/sdk/src/sysvar/stake_history.rs +++ b/sdk/src/sysvar/stake_history.rs @@ -2,13 +2,10 @@ //! //! this account carries history about stake activations and de-activations //! -use crate::account::Account; -use crate::sysvar; -use bincode::serialized_size; -use std::collections::HashMap; -use std::ops::Deref; - pub use crate::clock::Epoch; +use crate::{account::Account, sysvar}; +use bincode::serialized_size; +use std::ops::Deref; const ID: [u8; 32] = [ 6, 167, 213, 23, 25, 53, 132, 208, 254, 237, 155, 179, 67, 29, 19, 32, 107, 229, 68, 40, 27, @@ -27,9 +24,7 @@ pub struct StakeHistoryEntry { } #[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)] -pub struct StakeHistory { - inner: HashMap, -} +pub struct StakeHistory(Vec<(Epoch, StakeHistoryEntry)>); impl StakeHistory { pub fn from(account: &Account) -> Option { @@ -40,27 +35,33 @@ impl StakeHistory { } pub fn size_of() -> usize { - serialized_size( - &(0..MAX_STAKE_HISTORY) - .map(|i| (i as u64, StakeHistoryEntry::default())) - .collect::>(), - ) + serialized_size(&StakeHistory(vec![ + (0, StakeHistoryEntry::default()); + MAX_STAKE_HISTORY + ])) .unwrap() as usize } - pub fn add(&mut self, epoch: Epoch, entry: StakeHistoryEntry) { - self.inner.insert(epoch, entry); - if self.len() > MAX_STAKE_HISTORY { - let oldest = *self.inner.keys().min().unwrap(); - self.inner.remove(&oldest); + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn get(&self, epoch: &Epoch) -> Option<&StakeHistoryEntry> { + self.binary_search_by(|probe| epoch.cmp(&probe.0)) + .ok() + .map(|index| &self[index].1) + } + + pub fn add(&mut self, epoch: Epoch, entry: StakeHistoryEntry) { + match self.binary_search_by(|probe| epoch.cmp(&probe.0)) { + Ok(index) => (self.0)[index] = (epoch, entry), + Err(index) => (self.0).insert(index, (epoch, entry)), } + (self.0).truncate(MAX_STAKE_HISTORY); } } impl Deref for StakeHistory { - type Target = HashMap; + type Target = Vec<(Epoch, StakeHistoryEntry)>; fn deref(&self) -> &Self::Target { - &self.inner + &self.0 } } @@ -103,7 +104,15 @@ mod tests { ); } assert_eq!(stake_history.len(), MAX_STAKE_HISTORY); - assert_eq!(*stake_history.keys().min().unwrap(), 1); + assert_eq!(stake_history.iter().map(|entry| entry.0).min().unwrap(), 1); + assert_eq!(stake_history.get(&0), None); + assert_eq!( + stake_history.get(&1), + Some(&StakeHistoryEntry { + activating: 1, + ..StakeHistoryEntry::default() + }) + ); // verify the account can hold a full instance assert_eq!( StakeHistory::from(&create_account(lamports, &stake_history)),