Skip to main content

hawdb_cascades/
memo.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct GroupId(usize);
17
18impl GroupId {
19    pub fn new(index: usize) -> Self {
20        Self(index)
21    }
22
23    pub fn index(self) -> usize {
24        self.0
25    }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct MemoGroup<E> {
30    expressions: Vec<E>,
31}
32
33impl<E> MemoGroup<E> {
34    pub fn new(expression: E) -> Self {
35        Self {
36            expressions: vec![expression],
37        }
38    }
39
40    pub fn expressions(&self) -> &[E] {
41        &self.expressions
42    }
43
44    pub fn first_expression(&self) -> Option<&E> {
45        self.expressions.first()
46    }
47
48    pub fn expression_count(&self) -> usize {
49        self.expressions.len()
50    }
51
52    pub fn push(&mut self, expression: E) {
53        self.expressions.push(expression);
54    }
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct Memo<E> {
59    groups: Vec<MemoGroup<E>>,
60}
61
62impl<E> Default for Memo<E> {
63    fn default() -> Self {
64        Self { groups: Vec::new() }
65    }
66}
67
68impl<E> Memo<E> {
69    pub fn insert_group(&mut self, expression: E) -> GroupId {
70        let id = GroupId::new(self.groups.len());
71        self.groups.push(MemoGroup::new(expression));
72        id
73    }
74
75    pub fn group(&self, id: GroupId) -> Option<&MemoGroup<E>> {
76        self.groups.get(id.index())
77    }
78
79    pub fn group_mut(&mut self, id: GroupId) -> Option<&mut MemoGroup<E>> {
80        self.groups.get_mut(id.index())
81    }
82
83    pub fn group_count(&self) -> usize {
84        self.groups.len()
85    }
86
87    pub fn is_empty(&self) -> bool {
88        self.groups.is_empty()
89    }
90
91    pub fn groups(&self) -> &[MemoGroup<E>] {
92        &self.groups
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::Memo;
99
100    #[test]
101    fn memo_interns_group_ids_by_insertion_order() {
102        let mut memo = Memo::default();
103
104        let first = memo.insert_group("scan");
105        let second = memo.insert_group("filter");
106
107        assert_eq!(first.index(), 0);
108        assert_eq!(second.index(), 1);
109        assert_eq!(memo.group_count(), 2);
110        assert!(!memo.is_empty());
111        assert_eq!(memo.group(first).unwrap().expressions(), &["scan"]);
112        assert_eq!(memo.group(first).unwrap().first_expression(), Some(&"scan"));
113    }
114
115    #[test]
116    fn memo_group_can_hold_equivalent_expressions() {
117        let mut memo = Memo::default();
118        let group = memo.insert_group("seq_scan");
119
120        memo.group_mut(group).unwrap().push("index_scan");
121
122        assert_eq!(
123            memo.group(group).unwrap().expressions(),
124            &["seq_scan", "index_scan"]
125        );
126        assert_eq!(memo.group(group).unwrap().expression_count(), 2);
127    }
128}