1use itertools::Itertools;
5use vortex_array::ArrayRef;
6use vortex_array::ArrayView;
7use vortex_array::ExecutionCtx;
8use vortex_array::IntoArray;
9use vortex_array::arrays::BoolArray;
10use vortex_array::arrays::ConstantArray;
11use vortex_array::arrays::DecimalArray;
12use vortex_array::arrays::Primitive;
13use vortex_array::arrays::PrimitiveArray;
14use vortex_array::arrays::VarBinViewArray;
15use vortex_array::arrays::bool::BoolArrayExt;
16use vortex_array::arrays::decimal::DecimalArrayExt;
17use vortex_array::arrays::primitive::PrimitiveArrayExt;
18use vortex_array::buffer::BufferHandle;
19use vortex_array::dtype::NativePType;
20use vortex_array::dtype::Nullability;
21use vortex_array::expr::stats::Precision;
22use vortex_array::expr::stats::Stat;
23use vortex_array::match_each_decimal_value_type;
24use vortex_array::match_each_native_ptype;
25use vortex_array::match_each_unsigned_integer_ptype;
26use vortex_array::scalar::Scalar;
27use vortex_array::validity::Validity;
28use vortex_buffer::BitBuffer;
29use vortex_buffer::BitBufferMut;
30use vortex_buffer::Buffer;
31use vortex_buffer::BufferMut;
32use vortex_buffer::buffer;
33use vortex_error::VortexExpect;
34use vortex_error::VortexResult;
35use vortex_mask::Mask;
36
37use crate::iter::trimmed_ends_iter;
38
39pub fn runend_encode(
41 array: ArrayView<Primitive>,
42 ctx: &mut ExecutionCtx,
43) -> (PrimitiveArray, ArrayRef) {
44 let validity = match array
45 .validity()
46 .vortex_expect("run-end validity should be derivable")
47 {
48 Validity::NonNullable => None,
49 Validity::AllValid => None,
50 Validity::AllInvalid => {
51 let ends = PrimitiveArray::new(buffer![array.len() as u64], Validity::NonNullable);
53 ends.statistics()
54 .set(Stat::IsStrictSorted, Precision::Exact(true.into()));
55 return (
56 ends,
57 ConstantArray::new(Scalar::null(array.dtype().clone()), 1).into_array(),
58 );
59 }
60 Validity::Array(a) => {
61 let bool_array = a
62 .execute::<BoolArray>(ctx)
63 .vortex_expect("validity array must be convertible to bool");
64 Some(bool_array.to_bit_buffer())
65 }
66 };
67
68 let (ends, values) = match validity {
69 None => {
70 match_each_native_ptype!(array.ptype(), |P| {
71 let (ends, values) = runend_encode_primitive(array.as_slice::<P>());
72 (
73 PrimitiveArray::new(ends, Validity::NonNullable),
74 PrimitiveArray::new(values, array.dtype().nullability().into()).into_array(),
75 )
76 })
77 }
78 Some(validity) => {
79 match_each_native_ptype!(array.ptype(), |P| {
80 let (ends, values) =
81 runend_encode_nullable_primitive(array.as_slice::<P>(), validity);
82 (
83 PrimitiveArray::new(ends, Validity::NonNullable),
84 values.into_array(),
85 )
86 })
87 }
88 };
89
90 let ends = ends
91 .narrow(ctx)
92 .vortex_expect("Ends must succeed downcasting");
93
94 ends.statistics()
95 .set(Stat::IsStrictSorted, Precision::Exact(true.into()));
96
97 (ends, values)
98}
99
100fn runend_encode_primitive<T: NativePType>(elements: &[T]) -> (Buffer<u64>, Buffer<T>) {
101 let mut ends = BufferMut::empty();
102 let mut values = BufferMut::empty();
103
104 if elements.is_empty() {
105 return (ends.freeze(), values.freeze());
106 }
107
108 let mut prev = elements[0];
110 let mut end = 1;
111 for &e in elements.iter().skip(1) {
112 if e != prev {
113 ends.push(end);
114 values.push(prev);
115 }
116 prev = e;
117 end += 1;
118 }
119 ends.push(end);
120 values.push(prev);
121
122 (ends.freeze(), values.freeze())
123}
124
125fn runend_encode_nullable_primitive<T: NativePType>(
126 elements: &[T],
127 element_validity: BitBuffer,
128) -> (Buffer<u64>, PrimitiveArray) {
129 let mut ends = BufferMut::empty();
130 let mut values = BufferMut::empty();
131 let mut validity = BitBufferMut::with_capacity(values.capacity());
132
133 if elements.is_empty() {
134 return (
135 ends.freeze(),
136 PrimitiveArray::new(
137 values,
138 Validity::Array(BoolArray::from(validity.freeze()).into_array()),
139 ),
140 );
141 }
142
143 let mut prev = element_validity.value(0).then(|| elements[0]);
145 let mut end = 1;
146 for e in elements
147 .iter()
148 .zip(element_validity.iter())
149 .map(|(&e, is_valid)| is_valid.then_some(e))
150 .skip(1)
151 {
152 if e != prev {
153 ends.push(end);
154 match prev {
155 None => {
156 validity.append(false);
157 values.push(T::default());
158 }
159 Some(p) => {
160 validity.append(true);
161 values.push(p);
162 }
163 }
164 }
165 prev = e;
166 end += 1;
167 }
168 ends.push(end);
169
170 match prev {
171 None => {
172 validity.append(false);
173 values.push(T::default());
174 }
175 Some(p) => {
176 validity.append(true);
177 values.push(p);
178 }
179 }
180
181 (
182 ends.freeze(),
183 PrimitiveArray::new(values, Validity::from(validity.freeze())),
184 )
185}
186
187pub fn runend_decode_primitive(
188 ends: PrimitiveArray,
189 values: PrimitiveArray,
190 offset: usize,
191 length: usize,
192 ctx: &mut ExecutionCtx,
193) -> VortexResult<PrimitiveArray> {
194 let validity_mask = values
195 .as_ref()
196 .validity()?
197 .execute_mask(values.as_ref().len(), ctx)?;
198 Ok(match_each_native_ptype!(values.ptype(), |P| {
199 match_each_unsigned_integer_ptype!(ends.ptype(), |E| {
200 runend_decode_typed_primitive(
201 trimmed_ends_iter(ends.as_slice::<E>(), offset, length),
202 values.as_slice::<P>(),
203 validity_mask,
204 values.dtype().nullability(),
205 length,
206 )
207 })
208 }))
209}
210
211fn runend_decode_slice<T: Copy + Default>(
215 run_ends: impl Iterator<Item = usize>,
216 values: &[T],
217 values_validity: Mask,
218 values_nullability: Nullability,
219 length: usize,
220) -> (Buffer<T>, Validity) {
221 match values_validity {
222 Mask::AllTrue(_) => {
223 let mut decoded: BufferMut<T> = BufferMut::with_capacity(length);
224 for (end, value) in run_ends.zip_eq(values) {
225 assert!(
226 end >= decoded.len(),
227 "Runend ends must be monotonic, got {end} after {}",
228 decoded.len()
229 );
230 assert!(end <= length, "Runend end must be less than overall length");
231 unsafe { decoded.push_n_unchecked(*value, end - decoded.len()) };
234 }
235 (decoded.into(), values_nullability.into())
236 }
237 Mask::AllFalse(_) => (Buffer::<T>::zeroed(length), Validity::AllInvalid),
238 Mask::Values(mask) => {
239 let mut decoded = BufferMut::with_capacity(length);
240 let mut decoded_validity = BitBufferMut::with_capacity(length);
241 for (end, value) in run_ends.zip_eq(
242 values
243 .iter()
244 .zip(mask.bit_buffer().iter())
245 .map(|(&v, is_valid)| is_valid.then_some(v)),
246 ) {
247 assert!(
248 end >= decoded.len(),
249 "Runend ends must be monotonic, got {end} after {}",
250 decoded.len()
251 );
252 assert!(end <= length, "Runend end must be less than overall length");
253 match value {
254 None => {
255 decoded_validity.append_n(false, end - decoded.len());
256 unsafe { decoded.push_n_unchecked(T::default(), end - decoded.len()) };
259 }
260 Some(value) => {
261 decoded_validity.append_n(true, end - decoded.len());
262 unsafe { decoded.push_n_unchecked(value, end - decoded.len()) };
265 }
266 }
267 }
268 (decoded.into(), Validity::from(decoded_validity.freeze()))
269 }
270 }
271}
272
273pub fn runend_decode_typed_primitive<T: NativePType>(
274 run_ends: impl Iterator<Item = usize>,
275 values: &[T],
276 values_validity: Mask,
277 values_nullability: Nullability,
278 length: usize,
279) -> PrimitiveArray {
280 let (decoded, validity) = runend_decode_slice(
281 run_ends,
282 values,
283 values_validity,
284 values_nullability,
285 length,
286 );
287 PrimitiveArray::new(decoded, validity)
288}
289
290pub fn runend_decode_decimal(
292 ends: PrimitiveArray,
293 values: DecimalArray,
294 offset: usize,
295 length: usize,
296 ctx: &mut ExecutionCtx,
297) -> VortexResult<DecimalArray> {
298 let validity_mask = values
299 .as_ref()
300 .validity()?
301 .execute_mask(values.as_ref().len(), ctx)?;
302
303 Ok(match_each_decimal_value_type!(values.values_type(), |D| {
304 let (decoded, validity) = match_each_unsigned_integer_ptype!(ends.ptype(), |E| {
305 runend_decode_slice(
306 trimmed_ends_iter(ends.as_slice::<E>(), offset, length),
307 values.buffer::<D>().as_slice(),
308 validity_mask,
309 values.dtype().nullability(),
310 length,
311 )
312 });
313 DecimalArray::new(decoded, values.decimal_dtype(), validity)
314 }))
315}
316
317pub fn runend_decode_varbinview(
319 ends: PrimitiveArray,
320 values: VarBinViewArray,
321 offset: usize,
322 length: usize,
323 ctx: &mut ExecutionCtx,
324) -> VortexResult<VarBinViewArray> {
325 let validity_mask = values
326 .as_ref()
327 .validity()?
328 .execute_mask(values.as_ref().len(), ctx)?;
329 let views = values.views();
330
331 let (decoded_views, validity) = match_each_unsigned_integer_ptype!(ends.ptype(), |E| {
332 runend_decode_slice(
333 trimmed_ends_iter(ends.as_slice::<E>(), offset, length),
334 views,
335 validity_mask,
336 values.dtype().nullability(),
337 length,
338 )
339 });
340
341 let parts = values.into_data_parts();
342 let view_handle = BufferHandle::new_host(decoded_views.into_byte_buffer());
343
344 Ok(unsafe {
347 VarBinViewArray::new_handle_unchecked(view_handle, parts.buffers, parts.dtype, validity)
348 })
349}
350
351#[cfg(test)]
352mod tests {
353 use std::sync::LazyLock;
354
355 use vortex_array::VortexSessionExecute;
356 use vortex_array::arrays::PrimitiveArray;
357 use vortex_array::assert_arrays_eq;
358 use vortex_array::validity::Validity;
359 use vortex_buffer::BitBuffer;
360 use vortex_buffer::buffer;
361 use vortex_error::VortexResult;
362 use vortex_session::VortexSession;
363
364 use crate::compress::runend_decode_primitive;
365 use crate::compress::runend_encode;
366
367 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
368 let session = vortex_array::array_session();
369 crate::initialize(&session);
370 session
371 });
372
373 #[test]
374 fn encode() -> VortexResult<()> {
375 let mut ctx = SESSION.create_execution_ctx();
376 let arr = PrimitiveArray::from_iter([1i32, 1, 2, 2, 2, 3, 3, 3, 3, 3]);
377 let (ends, values) = runend_encode(arr.as_view(), &mut ctx);
378 let values = values.execute::<PrimitiveArray>(&mut ctx)?;
379
380 let expected_ends = PrimitiveArray::from_iter(vec![2u8, 5, 10]);
381 assert_arrays_eq!(ends, expected_ends, &mut ctx);
382 let expected_values = PrimitiveArray::from_iter(vec![1i32, 2, 3]);
383 assert_arrays_eq!(values, expected_values, &mut ctx);
384 Ok(())
385 }
386
387 #[test]
388 fn encode_nullable() -> VortexResult<()> {
389 let mut ctx = SESSION.create_execution_ctx();
390 let arr = PrimitiveArray::new(
391 buffer![1i32, 1, 2, 2, 2, 3, 3, 3, 3, 3],
392 Validity::from(BitBuffer::from(vec![
393 true, true, false, false, true, true, true, true, false, false,
394 ])),
395 );
396 let (ends, values) = runend_encode(arr.as_view(), &mut ctx);
397 let values = values.execute::<PrimitiveArray>(&mut ctx)?;
398
399 let expected_ends = PrimitiveArray::from_iter(vec![2u8, 4, 5, 8, 10]);
400 assert_arrays_eq!(ends, expected_ends, &mut ctx);
401 let expected_values =
402 PrimitiveArray::from_option_iter(vec![Some(1i32), None, Some(2), Some(3), None]);
403 assert_arrays_eq!(values, expected_values, &mut ctx);
404 Ok(())
405 }
406
407 #[test]
408 fn encode_all_null() -> VortexResult<()> {
409 let mut ctx = SESSION.create_execution_ctx();
410 let arr = PrimitiveArray::new(
411 buffer![0, 0, 0, 0, 0],
412 Validity::from(BitBuffer::new_unset(5)),
413 );
414 let (ends, values) = runend_encode(arr.as_view(), &mut ctx);
415 let values = values.execute::<PrimitiveArray>(&mut ctx)?;
416
417 let expected_ends = PrimitiveArray::from_iter(vec![5u64]);
418 assert_arrays_eq!(ends, expected_ends, &mut ctx);
419 let expected_values = PrimitiveArray::from_option_iter(vec![Option::<i32>::None]);
420 assert_arrays_eq!(values, expected_values, &mut ctx);
421 Ok(())
422 }
423
424 #[test]
425 fn decode() -> VortexResult<()> {
426 let mut ctx = SESSION.create_execution_ctx();
427 let ends = PrimitiveArray::from_iter([2u32, 5, 10]);
428 let values = PrimitiveArray::from_iter([1i32, 2, 3]);
429 let decoded = runend_decode_primitive(ends, values, 0, 10, &mut ctx)?;
430
431 let expected = PrimitiveArray::from_iter(vec![1i32, 1, 2, 2, 2, 3, 3, 3, 3, 3]);
432 assert_arrays_eq!(decoded, expected, &mut ctx);
433 Ok(())
434 }
435}