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