zcash_client_backend/data_api/
scanning.rs1use std::fmt;
4use std::ops::Range;
5
6use zcash_protocol::consensus::BlockHeight;
7
8#[cfg(feature = "unstable-spanning-tree")]
9pub mod spanning_tree;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub enum ScanPriority {
14 Ignored,
16 Scanned,
18 Historic,
20 OpenAdjacent,
22 FoundNote,
24 ChainTip,
26 Verify,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct ScanRange {
34 block_range: Range<BlockHeight>,
35 priority: ScanPriority,
36}
37
38impl fmt::Display for ScanRange {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(
41 f,
42 "{:?}({}..{})",
43 self.priority, self.block_range.start, self.block_range.end,
44 )
45 }
46}
47
48impl ScanRange {
49 pub fn from_parts(block_range: Range<BlockHeight>, priority: ScanPriority) -> Self {
51 assert!(
52 block_range.end >= block_range.start,
53 "{:?} is invalid for ScanRange({:?})",
54 block_range,
55 priority,
56 );
57 ScanRange {
58 block_range,
59 priority,
60 }
61 }
62
63 pub fn block_range(&self) -> &Range<BlockHeight> {
65 &self.block_range
66 }
67
68 pub fn priority(&self) -> ScanPriority {
70 self.priority
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.block_range.is_empty()
76 }
77
78 pub fn len(&self) -> usize {
80 usize::try_from(u32::from(self.block_range.end) - u32::from(self.block_range.start))
81 .unwrap()
82 }
83
84 pub fn truncate_start(&self, block_height: BlockHeight) -> Option<Self> {
88 if block_height >= self.block_range.end || self.is_empty() {
89 None
90 } else {
91 Some(ScanRange {
92 block_range: self.block_range.start.max(block_height)..self.block_range.end,
93 priority: self.priority,
94 })
95 }
96 }
97
98 pub fn truncate_end(&self, block_height: BlockHeight) -> Option<Self> {
102 if block_height <= self.block_range.start || self.is_empty() {
103 None
104 } else {
105 Some(ScanRange {
106 block_range: self.block_range.start..self.block_range.end.min(block_height),
107 priority: self.priority,
108 })
109 }
110 }
111
112 pub fn split_at(&self, p: BlockHeight) -> Option<(Self, Self)> {
116 (p > self.block_range.start && p < self.block_range.end).then_some((
117 ScanRange {
118 block_range: self.block_range.start..p,
119 priority: self.priority,
120 },
121 ScanRange {
122 block_range: p..self.block_range.end,
123 priority: self.priority,
124 },
125 ))
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::{ScanPriority, ScanRange};
132
133 fn scan_range(start: u32, end: u32) -> ScanRange {
134 ScanRange::from_parts((start.into())..(end.into()), ScanPriority::Scanned)
135 }
136
137 #[test]
138 fn truncate_start() {
139 let r = scan_range(5, 8);
140
141 assert_eq!(r.truncate_start(4.into()), Some(scan_range(5, 8)));
142 assert_eq!(r.truncate_start(5.into()), Some(scan_range(5, 8)));
143 assert_eq!(r.truncate_start(6.into()), Some(scan_range(6, 8)));
144 assert_eq!(r.truncate_start(7.into()), Some(scan_range(7, 8)));
145 assert_eq!(r.truncate_start(8.into()), None);
146 assert_eq!(r.truncate_start(9.into()), None);
147
148 let empty = scan_range(5, 5);
149 assert_eq!(empty.truncate_start(4.into()), None);
150 assert_eq!(empty.truncate_start(5.into()), None);
151 assert_eq!(empty.truncate_start(6.into()), None);
152 }
153
154 #[test]
155 fn truncate_end() {
156 let r = scan_range(5, 8);
157
158 assert_eq!(r.truncate_end(9.into()), Some(scan_range(5, 8)));
159 assert_eq!(r.truncate_end(8.into()), Some(scan_range(5, 8)));
160 assert_eq!(r.truncate_end(7.into()), Some(scan_range(5, 7)));
161 assert_eq!(r.truncate_end(6.into()), Some(scan_range(5, 6)));
162 assert_eq!(r.truncate_end(5.into()), None);
163 assert_eq!(r.truncate_end(4.into()), None);
164
165 let empty = scan_range(5, 5);
166 assert_eq!(empty.truncate_end(4.into()), None);
167 assert_eq!(empty.truncate_end(5.into()), None);
168 assert_eq!(empty.truncate_end(6.into()), None);
169 }
170
171 #[test]
172 fn split_at() {
173 let r = scan_range(5, 8);
174
175 assert_eq!(r.split_at(4.into()), None);
176 assert_eq!(r.split_at(5.into()), None);
177 assert_eq!(
178 r.split_at(6.into()),
179 Some((scan_range(5, 6), scan_range(6, 8)))
180 );
181 assert_eq!(
182 r.split_at(7.into()),
183 Some((scan_range(5, 7), scan_range(7, 8)))
184 );
185 assert_eq!(r.split_at(8.into()), None);
186 assert_eq!(r.split_at(9.into()), None);
187
188 let empty = scan_range(5, 5);
189 assert_eq!(empty.split_at(4.into()), None);
190 assert_eq!(empty.split_at(5.into()), None);
191 assert_eq!(empty.split_at(6.into()), None);
192 }
193}