Skip to main content

lsm_tree/config/
column_encoding.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026-present, Dmitry Prudnikov
3
4#[cfg(not(feature = "std"))]
5use alloc::vec::Vec;
6
7/// How a columnar table's column pages store their values.
8///
9/// A read hands a column out in its own layout. A page stored that way is
10/// handed out as a view of itself; any other encoding is built back into
11/// the layout by the read that wants the column whole, which costs time on
12/// every read of a warm page and saves bytes on the ones that come from disk.
13/// A level whose tables are read often wants [`Plain`](Self::Plain); a level
14/// whose tables are mostly stored wants [`Auto`](Self::Auto), as it wants a
15/// stronger compression.
16#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
17pub enum ColumnEncoding {
18    /// Every page in its column's own layout.
19    #[default]
20    Plain,
21    /// Each page as the expression over light operators (constant, runs,
22    /// dictionary, bit packing, deltas) that costs least to store and to
23    /// read, which is the plain layout when nothing beats it.
24    Auto,
25}
26
27/// Column encoding policy: the [`ColumnEncoding`] of each level's tables.
28///
29/// A level past the policy's last entry takes that entry.
30#[derive(Debug, Clone, Eq, PartialEq)]
31pub struct ColumnEncodingPolicy(Vec<ColumnEncoding>);
32
33impl core::ops::Deref for ColumnEncodingPolicy {
34    type Target = [ColumnEncoding];
35
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl Default for ColumnEncodingPolicy {
42    /// [`ColumnEncoding::Plain`] at every level.
43    fn default() -> Self {
44        Self::all(ColumnEncoding::Plain)
45    }
46}
47
48impl ColumnEncodingPolicy {
49    pub(crate) fn get(&self, level: usize) -> ColumnEncoding {
50        #[expect(clippy::expect_used, reason = "policy is expected not to be empty")]
51        self.0
52            .get(level)
53            .copied()
54            .unwrap_or_else(|| self.last().copied().expect("policy should not be empty"))
55    }
56
57    /// Uses the same encoding in every level.
58    #[must_use]
59    pub fn all(encoding: ColumnEncoding) -> Self {
60        Self(vec![encoding])
61    }
62
63    /// Constructs a custom policy, one entry per level from level 0.
64    ///
65    /// # Panics
66    ///
67    /// Panics if the policy is empty or contains more than 255 elements.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use lsm_tree::config::{ColumnEncoding, ColumnEncodingPolicy};
73    ///
74    /// // Plain where tables are read while warm, encoded from level 3 down.
75    /// let policy = ColumnEncodingPolicy::new([
76    ///     ColumnEncoding::Plain,
77    ///     ColumnEncoding::Plain,
78    ///     ColumnEncoding::Plain,
79    ///     ColumnEncoding::Auto,
80    /// ]);
81    /// assert_eq!(policy.len(), 4);
82    /// ```
83    #[must_use]
84    pub fn new(policy: impl Into<Vec<ColumnEncoding>>) -> Self {
85        let policy = policy.into();
86        assert!(
87            !policy.is_empty(),
88            "column encoding policy may not be empty"
89        );
90        assert!(policy.len() <= 255, "column encoding policy is too large");
91        Self(policy)
92    }
93}