solana_accounts_db/tiered_storage/
owners.rs1use {
2 crate::tiered_storage::{
3 file::TieredWritableFile, footer::TieredStorageFooter, mmap_utils::get_pod,
4 TieredStorageResult,
5 },
6 indexmap::set::IndexSet,
7 memmap2::Mmap,
8 solana_pubkey::Pubkey,
9};
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
17pub struct OwnerOffset(pub u32);
18
19#[repr(u16)]
23#[derive(
24 Clone,
25 Copy,
26 Debug,
27 Default,
28 Eq,
29 Hash,
30 PartialEq,
31 num_enum::IntoPrimitive,
32 num_enum::TryFromPrimitive,
33)]
34pub enum OwnersBlockFormat {
35 #[default]
39 AddressesOnly = 0,
40}
41
42impl OwnersBlockFormat {
43 pub fn write_owners_block(
45 &self,
46 file: &mut TieredWritableFile,
47 owners_table: &OwnersTable,
48 ) -> TieredStorageResult<usize> {
49 match self {
50 Self::AddressesOnly => {
51 let mut bytes_written = 0;
52 for address in &owners_table.owners_set {
53 bytes_written += file.write_pod(address)?;
54 }
55
56 Ok(bytes_written)
57 }
58 }
59 }
60
61 pub fn get_owner_address<'a>(
64 &self,
65 mmap: &'a Mmap,
66 footer: &TieredStorageFooter,
67 owner_offset: OwnerOffset,
68 ) -> TieredStorageResult<&'a Pubkey> {
69 match self {
70 Self::AddressesOnly => {
71 let offset = footer.owners_block_offset as usize
72 + (std::mem::size_of::<Pubkey>() * owner_offset.0 as usize);
73 let (pubkey, _) = get_pod::<Pubkey>(mmap, offset)?;
74
75 Ok(pubkey)
76 }
77 }
78 }
79}
80
81#[derive(Debug, Default)]
84pub struct OwnersTable {
85 owners_set: IndexSet<Pubkey>,
86}
87
88impl OwnersTable {
92 pub fn insert(&mut self, pubkey: &Pubkey) -> OwnerOffset {
96 let (offset, _existed) = self.owners_set.insert_full(*pubkey);
97
98 OwnerOffset(offset as u32)
99 }
100
101 pub fn len(&self) -> usize {
103 self.owners_set.len()
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.len() == 0
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use {
115 super::*, crate::tiered_storage::file::TieredWritableFile, memmap2::MmapOptions,
116 std::fs::OpenOptions, tempfile::TempDir,
117 };
118
119 #[test]
120 fn test_owners_block() {
121 let temp_dir = TempDir::new().unwrap();
123 let path = temp_dir.path().join("test_owners_block");
124 const NUM_OWNERS: u32 = 10;
125
126 let addresses: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
127 .take(NUM_OWNERS as usize)
128 .collect();
129
130 let footer = TieredStorageFooter {
131 owners_block_offset: 0,
134 ..TieredStorageFooter::default()
135 };
136
137 {
138 let mut file = TieredWritableFile::new(&path).unwrap();
139
140 let mut owners_table = OwnersTable::default();
141 addresses.iter().for_each(|owner_address| {
142 owners_table.insert(owner_address);
143 });
144 footer
145 .owners_block_format
146 .write_owners_block(&mut file, &owners_table)
147 .unwrap();
148
149 footer.write_footer_block(&mut file).unwrap();
152 }
153
154 let file = OpenOptions::new().read(true).open(path).unwrap();
155 let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
156
157 for (i, address) in addresses.iter().enumerate() {
158 assert_eq!(
159 footer
160 .owners_block_format
161 .get_owner_address(&mmap, &footer, OwnerOffset(i as u32))
162 .unwrap(),
163 address
164 );
165 }
166 }
167
168 #[test]
169 fn test_owners_table() {
170 let mut owners_table = OwnersTable::default();
171 const NUM_OWNERS: usize = 99;
172
173 let addresses: Vec<_> = std::iter::repeat_with(Pubkey::new_unique)
174 .take(NUM_OWNERS)
175 .collect();
176
177 for (i, address) in addresses.iter().enumerate() {
180 assert_eq!(owners_table.insert(address), OwnerOffset(i as u32));
181 }
182
183 let cloned_addresses = addresses.clone();
184
185 for (i, address) in cloned_addresses.iter().enumerate() {
187 assert_eq!(owners_table.insert(address), OwnerOffset(i as u32));
188 }
189
190 assert_eq!(owners_table.owners_set.len(), addresses.len());
193 }
194}