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