vortex_btrblocks/schemes/string/
onpair.rs1use vortex_array::ArrayRef;
7use vortex_array::Canonical;
8use vortex_array::ExecutionCtx;
9use vortex_array::IntoArray;
10use vortex_array::arrays::PrimitiveArray;
11use vortex_array::arrays::primitive::PrimitiveArrayExt;
12use vortex_compressor::estimate::CompressionEstimate;
13use vortex_compressor::estimate::DeferredEstimate;
14use vortex_compressor::scheme::SchemeId;
15use vortex_error::VortexResult;
16use vortex_onpair::DEFAULT_DICT12_CONFIG;
17use vortex_onpair::OnPair;
18use vortex_onpair::OnPairArrayExt;
19use vortex_onpair::OnPairArraySlotsExt;
20use vortex_onpair::onpair_compress;
21
22use crate::ArrayAndStats;
23use crate::CascadingCompressor;
24use crate::CompressorContext;
25use crate::Scheme;
26use crate::SchemeExt;
27use crate::schemes::integer::try_compress_delta;
28
29#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub struct OnPairScheme;
40
41impl Scheme for OnPairScheme {
42 fn scheme_name(&self) -> &'static str {
43 "vortex.string.onpair"
44 }
45
46 fn matches(&self, canonical: &Canonical) -> bool {
47 canonical.dtype().is_utf8()
48 }
49
50 fn num_children(&self) -> usize {
56 4
57 }
58
59 fn expected_compression_ratio(
60 &self,
61 _data: &ArrayAndStats,
62 _compress_ctx: CompressorContext,
63 _exec_ctx: &mut ExecutionCtx,
64 ) -> CompressionEstimate {
65 CompressionEstimate::Deferred(DeferredEstimate::Sample)
66 }
67
68 fn compress(
69 &self,
70 compressor: &CascadingCompressor,
71 data: &ArrayAndStats,
72 compress_ctx: CompressorContext,
73 exec_ctx: &mut ExecutionCtx,
74 ) -> VortexResult<ArrayRef> {
75 let utf8 = data.array_as_varbinview().into_owned();
76 let onpair_array = onpair_compress(utf8.as_array(), DEFAULT_DICT12_CONFIG, exec_ctx)?;
77
78 let dict_offsets = compress_offsets_child(
79 compressor,
80 onpair_array.dict_offsets(),
81 &compress_ctx,
82 self.id(),
83 0,
84 exec_ctx,
85 )?;
86 let codes = compress_primitive_child(
87 compressor,
88 onpair_array.codes(),
89 &compress_ctx,
90 self.id(),
91 1,
92 exec_ctx,
93 )?;
94 let codes_offsets = compress_offsets_child(
95 compressor,
96 onpair_array.codes_offsets(),
97 &compress_ctx,
98 self.id(),
99 2,
100 exec_ctx,
101 )?;
102 let uncompressed_lengths = compress_primitive_child(
103 compressor,
104 onpair_array.uncompressed_lengths(),
105 &compress_ctx,
106 self.id(),
107 3,
108 exec_ctx,
109 )?;
110
111 Ok(OnPair::try_new(
112 onpair_array.dtype().clone(),
113 onpair_array.dict_bytes_handle().clone(),
114 dict_offsets,
115 codes,
116 codes_offsets,
117 uncompressed_lengths,
118 onpair_array.array_validity(),
119 onpair_array.bits(),
120 )?
121 .into_array())
122 }
123}
124
125fn compress_primitive_child(
128 compressor: &CascadingCompressor,
129 child: &ArrayRef,
130 compress_ctx: &CompressorContext,
131 scheme_id: SchemeId,
132 child_idx: usize,
133 exec_ctx: &mut ExecutionCtx,
134) -> VortexResult<ArrayRef> {
135 let narrowed = child
136 .clone()
137 .execute::<PrimitiveArray>(exec_ctx)?
138 .narrow(exec_ctx)?
139 .into_array();
140 compressor.compress_child(&narrowed, compress_ctx, scheme_id, child_idx, exec_ctx)
141}
142
143const OFFSETS_DELTA_MIN_LEN: usize = 2048;
147
148fn compress_offsets_child(
155 compressor: &CascadingCompressor,
156 child: &ArrayRef,
157 compress_ctx: &CompressorContext,
158 scheme_id: SchemeId,
159 child_idx: usize,
160 exec_ctx: &mut ExecutionCtx,
161) -> VortexResult<ArrayRef> {
162 let narrowed = child
163 .clone()
164 .execute::<PrimitiveArray>(exec_ctx)?
165 .narrow(exec_ctx)?
166 .into_array();
167 let plain =
168 compressor.compress_child(&narrowed, compress_ctx, scheme_id, child_idx, exec_ctx)?;
169 if narrowed.len() < OFFSETS_DELTA_MIN_LEN {
170 return Ok(plain);
171 }
172 let delta = try_compress_delta(
173 compressor,
174 &narrowed,
175 compress_ctx,
176 scheme_id,
177 child_idx,
178 exec_ctx,
179 )?;
180 if delta.nbytes() < plain.nbytes() {
181 Ok(delta)
182 } else {
183 Ok(plain)
184 }
185}