1use std::io;
15use std::path::{Path, PathBuf};
16
17use kernel::ledger::chain_hash;
18use kernel::{B3Hash, GENESIS_PREV, Seq};
19
20use crate::error::{StorageError, io_err};
21use crate::real_fs::RealFs;
22use crate::vfs::Vfs;
23
24use super::first_line::FIRST_WINDOW_BYTES;
25use super::ledger::is_segment;
26use super::verify::{CheckedLine, LineCheck, LineFault};
27
28#[derive(Debug, Clone, PartialEq)]
30pub struct TailLine {
31 pub raw: Vec<u8>,
32 pub checked: CheckedLine,
33}
34
35pub struct TailLines {
38 vfs: Box<dyn Vfs>,
39 segments: Vec<PathBuf>,
40 reading: Option<Backward>,
41 newer: Option<Link>,
42 ended: bool,
43}
44
45#[derive(Clone, Copy)]
47struct Link {
48 seq: Seq,
49 prev: B3Hash,
50}
51
52struct Backward {
55 path: PathBuf,
56 unread: u64,
57 window: u64,
58 held: Vec<u8>,
59}
60
61impl TailLines {
62 pub fn at(dir: &Path) -> Result<Self, StorageError> {
68 Self::through(Box::new(RealFs::new()), dir)
69 }
70
71 pub(crate) fn through(vfs: Box<dyn Vfs>, dir: &Path) -> Result<Self, StorageError> {
72 let segments = vfs
73 .list(dir)
74 .map_err(io_err("list ledger dir", dir))?
75 .into_iter()
76 .filter(|p| is_segment(p))
77 .collect();
78 Ok(Self {
79 vfs,
80 segments,
81 reading: None,
82 newer: None,
83 ended: false,
84 })
85 }
86
87 fn next_raw(&mut self) -> Result<Option<Vec<u8>>, StorageError> {
89 loop {
90 let reading = match self.reading.as_mut() {
91 Some(reading) => reading,
92 None => match self.segments.pop() {
93 Some(path) => self
94 .reading
95 .insert(Backward::open(self.vfs.as_ref(), path)?),
96 None => return Ok(None),
97 },
98 };
99 match reading
100 .pop_line(self.vfs.as_ref())
101 .map_err(io_err("read segment", &reading.path))?
102 {
103 Some(line) if line.is_empty() => {}
104 Some(line) => return Ok(Some(line)),
105 None => self.reading = None,
106 }
107 }
108 }
109
110 fn link(&self, raw: &[u8]) -> Result<(Link, CheckedLine), LineFault> {
113 let judged = LineCheck::judge(raw)?;
114 if let Some(newer) = self.newer {
115 if chain_hash(raw) != newer.prev {
116 return Err(LineFault::ChainBreak);
117 }
118 if judged.seq.next().map_err(LineFault::SeqExhausted)? != newer.seq {
119 return Err(LineFault::SeqGap {
120 found: judged.seq,
121 expected: Seq::new(newer.seq.value().saturating_sub(1)),
122 });
123 }
124 }
125 if judged.seq == Seq::FIRST && judged.prev != GENESIS_PREV {
126 return Err(LineFault::ChainBreak);
127 }
128 let link = Link {
129 seq: judged.seq,
130 prev: judged.prev,
131 };
132 Ok((link, judged.checked))
133 }
134
135 fn refusal(&self, fault: LineFault) -> StorageError {
139 let line = self.newer.map_or(0, |newer| newer.seq.value());
140 StorageError::Envelope {
141 path: self
142 .reading
143 .as_ref()
144 .map(|reading| reading.path.clone())
145 .unwrap_or_default(),
146 line,
147 source: fault.into_ax(line),
148 }
149 }
150}
151
152impl Iterator for TailLines {
153 type Item = Result<TailLine, StorageError>;
154
155 fn next(&mut self) -> Option<Self::Item> {
156 if self.ended {
157 return None;
158 }
159 let raw = match self.next_raw() {
160 Ok(Some(raw)) => raw,
161 Ok(None) => return None,
162 Err(e) => {
163 self.ended = true;
164 return Some(Err(e));
165 }
166 };
167 match self.link(&raw) {
168 Ok((link, checked)) => {
169 self.newer = Some(link);
170 Some(Ok(TailLine { raw, checked }))
171 }
172 Err(fault) => {
173 self.ended = true;
174 Some(Err(self.refusal(fault)))
175 }
176 }
177 }
178}
179impl Backward {
180 fn open(vfs: &dyn Vfs, path: PathBuf) -> Result<Self, StorageError> {
183 let unread = vfs.size(&path).map_err(io_err("size segment", &path))?;
184 let mut backward = Self {
185 path,
186 unread,
187 window: FIRST_WINDOW_BYTES,
188 held: Vec::new(),
189 };
190 loop {
191 if let Some(end) = backward.held.iter().rposition(|byte| *byte == b'\n') {
192 backward.held.truncate(end.saturating_add(1));
193 return Ok(backward);
194 }
195 if backward.unread == 0 {
196 backward.held.clear();
197 return Ok(backward);
198 }
199 backward
200 .read_further(vfs)
201 .map_err(io_err("read segment", &backward.path))?;
202 }
203 }
204
205 fn pop_line(&mut self, vfs: &dyn Vfs) -> io::Result<Option<Vec<u8>>> {
208 loop {
209 let Some((_newline, body)) = self.held.split_last() else {
210 return Ok(None);
211 };
212 let start = match body.iter().rposition(|byte| *byte == b'\n') {
213 Some(newline) => newline.saturating_add(1),
214 None if self.unread == 0 => 0,
215 None => {
216 self.read_further(vfs)?;
217 continue;
218 }
219 };
220 let line = body.get(start..).map(<[u8]>::to_vec);
221 self.held.truncate(start);
222 return Ok(line);
223 }
224 }
225
226 fn read_further(&mut self, vfs: &dyn Vfs) -> io::Result<()> {
229 let take = self.window.min(self.unread);
230 let from = self.unread.saturating_sub(take);
231 let mut bytes = vfs.read_at(&self.path, from, take)?;
232 bytes.append(&mut self.held);
233 self.held = bytes;
234 self.unread = from;
235 self.window = self.window.saturating_mul(2);
236 Ok(())
237 }
238}
239
240#[cfg(test)]
241#[allow(
242 clippy::unwrap_used,
243 clippy::expect_used,
244 clippy::panic,
245 clippy::indexing_slicing,
246 clippy::as_conversions,
247 clippy::arithmetic_side_effects,
248 reason = "test code"
249)]
250mod tests;