reth_static_file_types/
lib.rs

1//! Commonly used types for static file usage.
2
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
5    html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
6    issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
7)]
8#![cfg_attr(not(test), warn(unused_crate_dependencies))]
9#![cfg_attr(docsrs, feature(doc_cfg))]
10#![cfg_attr(not(feature = "std"), no_std)]
11
12extern crate alloc;
13
14mod compression;
15mod event;
16mod segment;
17
18use alloy_primitives::BlockNumber;
19pub use compression::Compression;
20use core::ops::RangeInclusive;
21pub use event::StaticFileProducerEvent;
22pub use segment::{SegmentConfig, SegmentHeader, SegmentRangeInclusive, StaticFileSegment};
23
24/// Default static file block count.
25pub const DEFAULT_BLOCKS_PER_STATIC_FILE: u64 = 500_000;
26
27/// Highest static file block numbers, per data segment.
28#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
29pub struct HighestStaticFiles {
30    /// Highest static file block of receipts, inclusive.
31    /// If [`None`], no static file is available.
32    pub receipts: Option<BlockNumber>,
33}
34
35impl HighestStaticFiles {
36    /// Returns an iterator over all static file segments
37    fn iter(&self) -> impl Iterator<Item = Option<BlockNumber>> {
38        [self.receipts].into_iter()
39    }
40
41    /// Returns the minimum block of all segments.
42    pub fn min_block_num(&self) -> Option<u64> {
43        self.iter().flatten().min()
44    }
45
46    /// Returns the maximum block of all segments.
47    pub fn max_block_num(&self) -> Option<u64> {
48        self.iter().flatten().max()
49    }
50}
51
52/// Static File targets, per data segment, measured in [`BlockNumber`].
53#[derive(Debug, Clone, Eq, PartialEq)]
54pub struct StaticFileTargets {
55    /// Targeted range of receipts.
56    pub receipts: Option<RangeInclusive<BlockNumber>>,
57}
58
59impl StaticFileTargets {
60    /// Returns `true` if any of the targets are [Some].
61    pub const fn any(&self) -> bool {
62        self.receipts.is_some()
63    }
64
65    /// Returns `true` if all targets are either [`None`] or has beginning of the range equal to the
66    /// highest static file.
67    pub fn is_contiguous_to_highest_static_files(&self, static_files: HighestStaticFiles) -> bool {
68        core::iter::once(&(self.receipts.as_ref(), static_files.receipts)).all(
69            |(target_block_range, highest_static_file_block)| {
70                target_block_range.is_none_or(|target_block_range| {
71                    *target_block_range.start() ==
72                        highest_static_file_block
73                            .map_or(0, |highest_static_file_block| highest_static_file_block + 1)
74                })
75            },
76        )
77    }
78}
79
80/// Each static file has a fixed number of blocks. This gives out the range where the requested
81/// block is positioned. Used for segment filename.
82pub const fn find_fixed_range(
83    block: BlockNumber,
84    blocks_per_static_file: u64,
85) -> SegmentRangeInclusive {
86    let start = (block / blocks_per_static_file) * blocks_per_static_file;
87    SegmentRangeInclusive::new(start, start + blocks_per_static_file - 1)
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_highest_static_files_min() {
96        let files = HighestStaticFiles { receipts: Some(100) };
97
98        // Minimum value among the available segments
99        assert_eq!(files.min_block_num(), Some(100));
100
101        let empty_files = HighestStaticFiles::default();
102        // No values, should return None
103        assert_eq!(empty_files.min_block_num(), None);
104    }
105
106    #[test]
107    fn test_highest_static_files_max() {
108        let files = HighestStaticFiles { receipts: Some(100) };
109
110        // Maximum value among the available segments
111        assert_eq!(files.max_block_num(), Some(100));
112
113        let empty_files = HighestStaticFiles::default();
114        // No values, should return None
115        assert_eq!(empty_files.max_block_num(), None);
116    }
117
118    #[test]
119    fn test_find_fixed_range() {
120        // Test with default block size
121        let block: BlockNumber = 600_000;
122        let range = find_fixed_range(block, DEFAULT_BLOCKS_PER_STATIC_FILE);
123        assert_eq!(range.start(), 500_000);
124        assert_eq!(range.end(), 999_999);
125
126        // Test with a custom block size
127        let block: BlockNumber = 1_200_000;
128        let range = find_fixed_range(block, 1_000_000);
129        assert_eq!(range.start(), 1_000_000);
130        assert_eq!(range.end(), 1_999_999);
131    }
132}