vortex_fastlanes/rle/array/
mod.rs1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use vortex_array::ArrayRef;
8use vortex_array::ExecutionCtx;
9use vortex_array::TypedArrayRef;
10use vortex_array::array_slots;
11use vortex_error::VortexResult;
12use vortex_error::vortex_ensure;
13
14pub mod rle_compress;
15pub mod rle_decompress;
16
17#[array_slots(crate::RLE)]
18pub struct RLESlots {
19 #[slot(0)]
21 pub values: ArrayRef,
22 #[slot(1)]
24 pub indices: ArrayRef,
25 #[slot(2)]
35 pub values_idx_offsets: ArrayRef,
36}
37
38#[derive(Clone, Debug)]
39pub struct RLEData {
40 pub(super) offset: usize,
42}
43
44impl Display for RLEData {
45 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46 write!(f, "offset: {}", self.offset)
47 }
48}
49
50impl RLEData {
51 pub fn try_new(offset: usize) -> VortexResult<Self> {
61 vortex_ensure!(
62 offset < 1024,
63 "Offset must be smaller than 1024, got {}",
64 offset
65 );
66 Ok(Self { offset })
67 }
68
69 pub unsafe fn new_unchecked(offset: usize) -> Self {
77 Self { offset }
78 }
79
80 #[inline]
81 pub fn offset(&self) -> usize {
82 self.offset
83 }
84}
85
86pub trait RLEArrayExt: RLEArraySlotsExt {
87 #[expect(
93 clippy::expect_used,
94 reason = "expect is safe here as scalar_at returns a valid primitive"
95 )]
96 fn values_idx_offset(&self, chunk_idx: usize, ctx: &mut ExecutionCtx) -> usize {
97 self.values_idx_offsets()
98 .execute_scalar(chunk_idx, ctx)
99 .expect("index must be in bounds")
100 .as_primitive()
101 .as_::<usize>()
102 .expect("index must be of type usize")
103 - self
104 .values_idx_offsets()
105 .execute_scalar(0, ctx)
106 .expect("index must be in bounds")
107 .as_primitive()
108 .as_::<usize>()
109 .expect("index must be of type usize")
110 }
111
112 #[inline]
114 fn offset(&self) -> usize {
115 self.offset
116 }
117}
118
119impl<T: TypedArrayRef<crate::RLE>> RLEArrayExt for T {}
120
121#[cfg(test)]
122mod tests {
123 use vortex_array::ArrayContext;
124 use vortex_array::Canonical;
125 use vortex_array::IntoArray;
126 use vortex_array::VortexSessionExecute;
127 use vortex_array::arrays::PrimitiveArray;
128 use vortex_array::arrays::primitive::PrimitiveArrayExt;
129 use vortex_array::assert_arrays_eq;
130 use vortex_array::dtype::DType;
131 use vortex_array::dtype::Nullability;
132 use vortex_array::dtype::PType;
133 use vortex_array::serde::SerializeOptions;
134 use vortex_array::serde::SerializedArray;
135 use vortex_array::validity::Validity;
136 use vortex_buffer::Buffer;
137 use vortex_buffer::ByteBufferMut;
138 use vortex_error::VortexExpect;
139 use vortex_error::VortexResult;
140 use vortex_session::registry::ReadContext;
141
142 use crate::FL_CHUNK_SIZE;
143 use crate::RLE;
144 use crate::RLEData;
145 use crate::rle::array::RLEArrayExt;
146 use crate::rle::array::RLEArraySlotsExt;
147 use crate::test::SESSION;
148
149 #[test]
150 fn test_try_new() {
151 let values = PrimitiveArray::from_iter([10u32, 20, 30]).into_array();
152
153 let indices =
155 PrimitiveArray::from_iter([0u16, 0, 1, 1, 2].iter().cycle().take(1024).copied())
156 .into_array();
157 let values_idx_offsets = PrimitiveArray::from_iter([0u64]).into_array();
158 let rle_array = RLE::try_new(values, indices, values_idx_offsets, 0, 5)
159 .vortex_expect("RLEData is always valid");
160
161 assert_eq!(rle_array.len(), 5);
162 assert_eq!(rle_array.values().len(), 3);
163 assert_eq!(rle_array.values().dtype().as_ptype(), PType::U32);
164 }
165
166 #[test]
167 fn test_try_new_with_validity() {
168 let values = PrimitiveArray::from_iter([10u32, 20]).into_array();
169 let values_idx_offsets = PrimitiveArray::from_iter([0u64]).into_array();
170
171 let indices_pattern = [0u16, 1, 0];
172 let validity_pattern = [true, false, true];
173
174 let indices_with_validity = PrimitiveArray::new(
176 indices_pattern
177 .iter()
178 .cycle()
179 .take(1024)
180 .copied()
181 .collect::<Buffer<u16>>(),
182 Validity::from_iter(validity_pattern.iter().cycle().take(1024).copied()),
183 )
184 .into_array();
185
186 let rle_array = RLE::try_new(values, indices_with_validity, values_idx_offsets, 0, 3)
187 .vortex_expect("RLEData is always valid");
188
189 assert_eq!(rle_array.len(), 3);
190 assert_eq!(rle_array.values().len(), 2);
191 let mut ctx = SESSION.create_execution_ctx();
192 assert!(rle_array.is_valid(0, &mut ctx).unwrap());
193 assert!(!rle_array.is_valid(1, &mut ctx).unwrap());
194 assert!(rle_array.is_valid(2, &mut ctx).unwrap());
195 }
196
197 #[test]
198 fn test_all_valid() {
199 let values = PrimitiveArray::from_iter([10u32, 20, 30]).into_array();
200 let values_idx_offsets = PrimitiveArray::from_iter([0u64]).into_array();
201
202 let indices_pattern = [0u16, 1, 2, 0, 1];
203 let validity_pattern = [true, true, true, false, false];
204
205 let indices_with_validity = PrimitiveArray::new(
207 indices_pattern
208 .iter()
209 .cycle()
210 .take(1024)
211 .copied()
212 .collect::<Buffer<u16>>(),
213 Validity::from_iter(validity_pattern.iter().cycle().take(1024).copied()),
214 )
215 .into_array();
216
217 let rle_array = RLE::try_new(values, indices_with_validity, values_idx_offsets, 0, 5)
218 .vortex_expect("RLEData is always valid");
219
220 let mut ctx = SESSION.create_execution_ctx();
221 let valid_slice = rle_array
222 .slice(0..3)
223 .unwrap()
224 .execute::<PrimitiveArray>(&mut ctx)
225 .unwrap();
226 assert!(valid_slice.all_valid(&mut ctx).unwrap());
228
229 let mixed_slice = rle_array.slice(1..5).unwrap();
230 assert!(!mixed_slice.all_valid(&mut ctx).unwrap());
231 }
232
233 #[test]
234 fn test_all_invalid() {
235 let values = PrimitiveArray::from_iter([10u32, 20, 30]).into_array();
236 let values_idx_offsets = PrimitiveArray::from_iter([0u64]).into_array();
237
238 let indices_pattern = [0u16, 1, 2, 0, 1];
240 let validity_pattern = [true, true, false, false, false];
241
242 let indices_with_validity = PrimitiveArray::new(
243 indices_pattern
244 .iter()
245 .cycle()
246 .take(1024)
247 .copied()
248 .collect::<Buffer<u16>>(),
249 Validity::from_iter(validity_pattern.iter().cycle().take(1024).copied()),
250 )
251 .into_array();
252
253 let rle_array = RLE::try_new(values, indices_with_validity, values_idx_offsets, 0, 5)
254 .vortex_expect("RLEData is always valid");
255
256 let invalid_slice = rle_array
258 .slice(2..5)
259 .unwrap()
260 .execute::<Canonical>(&mut SESSION.create_execution_ctx())
261 .unwrap()
262 .into_primitive();
263 let mut ctx = SESSION.create_execution_ctx();
264 assert!(invalid_slice.all_invalid(&mut ctx).unwrap());
265
266 let mixed_slice = rle_array.slice(1..4).unwrap();
267 assert!(!mixed_slice.all_invalid(&mut ctx).unwrap());
268 }
269
270 #[test]
271 fn test_validity_mask() {
272 let values = PrimitiveArray::from_iter([10u32, 20, 30]).into_array();
273 let values_idx_offsets = PrimitiveArray::from_iter([0u64]).into_array();
274
275 let indices_pattern = [0u16, 1, 2, 0];
277 let validity_pattern = [true, false, true, false];
278
279 let indices_with_validity = PrimitiveArray::new(
280 indices_pattern
281 .iter()
282 .cycle()
283 .take(1024)
284 .copied()
285 .collect::<Buffer<u16>>(),
286 Validity::from_iter(validity_pattern.iter().cycle().take(1024).copied()),
287 )
288 .into_array();
289
290 let rle_array = RLE::try_new(values, indices_with_validity, values_idx_offsets, 0, 4)
291 .vortex_expect("RLEData is always valid");
292
293 let sliced_array = rle_array.slice(1..4).unwrap();
294 let validity_mask = sliced_array
295 .validity()
296 .unwrap()
297 .execute_mask(sliced_array.len(), &mut SESSION.create_execution_ctx())
298 .unwrap();
299
300 let mut ctx = SESSION.create_execution_ctx();
301 let expected_mask = Validity::from_iter([false, true, false])
302 .execute_mask(3, &mut ctx)
303 .unwrap();
304 assert_eq!(validity_mask.len(), expected_mask.len());
305 assert_eq!(validity_mask, expected_mask);
306 assert_eq!(validity_mask.len(), expected_mask.len());
307 assert_eq!(validity_mask, expected_mask);
308 }
309
310 #[test]
311 fn test_try_new_empty() {
312 let values = PrimitiveArray::from_iter(Vec::<u32>::new()).into_array();
313 let indices = PrimitiveArray::from_iter(Vec::<u16>::new()).into_array();
314 let values_idx_offsets = PrimitiveArray::from_iter(Vec::<u64>::new()).into_array();
315 let rle_array = RLE::try_new(
316 values,
317 indices.clone(),
318 values_idx_offsets,
319 0,
320 indices.len(),
321 )
322 .vortex_expect("RLEData is always valid");
323
324 assert_eq!(rle_array.len(), 0);
325 assert_eq!(rle_array.values().len(), 0);
326 }
327
328 #[test]
329 fn test_multi_chunk_two_chunks() {
330 let mut ctx = SESSION.create_execution_ctx();
331 let values = PrimitiveArray::from_iter([10u32, 20, 30, 40]).into_array();
332 let indices = PrimitiveArray::from_iter([0u16, 1].repeat(1024)).into_array();
333 let values_idx_offsets = PrimitiveArray::from_iter([0u64, 2]).into_array();
334 let rle_array = RLE::try_new(values, indices, values_idx_offsets, 0, 2048)
335 .vortex_expect("RLEData is always valid");
336
337 assert_eq!(rle_array.len(), 2048);
338 assert_eq!(rle_array.values().len(), 4);
339
340 assert_eq!(rle_array.values_idx_offset(0, &mut ctx), 0);
341 assert_eq!(rle_array.values_idx_offset(1, &mut ctx), 2);
342 }
343
344 #[test]
345 fn test_rle_serialization() -> VortexResult<()> {
346 let mut exec_ctx = SESSION.create_execution_ctx();
347 let primitive = PrimitiveArray::from_iter((0..2048).map(|i| (i / 100) as u32));
348 let rle_array = RLEData::encode(primitive.as_view(), &mut exec_ctx)?;
349 assert_eq!(rle_array.len(), 2048);
350
351 let original_data = rle_array
352 .as_array()
353 .clone()
354 .execute::<PrimitiveArray>(&mut exec_ctx)?;
355
356 let ctx = ArrayContext::empty();
357 let serialized =
358 rle_array
359 .into_array()
360 .serialize(&ctx, &SESSION, &SerializeOptions::default())?;
361
362 let mut concat = ByteBufferMut::empty();
363 for buf in serialized {
364 concat.extend_from_slice(buf.as_ref());
365 }
366 let concat = concat.freeze();
367
368 let parts = SerializedArray::try_from(concat)?;
369 let decoded = parts.decode(
370 &DType::Primitive(PType::U32, Nullability::NonNullable),
371 2048,
372 &ReadContext::new(ctx.to_ids()),
373 &SESSION,
374 )?;
375
376 let decoded_data = decoded.execute::<PrimitiveArray>(&mut exec_ctx)?;
377
378 assert_arrays_eq!(
379 original_data,
380 decoded_data,
381 &mut SESSION.create_execution_ctx()
382 );
383 Ok(())
384 }
385
386 #[test]
387 fn test_rle_serialization_slice() -> VortexResult<()> {
388 let mut exec_ctx = SESSION.create_execution_ctx();
389 let primitive = PrimitiveArray::from_iter((0..2048).map(|i| (i / 100) as u32));
390 let rle_array = RLEData::encode(primitive.as_view(), &mut exec_ctx)?;
391
392 let sliced = RLE::try_new(
393 rle_array.values().clone(),
394 rle_array.indices().clone(),
395 rle_array.values_idx_offsets().clone(),
396 100,
397 100,
398 )
399 .vortex_expect("RLEData is always valid");
400 assert_eq!(sliced.len(), 100);
401
402 let ctx = ArrayContext::empty();
403 let serialized =
404 sliced
405 .clone()
406 .into_array()
407 .serialize(&ctx, &SESSION, &SerializeOptions::default())?;
408
409 let mut concat = ByteBufferMut::empty();
410 for buf in serialized {
411 concat.extend_from_slice(buf.as_ref());
412 }
413 let concat = concat.freeze();
414
415 let parts = SerializedArray::try_from(concat)?;
416 let decoded = parts.decode(
417 sliced.dtype(),
418 sliced.len(),
419 &ReadContext::new(ctx.to_ids()),
420 &SESSION,
421 )?;
422
423 let original_data = sliced
424 .as_array()
425 .clone()
426 .execute::<PrimitiveArray>(&mut exec_ctx)?;
427 let decoded_data = decoded.execute::<PrimitiveArray>(&mut exec_ctx)?;
428
429 assert_arrays_eq!(
430 original_data,
431 decoded_data,
432 &mut SESSION.create_execution_ctx()
433 );
434 Ok(())
435 }
436
437 #[test]
446 fn test_recompress_indices_no_cross_chunk_leak() -> VortexResult<()> {
447 let mut ctx = SESSION.create_execution_ctx();
448 let len = FL_CHUNK_SIZE + 100;
449 let mut values: Vec<Option<i16>> = vec![None; len];
450 values[0] = Some(10);
452 values[500] = Some(20);
453 let original = PrimitiveArray::from_option_iter(values);
456 let rle = RLEData::encode(original.as_view(), &mut ctx)?;
457
458 let indices_prim = rle
461 .indices()
462 .clone()
463 .execute::<PrimitiveArray>(&mut ctx)?
464 .narrow(&mut ctx)?;
465 let re_encoded = RLEData::encode(indices_prim.as_view(), &mut ctx)?;
466
467 let reconstructed = unsafe {
470 RLE::new_unchecked(
471 rle.values().clone(),
472 re_encoded.into_array(),
473 rle.values_idx_offsets().clone(),
474 rle.offset(),
475 rle.len(),
476 )
477 };
478
479 let decoded = reconstructed
481 .as_array()
482 .clone()
483 .execute::<PrimitiveArray>(&mut ctx)?;
484 assert_arrays_eq!(decoded, original, &mut ctx);
485 Ok(())
486 }
487}