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 serialize(
142 array: ArrayView<'_, Self>,
143 _session: &VortexSession,
144 ) -> VortexResult<Option<Vec<u8>>> {
145 Ok(Some(
146 PatchedMetadata {
147 n_patches: u32::try_from(array.patch_indices().len())?,
148 n_lanes: u32::try_from(array.n_lanes())?,
149 offset: u32::try_from(array.offset())?,
150 }
151 .encode_to_vec(),
152 ))
153 }
154
155 fn deserialize(
156 &self,
157 dtype: &DType,
158 len: usize,
159 metadata: &[u8],
160 _buffers: &[BufferHandle],
161 children: &dyn ArrayChildren,
162 _session: &VortexSession,
163 ) -> VortexResult<ArrayParts<Self>> {
164 let metadata = PatchedMetadata::decode(metadata)?;
165 let n_patches = metadata.n_patches as usize;
166 let n_lanes = metadata.n_lanes as usize;
167 let offset = metadata.offset as usize;
168
169 let n_chunks = (len + offset).div_ceil(1024);
172
173 let inner = children.get(0, dtype, len)?;
174 let lane_offsets = children.get(1, PType::U32.into(), n_chunks * n_lanes + 1)?;
175 let indices = children.get(2, PType::U16.into(), n_patches)?;
176 let values = children.get(3, dtype, n_patches)?;
177
178 let data = PatchedData { n_lanes, offset };
179 let slots = PatchedSlots {
180 inner,
181 lane_offsets,
182 patch_indices: indices,
183 patch_values: values,
184 }
185 .into_slots();
186 Ok(ArrayParts::new(
187 self.clone(),
188 dtype.clone(),
189 len,
190 data,
191 slots,
192 ))
193 }
194
195 fn append_to_builder(
196 array: ArrayView<'_, Self>,
197 builder: &mut dyn ArrayBuilder,
198 ctx: &mut ExecutionCtx,
199 ) -> VortexResult<()> {
200 let dtype = array.array().dtype();
201
202 if !dtype.is_primitive() {
203 let canonical = array
205 .array()
206 .clone()
207 .execute::<Canonical>(ctx)?
208 .into_array();
209 return canonical.append_to_builder(builder, ctx);
210 }
211
212 let ptype = dtype.as_ptype();
213
214 let len = array.len();
215
216 array.inner().append_to_builder(builder, ctx)?;
217
218 let offset = array.offset();
219 let lane_offsets = array
220 .lane_offsets()
221 .clone()
222 .execute::<PrimitiveArray>(ctx)?;
223 let indices = array
224 .patch_indices()
225 .clone()
226 .execute::<PrimitiveArray>(ctx)?;
227 let values = array
228 .patch_values()
229 .clone()
230 .execute::<PrimitiveArray>(ctx)?;
231
232 match_each_native_ptype!(ptype, |V| {
233 let typed_builder = builder
234 .as_any_mut()
235 .downcast_mut::<PrimitiveBuilder<V>>()
236 .vortex_expect("correctly typed builder");
237
238 let output = typed_builder.values_mut();
241 let trailer = output.len() - len;
242
243 apply_patches_primitive::<V>(
244 &mut output[trailer..],
245 offset,
246 len,
247 array.n_lanes(),
248 lane_offsets.as_slice::<u32>(),
249 indices.as_slice::<u16>(),
250 values.as_slice::<V>(),
251 );
252 });
253
254 Ok(())
255 }
256
257 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
258 PatchedSlots::NAMES[idx].to_string()
259 }
260
261 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
262 let array = require_child!(array, array.inner(), PatchedSlots::INNER => Primitive);
263 let array =
264 require_child!(array, array.lane_offsets(), PatchedSlots::LANE_OFFSETS => Primitive);
265 let array =
266 require_child!(array, array.patch_indices(), PatchedSlots::PATCH_INDICES => Primitive);
267 let array =
268 require_child!(array, array.patch_values(), PatchedSlots::PATCH_VALUES => Primitive);
269
270 let len = array.len();
271
272 let n_lanes = array.n_lanes;
273 let offset = array.offset;
274 let slots = match array.try_into_parts() {
275 Ok(parts) => PatchedSlots::from_slots(parts.slots),
276 Err(array) => PatchedSlotsView::from_slots(array.slots()).to_owned(),
277 };
278
279 let PrimitiveDataParts {
281 buffer,
282 ptype,
283 validity,
284 } = slots.inner.downcast::<Primitive>().into_data_parts();
285
286 let values = slots.patch_values.downcast::<Primitive>();
287 let lane_offsets = slots.lane_offsets.downcast::<Primitive>();
288 let patch_indices = slots.patch_indices.downcast::<Primitive>();
289
290 let patched_values = match_each_native_ptype!(values.ptype(), |V| {
291 let mut output = Buffer::<V>::from_byte_buffer(buffer.unwrap_host()).into_mut();
292
293 apply_patches_primitive::<V>(
294 &mut output,
295 offset,
296 len,
297 n_lanes,
298 lane_offsets.as_slice::<u32>(),
299 patch_indices.as_slice::<u16>(),
300 values.as_slice::<V>(),
301 );
302
303 let output = output.freeze();
304
305 PrimitiveArray::from_byte_buffer(output.into_byte_buffer(), ptype, validity)
306 });
307
308 Ok(ExecutionResult::done(patched_values.into_array()))
309 }
310
311 fn reduce_parent(
312 array: ArrayView<'_, Self>,
313 parent: &ArrayRef,
314 child_idx: usize,
315 ) -> VortexResult<Option<ArrayRef>> {
316 PARENT_RULES.evaluate(array, parent, child_idx)
317 }
318}
319
320fn apply_patches_primitive<V: NativePType>(
322 output: &mut [V],
323 offset: usize,
324 len: usize,
325 n_lanes: usize,
326 lane_offsets: &[u32],
327 indices: &[u16],
328 values: &[V],
329) {
330 let n_chunks = (offset + len).div_ceil(1024);
331 for chunk in 0..n_chunks {
332 let start = lane_offsets[chunk * n_lanes] as usize;
333 let stop = lane_offsets[chunk * n_lanes + n_lanes] as usize;
334
335 for idx in start..stop {
336 let index = chunk * 1024 + indices[idx] as usize;
338 if index < offset || index >= offset + len {
339 continue;
340 }
341
342 let value = values[idx];
343 output[index - offset] = value;
344 }
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use rstest::rstest;
351 use vortex_buffer::ByteBufferMut;
352 use vortex_buffer::buffer;
353 use vortex_buffer::buffer_mut;
354 use vortex_error::VortexResult;
355 use vortex_session::registry::ReadContext;
356
357 use crate::Array;
358 use crate::ArrayContext;
359 use crate::ArrayParts;
360 use crate::ArraySlots;
361 use crate::IntoArray;
362 use crate::VortexSessionExecute;
363 use crate::array_session;
364 use crate::arrays::Patched;
365 use crate::arrays::PatchedArray;
366 use crate::arrays::PrimitiveArray;
367 use crate::arrays::patched::PatchedArrayExt;
368 use crate::arrays::patched::PatchedArraySlotsExt;
369 use crate::arrays::patched::PatchedData;
370 use crate::arrays::patched::PatchedSlots;
371 use crate::arrays::patched::PatchedSlotsView;
372 use crate::assert_arrays_eq;
373 use crate::builders::builder_with_capacity_in;
374 use crate::patches::Patches;
375 use crate::serde::SerializeOptions;
376 use crate::serde::SerializedArray;
377 use crate::session::ArraySessionExt;
378 use crate::validity::Validity;
379
380 #[test]
381 fn test_execute() {
382 let mut ctx = array_session().create_execution_ctx();
383 let values = buffer![0u16; 1024].into_array();
384 let patches = Patches::new(
385 1024,
386 0,
387 buffer![1u32, 2, 3].into_array(),
388 buffer![1u16; 3].into_array(),
389 None,
390 &mut ctx,
391 )
392 .unwrap();
393
394 let session = array_session();
395 let mut ctx = session.create_execution_ctx();
396
397 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
398 .unwrap()
399 .into_array();
400
401 let mut expected = buffer_mut![0u16; 1024];
402 expected[1] = 1;
403 expected[2] = 1;
404 expected[3] = 1;
405
406 assert_arrays_eq!(array, expected.into_array(), &mut ctx);
407 }
408
409 #[test]
410 fn test_execute_sliced() {
411 let mut ctx = array_session().create_execution_ctx();
412 let values = buffer![0u16; 1024].into_array();
413 let patches = Patches::new(
414 1024,
415 0,
416 buffer![1u32, 2, 3].into_array(),
417 buffer![1u16; 3].into_array(),
418 None,
419 &mut ctx,
420 )
421 .unwrap();
422
423 let session = array_session();
424 let mut ctx = session.create_execution_ctx();
425
426 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
427 .unwrap()
428 .into_array()
429 .slice(3..1024)
430 .unwrap();
431
432 let mut expected = buffer_mut![0u16; 1021];
433 expected[0] = 1;
434
435 assert_arrays_eq!(array, expected.into_array(), &mut ctx);
436 }
437
438 #[test]
439 fn test_append_to_builder_non_nullable() {
440 let mut ctx = array_session().create_execution_ctx();
441 let values = PrimitiveArray::new(buffer![0u16; 1024], Validity::NonNullable).into_array();
442 let patches = Patches::new(
443 1024,
444 0,
445 buffer![1u32, 2, 3].into_array(),
446 buffer![10u16, 20, 30].into_array(),
447 None,
448 &mut ctx,
449 )
450 .unwrap();
451
452 let session = array_session();
453 let mut ctx = session.create_execution_ctx();
454
455 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
456 .unwrap()
457 .into_array();
458
459 let mut builder = builder_with_capacity_in(
460 array.dtype(),
461 array.len(),
462 vortex_buffer::BufferAllocatorRef::static_ref(),
463 );
464 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
465
466 let result = builder.finish();
467
468 let mut expected = buffer_mut![0u16; 1024];
469 expected[1] = 10;
470 expected[2] = 20;
471 expected[3] = 30;
472 let expected = expected.into_array();
473
474 assert_arrays_eq!(result, expected, &mut ctx);
475 }
476
477 #[test]
478 fn test_append_to_builder_sliced() {
479 let mut ctx = array_session().create_execution_ctx();
480 let values = PrimitiveArray::new(buffer![0u16; 1024], Validity::NonNullable).into_array();
481 let patches = Patches::new(
482 1024,
483 0,
484 buffer![1u32, 2, 3].into_array(),
485 buffer![10u16, 20, 30].into_array(),
486 None,
487 &mut ctx,
488 )
489 .unwrap();
490
491 let session = array_session();
492 let mut ctx = session.create_execution_ctx();
493
494 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
495 .unwrap()
496 .into_array()
497 .slice(3..1024)
498 .unwrap();
499
500 let mut builder = builder_with_capacity_in(
501 array.dtype(),
502 array.len(),
503 vortex_buffer::BufferAllocatorRef::static_ref(),
504 );
505 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
506
507 let result = builder.finish();
508
509 let mut expected = buffer_mut![0u16; 1021];
510 expected[0] = 30;
511 let expected = expected.into_array();
512
513 assert_arrays_eq!(result, expected, &mut ctx);
514 }
515
516 #[test]
517 fn test_append_to_builder_with_validity() {
518 let mut ctx = array_session().create_execution_ctx();
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 &mut ctx,
531 )
532 .unwrap();
533
534 let session = array_session();
535 let mut ctx = session.create_execution_ctx();
536
537 let array = Patched::from_array_and_patches(values, &patches, &mut ctx)
538 .unwrap()
539 .into_array();
540
541 let mut builder = builder_with_capacity_in(
542 array.dtype(),
543 array.len(),
544 vortex_buffer::BufferAllocatorRef::static_ref(),
545 );
546 array.append_to_builder(builder.as_mut(), &mut ctx).unwrap();
547
548 let result = builder.finish();
549
550 let expected = PrimitiveArray::from_option_iter([
552 None,
553 Some(10u16),
554 Some(20),
555 Some(30),
556 Some(0),
557 None,
558 Some(0),
559 Some(0),
560 Some(0),
561 Some(0),
562 ])
563 .into_array();
564
565 assert_arrays_eq!(result, expected, &mut ctx);
566 }
567
568 fn make_patched_array(
569 inner: impl IntoIterator<Item = u16>,
570 patch_indices: &[u32],
571 patch_values: &[u16],
572 ) -> VortexResult<PatchedArray> {
573 let mut ctx = array_session().create_execution_ctx();
574 let values: Vec<u16> = inner.into_iter().collect();
575 let len = values.len();
576 let array = PrimitiveArray::from_iter(values).into_array();
577
578 let indices = PrimitiveArray::from_iter(patch_indices.iter().copied()).into_array();
579 let patch_vals = PrimitiveArray::from_iter(patch_values.iter().copied()).into_array();
580
581 let patches = Patches::new(len, 0, indices, patch_vals, None, &mut ctx)?;
582
583 let session = array_session();
584 let mut ctx = session.create_execution_ctx();
585
586 Patched::from_array_and_patches(array, &patches, &mut ctx)
587 }
588
589 #[rstest]
590 #[case::basic(
591 make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30]).unwrap().into_array(),
592 (0..1024).map(|i| match i { 1 => 10u16, 2 => 20, 3 => 30, _ => 0 }).collect::<Vec<_>>()
593 )]
594 #[case::multi_chunk(
595 make_patched_array(vec![0u16; 4096], &[100, 1500, 2500, 3500], &[11, 22, 33, 44]).unwrap().into_array(),
596 (0..4096).map(|i| match i { 100 => 11u16, 1500 => 22, 2500 => 33, 3500 => 44, _ => 0 }).collect::<Vec<_>>()
597 )]
598 #[case::sliced({
599 let arr = make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30]).unwrap();
600 arr.into_array().slice(2..1024).unwrap()
601 }, (2..1024).map(|i| match i { 2 => 20u16, 3 => 30, _ => 0 }).collect::<Vec<_>>())]
602 fn test_serde_roundtrip(#[case] array: crate::ArrayRef, #[case] expected: Vec<u16>) {
603 let dtype = array.dtype().clone();
604 let len = array.len();
605
606 let session = array_session();
607 session.arrays().register(Patched);
608
609 let ctx = ArrayContext::empty().with_allowed_ids(
610 session
611 .arrays()
612 .registry()
613 .read(|map| map.keys().copied().collect()),
614 );
615 let serialized = array
616 .serialize(&ctx, &session, &SerializeOptions::default())
617 .unwrap();
618
619 let mut concat = ByteBufferMut::empty();
621 for buf in serialized {
622 concat.extend_from_slice(buf.as_ref());
623 }
624 let concat = concat.freeze();
625
626 let parts = SerializedArray::try_from(concat).unwrap();
627 let decoded = parts
628 .decode(&dtype, len, &ReadContext::new(ctx.to_ids()), &session)
629 .unwrap();
630
631 assert!(decoded.is::<Patched>());
632 let expected = PrimitiveArray::from_iter(expected);
633 let mut exec_ctx = session.create_execution_ctx();
634 assert_arrays_eq!(array, &expected, &mut exec_ctx);
635 assert_arrays_eq!(decoded, expected, &mut exec_ctx);
636 }
637
638 #[test]
639 fn test_with_slots_basic() -> VortexResult<()> {
640 let array = make_patched_array(vec![0u16; 1024], &[1, 2, 3], &[10, 20, 30])?;
641
642 let slots = PatchedSlots::from_slots(
644 array
645 .as_array()
646 .slots()
647 .iter()
648 .cloned()
649 .collect::<ArraySlots>(),
650 );
651 let view = PatchedSlotsView::from_slots(array.as_array().slots());
652 assert_eq!(view.inner.len(), array.inner().len());
653
654 let array_ref = array.into_array();
656 let new_array = unsafe { array_ref.clone().with_slots(slots.into_slots()) }?;
659
660 assert!(new_array.is::<Patched>());
661 assert_eq!(array_ref.len(), new_array.len());
662 assert_eq!(array_ref.dtype(), new_array.dtype());
663
664 let mut ctx = array_session().create_execution_ctx();
665
666 let expected = PrimitiveArray::from_iter((0..1024).map(|i| match i {
667 1 => 10u16,
668 2 => 20,
669 3 => 30,
670 _ => 0,
671 }));
672 assert_arrays_eq!(new_array, expected, &mut ctx);
673
674 Ok(())
675 }
676
677 #[test]
678 fn test_rebuild_modified_inner_from_parts() -> VortexResult<()> {
679 let array = make_patched_array(vec![0u16; 10], &[1, 2, 3], &[10, 20, 30])?;
680
681 let new_inner = PrimitiveArray::from_iter(vec![5u16; 10]).into_array();
683 let slots = PatchedSlots {
684 inner: new_inner,
685 lane_offsets: array.lane_offsets().clone(),
686 patch_indices: array.patch_indices().clone(),
687 patch_values: array.patch_values().clone(),
688 };
689
690 let data = PatchedData {
691 n_lanes: array.n_lanes(),
692 offset: array.offset(),
693 };
694 let new_array = Array::try_from_parts(ArrayParts::new(
695 Patched,
696 array.dtype().clone(),
697 array.len(),
698 data,
699 slots.into_slots(),
700 ))?
701 .into_array();
702
703 let mut ctx = array_session().create_execution_ctx();
705
706 let expected = PrimitiveArray::from_iter([5u16, 10, 20, 30, 5, 5, 5, 5, 5, 5]);
708 assert_arrays_eq!(new_array, expected, &mut ctx);
709
710 Ok(())
711 }
712}