1use std::cmp::Ordering;
23use std::collections::BTreeMap;
24
25use kernel::{RunId, Seq};
26
27mod entries;
28use entries::{Entries, Seqs};
29
30#[derive(serde::Serialize, serde::Deserialize)]
31pub(crate) struct Folded {
32 pub(crate) scanned: BTreeMap<String, u64>,
33 entries: Entries,
34 runs: RunTable,
35}
36
37impl Folded {
38 pub(crate) fn empty() -> Folded {
39 Folded {
40 scanned: BTreeMap::new(),
41 entries: Entries::empty(),
42 runs: RunTable::empty(),
43 }
44 }
45
46 pub(crate) fn fold_segment(&mut self, name: &str, from: u64, tail: &[u8]) -> u64 {
49 let mut offset = from;
50 let mut complete_bytes = 0u64;
51 for line in tail.split_inclusive(|b| *b == b'\n') {
52 if line.last().copied() != Some(b'\n') {
53 break;
54 }
55 let body = line.get(..line.len().saturating_sub(1)).unwrap_or(line);
56 if !body.is_empty() {
57 self.insert_line(name, offset, body);
58 }
59 let len = u64::try_from(line.len()).unwrap_or(0);
60 offset = offset.saturating_add(len);
61 complete_bytes = complete_bytes.saturating_add(len);
62 }
63 complete_bytes
64 }
65
66 pub(crate) fn insert_line(&mut self, name: &str, offset: u64, body: &[u8]) {
76 if let Some(located) = locate(body) {
77 self.insert_located(name, offset, located);
78 }
79 }
80
81 pub(crate) fn insert_located(&mut self, name: &str, offset: u64, located: Located) {
84 self.entries.insert(located.seq, name, offset);
85 if let Some(run) = located.run {
86 self.runs.add(run, located.seq);
87 }
88 }
89
90 pub(crate) fn loc_of(&self, seq: Seq) -> Option<(&str, u64)> {
92 self.entries.loc_of(seq)
93 }
94
95 pub(crate) fn run_seqs_before(
97 &self,
98 run: RunId,
99 before: Option<Seq>,
100 ) -> impl Iterator<Item = Seq> {
101 self.runs.seqs_before(run, before)
102 }
103
104 pub(crate) fn tail_seq(&self) -> Option<Seq> {
105 self.entries.tail_seq()
106 }
107
108 pub(crate) fn seqs(&self) -> Seqs<'_> {
109 self.entries.seqs()
110 }
111
112 pub(crate) fn seqs_from(&self, from: Seq) -> Seqs<'_> {
113 self.entries.seqs_from(from)
114 }
115
116 pub(crate) fn len(&self) -> usize {
117 self.entries.len()
118 }
119
120 pub(crate) fn is_empty(&self) -> bool {
121 self.entries.len() == 0
122 }
123}
124
125#[derive(serde::Serialize, serde::Deserialize)]
134struct RunTable {
135 runs: Vec<RunSpans>,
137}
138
139#[derive(serde::Serialize, serde::Deserialize)]
142struct RunSpans {
143 run: RunId,
144 spans: Vec<Span>,
145}
146
147#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
149struct Span {
150 start: Seq,
151 end: Seq,
152}
153
154impl RunTable {
155 fn empty() -> RunTable {
156 RunTable { runs: Vec::new() }
157 }
158
159 fn add(&mut self, run: RunId, seq: Seq) {
160 match self.runs.binary_search_by(|held| held.run.cmp(&run)) {
161 Ok(at) => {
162 if let Some(held) = self.runs.get_mut(at) {
163 held.add(seq);
164 }
165 }
166 Err(at) => self.runs.insert(
167 at,
168 RunSpans {
169 run,
170 spans: vec![Span {
171 start: seq,
172 end: seq,
173 }],
174 },
175 ),
176 }
177 }
178
179 fn seqs_before(&self, run: RunId, before: Option<Seq>) -> impl Iterator<Item = Seq> {
182 let spans: &[Span] = match self
183 .runs
184 .binary_search_by(|held| held.run.cmp(&run))
185 .ok()
186 .and_then(|at| self.runs.get(at))
187 {
188 Some(held) => &held.spans,
189 None => &[],
190 };
191 let ceiling = match before {
194 None => Some(u64::MAX),
195 Some(before) => before.value().checked_sub(1),
196 };
197 spans
198 .iter()
199 .rev()
200 .filter_map(move |span| {
201 let top = ceiling?.min(span.end.value());
202 (top >= span.start.value()).then_some((span.start.value(), top))
203 })
204 .flat_map(|(low, top)| (low..=top).rev().map(Seq::new))
205 }
206}
207
208impl RunSpans {
209 fn add(&mut self, seq: Seq) {
210 let at = match self.spans.binary_search_by(|span| span.place(seq)) {
211 Ok(_) => return, Err(at) => at,
213 };
214 let joins_below = self.spans.get(at).is_some_and(|span| span.abuts_after(seq));
215 let joins_above = at
216 .checked_sub(1)
217 .and_then(|at| self.spans.get(at))
218 .is_some_and(|span| span.abuts_before(seq));
219 match (joins_above, joins_below) {
220 (true, true) => {
221 let stretch = self.spans.get(at).map(|span| span.end);
222 let reach = at.checked_sub(1).and_then(|at| self.spans.get_mut(at));
223 if let (Some(stretch), Some(reach)) = (stretch, reach) {
224 reach.end = stretch;
225 }
226 self.spans.remove(at);
227 }
228 (true, false) => {
229 if let Some(reach) = at.checked_sub(1).and_then(|at| self.spans.get_mut(at)) {
230 reach.end = seq;
231 }
232 }
233 (false, true) => {
234 if let Some(reach) = self.spans.get_mut(at) {
235 reach.start = seq;
236 }
237 }
238 (false, false) => self.spans.insert(
239 at,
240 Span {
241 start: seq,
242 end: seq,
243 },
244 ),
245 }
246 }
247}
248
249impl Span {
250 fn place(&self, seq: Seq) -> Ordering {
253 if seq < self.start {
254 Ordering::Greater
255 } else if self.end < seq {
256 Ordering::Less
257 } else {
258 Ordering::Equal
259 }
260 }
261
262 fn abuts_before(&self, seq: Seq) -> bool {
265 self.end.value().checked_add(1) == Some(seq.value())
266 }
267
268 fn abuts_after(&self, seq: Seq) -> bool {
270 seq.value().checked_add(1) == Some(self.start.value())
271 }
272}
273
274pub struct Located {
276 pub seq: Seq,
277 pub run: Option<RunId>,
282}
283
284pub(crate) fn locate(line: &[u8]) -> Option<Located> {
288 let value: serde_json::Value = serde_json::from_slice(line).ok()?;
289 let seq = Seq::new(value.get("seq")?.as_u64()?);
290 let run = value
291 .get("run")
292 .and_then(serde_json::Value::as_str)
293 .and_then(|raw| RunId::parse(raw).ok());
294 Some(Located { seq, run })
295}
296
297#[cfg(test)]
298#[allow(
299 clippy::unwrap_used,
300 clippy::expect_used,
301 clippy::panic,
302 clippy::indexing_slicing,
303 reason = "test code"
304)]
305mod tests;