vortex_btrblocks/
builder.rs1use vortex_array::ArrayId;
7use vortex_utils::aliases::hash_set::HashSet;
8
9use crate::BtrBlocksCompressor;
10use crate::CascadingCompressor;
11use crate::Scheme;
12use crate::SchemeExt;
13use crate::SchemeId;
14use crate::schemes::binary;
15use crate::schemes::decimal;
16use crate::schemes::float;
17use crate::schemes::integer;
18use crate::schemes::string;
19use crate::schemes::temporal;
20
21pub const ALL_SCHEMES: &[&dyn Scheme] = &[
26 &integer::FoRScheme,
31 &integer::ZigZagScheme,
33 &integer::BitPackingScheme,
34 &integer::SparseScheme,
35 &integer::IntDictScheme,
36 &integer::RunEndScheme,
37 &integer::SequenceScheme,
38 &integer::IntRLEScheme,
39 #[cfg(feature = "unstable_encodings")]
41 &integer::DeltaScheme::new(1.25),
42 &float::ALPScheme,
46 &float::ALPRDScheme,
47 &float::FloatDictScheme,
48 &float::NullDominatedSparseScheme,
49 &float::FloatRLEScheme,
50 &string::StringDictScheme,
54 &string::FSSTScheme,
57 #[cfg(feature = "unstable_encodings")]
58 &string::OnPairScheme,
59 &string::NullDominatedSparseScheme,
60 &binary::BinaryDictScheme,
64 &decimal::DecimalScheme,
66 &temporal::TemporalScheme,
68];
69
70#[derive(Debug, Clone)]
92pub struct BtrBlocksCompressorBuilder {
93 schemes: Vec<&'static dyn Scheme>,
94}
95
96impl Default for BtrBlocksCompressorBuilder {
97 fn default() -> Self {
98 Self {
99 schemes: ALL_SCHEMES.to_vec(),
100 }
101 }
102}
103
104impl BtrBlocksCompressorBuilder {
105 pub fn empty() -> Self {
109 Self {
110 schemes: Vec::new(),
111 }
112 }
113
114 pub fn with_new_scheme(mut self, scheme: &'static dyn Scheme) -> Self {
123 assert!(
124 !self.schemes.iter().any(|s| s.id() == scheme.id()),
125 "scheme {:?} is already present in the builder",
126 scheme.id(),
127 );
128
129 self.schemes.push(scheme);
130 self
131 }
132
133 #[cfg(feature = "zstd")]
143 pub fn with_compact(self) -> Self {
144 let builder = self
145 .with_new_scheme(&string::ZstdScheme)
146 .with_new_scheme(&binary::ZstdScheme);
147
148 #[cfg(feature = "pco")]
149 let builder = builder
150 .with_new_scheme(&integer::PcoScheme)
151 .with_new_scheme(&float::PcoScheme);
152
153 builder
154 }
155
156 pub fn only_cuda_compatible(self) -> Self {
166 #[cfg_attr(
169 not(any(feature = "pco", feature = "unstable_encodings")),
170 allow(unused_mut)
171 )]
172 let mut excluded: Vec<SchemeId> = vec![
173 integer::SparseScheme.id(),
174 integer::IntRLEScheme.id(),
175 float::ALPRDScheme.id(),
176 float::FloatRLEScheme.id(),
177 float::NullDominatedSparseScheme.id(),
178 string::StringDictScheme.id(),
179 binary::BinaryDictScheme.id(),
180 ];
181 #[cfg(feature = "unstable_encodings")]
182 excluded.push(string::OnPairScheme.id());
183 #[cfg(feature = "unstable_encodings")]
186 excluded.push(integer::DeltaScheme::default().id());
187 #[cfg(feature = "pco")]
188 excluded.extend([integer::PcoScheme.id(), float::PcoScheme.id()]);
189 let builder = self.exclude_schemes(excluded);
190
191 #[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
192 let builder = builder.with_new_scheme(&binary::ZstdBuffersScheme);
193 #[cfg(all(feature = "zstd", not(feature = "unstable_encodings")))]
194 let builder = builder.with_new_scheme(&binary::ZstdScheme);
195
196 builder
197 }
198
199 pub fn exclude_schemes(mut self, ids: impl IntoIterator<Item = SchemeId>) -> Self {
201 let ids: HashSet<_> = ids.into_iter().collect();
202 self.schemes.retain(|s| !ids.contains(&s.id()));
203 self
204 }
205
206 pub fn retain_allowed_encodings(mut self, allowed: &HashSet<ArrayId>) -> Self {
211 self.schemes
212 .retain(|s| s.produced_encodings().iter().all(|id| allowed.contains(id)));
213 self
214 }
215
216 pub fn build(self) -> BtrBlocksCompressor {
218 BtrBlocksCompressor(CascadingCompressor::new(self.schemes))
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use vortex_array::VTable;
225 use vortex_fastlanes::FoR;
226
227 use super::*;
228
229 #[test]
230 fn empty_starts_with_no_schemes() {
231 let builder = BtrBlocksCompressorBuilder::empty();
232 assert!(builder.schemes.is_empty());
233 }
234
235 #[test]
236 fn default_includes_all_schemes() {
237 let builder = BtrBlocksCompressorBuilder::default();
238 assert_eq!(builder.schemes.len(), ALL_SCHEMES.len());
239 }
240
241 #[test]
242 fn retain_allowed_encodings_filters_schemes() {
243 let allowed: HashSet<ArrayId> = [FoR.id()].into_iter().collect();
244 let builder = BtrBlocksCompressorBuilder::default().retain_allowed_encodings(&allowed);
245 assert_eq!(builder.schemes.len(), 1);
246 assert_eq!(builder.schemes[0].id(), integer::FoRScheme.id());
247
248 let none = BtrBlocksCompressorBuilder::default().retain_allowed_encodings(&HashSet::new());
249 assert!(none.schemes.is_empty());
250 }
251
252 #[test]
253 fn retaining_all_declared_outputs_keeps_every_scheme() {
254 let allowed: HashSet<ArrayId> = ALL_SCHEMES
255 .iter()
256 .flat_map(|scheme| scheme.produced_encodings())
257 .collect();
258 let builder = BtrBlocksCompressorBuilder::default().retain_allowed_encodings(&allowed);
259 assert_eq!(builder.schemes.len(), ALL_SCHEMES.len());
260 }
261
262 #[test]
263 fn cuda_compatible_excludes_alprd() {
264 let builder = BtrBlocksCompressorBuilder::default().only_cuda_compatible();
265 assert!(
266 !builder
267 .schemes
268 .iter()
269 .any(|s| s.id() == float::ALPRDScheme.id())
270 );
271 }
272
273 #[test]
274 fn cuda_compatible_uses_fsst_for_strings() {
275 let builder = BtrBlocksCompressorBuilder::default().only_cuda_compatible();
276 assert!(
277 builder
278 .schemes
279 .iter()
280 .any(|scheme| scheme.id() == string::FSSTScheme.id())
281 );
282 #[cfg(feature = "zstd")]
283 assert!(
284 !builder
285 .schemes
286 .iter()
287 .any(|scheme| scheme.id() == string::ZstdScheme.id())
288 );
289 }
290
291 #[test]
292 #[cfg(feature = "pco")]
293 fn cuda_compatible_excludes_pco() {
294 let builder = BtrBlocksCompressorBuilder::default()
295 .with_new_scheme(&integer::PcoScheme)
296 .with_new_scheme(&float::PcoScheme)
297 .only_cuda_compatible();
298 for scheme in [integer::PcoScheme.id(), float::PcoScheme.id()] {
299 assert!(!builder.schemes.iter().any(|s| s.id() == scheme));
300 }
301 }
302}