1use std::ops::Deref;
7
8use vortex_array::ArrayRef;
9use vortex_array::ExecutionCtx;
10use vortex_error::VortexResult;
11
12use crate::BtrBlocksCompressorBuilder;
13use crate::CascadingCompressor;
14
15#[derive(Clone)]
35pub struct BtrBlocksCompressor(
36 pub CascadingCompressor,
38);
39
40impl BtrBlocksCompressor {
41 pub fn compress(&self, array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<ArrayRef> {
43 self.0.compress(array, ctx)
44 }
45}
46
47impl Deref for BtrBlocksCompressor {
48 type Target = CascadingCompressor;
49
50 fn deref(&self) -> &CascadingCompressor {
51 &self.0
52 }
53}
54
55impl Default for BtrBlocksCompressor {
56 fn default() -> Self {
57 BtrBlocksCompressorBuilder::default().build()
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use std::sync::LazyLock;
64
65 use rstest::rstest;
66 use vortex_array::IntoArray;
67 use vortex_array::VortexSessionExecute;
68 use vortex_array::arrays::BoolArray;
69 use vortex_array::arrays::Constant;
70 use vortex_array::arrays::Dict;
71 use vortex_array::arrays::List;
72 use vortex_array::arrays::ListView;
73 use vortex_array::arrays::ListViewArray;
74 use vortex_array::arrays::VarBinViewArray;
75 use vortex_array::assert_arrays_eq;
76 use vortex_array::dtype::DType;
77 use vortex_array::dtype::Nullability;
78 use vortex_array::validity::Validity;
79 use vortex_buffer::BitBuffer;
80 use vortex_buffer::buffer;
81 use vortex_error::VortexResult;
82 use vortex_session::VortexSession;
83
84 use crate::BtrBlocksCompressor;
85 #[cfg(feature = "zstd")]
86 use crate::BtrBlocksCompressorBuilder;
87
88 static SESSION: LazyLock<VortexSession> = LazyLock::new(vortex_array::array_session);
89
90 #[rstest]
91 #[case::zctl(
92 unsafe {
93 ListViewArray::new_unchecked(
94 buffer![1i32, 2, 3, 4, 5].into_array(),
95 buffer![0i32, 3].into_array(),
96 buffer![3i32, 2].into_array(),
97 Validity::NonNullable,
98 ).with_zero_copy_to_list(true)
99 },
100 true,
101 )]
102 #[case::overlapping(
103 ListViewArray::new(
104 buffer![1i32, 2, 3].into_array(),
105 buffer![0i32, 0, 0].into_array(),
106 buffer![3i32, 3, 3].into_array(),
107 Validity::NonNullable,
108 ),
109 false,
110 )]
111 fn listview_compress_roundtrip(
112 #[case] input: ListViewArray,
113 #[case] expect_list: bool,
114 ) -> VortexResult<()> {
115 let mut ctx = SESSION.create_execution_ctx();
116 let array_ref = input.clone().into_array();
117 let result = BtrBlocksCompressor::default()
118 .compress(&array_ref, &mut SESSION.create_execution_ctx())?;
119 if expect_list {
120 assert!(result.as_opt::<List>().is_some());
121 } else {
122 assert!(result.as_opt::<ListView>().is_some());
123 }
124 assert_arrays_eq!(result, input, &mut ctx);
125 Ok(())
126 }
127
128 #[test]
129 fn test_constant_all_true() -> VortexResult<()> {
130 let mut ctx = SESSION.create_execution_ctx();
131 let array = BoolArray::new(BitBuffer::from(vec![true; 100]), Validity::NonNullable);
132 let btr = BtrBlocksCompressor::default();
133 let compressed = btr.compress(
134 &array.clone().into_array(),
135 &mut SESSION.create_execution_ctx(),
136 )?;
137 assert!(compressed.is::<Constant>());
138 assert_arrays_eq!(compressed, array, &mut ctx);
139 Ok(())
140 }
141
142 #[test]
143 fn test_constant_all_false() -> VortexResult<()> {
144 let mut ctx = SESSION.create_execution_ctx();
145 let array = BoolArray::new(BitBuffer::from(vec![false; 100]), Validity::NonNullable);
146 let btr = BtrBlocksCompressor::default();
147 let compressed = btr.compress(
148 &array.clone().into_array(),
149 &mut SESSION.create_execution_ctx(),
150 )?;
151 assert!(compressed.is::<Constant>());
152 assert_arrays_eq!(compressed, array, &mut ctx);
153 Ok(())
154 }
155
156 #[test]
157 fn test_nullable_all_valid_compressed() -> VortexResult<()> {
158 let mut ctx = SESSION.create_execution_ctx();
159 let array = BoolArray::new(
160 BitBuffer::from(vec![true; 100]),
161 Validity::from(BitBuffer::from(vec![true; 100])),
162 );
163 let btr = BtrBlocksCompressor::default();
164 let compressed = btr.compress(
165 &array.clone().into_array(),
166 &mut SESSION.create_execution_ctx(),
167 )?;
168 assert!(compressed.is::<Constant>());
169 assert_arrays_eq!(compressed, array, &mut ctx);
170 Ok(())
171 }
172
173 #[test]
174 fn test_nullable_with_nulls_not_compressed() -> VortexResult<()> {
175 let mut ctx = SESSION.create_execution_ctx();
176 let validity = Validity::from(BitBuffer::from_iter((0..100).map(|i| i % 3 != 0)));
177 let array = BoolArray::new(BitBuffer::from(vec![true; 100]), validity);
178 let btr = BtrBlocksCompressor::default();
179 let compressed = btr.compress(
180 &array.clone().into_array(),
181 &mut SESSION.create_execution_ctx(),
182 )?;
183 assert!(!compressed.is::<Constant>());
184 assert_arrays_eq!(compressed, array, &mut ctx);
185 Ok(())
186 }
187
188 #[test]
189 fn test_mixed_not_constant() -> VortexResult<()> {
190 let mut ctx = SESSION.create_execution_ctx();
191 let array = BoolArray::new(
192 BitBuffer::from(vec![true, false, true, false, true]),
193 Validity::NonNullable,
194 );
195 let btr = BtrBlocksCompressor::default();
196 let compressed = btr.compress(
197 &array.clone().into_array(),
198 &mut SESSION.create_execution_ctx(),
199 )?;
200 assert!(!compressed.is::<Constant>());
201 assert_arrays_eq!(compressed, array, &mut ctx);
202 Ok(())
203 }
204
205 #[test]
206 fn test_binary_constant_compressed() -> VortexResult<()> {
207 let mut ctx = SESSION.create_execution_ctx();
208 let values = vec![Some(b"constant-bytes".as_slice()); 100];
209 let array = VarBinViewArray::from_iter(values, DType::Binary(Nullability::NonNullable));
210 let btr = BtrBlocksCompressor::default();
211 let compressed = btr.compress(
212 &array.clone().into_array(),
213 &mut SESSION.create_execution_ctx(),
214 )?;
215 assert!(compressed.is::<Constant>());
216 assert_arrays_eq!(compressed, array, &mut ctx);
217 Ok(())
218 }
219
220 #[test]
221 fn test_binary_dict_compressed() -> VortexResult<()> {
222 let mut ctx = SESSION.create_execution_ctx();
223 let distinct_values: [&[u8]; 3] = [b"alpha", b"beta", b"gamma"];
224 let values = (0..1000)
225 .map(|idx| Some(distinct_values[idx % distinct_values.len()]))
226 .collect::<Vec<_>>();
227 let array = VarBinViewArray::from_iter(values, DType::Binary(Nullability::NonNullable));
228 let btr = BtrBlocksCompressor::default();
229 let compressed = btr.compress(
230 &array.clone().into_array(),
231 &mut SESSION.create_execution_ctx(),
232 )?;
233 assert!(compressed.is::<Dict>());
234 assert_arrays_eq!(compressed, array, &mut ctx);
235 Ok(())
236 }
237
238 #[cfg(feature = "zstd")]
239 #[test]
240 fn test_compact_binary_zstd_compressed() -> VortexResult<()> {
241 let values = (0..1024)
242 .map(|idx| {
243 let mut value = Vec::from(&b"common binary payload prefix "[..]);
244 value.extend_from_slice(&(idx as u32).to_le_bytes());
245 value.extend_from_slice(&[b'x'; 96]);
246 value
247 })
248 .collect::<Vec<_>>();
249 let array = VarBinViewArray::from_iter(
250 values.iter().map(|value| Some(value.as_slice())),
251 DType::Binary(Nullability::NonNullable),
252 );
253
254 let compressor = BtrBlocksCompressorBuilder::default().with_compact().build();
255 let mut ctx = SESSION.create_execution_ctx();
256 let compressed = compressor.compress(&array.clone().into_array(), &mut ctx)?;
257
258 assert!(
259 compressed.is::<vortex_zstd::Zstd>(),
260 "expected Zstd, got {}",
261 compressed.encoding_id()
262 );
263 assert_arrays_eq!(compressed, array, &mut ctx);
264 Ok(())
265 }
266
267 #[cfg(all(feature = "zstd", feature = "unstable_encodings"))]
268 #[test]
269 fn test_cuda_compatible_binary_zstd_buffers_compressed() -> VortexResult<()> {
270 let values = (0..1024)
271 .map(|idx| {
272 let mut value = Vec::from(&b"common binary payload prefix "[..]);
273 value.extend_from_slice(&(idx as u32).to_le_bytes());
274 value.extend_from_slice(&[b'x'; 96]);
275 value
276 })
277 .collect::<Vec<_>>();
278 let array = VarBinViewArray::from_iter(
279 values.iter().map(|value| Some(value.as_slice())),
280 DType::Binary(Nullability::NonNullable),
281 );
282
283 let compressor = BtrBlocksCompressorBuilder::default()
284 .only_cuda_compatible()
285 .build();
286 let mut ctx = SESSION.create_execution_ctx();
287 let compressed = compressor.compress(&array.clone().into_array(), &mut ctx)?;
288
289 assert!(
290 compressed.is::<vortex_zstd::ZstdBuffers>(),
291 "expected ZstdBuffers, got {}",
292 compressed.encoding_id()
293 );
294 assert_arrays_eq!(compressed, array, &mut ctx);
295 Ok(())
296 }
297}