solana_accounts_db/tiered_storage/
byte_block.rs1use {
5 crate::tiered_storage::{footer::AccountBlockFormat, meta::AccountMetaOptionalFields},
6 std::{
7 io::{Cursor, Read, Result as IoResult, Write},
8 mem, ptr,
9 },
10};
11
12#[derive(Debug)]
14pub enum ByteBlockEncoder {
15 Raw(Cursor<Vec<u8>>),
16 Lz4(lz4::Encoder<Vec<u8>>),
17}
18
19#[derive(Debug)]
27pub struct ByteBlockWriter {
28 encoder: ByteBlockEncoder,
30 len: usize,
32}
33
34impl ByteBlockWriter {
35 pub fn new(encoding: AccountBlockFormat) -> Self {
37 Self {
38 encoder: match encoding {
39 AccountBlockFormat::AlignedRaw => ByteBlockEncoder::Raw(Cursor::new(Vec::new())),
40 AccountBlockFormat::Lz4 => ByteBlockEncoder::Lz4(
41 lz4::EncoderBuilder::new()
42 .level(0)
43 .build(Vec::new())
44 .unwrap(),
45 ),
46 },
47 len: 0,
48 }
49 }
50
51 pub fn raw_len(&self) -> usize {
53 self.len
54 }
55
56 pub fn write_pod<T: bytemuck::NoUninit>(&mut self, value: &T) -> IoResult<usize> {
60 unsafe { self.write_type(value) }
62 }
63
64 pub unsafe fn write_type<T>(&mut self, value: &T) -> IoResult<usize> {
76 let size = mem::size_of::<T>();
77 let ptr = ptr::from_ref(value).cast();
78 let slice = unsafe { std::slice::from_raw_parts(ptr, size) };
82 self.write(slice)?;
83 Ok(size)
84 }
85
86 pub fn write_optional_fields(
91 &mut self,
92 opt_fields: &AccountMetaOptionalFields,
93 ) -> IoResult<usize> {
94 let mut size = 0;
95 if let Some(rent_epoch) = opt_fields.rent_epoch {
96 size += self.write_pod(&rent_epoch)?;
97 }
98
99 debug_assert_eq!(size, opt_fields.size());
100
101 Ok(size)
102 }
103
104 pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
107 match &mut self.encoder {
108 ByteBlockEncoder::Raw(cursor) => cursor.write_all(buf)?,
109 ByteBlockEncoder::Lz4(lz4_encoder) => lz4_encoder.write_all(buf)?,
110 };
111 self.len += buf.len();
112 Ok(())
113 }
114
115 pub fn finish(self) -> IoResult<Vec<u8>> {
118 match self.encoder {
119 ByteBlockEncoder::Raw(cursor) => Ok(cursor.into_inner()),
120 ByteBlockEncoder::Lz4(lz4_encoder) => {
121 let (compressed_block, result) = lz4_encoder.finish();
122 result?;
123 Ok(compressed_block)
124 }
125 }
126 }
127}
128
129pub struct ByteBlockReader;
131
132pub fn read_pod<T: bytemuck::AnyBitPattern>(byte_block: &[u8], offset: usize) -> Option<&T> {
138 unsafe { read_type(byte_block, offset) }
140}
141
142pub unsafe fn read_type<T>(byte_block: &[u8], offset: usize) -> Option<&T> {
157 let (next, overflow) = offset.overflowing_add(std::mem::size_of::<T>());
158 if overflow || next > byte_block.len() {
159 return None;
160 }
161 let ptr = byte_block[offset..].as_ptr().cast();
162 debug_assert!(ptr as usize % std::mem::align_of::<T>() == 0);
163 Some(unsafe { &*ptr })
167}
168
169impl ByteBlockReader {
170 pub fn decode(encoding: AccountBlockFormat, input: &[u8]) -> IoResult<Vec<u8>> {
177 match encoding {
178 AccountBlockFormat::Lz4 => {
179 let mut decoder = lz4::Decoder::new(input).unwrap();
180 let mut output = vec![];
181 decoder.read_to_end(&mut output)?;
182 Ok(output)
183 }
184 AccountBlockFormat::AlignedRaw => panic!("the input buffer is already decoded"),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use {super::*, solana_sdk::stake_history::Epoch};
192
193 fn read_type_unaligned<T>(buffer: &[u8], offset: usize) -> (T, usize) {
194 let size = std::mem::size_of::<T>();
195 let (next, overflow) = offset.overflowing_add(size);
196 assert!(!overflow && next <= buffer.len());
197 let data = &buffer[offset..next];
198 let ptr = data.as_ptr().cast();
199
200 (unsafe { std::ptr::read_unaligned(ptr) }, next)
201 }
202
203 fn write_single(format: AccountBlockFormat) {
204 let mut writer = ByteBlockWriter::new(format);
205 let value: u32 = 42;
206
207 writer.write_pod(&value).unwrap();
208 assert_eq!(writer.raw_len(), mem::size_of::<u32>());
209
210 let buffer = writer.finish().unwrap();
211
212 let decoded_buffer = if format == AccountBlockFormat::AlignedRaw {
213 buffer
214 } else {
215 ByteBlockReader::decode(format, &buffer).unwrap()
216 };
217
218 assert_eq!(decoded_buffer.len(), mem::size_of::<u32>());
219
220 let (value_from_buffer, next) = read_type_unaligned::<u32>(&decoded_buffer, 0);
221 assert_eq!(value, value_from_buffer);
222
223 if format != AccountBlockFormat::AlignedRaw {
224 assert_eq!(next, mem::size_of::<u32>());
225 }
226 }
227
228 #[test]
229 fn test_write_single_raw_format() {
230 write_single(AccountBlockFormat::AlignedRaw);
231 }
232
233 #[test]
234 fn test_write_single_encoded_format() {
235 write_single(AccountBlockFormat::Lz4);
236 }
237
238 #[derive(Debug, PartialEq)]
239 struct TestMetaStruct {
240 lamports: u64,
241 owner_index: u32,
242 data_len: usize,
243 }
244
245 fn write_multiple(format: AccountBlockFormat) {
246 let mut writer = ByteBlockWriter::new(format);
247 let test_metas: Vec<TestMetaStruct> = vec![
248 TestMetaStruct {
249 lamports: 10,
250 owner_index: 0,
251 data_len: 100,
252 },
253 TestMetaStruct {
254 lamports: 20,
255 owner_index: 1,
256 data_len: 200,
257 },
258 TestMetaStruct {
259 lamports: 30,
260 owner_index: 2,
261 data_len: 300,
262 },
263 ];
264 let test_data1 = [11u8; 100];
265 let test_data2 = [22u8; 200];
266 let test_data3 = [33u8; 300];
267
268 unsafe {
270 writer.write_type(&test_metas[0]).unwrap();
271 writer.write_type(&test_data1).unwrap();
272 writer.write_type(&test_metas[1]).unwrap();
273 writer.write_type(&test_data2).unwrap();
274 writer.write_type(&test_metas[2]).unwrap();
275 writer.write_type(&test_data3).unwrap();
276 }
277 assert_eq!(
278 writer.raw_len(),
279 mem::size_of::<TestMetaStruct>() * 3
280 + mem::size_of_val(&test_data1)
281 + mem::size_of_val(&test_data2)
282 + mem::size_of_val(&test_data3)
283 );
284
285 let buffer = writer.finish().unwrap();
286
287 let decoded_buffer = if format == AccountBlockFormat::AlignedRaw {
288 buffer
289 } else {
290 ByteBlockReader::decode(format, &buffer).unwrap()
291 };
292
293 assert_eq!(
294 decoded_buffer.len(),
295 mem::size_of::<TestMetaStruct>() * 3
296 + mem::size_of_val(&test_data1)
297 + mem::size_of_val(&test_data2)
298 + mem::size_of_val(&test_data3)
299 );
300
301 let (meta1_from_buffer, next1) = read_type_unaligned::<TestMetaStruct>(&decoded_buffer, 0);
303 assert_eq!(test_metas[0], meta1_from_buffer);
304 assert_eq!(
305 test_data1,
306 decoded_buffer[next1..][..meta1_from_buffer.data_len]
307 );
308
309 let (meta2_from_buffer, next2) = read_type_unaligned::<TestMetaStruct>(
311 &decoded_buffer,
312 next1 + meta1_from_buffer.data_len,
313 );
314 assert_eq!(test_metas[1], meta2_from_buffer);
315 assert_eq!(
316 test_data2,
317 decoded_buffer[next2..][..meta2_from_buffer.data_len]
318 );
319
320 let (meta3_from_buffer, next3) = read_type_unaligned::<TestMetaStruct>(
322 &decoded_buffer,
323 next2 + meta2_from_buffer.data_len,
324 );
325 assert_eq!(test_metas[2], meta3_from_buffer);
326 assert_eq!(
327 test_data3,
328 decoded_buffer[next3..][..meta3_from_buffer.data_len]
329 );
330 }
331
332 #[test]
333 fn test_write_multiple_raw_format() {
334 write_multiple(AccountBlockFormat::AlignedRaw);
335 }
336
337 #[test]
338 fn test_write_multiple_lz4_format() {
339 write_multiple(AccountBlockFormat::Lz4);
340 }
341
342 fn write_optional_fields(format: AccountBlockFormat) {
343 let mut test_epoch = 5432312;
344
345 let mut writer = ByteBlockWriter::new(format);
346 let mut opt_fields_vec = vec![];
347 let mut some_count = 0;
348
349 for rent_epoch in [None, Some(test_epoch)] {
352 some_count += rent_epoch.iter().count();
353
354 opt_fields_vec.push(AccountMetaOptionalFields { rent_epoch });
355 test_epoch += 1;
356 }
357
358 let mut expected_size = 0;
360 for opt_fields in &opt_fields_vec {
361 writer.write_optional_fields(opt_fields).unwrap();
362 expected_size += opt_fields.size();
363 }
364
365 let buffer = writer.finish().unwrap();
366 let decoded_buffer = if format == AccountBlockFormat::AlignedRaw {
367 buffer
368 } else {
369 ByteBlockReader::decode(format, &buffer).unwrap()
370 };
371
372 assert_eq!(decoded_buffer.len(), expected_size);
375
376 let mut verified_count = 0;
378 let mut offset = 0;
379 for opt_fields in &opt_fields_vec {
380 if let Some(expected_rent_epoch) = opt_fields.rent_epoch {
381 let rent_epoch = read_pod::<Epoch>(&decoded_buffer, offset).unwrap();
382 assert_eq!(*rent_epoch, expected_rent_epoch);
383 verified_count += 1;
384 offset += std::mem::size_of::<Epoch>();
385 }
386 }
387
388 assert_eq!(some_count, verified_count);
391 }
392
393 #[test]
394 fn test_write_optionl_fields_raw_format() {
395 write_optional_fields(AccountBlockFormat::AlignedRaw);
396 }
397
398 #[test]
399 fn test_write_optional_fields_lz4_format() {
400 write_optional_fields(AccountBlockFormat::Lz4);
401 }
402}