vortex_sampling_compressor/compressors/
for.rs1use vortex_array::aliases::hash_set::HashSet;
2use vortex_array::array::PrimitiveArray;
3use vortex_array::stats::trailing_zeros;
4use vortex_array::variants::PrimitiveArrayTrait;
5use vortex_array::{Array, Encoding, EncodingId, IntoArray, IntoArrayVariant};
6use vortex_dtype::match_each_integer_ptype;
7use vortex_error::VortexResult;
8use vortex_fastlanes::{for_compress, FoRArray, FoREncoding};
9
10use crate::compressors::{CompressedArray, CompressionTree, EncodingCompressor};
11use crate::{constants, SamplingCompressor};
12
13#[derive(Debug)]
14pub struct FoRCompressor;
15
16impl EncodingCompressor for FoRCompressor {
17 fn id(&self) -> &str {
18 FoREncoding::ID.as_ref()
19 }
20
21 fn cost(&self) -> u8 {
22 constants::FOR_COST
23 }
24
25 fn can_compress(&self, array: &Array) -> Option<&dyn EncodingCompressor> {
26 let parray = PrimitiveArray::maybe_from(array)?;
28
29 if !parray.ptype().is_int() {
31 return None;
32 }
33
34 if parray.validity_mask().ok()?.all_false() {
36 return None;
37 }
38
39 let shift = trailing_zeros(array);
41 match_each_integer_ptype!(parray.ptype(), |$P| {
42 let min: $P = parray.statistics().compute_min()?;
43 if min == 0 && shift == 0 {
44 return None;
45 }
46 });
47
48 Some(self)
49 }
50
51 fn compress<'a>(
52 &'a self,
53 array: &Array,
54 like: Option<CompressionTree<'a>>,
55 ctx: SamplingCompressor<'a>,
56 ) -> VortexResult<CompressedArray<'a>> {
57 let compressed = for_compress(array.clone().into_primitive()?)?;
58
59 let compressed_child = ctx.named("for_encoded").excluding(self).compress(
60 &compressed.encoded(),
61 like.as_ref().and_then(|l| l.child(0)),
62 )?;
63 Ok(CompressedArray::compressed(
64 FoRArray::try_new(
65 compressed_child.array,
66 compressed.reference_scalar(),
67 compressed.shift(),
68 )
69 .map(|a| a.into_array())?,
70 Some(CompressionTree::new(self, vec![compressed_child.path])),
71 array,
72 ))
73 }
74
75 fn used_encodings(&self) -> HashSet<EncodingId> {
76 HashSet::from([FoREncoding::ID])
77 }
78}