1use rudb_common::LogicalType;
30use rudb_vector::{Data, Form, Selection, Validity, Vector};
31
32use crate::fallback::{self, Kernel};
33use crate::logic::is_true;
34use crate::shape::{identity, nulls_of};
35
36#[must_use]
41pub fn selection(flags: &Vector, rows: usize) -> Selection {
42 let rows = rows.min(flags.len());
43 if let Some(kept) = swept(flags, rows) {
44 return kept;
45 }
46 fallback::record(Kernel::Select, flags.form(), flags.form());
49 Selection::from_predicate(rows, |index| is_true(&flags.value_at(index)))
50}
51
52fn swept(flags: &Vector, rows: usize) -> Option<Selection> {
53 if *flags.logical_type() != LogicalType::Boolean {
54 return None;
55 }
56 if rows > u32::MAX as usize {
59 return None;
60 }
61 match flags.form() {
62 Form::Constant => Some(if is_true(flags.constant_value()?) {
64 Selection::identity(rows)
65 } else {
66 Selection::empty()
67 }),
68 Form::Flat => {
69 let Data::Bool(values) = flags.data()? else {
70 return None;
71 };
72 if values.len() < rows {
73 return None;
74 }
75 Some(picked(values, identity, rows, &nulls_of(flags)))
76 }
77 Form::Dictionary => {
78 let (codes, inner) = flags.dictionary_parts()?;
79 if codes.len() < rows {
80 return None;
81 }
82 let Data::Bool(values) = inner.data()? else {
83 return None;
84 };
85 Some(picked(values, |index| codes[index] as usize, rows, &nulls_of(flags)))
88 }
89 _ => None,
90 }
91}
92
93#[expect(
94 clippy::cast_possible_truncation,
95 reason = "the caller checked that the row count fits in a u32 before getting here"
96)]
97fn picked<M: Fn(usize) -> usize>(
98 values: &[bool],
99 at: M,
100 rows: usize,
101 nulls: &Validity,
102) -> Selection {
103 let mut out = vec![0_u32; rows];
104 let mut kept = 0;
105 match nulls {
106 Validity::AllValid => {
107 for index in 0..rows {
108 out[kept] = index as u32;
109 kept += usize::from(values[at(index)]);
110 }
111 }
112 Validity::AllInvalid => {}
113 Validity::Mask(mask) => {
114 for start in (0..rows).step_by(64) {
115 let word = mask.word(start / 64);
116 for index in start..(start + 64).min(rows) {
117 out[kept] = index as u32;
118 let live = word >> (index - start) & 1 == 1;
119 kept += usize::from(live & values[at(index)]);
122 }
123 }
124 }
125 }
126 out.truncate(kept);
127 Selection::from_indices(out)
128}
129
130#[cfg(test)]
131mod tests {
132 use rudb_common::Value;
133
134 use super::*;
135
136 fn flags(values: &[Value]) -> Vector {
137 Vector::from_values(LogicalType::Boolean, values).expect("a vector of booleans")
138 }
139
140 const YES: Value = Value::Boolean(true);
141 const NO: Value = Value::Boolean(false);
142
143 fn oracle(vector: &Vector, rows: usize) -> Selection {
145 Selection::from_predicate(rows, |index| is_true(&vector.value_at(index)))
146 }
147
148 struct Rng(u64);
149
150 impl Rng {
151 fn next(&mut self) -> u64 {
152 self.0 ^= self.0 << 13;
153 self.0 ^= self.0 >> 7;
154 self.0 ^= self.0 << 17;
155 self.0
156 }
157 }
158
159 #[test]
160 fn a_null_flag_is_not_a_true_flag() {
161 let vector = flags(&[YES, Value::Null, NO, YES]);
162 let kept = selection(&vector, 4);
163 assert_eq!(kept.indices(), &[0, 3]);
164 assert_eq!(kept, oracle(&vector, 4));
165 }
166
167 #[test]
170 fn the_rows_kept_are_the_rows_the_row_at_a_time_path_keeps() {
171 let _turn = fallback::TURN.lock().expect("no test panics while holding this");
172 let mut rng = Rng(0x5eed_ca11_ab1e_0005);
173 for nulls in [0_usize, 8, 3, 1] {
174 for share in [0_u64, 1, 16, 50, 84, 99, 100] {
175 let values: Vec<Value> = (0..251)
176 .map(|index| {
177 if nulls > 0 && index % nulls == 0 {
178 Value::Null
179 } else {
180 Value::Boolean(rng.next() % 100 < share)
181 }
182 })
183 .collect();
184 let vector = flags(&values);
185 let note = format!("{share} percent true, one null in {nulls}");
186 assert_eq!(selection(&vector, 251), oracle(&vector, 251), "{note}, flat");
187 let codes: Vec<u32> = (0..251).map(|index| (index % 37) as u32).collect();
188 let coded = Vector::dictionary(codes, vector).expect("codes are in range");
189 assert_eq!(selection(&coded, 251), oracle(&coded, 251), "{note}, dictionary");
190 }
191 }
192 }
193
194 #[test]
195 fn a_constant_is_answered_without_a_loop_and_a_non_boolean_is_not_answered_at_all() {
196 let _turn = fallback::TURN.lock().expect("no test panics while holding this");
197 fallback::reset();
198 let all = Vector::constant(LogicalType::Boolean, YES, 500);
199 assert_eq!(selection(&all, 500), Selection::identity(500));
200 let none = Vector::constant(LogicalType::Boolean, Value::Null, 500);
201 assert!(selection(&none, 500).is_empty());
202 assert_eq!(fallback::count(Kernel::Select, Form::Constant, Form::Constant), 0);
203
204 let numbers = Vector::from_values(LogicalType::Integer, &[Value::Integer(1)])
207 .expect("a vector of integers");
208 assert!(selection(&numbers, 1).is_empty());
209 assert_eq!(fallback::count(Kernel::Select, Form::Flat, Form::Flat), 1);
210 fallback::reset();
211 }
212
213 #[test]
215 fn only_the_rows_asked_for_are_looked_at() {
216 let vector = flags(&[YES, YES, YES, YES]);
217 assert_eq!(selection(&vector, 2).indices(), &[0, 1]);
218 assert_eq!(selection(&vector, 9).indices(), &[0, 1, 2, 3]);
220 }
221}