Skip to main content

reifydb_column/compute/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4pub mod canonical;
5
6use reifydb_core::value::column::{array::Column, mask::RowMask};
7use reifydb_type::{Result, value::Value};
8
9use crate::encoding;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum CompareOp {
13	Eq,
14	Ne,
15	Lt,
16	LtEq,
17	Gt,
18	GtEq,
19}
20
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum SearchResult {
23	Found(usize),
24	NotFound(usize),
25}
26
27// Per-encoding compute specialization. Each method returns `Option<Result<_>>`:
28// `None` means "this encoding doesn't specialize - fall back to canonical;"
29// `Some(Ok(_))` is a real result; `Some(Err(_))` is a real error encountered
30// while running the specialization. Free functions below dispatch through
31// this trait and fall back to canonicalize-and-run when `None` is returned.
32pub trait Compute: Send + Sync {
33	fn filter(&self, _array: &Column, _mask: &RowMask) -> Option<Result<Column>> {
34		None
35	}
36
37	fn take(&self, _array: &Column, _indices: &Column) -> Option<Result<Column>> {
38		None
39	}
40
41	fn slice(&self, _array: &Column, _start: usize, _end: usize) -> Option<Result<Column>> {
42		None
43	}
44
45	fn compare(&self, _array: &Column, _rhs: &Value, _op: CompareOp) -> Option<Result<Column>> {
46		None
47	}
48
49	fn search_sorted(&self, _array: &Column, _needle: &Value) -> Option<Result<SearchResult>> {
50		None
51	}
52
53	fn min_max(&self, _array: &Column) -> Option<Result<(Value, Value)>> {
54		None
55	}
56
57	fn sum(&self, _array: &Column) -> Option<Result<Value>> {
58		None
59	}
60}
61
62pub struct DefaultCompute;
63
64impl Compute for DefaultCompute {}
65
66// Each free function first asks the array's encoding for a specialization; if
67// the encoding returns `None`, the caller canonicalizes and runs the canonical
68// kernel. This preserves the "compressed encodings can always fall back
69// correctly" invariant - correctness only depends on the canonical path.
70
71pub fn filter(array: &Column, mask: &RowMask) -> Result<Column> {
72	if let Some(result) = specialized(array, |c| c.filter(array, mask)) {
73		return result;
74	}
75	let canon = array.to_canonical()?;
76	Ok(Column::from_canonical(canonical::filter::filter(&canon, mask)?))
77}
78
79pub fn take(array: &Column, indices: &Column) -> Result<Column> {
80	if let Some(result) = specialized(array, |c| c.take(array, indices)) {
81		return result;
82	}
83	let canon = array.to_canonical()?;
84	let idx = indices.to_canonical()?;
85	Ok(Column::from_canonical(canonical::take::take(&canon, &idx)?))
86}
87
88pub fn slice(array: &Column, start: usize, end: usize) -> Result<Column> {
89	if let Some(result) = specialized(array, |c| c.slice(array, start, end)) {
90		return result;
91	}
92	let canon = array.to_canonical()?;
93	Ok(Column::from_canonical(canonical::slice::slice(&canon, start, end)?))
94}
95
96pub fn compare(array: &Column, rhs: &Value, op: CompareOp) -> Result<Column> {
97	if let Some(result) = specialized(array, |c| c.compare(array, rhs, op)) {
98		return result;
99	}
100	let canon = array.to_canonical()?;
101	Ok(Column::from_canonical(canonical::compare::compare(&canon, rhs, op)?))
102}
103
104pub fn search_sorted(array: &Column, needle: &Value) -> Result<SearchResult> {
105	if let Some(result) = specialized(array, |c| c.search_sorted(array, needle)) {
106		return result;
107	}
108	let canon = array.to_canonical()?;
109	canonical::search_sorted::search_sorted(&canon, needle)
110}
111
112pub fn min_max(array: &Column) -> Result<(Value, Value)> {
113	if let Some(result) = specialized(array, |c| c.min_max(array)) {
114		return result;
115	}
116	let canon = array.to_canonical()?;
117	canonical::min_max::min_max(&canon)
118}
119
120pub fn sum(array: &Column) -> Result<Value> {
121	if let Some(result) = specialized(array, |c| c.sum(array)) {
122		return result;
123	}
124	let canon = array.to_canonical()?;
125	canonical::sum::sum(&canon)
126}
127
128fn specialized<T>(array: &Column, hook: impl FnOnce(&dyn Compute) -> Option<Result<T>>) -> Option<Result<T>> {
129	let registry = encoding::global();
130	let encoding = registry.get(array.encoding())?;
131	hook(encoding.compute())
132}