Skip to main content

aranya_runtime/storage/linear/
mod.rs

1//! Persistant linear storage implemenatation.
2//!
3//! `LinearStorage` is a graph storage implementation backed by a file-like byte
4//! storage interface. This is designed to be usable across many environments
5//! with minimal assumptions on the underlying storage.
6//!
7//! # Layout
8//!
9//! `[x]` is page aligned.
10//!
11//! ```text
12//! // Control section
13//! [Base] [Root] [Root]
14//! // Data section
15//! [Segment or FactIndex]
16//! |
17//! V
18//! ```
19//!
20//! The `LinearStorage` will exclusively modify the control section. The data
21//! section is append-only but can be read concurrently. If written data is not
22//! committed, it may be overwritten and will become unreachable by intended
23//! means.
24
25pub mod libc;
26
27#[cfg(feature = "testing")]
28pub mod testing;
29
30use alloc::{boxed::Box, collections::BTreeMap, string::String, vec, vec::Vec};
31use core::ops::Bound;
32
33use buggy::{Bug, BugExt as _, bug};
34use serde::{Deserialize, Serialize};
35use vec1::Vec1;
36
37use crate::{
38    Address, Bytes, Checkpoint, CmdId, Command, Fact, FactIndex, FactPerspective, GraphId, Keys,
39    Location, MaxCut, Perspective, PolicyId, Prior, Priority, Query, QueryMut, Revertable, Segment,
40    SegmentIndex, Storage, StorageError, StorageProvider, TraversalBuffer,
41};
42
43pub mod io;
44pub use io::*;
45
46/// Maximum depth of fact indices before compaction.
47///
48/// A lower value will speed up search queries but require more compaction,
49/// slowing down fact index creation and using more storage space.
50///
51/// In the future, this may be configurable at runtime or dynamic based on
52/// heuristics such as fact density.
53///
54/// 16 is our initial guess for balance.
55///
56/// This must be at least 2.
57const MAX_FACT_INDEX_DEPTH: u64 = 16;
58
59pub struct LinearStorageProvider<FM: IoManager> {
60    manager: FM,
61    storage: BTreeMap<GraphId, LinearStorage<FM::Writer>>,
62}
63
64pub struct LinearStorage<W> {
65    writer: W,
66}
67
68#[derive(Debug)]
69pub struct LinearSegment<R> {
70    repr: SegmentRepr,
71    reader: R,
72}
73
74#[derive(Debug, Serialize, Deserialize)]
75struct SegmentRepr {
76    /// Self offset in file.
77    offset: SegmentIndex,
78    prior: Prior<Location>,
79    parents: Prior<Address>,
80    policy: PolicyId,
81    /// Offset in file to associated fact index.
82    facts: u64,
83    /// Prior fact offset used to reconstruct facts within segment.
84    prior_facts: Option<u64>,
85    commands: Vec1<CommandData>,
86    max_cut: MaxCut,
87    skip_list: Vec<Location>,
88}
89
90#[derive(Debug, Serialize, Deserialize)]
91struct CommandData {
92    id: CmdId,
93    priority: Priority,
94    policy: Option<Bytes>,
95    data: Bytes,
96    updates: Vec<Update>,
97}
98
99pub struct LinearCommand<'a> {
100    id: &'a CmdId,
101    parent: Prior<Address>,
102    priority: Priority,
103    policy: Option<&'a [u8]>,
104    data: &'a [u8],
105    max_cut: MaxCut,
106}
107
108type Update = (String, Keys, Option<Bytes>);
109type FactMap = BTreeMap<Keys, Option<Box<[u8]>>>;
110type NamedFactMap = BTreeMap<String, FactMap>;
111
112#[derive(Debug)]
113pub struct LinearFactIndex<R> {
114    repr: FactIndexRepr,
115    reader: R,
116}
117
118#[derive(Debug, Serialize, Deserialize)]
119struct FactIndexRepr {
120    /// Self offset in file.
121    offset: u64,
122    /// Offset of prior fact index.
123    prior: Option<u64>,
124    /// Depth of this fact index.
125    ///
126    /// `prior.depth + 1`, or just `1` if no prior
127    depth: u64,
128    /// Facts in sorted order
129    facts: NamedFactMap,
130}
131
132#[derive(Debug)]
133pub struct LinearPerspective<R> {
134    prior: Prior<Location>,
135    parents: Prior<Address>,
136    policy: PolicyId,
137    facts: LinearFactPerspective<R>,
138    commands: Vec<CommandData>,
139    current_updates: Vec<Update>,
140    max_cut: MaxCut,
141    last_common_ancestor: Option<Location>,
142}
143
144impl<R> LinearPerspective<R> {
145    fn new(
146        prior: Prior<Location>,
147        parents: Prior<Address>,
148        policy: PolicyId,
149        prior_facts: FactPerspectivePrior<R>,
150        max_cut: MaxCut,
151        last_common_ancestor: Option<Location>,
152    ) -> Self {
153        Self {
154            prior,
155            parents,
156            policy,
157            facts: LinearFactPerspective::new(prior_facts),
158            commands: Vec::new(),
159            current_updates: Vec::new(),
160            max_cut,
161            last_common_ancestor,
162        }
163    }
164}
165
166#[derive(Debug)]
167pub struct LinearFactPerspective<R> {
168    map: BTreeMap<String, BTreeMap<Keys, Option<Bytes>>>,
169    prior: FactPerspectivePrior<R>,
170}
171
172impl<R> LinearFactPerspective<R> {
173    fn new(prior: FactPerspectivePrior<R>) -> Self {
174        Self {
175            map: BTreeMap::new(),
176            prior,
177        }
178    }
179}
180
181#[derive(Debug)]
182enum FactPerspectivePrior<R> {
183    None,
184    FactPerspective(Box<LinearFactPerspective<R>>),
185    FactIndex { offset: u64, reader: R },
186}
187
188impl<R> FactPerspectivePrior<R> {
189    fn is_none(&self) -> bool {
190        matches!(self, Self::None)
191    }
192}
193
194impl<FM: IoManager + Default> Default for LinearStorageProvider<FM> {
195    fn default() -> Self {
196        Self {
197            manager: FM::default(),
198            storage: BTreeMap::new(),
199        }
200    }
201}
202
203impl<FM: IoManager> LinearStorageProvider<FM> {
204    pub fn new(manager: FM) -> Self {
205        Self {
206            manager,
207            storage: BTreeMap::new(),
208        }
209    }
210}
211
212impl<FM: IoManager> StorageProvider for LinearStorageProvider<FM> {
213    type Perspective = LinearPerspective<<FM::Writer as Write>::ReadOnly>;
214    type Segment = LinearSegment<<FM::Writer as Write>::ReadOnly>;
215    type Storage = LinearStorage<FM::Writer>;
216
217    fn new_perspective(&mut self, policy_id: PolicyId) -> Self::Perspective {
218        LinearPerspective::new(
219            Prior::None,
220            Prior::None,
221            policy_id,
222            FactPerspectivePrior::None,
223            MaxCut::new(0),
224            None,
225        )
226    }
227
228    fn new_storage(
229        &mut self,
230        init: Self::Perspective,
231    ) -> Result<(GraphId, &mut Self::Storage), StorageError> {
232        use alloc::collections::btree_map::Entry;
233
234        if init.commands.is_empty() {
235            return Err(StorageError::EmptyPerspective);
236        }
237        let graph_id = GraphId::transmute(init.commands[0].id);
238        let Entry::Vacant(entry) = self.storage.entry(graph_id) else {
239            return Err(StorageError::StorageExists);
240        };
241
242        let file = self.manager.create(graph_id)?;
243        Ok((graph_id, entry.insert(LinearStorage::create(file, init)?)))
244    }
245
246    fn get_storage(&mut self, graph: GraphId) -> Result<&mut Self::Storage, StorageError> {
247        use alloc::collections::btree_map::Entry;
248
249        let entry = match self.storage.entry(graph) {
250            Entry::Vacant(v) => v,
251            Entry::Occupied(o) => return Ok(o.into_mut()),
252        };
253
254        let file = self
255            .manager
256            .open(graph)?
257            .ok_or(StorageError::NoSuchStorage)?;
258        Ok(entry.insert(LinearStorage::open(file)?))
259    }
260
261    fn remove_storage(&mut self, graph: GraphId) -> Result<(), StorageError> {
262        self.manager.remove(graph)?;
263
264        self.storage
265            .remove(&graph)
266            .ok_or(StorageError::NoSuchStorage)?;
267
268        Ok(())
269    }
270
271    fn list_graph_ids(
272        &mut self,
273    ) -> Result<impl Iterator<Item = Result<GraphId, StorageError>>, StorageError> {
274        self.manager.list()
275    }
276}
277
278/// Maximum segment-walk distance for skip-list construction. Below this
279/// threshold the segments are cheap enough to walk one-by-one, so neither
280/// the rich-anchor probe nor a freshly built skip list pay for themselves.
281const MIN_SKIP_GAP: u64 = 10;
282
283/// Skip-list target boundaries for a segment of length `n`: `n/2`, `3n/4`,
284/// `7n/8`, ..., halving the remaining gap each step. Continues until the
285/// gap from the final boundary to `n` is ≤ [`MIN_SKIP_GAP`], so the walk
286/// from head to the first skip entry never exceeds the cheap-walk
287/// threshold. Returned ascending; callers walk backwards and pop
288/// highest-first. Empty when `n < 2`.
289fn skip_target_boundaries(n: u64) -> Result<Vec<MaxCut>, StorageError> {
290    let mut targets = vec![];
291    let mut boundary = n / 2;
292    while boundary > 0 {
293        targets.push(MaxCut::new(boundary));
294        let gap = n
295            .checked_sub(boundary)
296            .assume("boundary < n by loop invariant")?;
297        if gap <= MIN_SKIP_GAP {
298            break;
299        }
300        boundary = boundary
301            .checked_add(gap / 2)
302            .assume("boundary + gap/2 <= n <= u64::MAX")?;
303    }
304    Ok(targets)
305}
306
307impl<W: Write> LinearStorage<W> {
308    fn create(mut writer: W, init: LinearPerspective<W::ReadOnly>) -> Result<Self, StorageError> {
309        assert!(matches!(init.prior, Prior::None));
310        assert!(matches!(init.parents, Prior::None));
311        assert!(matches!(init.facts.prior, FactPerspectivePrior::None));
312
313        let mut map = init.facts.map;
314        map.retain(|_, kv| !kv.is_empty());
315
316        let facts = writer
317            .append(|offset| FactIndexRepr {
318                offset,
319                prior: None,
320                depth: 1,
321                facts: map,
322            })?
323            .offset;
324
325        let commands = init
326            .commands
327            .try_into()
328            .map_err(|_| StorageError::EmptyPerspective)?;
329        let segment = writer.append(|offset| SegmentRepr {
330            offset: SegmentIndex::new(offset),
331            prior: Prior::None,
332            parents: Prior::None,
333            policy: init.policy,
334            facts,
335            prior_facts: None,
336            commands,
337            max_cut: MaxCut::new(0),
338            skip_list: vec![],
339        })?;
340
341        let head = Location::new(
342            segment.offset,
343            segment
344                .max_cut
345                .checked_add(
346                    segment
347                        .commands
348                        .len()
349                        .checked_sub(1)
350                        .assume("vec1 length >= 1")? as u64,
351                )
352                .assume("valid max cut")?,
353        );
354
355        writer.commit(head)?;
356
357        let storage = Self { writer };
358
359        Ok(storage)
360    }
361
362    fn open(writer: W) -> Result<Self, StorageError> {
363        Ok(Self { writer })
364    }
365
366    fn compact(&mut self, mut repr: FactIndexRepr) -> Result<FactIndexRepr, StorageError> {
367        let mut map = NamedFactMap::new();
368        let reader = self.writer.readonly();
369        loop {
370            for (name, kv) in repr.facts {
371                let sub = map.entry(name).or_default();
372                for (k, v) in kv {
373                    sub.entry(k).or_insert(v);
374                }
375            }
376            let Some(offset) = repr.prior else { break };
377            repr = reader.fetch(offset)?;
378        }
379
380        // Since there's no prior, we can remove tombstones
381        map.retain(|_, kv| {
382            kv.retain(|_, v| v.is_some());
383            !kv.is_empty()
384        });
385
386        Ok(self
387            .write_facts(LinearFactPerspective {
388                map,
389                prior: FactPerspectivePrior::None,
390            })?
391            .repr)
392    }
393
394    /// Whether an ancestor within [`MIN_SKIP_GAP`] segments of `start` already
395    /// carries a rich skip list (`len > 1`). The walk crosses merges via the
396    /// LCA recorded as the sole entry in a merge segment's LCA-only skip list,
397    /// so a rich anchor past a merge is still reachable.
398    fn has_nearby_rich_anchor(&self, start: Location) -> Result<bool, StorageError> {
399        let mut check = start;
400        for _ in 0..MIN_SKIP_GAP {
401            let seg = self.get_segment(check)?;
402            if seg.skip_list().len() > 1 {
403                return Ok(true);
404            }
405            match seg.prior() {
406                Prior::Single(p) => check = p,
407                Prior::Merge(_, _) => {
408                    check = seg
409                        .skip_list()
410                        .last()
411                        .copied()
412                        .assume("merge skip list must end with LCA")?;
413                }
414                Prior::None => return Ok(false),
415            }
416        }
417        Ok(false)
418    }
419
420    /// Build the skip list for a new segment with the given `prior`,
421    /// `last_common_ancestor` (required for merges), and length `n`.
422    ///
423    /// Returns:
424    /// - empty for `Prior::None`,
425    /// - `[lca]` (or empty for non-merges) when a nearby ancestor already
426    ///   has a rich skip list or `n < MIN_SKIP_GAP`,
427    /// - otherwise, a list of skip targets at `n/2, 3n/4, 7n/8, ...` plus
428    ///   the LCA for merges. See [`skip_target_boundaries`].
429    fn build_skip_list(
430        &self,
431        prior: Prior<Location>,
432        last_common_ancestor: Option<Location>,
433        n: u64,
434    ) -> Result<Vec<Location>, StorageError> {
435        let (walk_start, lca) = match prior {
436            Prior::None => return Ok(vec![]),
437            Prior::Merge(_, _) => {
438                let lca = last_common_ancestor.assume("lca must exist")?;
439                (lca, Some(lca))
440            }
441            Prior::Single(l) => (l, None),
442        };
443
444        if self.has_nearby_rich_anchor(walk_start)? || n < MIN_SKIP_GAP {
445            return Ok(lca.into_iter().collect());
446        }
447
448        let targets = skip_target_boundaries(n)?;
449        let mut skips = self.walk_collecting_skips(walk_start, targets)?;
450
451        // Always include the LCA for merge segments.
452        if let Some(lca) = lca
453            && !skips.contains(&lca)
454        {
455            skips.push(lca);
456        }
457
458        skips.sort_by_key(|loc| loc.max_cut);
459        skips.dedup();
460        Ok(skips)
461    }
462
463    /// Walk backwards from `start`, recording the `first_location` of each
464    /// segment as it crosses a target in `targets` (ascending; consumed
465    /// highest-first via `pop`). At each segment, jump along the smallest
466    /// available skip entry that still stays at or above the next target;
467    /// otherwise step to the parent. Stops when targets are exhausted or
468    /// no further progress toward them is possible.
469    fn walk_collecting_skips(
470        &self,
471        start: Location,
472        mut targets: Vec<MaxCut>,
473    ) -> Result<Vec<Location>, StorageError> {
474        let mut skips = vec![];
475        let mut current = start;
476
477        loop {
478            let seg = self.get_segment(current)?;
479            let seg_min = seg.shortest_max_cut();
480
481            // Record any targets we've reached or passed.
482            while let Some(&t) = targets.last() {
483                if t >= seg_min {
484                    skips.push(seg.first_location());
485                    targets.pop();
486                } else {
487                    break;
488                }
489            }
490
491            let Some(&next_target) = targets.last() else {
492                break;
493            };
494
495            // Smallest skip entry at or above next_target (and below
496            // current), i.e. the tightest jump that still makes progress.
497            let best = seg
498                .skip_list()
499                .iter()
500                .copied()
501                .filter(|s| s.max_cut >= next_target && s.max_cut < current.max_cut)
502                .min_by_key(|s| s.max_cut);
503            if let Some(skip) = best {
504                current = skip;
505                continue;
506            }
507
508            match seg.prior() {
509                Prior::Single(p) if p.max_cut >= next_target => current = p,
510                _ => break,
511            }
512        }
513
514        Ok(skips)
515    }
516
517    /// Write a fact perspective out if non-empty, returning it with the prior fact offset to be stored in the segment.
518    fn write_facts_with_prior(
519        &mut self,
520        facts: <Self as Storage>::FactPerspective,
521    ) -> Result<(<Self as Storage>::FactIndex, Option<u64>), StorageError> {
522        let mut prior = match facts.prior {
523            FactPerspectivePrior::None => None,
524            FactPerspectivePrior::FactPerspective(prior) => {
525                let prior = self.write_facts(*prior)?;
526                if facts.map.is_empty() {
527                    let offset = prior.repr.offset;
528                    return Ok((prior, Some(offset)));
529                }
530                Some(prior.repr)
531            }
532            FactPerspectivePrior::FactIndex { offset, reader } => {
533                let repr: FactIndexRepr = reader.fetch(offset)?;
534                if facts.map.is_empty() {
535                    let offset = repr.offset;
536                    return Ok((LinearFactIndex { repr, reader }, Some(offset)));
537                }
538                Some(repr)
539            }
540        };
541
542        let depth = if let Some(mut p) = prior.take() {
543            if p.depth > MAX_FACT_INDEX_DEPTH - 1 {
544                p = self.compact(p)?;
545            }
546            prior.insert(p).depth
547        } else {
548            0
549        };
550
551        let depth = depth.checked_add(1).assume("depth won't overflow")?;
552
553        if depth > MAX_FACT_INDEX_DEPTH {
554            bug!("fact index too deep");
555        }
556
557        let prior_offset = prior.map(|p| p.offset);
558        let repr = self.writer.append(|offset| FactIndexRepr {
559            offset,
560            prior: prior_offset,
561            depth,
562            facts: facts.map,
563        })?;
564
565        Ok((
566            LinearFactIndex {
567                repr,
568                reader: self.writer.readonly(),
569            },
570            prior_offset,
571        ))
572    }
573}
574
575impl<F: Write> Storage for LinearStorage<F> {
576    type Perspective = LinearPerspective<F::ReadOnly>;
577    type FactPerspective = LinearFactPerspective<F::ReadOnly>;
578    type Segment = LinearSegment<F::ReadOnly>;
579    type FactIndex = LinearFactIndex<F::ReadOnly>;
580
581    fn get_linear_perspective(&self, parent: Location) -> Result<Self::Perspective, StorageError> {
582        let segment = self.get_segment(parent)?;
583        let command = segment
584            .get_command(parent)
585            .ok_or(StorageError::CommandOutOfBounds(parent))?;
586        let policy = segment.repr.policy;
587        let prior_facts: FactPerspectivePrior<F::ReadOnly> = if parent == segment.head_location()? {
588            FactPerspectivePrior::FactIndex {
589                offset: segment.repr.facts,
590                reader: self.writer.readonly(),
591            }
592        } else {
593            let prior = match segment.repr.prior_facts {
594                Some(offset) => FactPerspectivePrior::FactIndex {
595                    offset,
596                    reader: self.writer.readonly(),
597                },
598                None => FactPerspectivePrior::None,
599            };
600            let mut facts = LinearFactPerspective::new(prior);
601            for data in &segment.repr.commands[..=segment.repr.cmd_index(parent.max_cut)?] {
602                facts.apply_updates(&data.updates)?;
603            }
604            if facts.prior.is_none() {
605                facts.map.retain(|_, kv| !kv.is_empty());
606            }
607            if facts.map.is_empty() {
608                facts.prior
609            } else {
610                FactPerspectivePrior::FactPerspective(Box::new(facts))
611            }
612        };
613        let prior = Prior::Single(parent);
614
615        let perspective = LinearPerspective::new(
616            prior,
617            Prior::Single(command.address()?),
618            policy,
619            prior_facts,
620            command
621                .max_cut()?
622                .checked_add(1)
623                .assume("must not overflow")?,
624            None,
625        );
626
627        Ok(perspective)
628    }
629
630    fn get_fact_perspective(
631        &self,
632        location: Location,
633    ) -> Result<Self::FactPerspective, StorageError> {
634        let segment = self.get_segment(location)?;
635
636        // If at head of segment, or no facts in segment,
637        // we don't need to apply updates.
638        if location == segment.head_location()?
639            || segment
640                .repr
641                .commands
642                .iter()
643                .all(|cmd| cmd.updates.is_empty())
644        {
645            return Ok(LinearFactPerspective::new(
646                FactPerspectivePrior::FactIndex {
647                    offset: segment.repr.facts,
648                    reader: self.writer.readonly(),
649                },
650            ));
651        }
652
653        let prior = match segment.repr.prior_facts {
654            Some(offset) => FactPerspectivePrior::FactIndex {
655                offset,
656                reader: self.writer.readonly(),
657            },
658            None => FactPerspectivePrior::None,
659        };
660        let mut facts = LinearFactPerspective::new(prior);
661        for data in &segment.repr.commands[..=segment.repr.cmd_index(location.max_cut)?] {
662            facts.apply_updates(&data.updates)?;
663        }
664
665        Ok(facts)
666    }
667
668    fn new_merge_perspective(
669        &self,
670        left: Location,
671        right: Location,
672        last_common_ancestor: Location,
673        policy_id: PolicyId,
674        braid: Self::FactIndex,
675    ) -> Result<Self::Perspective, StorageError> {
676        // TODO(jdygert): ensure braid belongs to this storage.
677        // TODO(jdygert): ensure braid ends at given command?
678        let left_segment = self.get_segment(left)?;
679        let left_command = left_segment
680            .get_command(left)
681            .ok_or(StorageError::CommandOutOfBounds(left))?;
682        let right_segment = self.get_segment(right)?;
683        let right_command = right_segment
684            .get_command(right)
685            .ok_or(StorageError::CommandOutOfBounds(right))?;
686
687        let parent = Prior::Merge(left_command.address()?, right_command.address()?);
688
689        if policy_id != left_segment.policy() && policy_id != right_segment.policy() {
690            return Err(StorageError::PolicyMismatch);
691        }
692
693        let prior = Prior::Merge(left, right);
694
695        let perspective = LinearPerspective::new(
696            prior,
697            parent,
698            policy_id,
699            FactPerspectivePrior::FactIndex {
700                offset: braid.repr.offset,
701                reader: braid.reader,
702            },
703            left_command
704                .max_cut()?
705                .max(right_command.max_cut()?)
706                .checked_add(1)
707                .assume("must not overflow")?,
708            Some(last_common_ancestor),
709        );
710
711        Ok(perspective)
712    }
713
714    fn get_segment(&self, location: Location) -> Result<Self::Segment, StorageError> {
715        let reader = self.writer.readonly();
716        let repr = reader.fetch(location.segment.get())?;
717        let seg = LinearSegment { repr, reader };
718
719        Ok(seg)
720    }
721
722    fn get_head(&self) -> Result<Location, StorageError> {
723        self.writer.head()
724    }
725
726    fn commit(&mut self, segment: Self::Segment) -> Result<(), StorageError> {
727        debug_assert!(
728            self.is_ancestor(
729                self.get_head()?,
730                segment.head_location()?,
731                #[allow(unused_allocation, reason = "box large type to reduce stack usage")]
732                Box::new(TraversalBuffer::new()).as_mut()
733            )?,
734            "new head segment must be descendant of old head"
735        );
736
737        self.writer.commit(segment.head_location()?)
738    }
739
740    fn write(&mut self, perspective: Self::Perspective) -> Result<Self::Segment, StorageError> {
741        // TODO(jdygert): Validate prior?
742
743        let (facts, prior_facts) = self.write_facts_with_prior(perspective.facts)?;
744        let facts = facts.repr.offset;
745
746        let commands: Vec1<CommandData> = perspective
747            .commands
748            .try_into()
749            .map_err(|_| StorageError::EmptyPerspective)?;
750
751        let skip_list = self.build_skip_list(
752            perspective.prior,
753            perspective.last_common_ancestor,
754            perspective.max_cut.get(),
755        )?;
756
757        let repr = self.writer.append(|offset| SegmentRepr {
758            offset: SegmentIndex::new(offset),
759            prior: perspective.prior,
760            parents: perspective.parents,
761            policy: perspective.policy,
762            facts,
763            prior_facts,
764            commands,
765            max_cut: perspective.max_cut,
766            skip_list,
767        })?;
768
769        Ok(LinearSegment {
770            repr,
771            reader: self.writer.readonly(),
772        })
773    }
774
775    fn write_facts(
776        &mut self,
777        facts: Self::FactPerspective,
778    ) -> Result<Self::FactIndex, StorageError> {
779        self.write_facts_with_prior(facts)
780            .map(|(fact_index, _)| fact_index)
781    }
782}
783
784impl<R: Read> Segment for LinearSegment<R> {
785    type FactIndex = LinearFactIndex<R>;
786    type Command<'a>
787        = LinearCommand<'a>
788    where
789        R: 'a;
790
791    fn index(&self) -> SegmentIndex {
792        self.repr.offset
793    }
794
795    fn head_id(&self) -> CmdId {
796        self.repr.commands.last().id
797    }
798
799    fn first_location(&self) -> Location {
800        Location::new(self.repr.offset, self.repr.max_cut)
801    }
802
803    fn policy(&self) -> PolicyId {
804        self.repr.policy
805    }
806
807    fn prior(&self) -> Prior<Location> {
808        self.repr.prior
809    }
810
811    fn get_command(&self, location: Location) -> Option<Self::Command<'_>> {
812        if self.repr.offset != location.segment {
813            return None;
814        }
815        let cmd_idx = self.repr.cmd_index(location.max_cut).ok()?;
816        let data = self.repr.commands.get(cmd_idx)?;
817        let parent = if let Some(prev) = usize::checked_sub(cmd_idx, 1) {
818            if let Some(max_cut) = self.repr.max_cut.checked_add(prev as u64) {
819                Prior::Single(Address {
820                    id: self.repr.commands[prev].id,
821                    max_cut,
822                })
823            } else {
824                return None;
825            }
826        } else {
827            self.repr.parents
828        };
829        Some(LinearCommand {
830            id: &data.id,
831            parent,
832            priority: data.priority.clone(),
833            policy: data.policy.as_deref(),
834            data: &data.data,
835            max_cut: location.max_cut,
836        })
837    }
838
839    fn facts(&self) -> Result<Self::FactIndex, StorageError> {
840        Ok(LinearFactIndex {
841            repr: self.reader.fetch(self.repr.facts)?,
842            reader: self.reader.clone(),
843        })
844    }
845
846    fn skip_list(&self) -> &[Location] {
847        &self.repr.skip_list
848    }
849
850    fn shortest_max_cut(&self) -> MaxCut {
851        self.repr.max_cut
852    }
853
854    fn longest_max_cut(&self) -> Result<MaxCut, StorageError> {
855        Ok(self
856            .repr
857            .max_cut
858            .checked_add(
859                self.repr
860                    .commands
861                    .len()
862                    .checked_sub(1)
863                    .assume("must not overflow")? as u64,
864            )
865            .assume("must not overflow")?)
866    }
867}
868
869impl SegmentRepr {
870    fn cmd_index(&self, max_cut: MaxCut) -> Result<usize, StorageError> {
871        max_cut
872            .distance_from(self.max_cut)
873            .and_then(|x| usize::try_from(x).ok())
874            .ok_or(StorageError::CommandOutOfBounds(Location::new(
875                self.offset,
876                max_cut,
877            )))
878    }
879}
880
881impl<R: Read> FactIndex for LinearFactIndex<R> {}
882
883#[cfg(all(test, feature = "graphviz"))]
884impl<R: Read> crate::storage::FactIndexExtra for LinearFactIndex<R> {
885    fn name(&self) -> String {
886        use alloc::string::ToString as _;
887        self.repr.offset.to_string()
888    }
889
890    fn prior(&self) -> Result<Option<Self>, StorageError> {
891        self.repr
892            .prior
893            .map(|p| {
894                let repr = self.reader.fetch(p)?;
895                Ok(Self {
896                    repr,
897                    reader: self.reader.clone(),
898                })
899            })
900            .transpose()
901    }
902}
903
904type MapIter = alloc::collections::btree_map::IntoIter<Keys, Option<Bytes>>;
905pub struct QueryIterator {
906    it: MapIter,
907}
908
909impl QueryIterator {
910    fn new(it: MapIter) -> Self {
911        Self { it }
912    }
913}
914
915impl Iterator for QueryIterator {
916    type Item = Result<Fact, StorageError>;
917    fn next(&mut self) -> Option<Self::Item> {
918        loop {
919            // filter out tombstones
920            if let (key, Some(value)) = self.it.next()? {
921                return Some(Ok(Fact { key, value }));
922            }
923        }
924    }
925}
926
927impl<R: Read> Query for LinearFactIndex<R> {
928    fn query(&self, name: &str, keys: &[Bytes]) -> Result<Option<Bytes>, StorageError> {
929        let mut prior = Some(&self.repr);
930        let mut slot; // Need to store deserialized value.
931        while let Some(facts) = prior {
932            if let Some(v) = facts.facts.get(name).and_then(|m| m.get(keys)) {
933                return Ok(v.clone());
934            }
935            slot = facts.prior.map(|p| self.reader.fetch(p)).transpose()?;
936            prior = slot.as_ref();
937        }
938        Ok(None)
939    }
940
941    type QueryIterator = QueryIterator;
942    fn query_prefix(&self, name: &str, prefix: &[Bytes]) -> Result<QueryIterator, StorageError> {
943        Ok(QueryIterator::new(
944            self.query_prefix_inner(name, prefix)?.into_iter(),
945        ))
946    }
947}
948
949impl<R: Read> LinearFactIndex<R> {
950    fn query_prefix_inner(&self, name: &str, prefix: &[Bytes]) -> Result<FactMap, StorageError> {
951        let mut matches = BTreeMap::new();
952        let mut prior = Some(&self.repr);
953        let mut slot; // Need to store deserialized value.
954        while let Some(facts) = prior {
955            if let Some(map) = facts.facts.get(name) {
956                for (k, v) in find_prefixes(map, prefix) {
957                    // don't override, if we've already found the fact (including deletions)
958                    if !matches.contains_key(k) {
959                        matches.insert(k.clone(), v.map(Into::into));
960                    }
961                }
962            }
963            slot = facts.prior.map(|p| self.reader.fetch(p)).transpose()?;
964            prior = slot.as_ref();
965        }
966        Ok(matches)
967    }
968}
969
970impl<R> LinearFactPerspective<R> {
971    fn clear(&mut self) {
972        self.map.clear();
973    }
974
975    fn apply_updates(&mut self, updates: &[Update]) -> Result<(), StorageError> {
976        for (name, keys, value) in updates {
977            if self.prior.is_none() {
978                if let Some(value) = value {
979                    self.map
980                        .entry(name.clone())
981                        .or_default()
982                        .insert(keys.clone(), Some(value.clone()));
983                } else if let Some(e) = self.map.get_mut(name) {
984                    e.remove(keys);
985                }
986            } else {
987                self.map
988                    .entry(name.clone())
989                    .or_default()
990                    .insert(keys.clone(), value.clone());
991            }
992        }
993        Ok(())
994    }
995}
996
997impl<R: Read> FactPerspective for LinearFactPerspective<R> {}
998
999impl<R: Read> Query for LinearFactPerspective<R> {
1000    fn query(&self, name: &str, keys: &[Bytes]) -> Result<Option<Bytes>, StorageError> {
1001        if let Some(wrapped) = self.map.get(name).and_then(|m| m.get(keys)) {
1002            return Ok(wrapped.as_deref().map(Bytes::from));
1003        }
1004        match &self.prior {
1005            FactPerspectivePrior::None => Ok(None),
1006            FactPerspectivePrior::FactPerspective(prior) => prior.query(name, keys),
1007            FactPerspectivePrior::FactIndex { offset, reader } => {
1008                let repr: FactIndexRepr = reader.fetch(*offset)?;
1009                let prior = LinearFactIndex {
1010                    repr,
1011                    reader: reader.clone(),
1012                };
1013                prior.query(name, keys)
1014            }
1015        }
1016    }
1017
1018    type QueryIterator = QueryIterator;
1019    fn query_prefix(&self, name: &str, prefix: &[Bytes]) -> Result<QueryIterator, StorageError> {
1020        Ok(QueryIterator::new(
1021            self.query_prefix_inner(name, prefix)?.into_iter(),
1022        ))
1023    }
1024}
1025
1026impl<R: Read> LinearFactPerspective<R> {
1027    fn query_prefix_inner(&self, name: &str, prefix: &[Bytes]) -> Result<FactMap, StorageError> {
1028        let mut matches = match &self.prior {
1029            FactPerspectivePrior::None => BTreeMap::new(),
1030            FactPerspectivePrior::FactPerspective(prior) => {
1031                prior.query_prefix_inner(name, prefix)?
1032            }
1033            FactPerspectivePrior::FactIndex { offset, reader } => {
1034                let repr: FactIndexRepr = reader.fetch(*offset)?;
1035                let prior = LinearFactIndex {
1036                    repr,
1037                    reader: reader.clone(),
1038                };
1039                prior.query_prefix_inner(name, prefix)?
1040            }
1041        };
1042        if let Some(map) = self.map.get(name) {
1043            for (k, v) in find_prefixes(map, prefix) {
1044                // overwrite "earlier" facts
1045                matches.insert(k.clone(), v.map(Into::into));
1046            }
1047        }
1048        Ok(matches)
1049    }
1050}
1051
1052impl<R: Read> QueryMut for LinearFactPerspective<R> {
1053    fn insert(&mut self, name: String, keys: Keys, value: Bytes) -> Result<(), StorageError> {
1054        self.map.entry(name).or_default().insert(keys, Some(value));
1055        Ok(())
1056    }
1057
1058    fn delete(&mut self, name: String, keys: Keys) -> Result<(), StorageError> {
1059        if self.prior.is_none() {
1060            // No need for tombstones with no prior.
1061            if let Some(kv) = self.map.get_mut(&name) {
1062                kv.remove(&keys);
1063            }
1064        } else {
1065            self.map.entry(name).or_default().insert(keys, None);
1066        }
1067        Ok(())
1068    }
1069}
1070
1071impl<R: Read> FactPerspective for LinearPerspective<R> {}
1072
1073impl<R: Read> Query for LinearPerspective<R> {
1074    fn query(&self, name: &str, keys: &[Bytes]) -> Result<Option<Bytes>, StorageError> {
1075        self.facts.query(name, keys)
1076    }
1077
1078    type QueryIterator = QueryIterator;
1079    fn query_prefix(&self, name: &str, prefix: &[Bytes]) -> Result<QueryIterator, StorageError> {
1080        self.facts.query_prefix(name, prefix)
1081    }
1082}
1083
1084impl<R: Read> QueryMut for LinearPerspective<R> {
1085    fn insert(&mut self, name: String, keys: Keys, value: Bytes) -> Result<(), StorageError> {
1086        self.facts
1087            .insert(name.clone(), keys.clone(), value.clone())?;
1088        self.current_updates.push((name, keys, Some(value)));
1089        Ok(())
1090    }
1091
1092    fn delete(&mut self, name: String, keys: Keys) -> Result<(), StorageError> {
1093        self.facts.delete(name.clone(), keys.clone())?;
1094        self.current_updates.push((name, keys, None));
1095        Ok(())
1096    }
1097}
1098
1099impl<R: Read> Revertable for LinearPerspective<R> {
1100    fn checkpoint(&self) -> Checkpoint {
1101        Checkpoint {
1102            index: self.commands.len(),
1103        }
1104    }
1105
1106    fn revert(&mut self, checkpoint: Checkpoint) -> Result<(), StorageError> {
1107        if checkpoint.index == self.commands.len() {
1108            return Ok(());
1109        }
1110
1111        if checkpoint.index > self.commands.len() {
1112            bug!(
1113                "A checkpoint's index should always be less than or equal to the length of a perspective's command history!"
1114            );
1115        }
1116
1117        self.commands.truncate(checkpoint.index);
1118        self.facts.clear();
1119        self.current_updates.clear();
1120        for data in &self.commands {
1121            self.facts.apply_updates(&data.updates)?;
1122        }
1123
1124        Ok(())
1125    }
1126}
1127
1128impl<R: Read> Perspective for LinearPerspective<R> {
1129    fn policy(&self) -> PolicyId {
1130        self.policy
1131    }
1132
1133    fn add_command(&mut self, command: &impl Command) -> Result<usize, StorageError> {
1134        if command.parent() != self.head_address()? {
1135            return Err(StorageError::PerspectiveHeadMismatch);
1136        }
1137
1138        self.commands.push(CommandData {
1139            id: command.id(),
1140            priority: command.priority(),
1141            policy: command.policy().map(Bytes::from),
1142            data: command.bytes().into(),
1143            updates: core::mem::take(&mut self.current_updates),
1144        });
1145        Ok(self.commands.len())
1146    }
1147
1148    fn includes(&self, id: CmdId) -> bool {
1149        self.commands.iter().any(|cmd| cmd.id == id)
1150    }
1151
1152    fn head_address(&self) -> Result<Prior<Address>, Bug> {
1153        Ok(if let Some(last) = self.commands.last() {
1154            Prior::Single(Address {
1155                id: last.id,
1156                max_cut: self
1157                    .max_cut
1158                    .checked_add(
1159                        self.commands
1160                            .len()
1161                            .checked_sub(1)
1162                            .assume("must not overflow")? as u64,
1163                    )
1164                    .assume("must not overflow")?,
1165            })
1166        } else {
1167            self.parents
1168        })
1169    }
1170}
1171
1172impl From<Prior<Address>> for Prior<CmdId> {
1173    fn from(p: Prior<Address>) -> Self {
1174        match p {
1175            Prior::None => Self::None,
1176            Prior::Single(l) => Self::Single(l.id),
1177            Prior::Merge(l, r) => Self::Merge(l.id, r.id),
1178        }
1179    }
1180}
1181
1182impl Command for LinearCommand<'_> {
1183    fn priority(&self) -> Priority {
1184        self.priority.clone()
1185    }
1186
1187    fn id(&self) -> CmdId {
1188        *self.id
1189    }
1190
1191    fn parent(&self) -> Prior<Address> {
1192        self.parent
1193    }
1194
1195    fn policy(&self) -> Option<&[u8]> {
1196        self.policy
1197    }
1198
1199    fn bytes(&self) -> &[u8] {
1200        self.data
1201    }
1202
1203    fn max_cut(&self) -> Result<MaxCut, Bug> {
1204        Ok(self.max_cut)
1205    }
1206}
1207
1208fn find_prefixes<'m, 'p: 'm>(
1209    map: &'m FactMap,
1210    prefix: &'p [Bytes],
1211) -> impl Iterator<Item = (&'m Keys, Option<&'m [u8]>)> + 'm {
1212    map.range::<[Bytes], _>((Bound::Included(prefix), Bound::Unbounded))
1213        .take_while(|(k, _)| k.starts_with(prefix))
1214        .map(|(k, v)| (k, v.as_deref()))
1215}
1216
1217#[cfg(test)]
1218mod test {
1219    use testing::Manager;
1220
1221    use super::*;
1222    use crate::testing::dsl::{StorageBackend, test_suite};
1223
1224    #[test]
1225    fn test_query_prefix() {
1226        let mut provider = LinearStorageProvider::new(Manager::new());
1227        let mut fp = provider.new_perspective(PolicyId::new(0));
1228
1229        let name = "x";
1230
1231        let keys: &[&[&str]] = &[
1232            &["aa", "xy", "123"],
1233            &["aa", "xz", "123"],
1234            &["bb", "ccc"],
1235            &["bc", ""],
1236        ];
1237        let keys: Vec<Keys> = keys
1238            .iter()
1239            .map(|ks| ks.iter().map(|k| Bytes::from(k.as_bytes())).collect())
1240            .collect();
1241
1242        for ks in &keys {
1243            fp.insert(
1244                name.into(),
1245                ks.clone(),
1246                format!("{ks:?}").into_bytes().into(),
1247            )
1248            .unwrap();
1249        }
1250
1251        let prefixes: &[&[&str]] = &[
1252            &["aa", "xy", "12"],
1253            &["aa", "xy"],
1254            &["aa", "xz"],
1255            &["aa", "x"],
1256            &["bb", ""],
1257            &["bb", "ccc"],
1258            &["bc", ""],
1259        ];
1260
1261        for prefix in prefixes {
1262            let prefix: Keys = prefix.iter().map(|k| Bytes::from(k.as_bytes())).collect();
1263            let found: Vec<_> = fp.query_prefix(name, &prefix).unwrap().collect();
1264            let mut expected: Vec<_> = keys.iter().filter(|k| k.starts_with(&prefix)).collect();
1265            expected.sort();
1266            assert_eq!(found.len(), expected.len());
1267            for (a, b) in std::iter::zip(found, expected) {
1268                let a = a.unwrap();
1269                assert_eq!(&a.key, b);
1270                assert_eq!(a.value.as_ref(), format!("{b:?}").as_bytes());
1271            }
1272        }
1273    }
1274
1275    struct LinearBackend;
1276    impl StorageBackend for LinearBackend {
1277        type StorageProvider = LinearStorageProvider<Manager>;
1278
1279        fn provider(&mut self, _client_id: u64) -> Self::StorageProvider {
1280            LinearStorageProvider::new(Manager::new())
1281        }
1282    }
1283    test_suite!(|| LinearBackend);
1284}