1use prost::Message;
5
6use crate::ArrayEq;
7use crate::ArrayHash;
8mod kernels;
9mod operations;
10mod slice;
11
12use std::hash::Hash;
13use std::hash::Hasher;
14
15use vortex_buffer::Buffer;
16use vortex_error::VortexExpect;
17use vortex_error::VortexResult;
18use vortex_error::vortex_panic;
19use vortex_session::VortexSession;
20use vortex_session::registry::CachedId;
21
22use crate::ArrayRef;
23use crate::Canonical;
24use crate::EqMode;
25use crate::ExecutionCtx;
26use crate::ExecutionResult;
27use crate::IntoArray;
28use crate::array::Array;
29use crate::array::ArrayId;
30use crate::array::ArrayParts;
31use crate::array::ArrayView;
32use crate::array::VTable;
33use crate::array::ValidityChild;
34use crate::array::ValidityVTableFromChild;
35use crate::array::with_empty_buffers;
36use crate::arrays::Primitive;
37use crate::arrays::PrimitiveArray;
38use crate::arrays::patched::PatchedArrayExt;
39use crate::arrays::patched::PatchedArraySlotsExt;
40use crate::arrays::patched::PatchedData;
41use crate::arrays::patched::PatchedSlots;
42use crate::arrays::patched::PatchedSlotsView;
43use crate::arrays::patched::compute::rules::PARENT_RULES;
44use crate::arrays::primitive::PrimitiveDataParts;
45use crate::buffer::BufferHandle;
46use crate::builders::ArrayBuilder;
47use crate::builders::PrimitiveBuilder;
48use crate::dtype::DType;
49use crate::dtype::NativePType;
50use crate::dtype::PType;
51use crate::match_each_native_ptype;
52use crate::require_child;
53use crate::serde::ArrayChildren;
54
55pub type PatchedArray = Array<Patched>;
57
58pub(crate) fn initialize(session: &VortexSession) {
59 kernels::initialize(session);
60}
61
62#[derive(Clone, Debug)]
63pub struct Patched;
64
65impl ValidityChild<Patched> for Patched {
66 fn validity_child(array: ArrayView<'_, Patched>) -> ArrayRef {
67 array.inner().clone()
68 }
69}
70
71#[derive(Clone, prost::Message)]
72pub struct PatchedMetadata {
73 #[prost(uint32, tag = "1")]
75 pub(crate) n_patches: u32,
76
77 #[prost(uint32, tag = "2")]
79 pub(crate) n_lanes: u32,
80
81 #[prost(uint32, tag = "3")]
85 pub(crate) offset: u32,
86}
87
88impl ArrayHash for PatchedData {
89 fn array_hash<H: Hasher>(&self, state: &mut H, _accuracy: EqMode) {
90 self.offset.hash(state);
91 self.n_lanes.hash(state);
92 }
93}
94
95impl ArrayEq for PatchedData {
96 fn array_eq(&self, other: &Self, _accuracy: EqMode) -> bool {
97 self.offset == other.offset && self.n_lanes == other.n_lanes
98 }
99}
100
101impl VTable for Patched {
102 type TypedArrayData = PatchedData;
103 type OperationsVTable = Self;
104 type ValidityVTable = ValidityVTableFromChild;
105
106 fn id(&self) -> ArrayId {
107 static ID: CachedId = CachedId::new("vortex.patched");
108 *ID
109 }
110
111 fn validate(
112 &self,
113 data: &PatchedData,
114 dtype: &DType,
115 len: usize,
116 slots: &[Option<ArrayRef>],
117 ) -> VortexResult<()> {
118 data.validate(dtype, len, &PatchedSlotsView::from_slots(slots))
119 }
120
121 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
122 0
123 }
124
125 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
126 vortex_panic!("invalid buffer index for PatchedArray: {idx}");
127 }
128
129 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
130 vortex_panic!("invalid buffer index for PatchedArray: {idx}");
131 }
132
133 fn with_buffers(
134 &self,
135 array: ArrayView<'_, Self>,
136 buffers: &[BufferHandle],
137 ) -> VortexResult<ArrayParts<Self>> {
138 with_empty_buffers(self, array, buffers)
139 }
140
141 fn child(array: ArrayView<'_, Self>, idx: usize) -> ArrayRef {
142 match idx {
143 PatchedSlots::INNER => array.inner().clone(),
144 PatchedSlots::LANE_OFFSETS => array.lane_offsets().clone(),
145 PatchedSlots::PATCH_INDICES => array.patch_indices().clone(),
146 PatchedSlots::PATCH_VALUES => array.patch_values().clone(),
147 _ => vortex_panic!("invalid child index for PatchedArray: {idx}"),
148 }
149 }
150
151 fn serialize(
152 array: ArrayView<'_, Self>,
153 _session: &VortexSession,
154 ) -> VortexResult<Option<Vec<u8>>> {
155 Ok(Some(
156 PatchedMetadata {
157 n_patches: u32::try_from(array.patch_indices().len())?,
158 n_lanes: u32::try_from(array.n_lanes())?,
159 offset: u32::try_from(array.offset())?,
160 }
161 .encode_to_vec(),
162 ))
163 }
164
165 fn deserialize(
166 &self,
167 dtype: &DType,
168 len: usize,
169 metadata: &[u8],
170 _buffers: &[BufferHandle],
171 children: &dyn ArrayChildren,
172 _session: &VortexSession,
173 ) -> VortexResult<ArrayParts<Self>> {
174 let metadata = PatchedMetadata::decode(metadata)?;
175 let n_patches = metadata.n_patches as usize;
176 let n_lanes = metadata.n_lanes as usize;
177 let offset = metadata.offset as usize;
178
179 let n_chunks = (len + offset).div_ceil(1024);
182
183 let inner = children.get(0, dtype, len)?;
184 let lane_offsets = children.get(1, PType::U32.into(), n_chunks * n_lanes + 1)?;
185 let indices = children.get(2, PType::U16.into(), n_patches)?;
186 let values = children.get(3, dtype, n_patches)?;
187
188 let data = PatchedData { n_lanes, offset };
189 let slots = PatchedSlots {
190 inner,
191 lane_offsets,
192 patch_indices: indices,
193 patch_values: values,
194 }
195 .into_slots();
196 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
197 }
198
199 fn append_to_builder(
200 array: ArrayView<'_, Self>,
201 builder: &mut dyn ArrayBuilder,
202 ctx: &mut ExecutionCtx,
203 ) -> VortexResult<()> {
204 let dtype = array.array().dtype();
205
206 if !dtype.is_primitive() {
207 let canonical = array
209 .array()
210 .clone()
211 .execute::<Canonical>(ctx)?
212 .into_array();
213 return canonical.append_to_builder(builder, ctx);
214 }
215
216 let ptype = dtype.as_ptype();
217
218 let len = array.len();
219
220 array.inner().append_to_builder(builder, ctx)?;
221
222 let offset = array.offset();
223 let lane_offsets = array
224 .lane_offsets()
225 .clone()
226 .execute::<PrimitiveArray>(ctx)?;
227 let indices = array
228 .patch_indices()
229 .clone()
230 .execute::<PrimitiveArray>(ctx)?;
231 let values = array
232 .patch_values()
233 .clone()
234 .execute::<PrimitiveArray>(ctx)?;
235
236 match_each_native_ptype!(ptype, |V| {
237 let typed_builder = builder
238 .as_any_mut()
239 .downcast_mut::<PrimitiveBuilder<V>>()
240 .vortex_expect("correctly typed builder");
241
242 let output = typed_builder.values_mut();
245 let trailer = output.len() - len;
246
247 apply_patches_primitive::<V>(
248 &mut output[trailer..],
249 offset,
250 len,
251 array.n_lanes(),
252 lane_offsets.as_slice::<u32>(),
253 indices.as_slice::<u16>(),
254 values.as_slice::<V>(),
255 );
256 });
257
258 Ok(())
259 }
260
261 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
262 PatchedSlots::NAMES[idx].to_string()
263 }
264
265 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
266 let array = require_child!(array, array.inner(), PatchedSlots::INNER => Primitive);
267 let array =
268 require_child!(array, array.lane_offsets(), PatchedSlots::LANE_OFFSETS => Primitive);
269 let array =
270 require_child!(array, array.patch_indices(), PatchedSlots::PATCH_INDICES => Primitive);
271 let array =
272 require_child!(array, array.patch_values(), PatchedSlots::PATCH_VALUES => Primitive);
273
274 let len = array.len();
275
276 let n_lanes = array.n_lanes;
277 let offset = array.offset;
278 let slots = match array.try_into_parts() {
279 Ok(parts) => PatchedSlots::from_slots(parts.slots),
280 Err(array) => PatchedSlotsView::from_slots(array.slots()).to_owned(),
281 };
282
283 let PrimitiveDataParts {
285 buffer,
286 ptype,
287 validity,
288 } = slots.inner.downcast::<Primitive>().into_data_parts();
289
290 let values = slots.patch_values.downcast::<Primitive>();
291 let lane_offsets = slots.lane_offsets.downcast::<Primitive>();
292 let patch_indices = slots.patch_indices.downcast::<Primitive>();
293
294 let patched_values = match_each_native_ptype!(values.ptype(), |V| {
295 let mut output = Buffer::<V>::from_byte_buffer(buffer.unwrap_host()).into_mut();
296
297 apply_patches_primitive::<V>(
298 &mut output,
299 offset,
300 len,
301 n_lanes,
302 lane_offsets.as_slice::<u32>(),
303 patch_indices.as_slice::<u16>(),
304 values.as_slice::<V>(),
305 );
306
307 let output = output.freeze();
308
309 PrimitiveArray::from_byte_buffer(output.into_byte_buffer(), ptype, validity)
310 });
311
312 Ok(ExecutionResult::done(patched_values.into_array()))
313 }
314
315 fn reduce_parent(
316 array: ArrayView<'_, Self>,
317 parent: &ArrayRef,
318 child_idx: usize,
319 ) -> VortexResult<Option<ArrayRef>> {
320 PARENT_RULES.evaluate(array, parent, child_idx)
321 }
322}
323
324fn apply_patches_primitive<V: NativePType>(
326 output: &mut [V],
327 offset: usize,
328 len: usize,
329 n_lanes: usize,
330 lane_offsets: &[u32],
331 indices: &[u16],
332 values: &[V],
333) {
334 let n_chunks = (offset + len).div_ceil(1024);
335 for chunk in 0..n_chunks {
336 let start = lane_offsets[chunk * n_lanes] as usize;
337 let stop = lane_offsets[chunk * n_lanes + n_lanes] as usize;
338
339 for idx in start..stop {
340 let index = chunk * 1024 + indices[idx] as usize;
342 if index < offset || index >= offset + len {
343 continue;
344 }
345
346 let value = values[idx];
347 output[index - offset] = value;
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use rstest::rstest;
355 use vortex_buffer::ByteBufferMut;
356 use vortex_buffer::buffer;
357 use vortex_buffer::buffer_mut;
358 use vortex_error::VortexResult;
359 use vortex_session::registry::ReadContext;
360
361 use crate::Array;
362 use crate::ArrayContext;
363 use crate::ArrayParts;
364 use crate::ArraySlots;
365 use crate::Canonical;
366 use crate::IntoArray;
367 use crate::VortexSessionExecute;
368 use crate::array_session;
369 use crate::arrays::Patched;
370 use crate::arrays::PatchedArray;
371 use crate::arrays::PrimitiveArray;
372 use crate::arrays::patched::PatchedArrayExt;
373 use crate::arrays::patched::PatchedArraySlotsExt;
374 use crate::arrays::patched::PatchedData;
375 use crate::arrays::patched::PatchedSlots;
376 use crate::arrays::patched::PatchedSlotsView;
377 use crate::assert_arrays_eq;
378 use crate::builders::builder_with_capacity;
379 use crate::patches::Patches;
380 use crate::serde::SerializeOptions;
381 use crate::serde::SerializedArray;
382 use crate::session::ArraySessionExt;
383 use crate::validity::Validity;
384
385 #[test]
386 fn test_execute() {
387 let values = buffer![0u16; 1024].into_array();
388 let patches = Patches::new(
389 1024,
390 0,
391 buffer![1u32, 2, 3].into_array(),
392 buffer![1u16; 3].into_array(),
393 None,
394 )
395 .unwrap();
396
397 let session = array_session();
398 let mut ctx = session.create_execution_ctx();
399
400 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
401 .unwrap()
402 .into_array();
403
404 let executed = array
405 .execute::<Canonical>(&mut ctx)
406 .unwrap()
407 .into_primitive()
408 .into_buffer::<u16>();
409
410 let mut expected = buffer_mut![0u16; 1024];
411 expected[1] = 1;
412 expected[2] = 1;
413 expected[3] = 1;
414
415 assert_eq!(executed, expected.freeze());
416 }
417
418 #[test]
419 fn test_execute_sliced() {
420 let values = buffer![0u16; 1024].into_array();
421 let patches = Patches::new(
422 1024,
423 0,
424 buffer![1u32, 2, 3].into_array(),
425 buffer![1u16; 3].into_array(),
426 None,
427 )
428 .unwrap();
429
430 let session = array_session();
431 let mut ctx = session.create_execution_ctx();
432
433 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
434 .unwrap()
435 .into_array()
436 .slice(3..1024)
437 .unwrap();
438
439 let executed = array
440 .execute::<Canonical>(&mut ctx)
441 .unwrap()
442 .into_primitive()
443 .into_buffer::<u16>();
444
445 let mut expected = buffer_mut![0u16; 1021];
446 expected[0] = 1;
447
448 assert_eq!(executed, expected.freeze());
449 }
450
451 #[test]
452 fn test_append_to_builder_non_nullable() {
453 let values = PrimitiveArray::new(buffer![0u16; 1024], Validity::NonNullable).into_array();
454 let patches = Patches::new(
455 1024,
456 0,
457 buffer![1u32, 2, 3].into_array(),
458 buffer![10u16, 20, 30].into_array(),
459 None,
460 )
461 .unwrap();
462
463 let session = array_session();
464 let mut ctx = session.create_execution_ctx();
465
466 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
467 .unwrap()
468 .into_array();
469
470 let mut builder = builder_with_capacity(array.dtype(), array.len());
471 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
472
473 let result = builder.finish();
474
475 let mut expected = buffer_mut![0u16; 1024];
476 expected[1] = 10;
477 expected[2] = 20;
478 expected[3] = 30;
479 let expected = expected.into_array();
480
481 assert_arrays_eq!(expected, result, &mut ctx);
482 }
483
484 #[test]
485 fn test_append_to_builder_sliced() {
486 let values = PrimitiveArray::new(buffer![0u16; 1024], Validity::NonNullable).into_array();
487 let patches = Patches::new(
488 1024,
489 0,
490 buffer![1u32, 2, 3].into_array(),
491 buffer![10u16, 20, 30].into_array(),
492 None,
493 )
494 .unwrap();
495
496 let session = array_session();
497 let mut ctx = session.create_execution_ctx();
498
499 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
500 .unwrap()
501 .into_array()
502 .slice(3..1024)
503 .unwrap();
504
505 let mut builder = builder_with_capacity(array.dtype(), array.len());
506 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
507
508 let result = builder.finish();
509
510 let mut expected = buffer_mut![0u16; 1021];
511 expected[0] = 30;
512 let expected = expected.into_array();
513
514 assert_arrays_eq!(expected, result, &mut ctx);
515 }
516
517 #[test]
518 fn test_append_to_builder_with_validity() {
519 let validity = Validity::from_iter((0..10).map(|i| i != 0 && i != 5));
521 let values = PrimitiveArray::new(buffer![0u16; 10], validity).into_array();
522
523 let patches = Patches::new(
525 10,
526 0,
527 buffer![1u32, 2, 3].into_array(),
528 buffer![10u16, 20, 30].into_array(),
529 None,
530 )
531 .unwrap();
532
533 let session = array_session();
534 let mut ctx = session.create_execution_ctx();
535
536 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
537 .unwrap()
538 .into_array();
539
540 let mut builder = builder_with_capacity(array.dtype(), array.len());
541 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
542
543 let result = builder.finish();
544
545 let expected = PrimitiveArray::from_option_iter([
547 None,
548 Some(10u16),
549 Some(20),
550 Some(30),
551 Some(0),
552 None,
553 Some(0),
554 Some(0),
555 Some(0),
556 Some(0),
557 ])
558 .into_array();
559
560 assert_arrays_eq!(expected, result, &mut ctx);
561 }
562
563 fn make_patched_array(
564 inner: impl IntoIterator<Item = u16>,
565 patch_indices: &[u32],
566 patch_values: &[u16],
567 ) -> VortexResult<PatchedArray> {
568 let values: Vec<u16> = inner.into_iter().collect();
569 let len = values.len();
570 let array = PrimitiveArray::from_iter(values).into_array();
571
572 let indices = PrimitiveArray::from_iter(patch_indices.iter().copied()).into_array();
573 let patch_vals = PrimitiveArray::from_iter(patch_values.iter().copied()).into_array();
574
575 let patches = Patches::new(len, 0, indices, patch_vals, None)?;
576
577 let session = array_session();
578 let mut ctx = session.create_execution_ctx();
579
580 Patched::from_array_and_patches(array, &patches, &mut ctx)
581 }
582
583 #[rstest]
584 #[case::basic(
585 make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30]).unwrap().into_array()
586 )]
587 #[case::multi_chunk(
588 make_patched_array(vec![0u16; 4096], &[100, 1500, 2500, 3500], &[11, 22, 33, 44]).unwrap().into_array()
589 )]
590 #[case::sliced({
591 let arr = make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30]).unwrap();
592 arr.into_array().slice(2..1024).unwrap()
593 })]
594 fn test_serde_roundtrip(#[case] array: crate::ArrayRef) {
595 let dtype = array.dtype().clone();
596 let len = array.len();
597
598 let session = array_session();
599 session.arrays().register(Patched);
600
601 let ctx = ArrayContext::empty().with_registry(session.arrays().registry().clone());
602 let serialized = array
603 .serialize(&ctx, &session, &SerializeOptions::default())
604 .unwrap();
605
606 let mut concat = ByteBufferMut::empty();
608 for buf in serialized {
609 concat.extend_from_slice(buf.as_ref());
610 }
611 let concat = concat.freeze();
612
613 let parts = SerializedArray::try_from(concat).unwrap();
614 let decoded = parts
615 .decode(&dtype, len, &ReadContext::new(ctx.to_ids()), &session)
616 .unwrap();
617
618 assert!(decoded.is::<Patched>());
619 assert_eq!(
620 array.display_values().to_string(),
621 decoded.display_values().to_string()
622 );
623 }
624
625 #[test]
626 fn test_with_slots_basic() -> VortexResult<()> {
627 let array = make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30])?;
628
629 let slots = PatchedSlots::from_slots(
631 array
632 .as_array()
633 .slots()
634 .iter()
635 .cloned()
636 .collect::<ArraySlots>(),
637 );
638 let view = PatchedSlotsView::from_slots(array.as_array().slots());
639 assert_eq!(view.inner.len(), array.inner().len());
640
641 let array_ref = array.into_array();
643 let new_array = unsafe { array_ref.clone().with_slots(slots.into_slots()) }?;
646
647 assert!(new_array.is::<Patched>());
648 assert_eq!(array_ref.len(), new_array.len());
649 assert_eq!(array_ref.dtype(), new_array.dtype());
650
651 let mut ctx = array_session().create_execution_ctx();
653 let original_executed = array_ref.execute::<Canonical>(&mut ctx)?.into_primitive();
654 let new_executed = new_array.execute::<Canonical>(&mut ctx)?.into_primitive();
655
656 assert_arrays_eq!(original_executed, new_executed, &mut ctx);
657
658 Ok(())
659 }
660
661 #[test]
662 fn test_rebuild_modified_inner_from_parts() -> VortexResult<()> {
663 let array = make_patched_array(vec![0u16; 10], &[1, 2, 3], &[10, 20, 30])?;
664
665 let new_inner = PrimitiveArray::from_iter(vec![5u16; 10]).into_array();
667 let slots = PatchedSlots {
668 inner: new_inner,
669 lane_offsets: array.lane_offsets().clone(),
670 patch_indices: array.patch_indices().clone(),
671 patch_values: array.patch_values().clone(),
672 };
673
674 let data = PatchedData {
675 n_lanes: array.n_lanes(),
676 offset: array.offset(),
677 };
678 let new_array = Array::try_from_parts(
679 ArrayParts::new(Patched, array.dtype().clone(), array.len(), data)
680 .with_slots(slots.into_slots()),
681 )?
682 .into_array();
683
684 let mut ctx = array_session().create_execution_ctx();
686 let executed = new_array.execute::<Canonical>(&mut ctx)?.into_primitive();
687
688 let expected = PrimitiveArray::from_iter([5u16, 10, 20, 30, 5, 5, 5, 5, 5, 5]);
690 assert_arrays_eq!(expected, executed, &mut ctx);
691
692 Ok(())
693 }
694}