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}