1use std::hash::Hash;
5use std::hash::Hasher;
6
7use prost::Message;
8use vortex_array::Array;
9use vortex_array::ArrayEq;
10use vortex_array::ArrayHash;
11use vortex_array::ArrayId;
12use vortex_array::ArrayParts;
13use vortex_array::ArrayRef;
14use vortex_array::ArrayView;
15use vortex_array::EqMode;
16use vortex_array::ExecutionCtx;
17use vortex_array::ExecutionResult;
18use vortex_array::IntoArray;
19use vortex_array::arrays::Primitive;
20use vortex_array::buffer::BufferHandle;
21use vortex_array::dtype::DType;
22use vortex_array::dtype::Nullability;
23use vortex_array::dtype::PType;
24use vortex_array::serde::ArrayChildren;
25use vortex_array::vtable::VTable;
26use vortex_error::VortexResult;
27use vortex_error::vortex_ensure;
28use vortex_error::vortex_panic;
29use vortex_session::VortexSession;
30use vortex_session::registry::CachedId;
31
32use crate::RLEData;
33use crate::rle::array::RLEArrayExt;
34use crate::rle::array::RLEArraySlotsExt;
35use crate::rle::array::RLESlots;
36use crate::rle::array::RLESlotsView;
37use crate::rle::array::rle_decompress::rle_decompress;
38use crate::rle::vtable::rules::RULES;
39
40mod operations;
41mod rules;
42mod validity;
43
44pub type RLEArray = Array<RLE>;
46
47#[derive(Clone, prost::Message)]
48pub struct RLEMetadata {
49 #[prost(uint64, tag = "1")]
50 pub values_len: u64,
51 #[prost(uint64, tag = "2")]
52 pub indices_len: u64,
53 #[prost(enumeration = "PType", tag = "3")]
54 pub indices_ptype: i32,
55 #[prost(uint64, tag = "4")]
56 pub values_idx_offsets_len: u64,
57 #[prost(enumeration = "PType", tag = "5")]
58 pub values_idx_offsets_ptype: i32,
59 #[prost(uint64, tag = "6", default = "0")]
60 pub offset: u64,
61}
62
63impl ArrayHash for RLEData {
64 fn array_hash<H: Hasher>(&self, state: &mut H, _accuracy: EqMode) {
65 self.offset.hash(state);
66 }
67}
68
69impl ArrayEq for RLEData {
70 fn array_eq(&self, other: &Self, _accuracy: EqMode) -> bool {
71 self.offset == other.offset
72 }
73}
74
75impl VTable for RLE {
76 type TypedArrayData = RLEData;
77
78 type OperationsVTable = Self;
79 type ValidityVTable = Self;
80
81 fn id(&self) -> ArrayId {
82 static ID: CachedId = CachedId::new("fastlanes.rle");
83 *ID
84 }
85
86 fn validate(
87 &self,
88 data: &Self::TypedArrayData,
89 dtype: &DType,
90 len: usize,
91 slots: &[Option<ArrayRef>],
92 ) -> VortexResult<()> {
93 let rle_slots = RLESlotsView::from_slots(slots);
94 validate_parts(
95 rle_slots.values,
96 rle_slots.indices,
97 rle_slots.values_idx_offsets,
98 data.offset,
99 dtype,
100 len,
101 )
102 }
103
104 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
105 0
106 }
107
108 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
109 vortex_panic!("RLEArray buffer index {idx} out of bounds")
110 }
111
112 fn buffer_name(_array: ArrayView<'_, Self>, _idx: usize) -> Option<String> {
113 None
114 }
115
116 fn with_buffers(
117 &self,
118 array: ArrayView<'_, Self>,
119 buffers: &[BufferHandle],
120 ) -> VortexResult<ArrayParts<Self>> {
121 vortex_array::vtable::with_empty_buffers(self, array, buffers)
122 }
123
124 fn reduce_parent(
125 array: ArrayView<'_, Self>,
126 parent: &ArrayRef,
127 child_idx: usize,
128 ) -> VortexResult<Option<ArrayRef>> {
129 RULES.evaluate(array, parent, child_idx)
130 }
131
132 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
133 RLESlots::NAMES[idx].to_string()
134 }
135
136 fn serialize(
137 array: ArrayView<'_, Self>,
138 _session: &VortexSession,
139 ) -> VortexResult<Option<Vec<u8>>> {
140 Ok(Some(
141 RLEMetadata {
142 values_len: array.values().len() as u64,
143 indices_len: array.indices().len() as u64,
144 indices_ptype: PType::try_from(array.indices().dtype())? as i32,
145 values_idx_offsets_len: array.values_idx_offsets().len() as u64,
146 values_idx_offsets_ptype: PType::try_from(array.values_idx_offsets().dtype())?
147 as i32,
148 offset: array.offset() as u64,
149 }
150 .encode_to_vec(),
151 ))
152 }
153
154 fn deserialize(
155 &self,
156 dtype: &DType,
157 len: usize,
158 metadata: &[u8],
159 buffers: &[BufferHandle],
160 children: &dyn ArrayChildren,
161 _session: &VortexSession,
162 ) -> VortexResult<ArrayParts<Self>> {
163 vortex_ensure!(
164 buffers.is_empty(),
165 "RLEArray expects 0 buffers, got {}",
166 buffers.len()
167 );
168 let metadata = RLEMetadata::decode(metadata)?;
169 let values = children.get(
170 0,
171 &DType::Primitive(dtype.as_ptype(), Nullability::NonNullable),
172 usize::try_from(metadata.values_len)?,
173 )?;
174
175 let indices = children.get(
176 1,
177 &DType::Primitive(metadata.indices_ptype(), dtype.nullability()),
178 usize::try_from(metadata.indices_len)?,
179 )?;
180
181 let values_idx_offsets = children.get(
182 2,
183 &DType::Primitive(
184 metadata.values_idx_offsets_ptype(),
185 Nullability::NonNullable,
186 ),
187 usize::try_from(metadata.values_idx_offsets_len)?,
188 )?;
189
190 let slots = RLESlots {
191 values,
192 indices,
193 values_idx_offsets,
194 }
195 .into_slots();
196 let data = RLEData::try_new(metadata.offset as usize)?;
197 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
198 }
199
200 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
201 Ok(ExecutionResult::done(
202 rle_decompress(&array, ctx)?.into_array(),
203 ))
204 }
205}
206
207#[derive(Clone, Debug)]
208pub struct RLE;
209
210impl RLE {
211 pub fn try_new(
212 values: ArrayRef,
213 indices: ArrayRef,
214 values_idx_offsets: ArrayRef,
215 offset: usize,
216 length: usize,
217 ) -> VortexResult<RLEArray> {
218 let dtype = DType::Primitive(values.dtype().as_ptype(), indices.dtype().nullability());
219 let slots = RLESlots {
220 values,
221 indices,
222 values_idx_offsets,
223 }
224 .into_slots();
225 let data = RLEData::try_new(offset)?;
226 Array::try_from_parts(ArrayParts::new(RLE, dtype, length, data).with_slots(slots))
227 }
228
229 pub unsafe fn new_unchecked(
234 values: ArrayRef,
235 indices: ArrayRef,
236 values_idx_offsets: ArrayRef,
237 offset: usize,
238 length: usize,
239 ) -> RLEArray {
240 let dtype = DType::Primitive(values.dtype().as_ptype(), indices.dtype().nullability());
241 let slots = RLESlots {
242 values,
243 indices,
244 values_idx_offsets,
245 }
246 .into_slots();
247 let data = unsafe { RLEData::new_unchecked(offset) };
248 unsafe {
249 Array::from_parts_unchecked(ArrayParts::new(RLE, dtype, length, data).with_slots(slots))
250 }
251 }
252
253 pub fn encode(
255 array: ArrayView<'_, Primitive>,
256 ctx: &mut ExecutionCtx,
257 ) -> VortexResult<RLEArray> {
258 RLEData::encode(array, ctx)
259 }
260}
261
262fn validate_parts(
263 values: &ArrayRef,
264 indices: &ArrayRef,
265 values_idx_offsets: &ArrayRef,
266 offset: usize,
267 dtype: &DType,
268 length: usize,
269) -> VortexResult<()> {
270 vortex_ensure!(
271 matches!(
272 values.dtype(),
273 DType::Primitive(_, Nullability::NonNullable)
274 ),
275 "RLE values must be a non-nullable primitive type, got {}",
276 values.dtype()
277 );
278
279 vortex_ensure!(
280 matches!(indices.dtype().as_ptype(), PType::U8 | PType::U16),
281 "RLE indices must be u8 or u16, got {}",
282 indices.dtype()
283 );
284
285 vortex_ensure!(
286 values_idx_offsets.dtype().is_unsigned_int() && !values_idx_offsets.dtype().is_nullable(),
287 "RLE value idx offsets must be non-nullable unsigned integer, got {}",
288 values_idx_offsets.dtype()
289 );
290
291 vortex_ensure!(
292 indices.len().is_multiple_of(crate::FL_CHUNK_SIZE),
293 "RLE indices length must be a multiple of {}, got {}",
294 crate::FL_CHUNK_SIZE,
295 indices.len()
296 );
297
298 vortex_ensure!(
299 offset + length <= indices.len(),
300 "RLE offset + length, {offset} + {length}, must not exceed the indices length {}",
301 indices.len()
302 );
303
304 vortex_ensure!(
305 indices.len().div_ceil(crate::FL_CHUNK_SIZE) == values_idx_offsets.len(),
306 "RLE must have one value idx offset per chunk, got {}",
307 values_idx_offsets.len()
308 );
309
310 vortex_ensure!(
311 indices.len() >= values.len(),
312 "RLE must have at least as many indices as values, got {} indices and {} values",
313 indices.len(),
314 values.len()
315 );
316
317 let expected_dtype = DType::Primitive(values.dtype().as_ptype(), indices.dtype().nullability());
318 vortex_ensure!(
319 dtype == &expected_dtype,
320 "RLE dtype mismatch: expected {expected_dtype}, got {dtype}"
321 );
322
323 Ok(())
324}
325
326#[cfg(test)]
327mod tests {
328 use prost::Message;
329 use vortex_array::test_harness::check_metadata;
330
331 use super::RLEMetadata;
332
333 #[cfg_attr(miri, ignore)]
334 #[test]
335 fn test_rle_metadata() {
336 check_metadata(
337 "rle.metadata",
338 &RLEMetadata {
339 values_len: u64::MAX,
340 indices_len: u64::MAX,
341 indices_ptype: i32::MAX,
342 values_idx_offsets_len: u64::MAX,
343 values_idx_offsets_ptype: i32::MAX,
344 offset: u64::MAX,
345 }
346 .encode_to_vec(),
347 );
348 }
349}