pallas_miniprotocols/chainsync/
buffer.rs1use std::collections::{vec_deque::Iter, VecDeque};
2
3use crate::Point;
4
5#[derive(Debug)]
22pub struct RollbackBuffer {
23 points: VecDeque<Point>,
24}
25
26impl Default for RollbackBuffer {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32pub enum RollbackEffect {
33 Handled,
34 OutOfScope,
35}
36
37impl RollbackBuffer {
38 pub fn new() -> Self {
39 Self {
40 points: VecDeque::new(),
41 }
42 }
43
44 pub fn roll_forward(&mut self, point: Point) {
46 self.points.push_back(point);
47 }
48
49 pub fn pop_with_depth(&mut self, min_depth: usize) -> Vec<Point> {
51 match self.points.len().checked_sub(min_depth) {
52 Some(ready) => self.points.drain(0..ready).collect(),
53 None => vec![],
54 }
55 }
56
57 pub fn position(&self, point: &Point) -> Option<usize> {
59 self.points.iter().position(|p| p.eq(point))
60 }
61
62 pub fn peek(&self) -> Iter<Point> {
64 self.points.iter()
65 }
66
67 pub fn size(&self) -> usize {
69 self.points.len()
70 }
71
72 pub fn latest(&self) -> Option<&Point> {
74 self.points.back()
75 }
76
77 pub fn oldest(&self) -> Option<&Point> {
79 self.points.front()
80 }
81
82 pub fn roll_back(&mut self, point: &Point) -> RollbackEffect {
89 if let Some(x) = self.position(point) {
90 self.points.truncate(x + 1);
91 RollbackEffect::Handled
92 } else {
93 self.points.clear();
94 RollbackEffect::OutOfScope
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::{chainsync::RollbackEffect, Point};
102
103 use super::RollbackBuffer;
104
105 fn dummy_point(i: u64) -> Point {
106 Point::new(i, i.to_le_bytes().to_vec())
107 }
108
109 fn build_filled_buffer(n: usize) -> RollbackBuffer {
110 let mut buffer = RollbackBuffer::new();
111
112 for i in 0..n {
113 let point = dummy_point(i as u64);
114 buffer.roll_forward(point);
115 }
116
117 buffer
118 }
119
120 #[test]
121 fn roll_forward_accumulates_points() {
122 let buffer = build_filled_buffer(3);
123
124 assert!(matches!(buffer.position(&dummy_point(0)), Some(0)));
125 assert!(matches!(buffer.position(&dummy_point(1)), Some(1)));
126 assert!(matches!(buffer.position(&dummy_point(2)), Some(2)));
127
128 assert_eq!(buffer.oldest().unwrap(), &dummy_point(0));
129 assert_eq!(buffer.latest().unwrap(), &dummy_point(2));
130 }
131
132 #[test]
133 fn pop_from_valid_depth_works() {
134 let mut buffer = build_filled_buffer(5);
135
136 let ready = buffer.pop_with_depth(2);
137
138 assert_eq!(dummy_point(0), ready[0]);
139 assert_eq!(dummy_point(1), ready[1]);
140 assert_eq!(dummy_point(2), ready[2]);
141
142 assert_eq!(ready.len(), 3);
143
144 assert_eq!(buffer.oldest().unwrap(), &dummy_point(3));
145 assert_eq!(buffer.latest().unwrap(), &dummy_point(4));
146 }
147
148 #[test]
149 fn pop_from_excessive_depth_returns_empty() {
150 let mut buffer = build_filled_buffer(6);
151
152 let ready = buffer.pop_with_depth(10);
153
154 assert_eq!(ready.len(), 0);
155
156 assert_eq!(buffer.oldest().unwrap(), &dummy_point(0));
157 assert_eq!(buffer.latest().unwrap(), &dummy_point(5));
158 }
159
160 #[test]
161 fn roll_back_within_scope_works() {
162 let mut buffer = build_filled_buffer(6);
163
164 let result = buffer.roll_back(&dummy_point(2));
165
166 assert!(matches!(result, RollbackEffect::Handled));
167
168 assert_eq!(buffer.size(), 3);
169 assert_eq!(buffer.oldest().unwrap(), &dummy_point(0));
170 assert_eq!(buffer.latest().unwrap(), &dummy_point(2));
171
172 let remaining = buffer.pop_with_depth(0);
173
174 assert_eq!(dummy_point(0), remaining[0]);
175 assert_eq!(dummy_point(1), remaining[1]);
176 assert_eq!(dummy_point(2), remaining[2]);
177
178 assert_eq!(remaining.len(), 3);
179 }
180
181 #[test]
182 fn roll_back_outside_scope_works() {
183 let mut buffer = build_filled_buffer(6);
184
185 let result = buffer.roll_back(&dummy_point(100));
186
187 assert!(matches!(result, RollbackEffect::OutOfScope));
188
189 assert_eq!(buffer.size(), 0);
190 assert_eq!(buffer.oldest(), None);
191 assert_eq!(buffer.latest(), None);
192 }
193}