1use std::collections::{BTreeSet, HashMap};
42use std::ops::RangeInclusive;
43use std::sync::{Arc, Mutex};
44
45use rfc9162_tiles::bloom::Bloom;
46use rfc9162_tiles::index::{identity, KeyFn};
47use rfc9162_tiles::log::{Reader, Writer};
48use rfc9162_tiles::store::TileStore;
49
50use crate::{AuthenticatedLog, Compaction, Error, Inclusion, Located, Recovery, RunManifest};
51
52pub const MANIFEST_MAGIC: &[u8] = b"rfc9162-log/manifest/v2\n";
54pub const RUN_MAGIC: &[u8] = b"rfc9162-log/run/v2\n";
57pub const RUN_MAGIC_V3: &[u8] = b"rfc9162-log/run/v3\n";
60pub const BLOCK_PAIRS: usize = 64;
63pub const RUN_MAGIC_V1: &[u8] = b"rfc9162-log/run/v1\n";
65const LIVE: &str = "runs/live";
66const BITS_PER_KEY: usize = 10;
67
68pub type Keys = Arc<dyn Fn(&[u8]) -> Vec<Vec<u8>> + Send + Sync>;
71
72pub type RunCache = Arc<Mutex<HashMap<u64, Arc<Run>>>>;
75
76fn run_path(entries: &RangeInclusive<u64>) -> String {
77 format!("runs/{:020}-{:020}", entries.start(), entries.end())
78}
79
80pub fn tiles_keys(keys: &Keys) -> KeyFn {
83 let keys = Arc::clone(keys);
84 Box::new(move |entry: &[u8]| {
85 if entry.starts_with(MANIFEST_MAGIC) {
86 Vec::new()
87 } else {
88 keys(entry)
89 }
90 })
91}
92
93fn put_u64(out: &mut Vec<u8>, value: u64) {
94 out.extend_from_slice(&value.to_be_bytes());
95}
96
97fn put_bytes(out: &mut Vec<u8>, bytes: &[u8]) -> Result<(), Error> {
98 let len = u16::try_from(bytes.len()).map_err(|_| Error::Tree("key over 65535 bytes"))?;
99 out.extend_from_slice(&len.to_be_bytes());
100 out.extend_from_slice(bytes);
101 Ok(())
102}
103
104fn put_varint(out: &mut Vec<u8>, mut value: u64) {
105 while value >= 0x80 {
106 out.push((value as u8) | 0x80);
107 value >>= 7;
108 }
109 out.push(value as u8);
110}
111
112fn shared_prefix(a: &[u8], b: &[u8]) -> usize {
114 a.iter().zip(b).take_while(|(x, y)| x == y).count()
115}
116
117struct Cursor<'a> {
118 bytes: &'a [u8],
119 at: usize,
120 what: &'static str,
121}
122
123impl<'a> Cursor<'a> {
124 fn take(&mut self, n: usize) -> Result<&'a [u8], Error> {
125 let end = self
126 .at
127 .checked_add(n)
128 .ok_or(Error::Tampered { what: self.what })?;
129 if end > self.bytes.len() {
130 return Err(Error::Tampered { what: self.what });
131 }
132 let out = &self.bytes[self.at..end];
133 self.at = end;
134 Ok(out)
135 }
136 fn u64(&mut self) -> Result<u64, Error> {
137 let b = self.take(8)?;
138 Ok(u64::from_be_bytes(b.try_into().expect("eight bytes")))
139 }
140 fn u32(&mut self) -> Result<u32, Error> {
141 let b = self.take(4)?;
142 Ok(u32::from_be_bytes(b.try_into().expect("four bytes")))
143 }
144 fn bytes(&mut self) -> Result<&'a [u8], Error> {
145 let b = self.take(2)?;
146 let len = u16::from_be_bytes(b.try_into().expect("two bytes")) as usize;
147 self.take(len)
148 }
149 fn varint(&mut self) -> Result<u64, Error> {
151 let mut value = 0u64;
152 for shift in (0..64).step_by(7) {
153 let byte = self.take(1)?[0];
154 let low = u64::from(byte & 0x7f);
155 if shift == 63 && low > 1 {
156 return Err(Error::Tampered { what: self.what });
157 }
158 value |= low << shift;
159 if byte & 0x80 == 0 {
160 return Ok(value);
161 }
162 }
163 Err(Error::Tampered { what: self.what })
164 }
165 fn done(&self) -> Result<(), Error> {
166 if self.at == self.bytes.len() {
167 Ok(())
168 } else {
169 Err(Error::Tampered { what: self.what })
170 }
171 }
172}
173
174#[derive(Debug, Clone, PartialEq, Eq)]
178pub struct Logged {
179 pub run: RunManifest,
181 pub data_size: u64,
183 pub data_root: [u8; 32],
185 pub replaced: Vec<u64>,
187}
188
189impl Logged {
190 fn encode(&self) -> Result<Vec<u8>, Error> {
191 let mut out = MANIFEST_MAGIC.to_vec();
192 put_u64(&mut out, *self.run.entries.start());
193 put_u64(&mut out, *self.run.entries.end());
194 put_bytes(&mut out, &self.run.key_lo)?;
195 put_bytes(&mut out, &self.run.key_hi)?;
196 out.extend_from_slice(&self.run.run_hash);
197 put_u64(&mut out, self.data_size);
198 out.extend_from_slice(&self.data_root);
199 let n = u32::try_from(self.replaced.len())
200 .map_err(|_| Error::Tree("too many replaced runs"))?;
201 out.extend_from_slice(&n.to_be_bytes());
202 for index in &self.replaced {
203 put_u64(&mut out, *index);
204 }
205 Ok(out)
206 }
207
208 pub fn decode(bytes: &[u8], manifest_index: u64, shared: bool) -> Result<Self, Error> {
212 let what = "manifest entry";
213 let body = bytes
214 .strip_prefix(MANIFEST_MAGIC)
215 .ok_or(Error::Tampered { what })?;
216 let mut c = Cursor {
217 bytes: body,
218 at: 0,
219 what,
220 };
221 let lo = c.u64()?;
222 let hi = c.u64()?;
223 let key_lo = c.bytes()?.to_vec();
224 let key_hi = c.bytes()?.to_vec();
225 let run_hash: [u8; 32] = c.take(32)?.try_into().expect("32 bytes");
226 let data_size = c.u64()?;
227 let data_root: [u8; 32] = c.take(32)?.try_into().expect("32 bytes");
228 let n = c.u32()?;
229 let mut replaced = Vec::with_capacity(n as usize);
230 for _ in 0..n {
231 replaced.push(c.u64()?);
232 }
233 c.done()?;
234 if lo > hi || hi >= data_size || (shared && hi >= manifest_index) {
235 return Err(Error::Tampered { what });
236 }
237 Ok(Self {
238 run: RunManifest {
239 entries: lo..=hi,
240 key_lo,
241 key_hi,
242 run_hash,
243 manifest_index: Some(manifest_index),
244 },
245 data_size,
246 data_root,
247 replaced,
248 })
249 }
250}
251
252pub struct Run {
256 pairs: Vec<(Vec<u8>, u64)>,
257 blocks: Option<Blocks>,
258 filter: Bloom,
259}
260
261struct Blocks {
263 lo: u64,
264 hi: u64,
265 body: Vec<u8>,
266 index: Vec<BlockRef>,
267}
268
269struct BlockRef {
271 first: Vec<u8>,
272 start: usize,
273 end: usize,
274 count: usize,
275}
276
277impl Blocks {
278 fn decode(&self, b: usize) -> Result<Vec<(Vec<u8>, u64)>, Error> {
281 let what = "run block";
282 let block = &self.index[b];
283 let mut c = Cursor {
284 bytes: &self.body[block.start..block.end],
285 at: 0,
286 what,
287 };
288 let mut pairs: Vec<(Vec<u8>, u64)> = Vec::with_capacity(block.count);
289 for _ in 0..block.count {
290 let shared = c.varint()?;
291 let unshared = c.varint()?;
292 let previous = pairs.last().map(|(k, _)| k.as_slice()).unwrap_or(&[]);
293 if shared > previous.len() as u64
294 || shared.saturating_add(unshared) > u64::from(u16::MAX)
295 {
296 return Err(Error::Tampered { what });
297 }
298 let mut key = previous[..shared as usize].to_vec();
299 key.extend_from_slice(c.take(unshared as usize)?);
300 let index = self
301 .lo
302 .checked_add(c.varint()?)
303 .ok_or(Error::Tampered { what })?;
304 if index > self.hi {
305 return Err(Error::Tampered { what });
306 }
307 if let Some(last) = pairs.last() {
308 if (&key, index) <= (&last.0, last.1) {
309 return Err(Error::Tampered { what });
310 }
311 }
312 pairs.push((key, index));
313 }
314 c.done()?;
315 match pairs.first() {
316 Some((key, _)) if *key == block.first => Ok(pairs),
317 _ => Err(Error::Tampered { what }),
318 }
319 }
320
321 fn from(&self, lo: &[u8]) -> usize {
324 self.index
325 .partition_point(|b| b.first.as_slice() < lo)
326 .saturating_sub(1)
327 }
328}
329
330impl Run {
331 fn encode_blocks(
335 entries: &RangeInclusive<u64>,
336 pairs: &[(Vec<u8>, u64)],
337 ) -> Result<Vec<u8>, Error> {
338 let lo = *entries.start();
339 let mut bodies: Vec<Vec<u8>> = Vec::new();
340 for chunk in pairs.chunks(BLOCK_PAIRS) {
341 let mut body = Vec::new();
342 let mut previous: &[u8] = &[];
343 for (key, index) in chunk {
344 if key.len() > usize::from(u16::MAX) {
345 return Err(Error::Tree("key over 65535 bytes"));
346 }
347 let offset = index
348 .checked_sub(lo)
349 .ok_or(Error::Tree("a run pair below the run's entry range"))?;
350 let shared = shared_prefix(previous, key);
351 put_varint(&mut body, shared as u64);
352 put_varint(&mut body, (key.len() - shared) as u64);
353 body.extend_from_slice(&key[shared..]);
354 put_varint(&mut body, offset);
355 previous = key;
356 }
357 bodies.push(body);
358 }
359 let mut out = RUN_MAGIC_V3.to_vec();
360 put_u64(&mut out, lo);
361 put_u64(&mut out, *entries.end());
362 put_varint(&mut out, pairs.len() as u64);
363 put_varint(&mut out, bodies.len() as u64);
364 for (chunk, body) in pairs.chunks(BLOCK_PAIRS).zip(&bodies) {
365 let first = &chunk[0].0;
366 put_varint(&mut out, first.len() as u64);
367 out.extend_from_slice(first);
368 put_varint(&mut out, chunk.len() as u64);
369 put_varint(&mut out, body.len() as u64);
370 }
371 for body in &bodies {
372 out.extend_from_slice(body);
373 }
374 let keys: Vec<&[u8]> = pairs.iter().map(|(k, _)| k.as_slice()).collect();
375 let filter = Bloom::build(&keys, BITS_PER_KEY).to_bytes();
376 put_varint(&mut out, filter.len() as u64);
377 out.extend_from_slice(&filter);
378 Ok(out)
379 }
380
381 fn decode_blocks(body: &[u8], expect: &RunManifest) -> Result<Self, Error> {
383 let what = "run file";
384 let mut c = Cursor {
385 bytes: body,
386 at: 0,
387 what,
388 };
389 let lo = c.u64()?;
390 let hi = c.u64()?;
391 if lo != *expect.entries.start() || hi != *expect.entries.end() {
392 return Err(Error::Tampered { what });
393 }
394 let n = c.varint()?;
395 let nblocks = c.varint()?;
396 if n > (body.len() as u64) / 3 || nblocks > n.max(1) {
397 return Err(Error::Tampered { what });
398 }
399 let mut heads = Vec::with_capacity(nblocks as usize);
400 let mut total = 0u64;
401 for _ in 0..nblocks {
402 let len = c.varint()?;
403 if len > u64::from(u16::MAX) {
404 return Err(Error::Tampered { what });
405 }
406 let first = c.take(len as usize)?.to_vec();
407 let count = c.varint()?;
408 let bytes = c.varint()?;
409 if count == 0 || count > BLOCK_PAIRS as u64 || bytes == 0 {
410 return Err(Error::Tampered { what });
411 }
412 if let Some((previous, _, _)) = heads.last() {
413 if first < *previous {
414 return Err(Error::Tampered { what });
415 }
416 }
417 total += count;
418 heads.push((first, count as usize, bytes as usize));
419 }
420 if total != n {
421 return Err(Error::Tampered { what });
422 }
423 let mut index = Vec::with_capacity(heads.len());
424 let mut start = 0usize;
425 let mut blob = Vec::new();
426 for (first, count, bytes) in heads {
427 blob.extend_from_slice(c.take(bytes)?);
428 index.push(BlockRef {
429 first,
430 start,
431 end: start + bytes,
432 count,
433 });
434 start += bytes;
435 }
436 let len = usize::try_from(c.varint()?).map_err(|_| Error::Tampered { what })?;
437 let filter = Bloom::from_bytes(c.take(len)?).map_err(|_| Error::Tampered { what })?;
438 c.done()?;
439 Ok(Self {
440 pairs: Vec::new(),
441 blocks: Some(Blocks {
442 lo,
443 hi,
444 body: blob,
445 index,
446 }),
447 filter,
448 })
449 }
450
451 fn all_pairs(&self) -> Result<Vec<(Vec<u8>, u64)>, Error> {
453 match &self.blocks {
454 None => Ok(self.pairs.clone()),
455 Some(blocks) => {
456 let mut out = Vec::new();
457 for b in 0..blocks.index.len() {
458 out.extend(blocks.decode(b)?);
459 }
460 Ok(out)
461 }
462 }
463 }
464
465 #[cfg(test)]
467 fn encode(entries: &RangeInclusive<u64>, pairs: &[(Vec<u8>, u64)]) -> Result<Vec<u8>, Error> {
468 let lo = *entries.start();
469 let mut out = RUN_MAGIC.to_vec();
470 put_u64(&mut out, lo);
471 put_u64(&mut out, *entries.end());
472 put_varint(&mut out, pairs.len() as u64);
473 let mut previous: &[u8] = &[];
474 for (key, index) in pairs {
475 if key.len() > usize::from(u16::MAX) {
476 return Err(Error::Tree("key over 65535 bytes"));
477 }
478 let offset = index
479 .checked_sub(lo)
480 .ok_or(Error::Tree("a run pair below the run's entry range"))?;
481 let shared = shared_prefix(previous, key);
482 put_varint(&mut out, shared as u64);
483 put_varint(&mut out, (key.len() - shared) as u64);
484 out.extend_from_slice(&key[shared..]);
485 put_varint(&mut out, offset);
486 previous = key;
487 }
488 let keys: Vec<&[u8]> = pairs.iter().map(|(k, _)| k.as_slice()).collect();
489 let filter = Bloom::build(&keys, BITS_PER_KEY).to_bytes();
490 put_varint(&mut out, filter.len() as u64);
491 out.extend_from_slice(&filter);
492 Ok(out)
493 }
494
495 #[cfg(test)]
496 fn encode_v1(
497 entries: &RangeInclusive<u64>,
498 pairs: &[(Vec<u8>, u64)],
499 ) -> Result<Vec<u8>, Error> {
500 let mut out = RUN_MAGIC_V1.to_vec();
501 put_u64(&mut out, *entries.start());
502 put_u64(&mut out, *entries.end());
503 let n = u32::try_from(pairs.len()).map_err(|_| Error::Tree("too many pairs in a run"))?;
504 out.extend_from_slice(&n.to_be_bytes());
505 for (key, index) in pairs {
506 put_bytes(&mut out, key)?;
507 put_u64(&mut out, *index);
508 }
509 let keys: Vec<&[u8]> = pairs.iter().map(|(k, _)| k.as_slice()).collect();
510 let filter = Bloom::build(&keys, BITS_PER_KEY).to_bytes();
511 let len = u32::try_from(filter.len()).map_err(|_| Error::Tree("filter too large"))?;
512 out.extend_from_slice(&len.to_be_bytes());
513 out.extend_from_slice(&filter);
514 Ok(out)
515 }
516
517 fn decode(bytes: &[u8], expect: &RunManifest) -> Result<Self, Error> {
518 let what = "run file";
519 if identity(bytes) != expect.run_hash {
520 return Err(Error::Tampered { what });
521 }
522 if let Some(body) = bytes.strip_prefix(RUN_MAGIC_V3) {
523 return Self::decode_blocks(body, expect);
524 }
525 let (v2, body) = if let Some(body) = bytes.strip_prefix(RUN_MAGIC) {
526 (true, body)
527 } else if let Some(body) = bytes.strip_prefix(RUN_MAGIC_V1) {
528 (false, body)
529 } else {
530 return Err(Error::Tampered { what });
531 };
532 let mut c = Cursor {
533 bytes: body,
534 at: 0,
535 what,
536 };
537 let lo = c.u64()?;
538 let hi = c.u64()?;
539 if lo != *expect.entries.start() || hi != *expect.entries.end() {
540 return Err(Error::Tampered { what });
541 }
542 let n = if v2 { c.varint()? } else { u64::from(c.u32()?) };
543 if n > (body.len() as u64) / 3 {
546 return Err(Error::Tampered { what });
547 }
548 let mut pairs: Vec<(Vec<u8>, u64)> = Vec::with_capacity(n as usize);
549 for _ in 0..n {
550 let (key, index) = if v2 {
551 let shared = c.varint()?;
552 let unshared = c.varint()?;
553 let previous = pairs.last().map(|(k, _)| k.as_slice()).unwrap_or(&[]);
554 if shared > previous.len() as u64
555 || shared.saturating_add(unshared) > u64::from(u16::MAX)
556 {
557 return Err(Error::Tampered { what });
558 }
559 let mut key = previous[..shared as usize].to_vec();
560 key.extend_from_slice(c.take(unshared as usize)?);
561 let index = lo
562 .checked_add(c.varint()?)
563 .ok_or(Error::Tampered { what })?;
564 (key, index)
565 } else {
566 let key = c.bytes()?.to_vec();
567 (key, c.u64()?)
568 };
569 if !expect.entries.contains(&index) {
570 return Err(Error::Tampered { what });
571 }
572 pairs.push((key, index));
573 }
574 if pairs.windows(2).any(|w| w[0] >= w[1]) {
575 return Err(Error::Tampered { what });
576 }
577 let len = if v2 { c.varint()? } else { u64::from(c.u32()?) };
578 let len = usize::try_from(len).map_err(|_| Error::Tampered { what })?;
579 let filter = Bloom::from_bytes(c.take(len)?).map_err(|_| Error::Tampered { what })?;
580 c.done()?;
581 Ok(Self {
582 pairs,
583 blocks: None,
584 filter,
585 })
586 }
587
588 #[cfg(test)]
590 fn indices_of(&self, key: &[u8]) -> Vec<u64> {
591 self.lookup(key).expect("a checked run")
592 }
593
594 fn lookup(&self, key: &[u8]) -> Result<Vec<u64>, Error> {
595 if !self.filter.contains(key) {
596 return Ok(Vec::new());
597 }
598 Ok(self
599 .pairs_in(&(key..=key))?
600 .into_iter()
601 .map(|(_, i)| i)
602 .collect())
603 }
604
605 fn pairs_in(&self, range: &RangeInclusive<&[u8]>) -> Result<Vec<(Vec<u8>, u64)>, Error> {
606 let Some(blocks) = &self.blocks else {
607 let start = self
608 .pairs
609 .partition_point(|(k, _)| k.as_slice() < *range.start());
610 return Ok(self.pairs[start..]
611 .iter()
612 .take_while(|(k, _)| k.as_slice() <= *range.end())
613 .cloned()
614 .collect());
615 };
616 let mut out = Vec::new();
617 for b in blocks.from(range.start())..blocks.index.len() {
618 if blocks.index[b].first.as_slice() > *range.end() {
619 break;
620 }
621 out.extend(
622 blocks
623 .decode(b)?
624 .into_iter()
625 .filter(|(k, _)| range.contains(&k.as_slice())),
626 );
627 }
628 Ok(out)
629 }
630}
631
632fn contiguous(runs: &[RunManifest]) -> bool {
634 let mut next = 0u64;
635 for run in runs {
636 if *run.entries.start() != next {
637 return false;
638 }
639 next = run.entries.end() + 1;
640 }
641 true
642}
643
644fn load_live<S: TileStore>(
650 manifests: &Reader<S>,
651 size: u64,
652 shared: bool,
653) -> Result<(Vec<RunManifest>, bool), Error> {
654 if let Some(bytes) = manifests.store().get(LIVE)? {
655 let text = String::from_utf8_lossy(&bytes);
656 let mut runs = Vec::new();
657 let mut ok = true;
658 for line in text.lines().filter(|l| !l.is_empty()) {
659 let Ok(index) = line.parse::<u64>() else {
660 ok = false;
661 break;
662 };
663 if index >= size {
664 ok = false;
665 break;
666 }
667 match manifests.get(index)? {
668 Some(entry) if entry.starts_with(MANIFEST_MAGIC) => {
669 runs.push(Logged::decode(&entry, index, shared)?.run);
670 }
671 _ => {
672 ok = false;
673 break;
674 }
675 }
676 }
677 if ok && contiguous(&runs) {
678 return Ok((runs, false));
679 }
680 }
681 let mut runs: Vec<RunManifest> = Vec::new();
682 for index in 0..size {
683 let Some(entry) = manifests.get(index)? else {
684 return Err(Error::Tree("entry missing below the tree size"));
685 };
686 if !entry.starts_with(MANIFEST_MAGIC) {
687 continue;
688 }
689 let logged = Logged::decode(&entry, index, shared)?;
690 runs.retain(|r| {
691 !logged
692 .replaced
693 .contains(&r.manifest_index.unwrap_or(u64::MAX))
694 });
695 runs.push(logged.run);
696 runs.sort_by_key(|r| *r.entries.start());
697 }
698 if !contiguous(&runs) {
699 return Err(Error::Tree("logged runs do not cover the sealed range"));
700 }
701 Ok((runs, true))
702}
703
704fn save_live(store: &impl TileStore, runs: &[RunManifest]) -> Result<(), Error> {
705 let mut text = String::new();
706 for run in runs {
707 let index = run
708 .manifest_index
709 .ok_or(Error::Tree("live run without a manifest index"))?;
710 text.push_str(&index.to_string());
711 text.push('\n');
712 }
713 store.put(LIVE, text.as_bytes())?;
714 Ok(())
715}
716
717pub struct Index<S: TileStore> {
724 data: Reader<S>,
725 manifests: Option<Reader<S>>,
726 keys: Keys,
727 live: Vec<RunManifest>,
728 cache: RunCache,
729}
730
731impl<S: TileStore> Index<S> {
732 pub fn at(store: S, size: u64, root: [u8; 32], keys: Keys) -> Result<Self, Error> {
734 Self::with_cache(store, size, root, keys, Arc::default())
735 }
736
737 pub fn with_cache(
739 store: S,
740 size: u64,
741 root: [u8; 32],
742 keys: Keys,
743 cache: RunCache,
744 ) -> Result<Self, Error> {
745 let data = Reader::at(store, size, root, tiles_keys(&keys))?;
746 let (live, _) = load_live(&data, size, true)?;
747 Ok(Self {
748 data,
749 manifests: None,
750 keys,
751 live,
752 cache,
753 })
754 }
755
756 #[allow(clippy::too_many_arguments)]
759 pub fn at_split(
760 data_store: S,
761 data_size: u64,
762 data_root: [u8; 32],
763 index_store: S,
764 index_size: u64,
765 index_root: [u8; 32],
766 keys: Keys,
767 cache: RunCache,
768 ) -> Result<Self, Error> {
769 let data = Reader::at(data_store, data_size, data_root, tiles_keys(&keys))?;
770 let manifests = Reader::at(index_store, index_size, index_root, tiles_keys(&keys))?;
771 let (live, _) = load_live(&manifests, index_size, false)?;
772 let live = live
774 .into_iter()
775 .take_while(|run| *run.entries.end() < data_size)
776 .collect();
777 Ok(Self {
778 data,
779 manifests: Some(manifests),
780 keys,
781 live,
782 cache,
783 })
784 }
785
786 #[must_use]
788 pub fn live_runs(&self) -> &[RunManifest] {
789 &self.live
790 }
791
792 #[must_use]
794 pub fn size(&self) -> u64 {
795 self.data.size()
796 }
797
798 pub fn reader(&self) -> &Reader<S> {
800 &self.data
801 }
802
803 pub fn manifest_reader(&self) -> &Reader<S> {
806 self.manifests.as_ref().unwrap_or(&self.data)
807 }
808
809 fn last_sealed(&self) -> Option<u64> {
810 self.live.last().map(|r| *r.entries.end())
811 }
812
813 fn run(&self, run: &RunManifest) -> Result<Arc<Run>, Error> {
814 let manifest_index = run
815 .manifest_index
816 .ok_or(Error::Tree("live run without a manifest index"))?;
817 if let Some(hit) = self
818 .cache
819 .lock()
820 .map_err(|_| Error::Tree("run cache poisoned"))?
821 .get(&manifest_index)
822 {
823 return Ok(Arc::clone(hit));
824 }
825 let path = run_path(&run.entries);
826 let bytes = self
827 .manifest_reader()
828 .store()
829 .get(&path)?
830 .ok_or(Error::Tampered { what: "run file" })?;
831 let decoded = Arc::new(Run::decode(&bytes, run)?);
832 self.cache
833 .lock()
834 .map_err(|_| Error::Tree("run cache poisoned"))?
835 .insert(manifest_index, Arc::clone(&decoded));
836 Ok(decoded)
837 }
838
839 fn inclusion_in(reader: &Reader<S>, index: u64) -> Result<Inclusion, Error> {
840 let path = reader
841 .inclusion(index)?
842 .ok_or(Error::Tree("no inclusion path below the tree size"))?;
843 Ok(Inclusion {
844 index,
845 tree_size: reader.size(),
846 path,
847 })
848 }
849
850 fn located(&self, index: u64, run: Option<&RunManifest>) -> Result<Located, Error> {
851 let run = match run {
852 Some(run) => {
853 let manifest_index = run
854 .manifest_index
855 .ok_or(Error::Tree("live run without a manifest index"))?;
856 Some(Self::inclusion_in(self.manifest_reader(), manifest_index)?)
857 }
858 None => None,
859 };
860 Ok(Located {
861 entry: Self::inclusion_in(&self.data, index)?,
862 run,
863 })
864 }
865
866 fn tail_pairs(&self) -> Result<Vec<(Vec<u8>, u64)>, Error> {
868 let from = self.last_sealed().map_or(0, |last| last + 1);
869 let mut pairs = Vec::new();
870 for index in from..self.data.size() {
871 let Some(entry) = self.data.get(index)? else {
872 return Err(Error::Tree("entry missing below the tree size"));
873 };
874 if entry.starts_with(MANIFEST_MAGIC) {
875 continue;
876 }
877 pairs.push((identity(&entry).to_vec(), index));
878 for key in (self.keys)(&entry) {
879 pairs.push((key, index));
880 }
881 }
882 Ok(pairs)
883 }
884
885 fn require_coverage(&self) -> Result<(), Error> {
886 if contiguous(&self.live) {
887 Ok(())
888 } else {
889 Err(Error::Tree("live runs do not cover the sealed range"))
890 }
891 }
892
893 pub fn find(&self, key: &[u8]) -> Result<Vec<Located>, Error> {
896 self.require_coverage()?;
897 let mut found = Vec::new();
898 for run in &self.live {
899 if key < run.key_lo.as_slice() || key > run.key_hi.as_slice() {
900 continue;
901 }
902 for index in self.run(run)?.lookup(key)? {
903 found.push(self.located(index, Some(run))?);
904 }
905 }
906 for (k, index) in self.tail_pairs()? {
907 if k == key {
908 found.push(self.located(index, None)?);
909 }
910 }
911 Ok(found)
912 }
913
914 pub fn range(&self, range: RangeInclusive<&[u8]>) -> Result<Vec<Located>, Error> {
917 self.require_coverage()?;
918 let mut hits: Vec<(Vec<u8>, u64, Option<usize>)> = Vec::new();
919 for (n, run) in self.live.iter().enumerate() {
920 if run.key_hi.as_slice() < *range.start() || run.key_lo.as_slice() > *range.end() {
921 continue;
922 }
923 for (k, index) in self.run(run)?.pairs_in(&range)? {
924 hits.push((k, index, Some(n)));
925 }
926 }
927 for (k, index) in self.tail_pairs()? {
928 if k.as_slice() >= *range.start() && k.as_slice() <= *range.end() {
929 hits.push((k, index, None));
930 }
931 }
932 hits.sort();
933 hits.into_iter()
934 .map(|(_, index, run)| self.located(index, run.map(|n| &self.live[n])))
935 .collect()
936 }
937}
938
939pub struct Store<S: TileStore> {
942 data: Writer<S>,
943 manifests: Option<Writer<S>>,
944 keys: Keys,
945 live: Vec<RunManifest>,
946 cache: RunCache,
947 trusted: Option<(u64, [u8; 32])>,
948}
949
950impl<S: TileStore> Store<S> {
951 pub fn open(store: S, keys: Keys) -> Result<Self, Error> {
954 let data = Writer::open(store, tiles_keys(&keys))?;
955 let mut this = Self {
956 data,
957 manifests: None,
958 keys,
959 live: Vec::new(),
960 cache: Arc::default(),
961 trusted: None,
962 };
963 this.reload_live()?;
964 Ok(this)
965 }
966
967 pub fn open_split(data: S, index: S, keys: Keys) -> Result<Self, Error> {
970 let data = Writer::open(data, tiles_keys(&keys))?;
971 let manifests = Writer::open(index, tiles_keys(&keys))?;
972 let mut this = Self {
973 data,
974 manifests: Some(manifests),
975 keys,
976 live: Vec::new(),
977 cache: Arc::default(),
978 trusted: None,
979 };
980 this.reload_live()?;
981 Ok(this)
982 }
983
984 pub fn open_trusting(store: S, keys: Keys, size: u64, root: [u8; 32]) -> Result<Self, Error> {
989 let mut this = Self::open(store, keys)?;
990 this.trusted = Some((size, root));
991 Ok(this)
992 }
993
994 #[must_use]
996 pub fn live_runs(&self) -> &[RunManifest] {
997 &self.live
998 }
999
1000 #[must_use]
1002 pub fn size(&self) -> u64 {
1003 self.data.size()
1004 }
1005
1006 #[must_use]
1008 pub fn root(&self) -> [u8; 32] {
1009 self.data.root()
1010 }
1011
1012 pub fn writer(&self) -> &Writer<S> {
1014 &self.data
1015 }
1016
1017 pub fn index_writer(&self) -> Option<&Writer<S>> {
1019 self.manifests.as_ref()
1020 }
1021
1022 fn manifest_writer(&self) -> &Writer<S> {
1023 self.manifests.as_ref().unwrap_or(&self.data)
1024 }
1025
1026 fn manifest_store(&self) -> &S {
1027 self.manifest_writer().store()
1028 }
1029
1030 pub fn index(&self) -> Result<Index<&S>, Error> {
1032 match &self.manifests {
1033 None => Index::with_cache(
1034 self.data.store(),
1035 self.data.size(),
1036 self.data.root(),
1037 Arc::clone(&self.keys),
1038 Arc::clone(&self.cache),
1039 ),
1040 Some(manifests) => Index::at_split(
1041 self.data.store(),
1042 self.data.size(),
1043 self.data.root(),
1044 manifests.store(),
1045 manifests.size(),
1046 manifests.root(),
1047 Arc::clone(&self.keys),
1048 Arc::clone(&self.cache),
1049 ),
1050 }
1051 }
1052
1053 pub fn append_batch<I>(&mut self, entries: I) -> Result<std::ops::Range<u64>, Error>
1057 where
1058 I: IntoIterator<Item = Vec<u8>>,
1059 {
1060 let entries: Vec<Vec<u8>> = entries.into_iter().collect();
1061 if entries.iter().any(|e| e.starts_with(MANIFEST_MAGIC)) {
1062 return Err(Error::Tree(
1063 "an entry may not start with the manifest magic",
1064 ));
1065 }
1066 Ok(self.data.append_batch(entries)?)
1067 }
1068
1069 fn reload_live(&mut self) -> Result<(), Error> {
1070 let shared = self.manifests.is_none();
1071 let writer = self.manifest_writer();
1072 let reader = Reader::at(
1073 writer.store(),
1074 writer.size(),
1075 writer.root(),
1076 tiles_keys(&self.keys),
1077 )?;
1078 let (live, rebuilt) = load_live(&reader, writer.size(), shared)?;
1079 drop(reader);
1080 if rebuilt {
1081 save_live(self.manifest_store(), &live)?;
1082 }
1083 self.live = live;
1084 Ok(())
1085 }
1086
1087 fn last_sealed(&self) -> Option<u64> {
1088 self.live.last().map(|r| *r.entries.end())
1089 }
1090
1091 fn log_run(
1092 &mut self,
1093 entries: RangeInclusive<u64>,
1094 pairs: Vec<(Vec<u8>, u64)>,
1095 replaced: Vec<u64>,
1096 ) -> Result<RunManifest, Error> {
1097 let bytes = Run::encode_blocks(&entries, &pairs)?;
1098 let path = run_path(&entries);
1099 if !self.manifest_store().create_new(&path, &bytes)? {
1100 return Err(Error::Tree("a run for that entry range is already stored"));
1101 }
1102 let (key_lo, key_hi) = match (pairs.first(), pairs.last()) {
1103 (Some(first), Some(last)) => (first.0.clone(), last.0.clone()),
1104 _ => (Vec::new(), Vec::new()),
1105 };
1106 let run = RunManifest {
1107 entries,
1108 key_lo,
1109 key_hi,
1110 run_hash: identity(&bytes),
1111 manifest_index: None,
1112 };
1113 let logged = Logged {
1114 run,
1115 data_size: self.data.size(),
1116 data_root: self.data.root(),
1117 replaced,
1118 };
1119 let encoded = logged.encode()?;
1120 let manifest_index = match &mut self.manifests {
1121 Some(manifests) => manifests.append(&encoded)?,
1122 None => self.data.append(&encoded)?,
1123 };
1124 let mut run = logged.run;
1125 run.manifest_index = Some(manifest_index);
1126 Ok(run)
1127 }
1128}
1129
1130impl<S: TileStore> AuthenticatedLog for Store<S> {
1131 fn append(&mut self, entry: &[u8]) -> Result<u64, Error> {
1132 Ok(self.append_batch([entry.to_vec()])?.start)
1133 }
1134
1135 fn find(&self, key: &[u8]) -> Result<Vec<Located>, Error> {
1136 self.index()?.find(key)
1137 }
1138
1139 fn range(&self, range: RangeInclusive<&[u8]>) -> Result<Vec<Located>, Error> {
1140 self.index()?.range(range)
1141 }
1142
1143 fn seal(&mut self) -> Result<RunManifest, Error> {
1144 let index = self.index()?;
1145 index.require_coverage()?;
1146 let lo = self.last_sealed().map_or(0, |last| last + 1);
1147 let size = self.data.size();
1148 if lo >= size {
1149 return Err(Error::Tree("nothing to seal"));
1150 }
1151 let mut pairs = index.tail_pairs()?;
1152 drop(index);
1153 if pairs.is_empty() {
1154 return Err(Error::Tree("nothing to seal"));
1157 }
1158 pairs.sort();
1159 pairs.dedup();
1160 let run = self.log_run(lo..=size - 1, pairs, Vec::new())?;
1161 self.live.push(run.clone());
1162 save_live(self.manifest_store(), &self.live)?;
1163 Ok(run)
1164 }
1165
1166 fn compact(&mut self) -> Result<Compaction, Error> {
1167 let index = self.index()?;
1168 index.require_coverage()?;
1169 if self.live.len() < 2 {
1170 return Err(Error::Tree("fewer than two runs to compact"));
1171 }
1172 let mut merged: BTreeSet<(Vec<u8>, u64)> = BTreeSet::new();
1173 let mut replaced = Vec::new();
1174 for run in &self.live {
1175 merged.extend(index.run(run)?.all_pairs()?);
1176 replaced.push(
1177 run.manifest_index
1178 .ok_or(Error::Tree("live run without a manifest index"))?,
1179 );
1180 }
1181 drop(index);
1182 let lo = *self.live[0].entries.start();
1183 let hi = *self.live[self.live.len() - 1].entries.end();
1184 let run = self.log_run(lo..=hi, merged.into_iter().collect(), replaced.clone())?;
1185 let written = run.manifest_index.expect("logged");
1186 self.live = vec![run];
1187 save_live(self.manifest_store(), &self.live)?;
1188 Ok(Compaction { replaced, written })
1189 }
1190
1191 fn recover(&mut self) -> Result<Recovery, Error> {
1192 let (checkpoint_size, checkpoint_root) = self
1193 .trusted
1194 .ok_or(Error::Tree("no trusted checkpoint to recover from"))?;
1195 let size = self.data.size();
1196 if checkpoint_size > size {
1197 return Err(Error::Tampered { what: "tree state" });
1198 }
1199 let consistency = if checkpoint_size == 0 {
1200 Vec::new()
1201 } else {
1202 self.index()?
1203 .reader()
1204 .consistency(checkpoint_size)?
1205 .ok_or(Error::Tree("no consistency path from the checkpoint"))?
1206 };
1207 let old_size =
1208 usize::try_from(checkpoint_size).map_err(|_| Error::Tree("size over usize"))?;
1209 let new_size = usize::try_from(size).map_err(|_| Error::Tree("size over usize"))?;
1210 if checkpoint_size > 0
1211 && !rfc9162::verify_consistency(
1212 old_size,
1213 &rfc9162::hex(&checkpoint_root),
1214 new_size,
1215 &rfc9162::hex(&self.data.root()),
1216 &consistency,
1217 )
1218 {
1219 return Err(Error::Tampered { what: "tree state" });
1220 }
1221 self.reload_live()?;
1222 let (quarantined, _) = self.data.quarantined();
1223 Ok(Recovery {
1224 checkpoint_size,
1225 replayed: size - checkpoint_size,
1226 quarantined: u64::try_from(quarantined.len()).unwrap_or(u64::MAX),
1227 consistency,
1228 })
1229 }
1230}
1231
1232impl From<rfc9162_tiles::Error> for Error {
1233 fn from(e: rfc9162_tiles::Error) -> Self {
1234 match e {
1235 rfc9162_tiles::Error::Io(e) => Self::Store(e),
1236 rfc9162_tiles::Error::Corrupt { .. } => Self::Tampered { what: "tile store" },
1237 other => Self::Tiles(other.to_string()),
1238 }
1239 }
1240}
1241
1242#[cfg(test)]
1243mod tests {
1244 use super::*;
1245
1246 fn pairs() -> Vec<(Vec<u8>, u64)> {
1247 let mut pairs: Vec<(Vec<u8>, u64)> = (0u64..300)
1248 .flat_map(|i| {
1249 [
1250 (
1251 format!("module=GROMACS/2026.{}-foss-2026.1", i % 7).into_bytes(),
1252 1000 + i,
1253 ),
1254 (
1255 format!("path=software/PETSc/3.24.{i}/lib/libpetsc.so").into_bytes(),
1256 1000 + i,
1257 ),
1258 (
1259 format!("target=x86_64/amd/zen{}", 2 + i % 3).into_bytes(),
1260 1000 + i,
1261 ),
1262 ]
1263 })
1264 .collect();
1265 pairs.sort();
1266 pairs
1267 }
1268
1269 fn manifest(entries: RangeInclusive<u64>, bytes: &[u8]) -> RunManifest {
1270 RunManifest {
1271 entries,
1272 key_lo: Vec::new(),
1273 key_hi: Vec::new(),
1274 run_hash: identity(bytes),
1275 manifest_index: None,
1276 }
1277 }
1278
1279 #[test]
1280 fn v2_round_trips_and_answers_lookups() {
1281 let entries = 1000..=1299;
1282 let pairs = pairs();
1283 let bytes = Run::encode(&entries, &pairs).unwrap();
1284 assert!(bytes.starts_with(RUN_MAGIC));
1285 let run = Run::decode(&bytes, &manifest(entries, &bytes)).unwrap();
1286 assert_eq!(run.pairs, pairs);
1287 assert_eq!(run.indices_of(b"target=x86_64/amd/zen3").len(), 100);
1288 assert!(run.indices_of(b"target=x86_64/amd/zen5").is_empty());
1289 }
1290
1291 #[test]
1292 fn v1_runs_stay_readable() {
1293 let entries = 1000..=1299;
1294 let pairs = pairs();
1295 let bytes = Run::encode_v1(&entries, &pairs).unwrap();
1296 let run = Run::decode(&bytes, &manifest(entries, &bytes)).unwrap();
1297 assert_eq!(run.pairs, pairs);
1298 }
1299
1300 #[test]
1301 fn v2_is_smaller_than_v1_on_shared_prefixes() {
1302 let entries = 1000..=1299;
1303 let pairs = pairs();
1304 let v1 = Run::encode_v1(&entries, &pairs).unwrap().len();
1305 let v2 = Run::encode(&entries, &pairs).unwrap().len();
1306 assert!(v2 * 2 < v1, "v2 {v2} bytes, v1 {v1} bytes");
1307 }
1308
1309 #[test]
1310 fn a_shared_length_past_the_previous_key_is_refused() {
1311 let entries = 0..=0;
1312 let mut bytes = RUN_MAGIC.to_vec();
1313 put_u64(&mut bytes, 0);
1314 put_u64(&mut bytes, 0);
1315 put_varint(&mut bytes, 1);
1316 put_varint(&mut bytes, 3);
1317 put_varint(&mut bytes, 1);
1318 bytes.push(b'k');
1319 put_varint(&mut bytes, 0);
1320 put_varint(&mut bytes, 0);
1321 let expect = manifest(entries, &bytes);
1322 assert!(matches!(
1323 Run::decode(&bytes, &expect),
1324 Err(Error::Tampered { .. })
1325 ));
1326 }
1327
1328 #[test]
1329 fn an_index_outside_the_run_is_refused() {
1330 let entries = 10..=11;
1331 let bytes = Run::encode(&(10..=20), &[(b"k".to_vec(), 15)]).unwrap();
1332 let mut patched = RUN_MAGIC.to_vec();
1333 patched.extend_from_slice(&bytes[RUN_MAGIC.len()..]);
1334 patched[RUN_MAGIC.len() + 8..RUN_MAGIC.len() + 16].copy_from_slice(&11u64.to_be_bytes());
1337 let expect = manifest(entries, &patched);
1338 assert!(matches!(
1339 Run::decode(&patched, &expect),
1340 Err(Error::Tampered { .. })
1341 ));
1342 }
1343
1344 #[test]
1345 fn an_overlong_varint_is_refused() {
1346 let mut c = Cursor {
1347 bytes: &[0xff; 11],
1348 at: 0,
1349 what: "test",
1350 };
1351 assert!(c.varint().is_err());
1352 let mut out = Vec::new();
1353 put_varint(&mut out, u64::MAX);
1354 let mut c = Cursor {
1355 bytes: &out,
1356 at: 0,
1357 what: "test",
1358 };
1359 assert_eq!(c.varint().unwrap(), u64::MAX);
1360 }
1361}
1362
1363#[cfg(test)]
1364mod block_tests {
1365 use super::*;
1366
1367 fn manifest(entries: RangeInclusive<u64>, bytes: &[u8]) -> RunManifest {
1368 RunManifest {
1369 entries,
1370 key_lo: Vec::new(),
1371 key_hi: Vec::new(),
1372 run_hash: identity(bytes),
1373 manifest_index: None,
1374 }
1375 }
1376
1377 fn pairs() -> Vec<(Vec<u8>, u64)> {
1379 let mut pairs: Vec<(Vec<u8>, u64)> = (0u64..1000)
1380 .flat_map(|i| {
1381 let module = if i % 3 == 0 && i < 900 {
1382 "module=hot".to_string()
1383 } else {
1384 format!("module=m{i:04}")
1385 };
1386 [
1387 (module.into_bytes(), i),
1388 (format!("target=t{}", i % 4).into_bytes(), i),
1389 ]
1390 })
1391 .collect();
1392 pairs.sort();
1393 pairs
1394 }
1395
1396 fn v3(pairs: &[(Vec<u8>, u64)]) -> Run {
1397 let bytes = Run::encode_blocks(&(0..=999), pairs).unwrap();
1398 assert!(bytes.starts_with(RUN_MAGIC_V3));
1399 Run::decode(&bytes, &manifest(0..=999, &bytes)).unwrap()
1400 }
1401
1402 fn brute(pairs: &[(Vec<u8>, u64)], lo: &[u8], hi: &[u8]) -> Vec<(Vec<u8>, u64)> {
1403 pairs
1404 .iter()
1405 .filter(|(k, _)| k.as_slice() >= lo && k.as_slice() <= hi)
1406 .cloned()
1407 .collect()
1408 }
1409
1410 #[test]
1411 fn v3_keeps_blocks_encoded_and_returns_every_pair() {
1412 let pairs = pairs();
1413 let run = v3(&pairs);
1414 let blocks = run.blocks.as_ref().unwrap();
1415 assert_eq!(blocks.index.len(), pairs.len().div_ceil(BLOCK_PAIRS));
1416 assert!(run.pairs.is_empty());
1417 assert_eq!(run.all_pairs().unwrap(), pairs);
1418 }
1419
1420 #[test]
1421 fn a_key_spanning_blocks_is_found_in_full() {
1422 let pairs = pairs();
1423 let run = v3(&pairs);
1424 let expect: Vec<u64> = brute(&pairs, b"module=hot", b"module=hot")
1425 .into_iter()
1426 .map(|(_, i)| i)
1427 .collect();
1428 assert_eq!(expect.len(), 300);
1429 assert_eq!(run.lookup(b"module=hot").unwrap(), expect);
1430 assert!(run.lookup(b"module=absent").unwrap().is_empty());
1431 }
1432
1433 #[test]
1434 fn ranges_match_a_scan_of_the_pairs() {
1435 let pairs = pairs();
1436 let run = v3(&pairs);
1437 for (lo, hi) in [
1438 (&b"module=m0100"[..], &b"module=m0200"[..]),
1439 (b"module=", b"module=\xff"),
1440 (b"target=t1", b"target=t2"),
1441 (b"a", b"z"),
1442 (b"zz", b"zzz"),
1443 ] {
1444 assert_eq!(run.pairs_in(&(lo..=hi)).unwrap(), brute(&pairs, lo, hi));
1445 }
1446 }
1447
1448 #[test]
1449 fn a_lookup_decodes_only_the_blocks_it_needs() {
1450 let pairs = pairs();
1451 let run = v3(&pairs);
1452 let blocks = run.blocks.as_ref().unwrap();
1453 let first = blocks.from(b"target=t3");
1454 let last = blocks
1455 .index
1456 .partition_point(|b| b.first.as_slice() <= &b"target=t3"[..]);
1457 assert!(last - first < blocks.index.len() / 4, "{first}..{last}");
1458 }
1459
1460 #[test]
1461 fn v2_and_v3_answer_alike() {
1462 let pairs = pairs();
1463 let bytes = Run::encode(&(0..=999), &pairs).unwrap();
1464 let old = Run::decode(&bytes, &manifest(0..=999, &bytes)).unwrap();
1465 let new = v3(&pairs);
1466 for key in [&b"module=hot"[..], b"module=m0001", b"target=t0", b"nope"] {
1467 assert_eq!(old.lookup(key).unwrap(), new.lookup(key).unwrap());
1468 }
1469 }
1470
1471 #[test]
1472 fn a_tampered_block_is_refused_when_read() {
1473 let pairs = pairs();
1474 let bytes = Run::encode_blocks(&(0..=999), &pairs).unwrap();
1475 let run = Run::decode(&bytes, &manifest(0..=999, &bytes)).unwrap();
1476 let mut blocks = run.blocks.unwrap();
1477 let b = blocks.index.len() / 2;
1478 let at = blocks.index[b].start;
1479 blocks.body[at] = 0x7f;
1480 assert!(matches!(blocks.decode(b), Err(Error::Tampered { .. })));
1481 }
1482
1483 #[test]
1484 fn a_count_that_disagrees_with_the_blocks_is_refused() {
1485 let pairs = pairs();
1486 let mut bytes = Run::encode_blocks(&(0..=999), &pairs).unwrap();
1487 let at = RUN_MAGIC_V3.len() + 16;
1489 assert_eq!(&bytes[at..at + 2], &[0xd0, 0x0f]);
1490 bytes[at] = 0xcf;
1491 let expect = manifest(0..=999, &bytes);
1492 assert!(matches!(
1493 Run::decode(&bytes, &expect),
1494 Err(Error::Tampered { .. })
1495 ));
1496 }
1497}