miniconf/
walk.rs

1use core::num::NonZero;
2
3use crate::{KeyLookup, Packed};
4
5/// Capability to be created from a walk through all representative nodes in a
6/// `TreeKey` using `traverse_all()`.
7///
8/// This is a bottom-up, breadth-first walk.
9pub trait Walk: Sized {
10    /// Create a leaf node
11    fn leaf() -> Self;
12
13    /// Create an internal node frmo child nodes.
14    ///
15    /// # Args
16    /// * `children`: Child nodes to merge.
17    /// * `lookup`: The namespace the child nodes are in.
18    fn internal(children: &[Self], lookup: &KeyLookup) -> Self;
19}
20
21/// Metadata about a `TreeKey` namespace.
22///
23/// Metadata includes paths that may be [`crate::Traversal::Absent`] at runtime.
24#[non_exhaustive]
25#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
26pub struct Metadata {
27    /// The maximum length of a path in bytes.
28    ///
29    /// This is the exact maximum of the length of the concatenation of the node names
30    /// in a [`crate::Path`] excluding the separators. See [`Self::max_length()`] for
31    /// the maximum length including separators.
32    pub max_length: usize,
33
34    /// The maximum key depth.
35    ///
36    /// This is equal to the exact maximum number of path hierarchy separators.
37    /// It's the exact maximum number of key indices.
38    pub max_depth: usize,
39
40    /// The exact total number of keys.
41    pub count: NonZero<usize>,
42
43    /// The maximum number of bits (see [`crate::Packed`])
44    pub max_bits: u32,
45}
46
47impl Metadata {
48    /// Add separator length to the maximum path length.
49    ///
50    /// To obtain an upper bound on the maximum length of all paths
51    /// including separators, this adds `max_depth*separator_length`.
52    #[inline]
53    pub fn max_length(&self, separator: &str) -> usize {
54        self.max_length + self.max_depth * separator.len()
55    }
56}
57
58impl Walk for Metadata {
59    #[inline]
60    fn leaf() -> Self {
61        Self {
62            count: NonZero::<usize>::MIN,
63            max_length: 0,
64            max_depth: 0,
65            max_bits: 0,
66        }
67    }
68
69    #[inline]
70    fn internal(children: &[Self], lookup: &KeyLookup) -> Self {
71        let mut max_depth = 0;
72        let mut max_length = 0;
73        let mut count = 0;
74        let mut max_bits = 0;
75        // TODO: swap loop and match
76        for (index, child) in children.iter().enumerate() {
77            let (len, n) = match lookup {
78                KeyLookup::Named(names) => {
79                    debug_assert_eq!(children.len(), names.len());
80                    (names[index].len(), 1)
81                }
82                KeyLookup::Numbered(len) => {
83                    debug_assert_eq!(children.len(), len.get());
84                    (index.checked_ilog10().unwrap_or_default() as usize + 1, 1)
85                }
86                KeyLookup::Homogeneous(len) => {
87                    debug_assert_eq!(children.len(), 1);
88                    (len.ilog10() as usize + 1, len.get())
89                }
90            };
91            max_depth = max_depth.max(1 + child.max_depth);
92            max_length = max_length.max(len + child.max_length);
93            count += n * child.count.get();
94            max_bits = max_bits.max(Packed::bits_for(lookup.len().get() - 1) + child.max_bits);
95        }
96        Self {
97            max_bits,
98            max_depth,
99            max_length,
100            count: NonZero::new(count).unwrap(),
101        }
102    }
103}