1use std::fmt::Display;
5use std::fmt::Formatter;
6use std::mem::size_of;
7use std::sync::Arc;
8
9use smallvec::smallvec;
10use vortex_buffer::Alignment;
11use vortex_buffer::Buffer;
12use vortex_buffer::BufferAllocatorRef;
13use vortex_buffer::ByteBuffer;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_bail;
17use vortex_error::vortex_ensure;
18use vortex_error::vortex_err;
19use vortex_error::vortex_panic;
20use vortex_mask::AllOr;
21use vortex_mask::Mask;
22
23use crate::ArrayRef;
24use crate::ArraySlots;
25use crate::ExecutionCtx;
26#[cfg(debug_assertions)]
27use crate::VortexSessionExecute;
28use crate::array::Array;
29use crate::array::ArrayParts;
30use crate::array::TypedArrayRef;
31use crate::array::child_to_validity;
32use crate::array::validity_to_child;
33use crate::array_slots;
34use crate::arrays::VarBinView;
35use crate::arrays::varbinview::BinaryView;
36use crate::buffer::BufferHandle;
37use crate::builders::ArrayBuilder;
38use crate::builders::VarBinViewBuilder;
39use crate::dtype::DType;
40use crate::dtype::Nullability;
41#[cfg(debug_assertions)]
42use crate::legacy_session;
43use crate::validity::Validity;
44
45#[array_slots(VarBinView)]
46pub struct VarBinViewSlots {
47 #[slot(0)]
49 pub validity: Option<ArrayRef>,
50}
51
52#[derive(Clone, Debug)]
112pub struct VarBinViewData {
113 pub(super) buffers: Arc<[BufferHandle]>,
114 pub(super) views: BufferHandle,
115}
116
117impl Display for VarBinViewData {
118 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
119 Ok(())
120 }
121}
122
123pub struct VarBinViewDataParts {
124 pub dtype: DType,
125 pub buffers: Arc<[BufferHandle]>,
126 pub views: BufferHandle,
127 pub validity: Validity,
128}
129
130fn for_each_invalid_range(mask: &Mask, len: usize, mut f: impl FnMut(usize, usize)) {
132 match mask.bit_buffer() {
133 AllOr::All => {}
134 AllOr::None => f(0, len),
135 AllOr::Some(buffer) => {
136 let mut prev = 0;
137 for (start, end) in buffer.set_slices() {
138 if start > prev {
139 f(prev, start);
140 }
141 prev = end;
142 }
143 if prev < len {
144 f(prev, len);
145 }
146 }
147 }
148}
149
150impl VarBinViewData {
151 fn dtype_parts(dtype: &DType) -> VortexResult<(bool, Nullability)> {
152 match dtype {
153 DType::Utf8(nullability) => Ok((true, *nullability)),
154 DType::Binary(nullability) => Ok((false, *nullability)),
155 _ => vortex_bail!(InvalidArgument: "invalid DType {dtype} for `VarBinViewArray`"),
156 }
157 }
158
159 pub(super) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
161 smallvec![validity_to_child(validity, len)]
162 }
163
164 pub fn new(
171 views: Buffer<BinaryView>,
172 buffers: Arc<[ByteBuffer]>,
173 dtype: DType,
174 validity: Validity,
175 ctx: &mut ExecutionCtx,
176 ) -> Self {
177 Self::try_new(views, buffers, dtype, validity, ctx)
178 .vortex_expect("VarBinViewArray construction failed")
179 }
180
181 pub fn new_handle(
188 views: BufferHandle,
189 buffers: Arc<[BufferHandle]>,
190 dtype: DType,
191 validity: Validity,
192 ) -> Self {
193 Self::try_new_handle(views, buffers, dtype, validity)
194 .vortex_expect("VarbinViewArray construction failed")
195 }
196
197 pub fn try_new(
206 views: Buffer<BinaryView>,
207 buffers: Arc<[ByteBuffer]>,
208 dtype: DType,
209 validity: Validity,
210 ctx: &mut ExecutionCtx,
211 ) -> VortexResult<Self> {
212 let views = Self::validate_and_fix(views, &buffers, &dtype, &validity, ctx)?;
213
214 Ok(unsafe { Self::new_unchecked(views, buffers, dtype, validity) })
216 }
217
218 pub fn try_new_handle(
227 views: BufferHandle,
228 buffers: Arc<[BufferHandle]>,
229 dtype: DType,
230 validity: Validity,
231 ) -> VortexResult<Self> {
232 let views_nbytes = views.len();
233 vortex_ensure!(
234 views_nbytes.is_multiple_of(size_of::<BinaryView>()),
235 "Expected views buffer length ({views_nbytes}) to be a multiple of {}",
236 size_of::<BinaryView>()
237 );
238
239 if let Some(host) = views.as_host_opt() {
241 vortex_ensure!(
242 host.is_aligned(Alignment::of::<BinaryView>()),
243 "Views on host must be 16 byte aligned"
244 );
245 }
246
247 Ok(unsafe { Self::new_handle_unchecked(views, buffers, dtype, validity) })
249 }
250
251 pub unsafe fn new_unchecked(
281 views: Buffer<BinaryView>,
282 buffers: Arc<[ByteBuffer]>,
283 dtype: DType,
284 validity: Validity,
285 ) -> Self {
286 #[cfg(debug_assertions)]
287 #[expect(clippy::disallowed_methods)]
288 Self::validate(
289 &views,
290 &buffers,
291 &dtype,
292 &validity,
293 &mut legacy_session().create_execution_ctx(),
294 )
295 .vortex_expect("[Debug Assertion]: Invalid `VarBinViewArray` parameters");
296
297 let handles: Vec<BufferHandle> = buffers
298 .iter()
299 .cloned()
300 .map(BufferHandle::new_host)
301 .collect();
302
303 let handles = Arc::from(handles);
304 let view_handle = BufferHandle::new_host(views.into_byte_buffer());
305 unsafe { Self::new_handle_unchecked(view_handle, handles, dtype, validity) }
306 }
307
308 pub unsafe fn new_handle_unchecked(
314 views: BufferHandle,
315 buffers: Arc<[BufferHandle]>,
316 dtype: DType,
317 _validity: Validity,
318 ) -> Self {
319 let _ =
320 Self::dtype_parts(&dtype).vortex_expect("VarBinViewArray dtype must be utf8 or binary");
321 Self { buffers, views }
322 }
323
324 pub fn validate(
329 views: &Buffer<BinaryView>,
330 buffers: &Arc<[ByteBuffer]>,
331 dtype: &DType,
332 validity: &Validity,
333 ctx: &mut ExecutionCtx,
334 ) -> VortexResult<()> {
335 let check_utf8 = Self::check_nullability(dtype, validity)?;
336 match validity {
337 Validity::Array(_) => {
341 let mask = validity.execute_mask(views.len(), ctx)?;
342 for ((idx, view), valid) in views.iter().enumerate().zip(mask.iter()) {
343 if valid {
344 Self::validate_view(idx, view, buffers, check_utf8)?;
345 }
346 }
347 }
348 Validity::AllInvalid => {}
350 Validity::NonNullable | Validity::AllValid => {
352 for (idx, view) in views.iter().enumerate() {
353 Self::validate_view(idx, view, buffers, check_utf8)?;
354 }
355 }
356 }
357 Ok(())
358 }
359
360 pub(crate) fn validate_and_fix(
362 views: Buffer<BinaryView>,
363 buffers: &Arc<[ByteBuffer]>,
364 dtype: &DType,
365 validity: &Validity,
366 ctx: &mut ExecutionCtx,
367 ) -> VortexResult<Buffer<BinaryView>> {
368 let check_utf8 = Self::check_nullability(dtype, validity)?;
369 let empty = BinaryView::empty_view();
370 let len = views.len();
371
372 match validity {
373 Validity::Array(_) => {
374 let mask = validity.execute_mask(len, ctx)?;
375 match views.try_into_mut() {
376 Ok(mut views) => {
377 let slice = views.as_mut_slice();
378 for (idx, valid) in mask.iter().enumerate() {
379 if valid {
380 Self::validate_view(idx, &slice[idx], buffers, check_utf8)?;
381 } else {
382 slice[idx] = empty;
383 }
384 }
385 Ok(views.freeze())
386 }
387 Err(views) => {
388 let mut needs_replace = false;
389 for ((idx, view), valid) in views.iter().enumerate().zip(mask.iter()) {
390 if valid {
391 Self::validate_view(idx, view, buffers, check_utf8)?;
392 } else if *view != empty {
393 needs_replace = true;
394 }
395 }
396 if !needs_replace {
397 return Ok(views);
398 }
399 let mut views = views.into_mut();
400 let slice = views.as_mut_slice();
401 for_each_invalid_range(&mask, len, |start, end| {
402 slice[start..end].fill(empty)
403 });
404 Ok(views.freeze())
405 }
406 }
407 }
408 Validity::AllInvalid => match views.try_into_mut() {
410 Ok(mut views) => {
411 views.as_mut_slice().fill(empty);
412 Ok(views.freeze())
413 }
414 Err(views) if views.iter().all(|view| *view == empty) => Ok(views),
415 Err(views) => {
416 let mut views = views.into_mut();
417 views.as_mut_slice().fill(empty);
418 Ok(views.freeze())
419 }
420 },
421 Validity::NonNullable | Validity::AllValid => {
423 for (idx, view) in views.iter().enumerate() {
424 Self::validate_view(idx, view, buffers, check_utf8)?;
425 }
426 Ok(views)
427 }
428 }
429 }
430
431 fn check_nullability(dtype: &DType, validity: &Validity) -> VortexResult<bool> {
432 let (is_utf8, nullability) = Self::dtype_parts(dtype)?;
433 vortex_ensure!(
434 validity.nullability() == nullability,
435 InvalidArgument: "validity {:?} incompatible with nullability {:?}",
436 validity,
437 nullability
438 );
439 Ok(is_utf8)
440 }
441
442 fn validate_view(
443 idx: usize,
444 view: &BinaryView,
445 buffers: &Arc<[ByteBuffer]>,
446 check_utf8: bool,
447 ) -> VortexResult<()> {
448 let valid_utf8 = |bytes: &[u8]| !check_utf8 || simdutf8::basic::from_utf8(bytes).is_ok();
449 if view.is_inlined() {
450 let bytes = &view.as_inlined().data[..view.len() as usize];
452 vortex_ensure!(
453 valid_utf8(bytes),
454 InvalidArgument: "view at index {idx}: inlined bytes failed utf-8 validation"
455 );
456 } else {
457 let view = view.as_view();
459 let buf_index = view.buffer_index as usize;
460 let start_offset = view.offset as usize;
461 let end_offset = start_offset.saturating_add(view.size as usize);
462
463 let buf = buffers.get(buf_index).ok_or_else(||
464 vortex_err!(InvalidArgument: "view at index {idx} references invalid buffer: {buf_index} out of bounds for VarBinViewData with {} buffers",
465 buffers.len()))?;
466
467 vortex_ensure!(
468 start_offset < buf.len(),
469 InvalidArgument: "start offset {start_offset} out of bounds for buffer {buf_index} with size {}",
470 buf.len(),
471 );
472
473 vortex_ensure!(
474 end_offset <= buf.len(),
475 InvalidArgument: "end offset {end_offset} out of bounds for buffer {buf_index} with size {}",
476 buf.len(),
477 );
478
479 let bytes = &buf[start_offset..end_offset];
481 vortex_ensure!(
482 view.prefix == bytes[..4],
483 InvalidArgument: "VarBinView prefix does not match full string"
484 );
485
486 vortex_ensure!(
488 valid_utf8(bytes),
489 InvalidArgument: "view at index {idx}: outlined bytes fails utf-8 validation"
490 );
491 }
492 Ok(())
493 }
494
495 pub fn len(&self) -> usize {
497 self.views.len() / size_of::<BinaryView>()
498 }
499
500 pub fn is_empty(&self) -> bool {
502 self.len() == 0
503 }
504
505 #[inline]
511 pub fn views(&self) -> &[BinaryView] {
512 let host_views = self.views.as_host();
513 let len = host_views.len() / size_of::<BinaryView>();
514
515 unsafe { std::slice::from_raw_parts(host_views.as_ptr().cast(), len) }
517 }
518
519 pub fn views_handle(&self) -> &BufferHandle {
521 &self.views
522 }
523
524 #[inline]
528 pub fn bytes_at(&self, index: usize) -> ByteBuffer {
529 let views = self.views();
530 let view = &views[index];
531 if !view.is_inlined() {
533 let view_ref = view.as_view();
534 self.buffer(view_ref.buffer_index as usize)
535 .slice(view_ref.as_range())
536 } else {
537 self.views_handle()
539 .as_host()
540 .clone()
541 .into_byte_buffer()
542 .slice_ref(view.as_inlined().value())
543 }
544 }
545
546 #[inline]
553 pub fn buffer(&self, idx: usize) -> &ByteBuffer {
554 if idx >= self.data_buffers().len() {
555 vortex_panic!(
556 "{idx} buffer index out of bounds, there are {} buffers",
557 self.data_buffers().len()
558 );
559 }
560 self.buffers[idx].as_host()
561 }
562
563 #[inline]
565 pub fn data_buffers(&self) -> &Arc<[BufferHandle]> {
566 &self.buffers
567 }
568
569 #[expect(
571 clippy::same_name_method,
572 reason = "intentionally named from_iter like Iterator::from_iter"
573 )]
574 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
575 iter: I,
576 dtype: DType,
577 ) -> Self {
578 let iter = iter.into_iter();
579 let mut builder = VarBinViewBuilder::with_capacity_in(
580 dtype,
581 iter.size_hint().0,
582 BufferAllocatorRef::statically_allocated(),
583 );
584
585 for item in iter {
586 match item {
587 None => builder.append_null(),
588 Some(v) => builder.append_value(v),
589 }
590 }
591
592 builder.finish_into_varbinview().into_data()
593 }
594
595 pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
596 let iter = iter.into_iter();
597 let mut builder = VarBinViewBuilder::with_capacity_in(
598 DType::Utf8(Nullability::NonNullable),
599 iter.size_hint().0,
600 BufferAllocatorRef::statically_allocated(),
601 );
602
603 for item in iter {
604 builder.append_value(item.as_ref());
605 }
606
607 builder.finish_into_varbinview().into_data()
608 }
609
610 pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
611 iter: I,
612 ) -> Self {
613 let iter = iter.into_iter();
614 let mut builder = VarBinViewBuilder::with_capacity_in(
615 DType::Utf8(Nullability::Nullable),
616 iter.size_hint().0,
617 BufferAllocatorRef::statically_allocated(),
618 );
619
620 for item in iter {
621 match item {
622 None => builder.append_null(),
623 Some(v) => builder.append_value(v.as_ref()),
624 }
625 }
626
627 builder.finish_into_varbinview().into_data()
628 }
629
630 pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
631 let iter = iter.into_iter();
632 let mut builder = VarBinViewBuilder::with_capacity_in(
633 DType::Binary(Nullability::NonNullable),
634 iter.size_hint().0,
635 BufferAllocatorRef::statically_allocated(),
636 );
637
638 for item in iter {
639 builder.append_value(item.as_ref());
640 }
641
642 builder.finish_into_varbinview().into_data()
643 }
644
645 pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
646 iter: I,
647 ) -> Self {
648 let iter = iter.into_iter();
649 let mut builder = VarBinViewBuilder::with_capacity_in(
650 DType::Binary(Nullability::Nullable),
651 iter.size_hint().0,
652 BufferAllocatorRef::statically_allocated(),
653 );
654
655 for item in iter {
656 match item {
657 None => builder.append_null(),
658 Some(v) => builder.append_value(v.as_ref()),
659 }
660 }
661
662 builder.finish_into_varbinview().into_data()
663 }
664}
665
666pub trait VarBinViewArrayExt: TypedArrayRef<VarBinView> {
667 fn dtype_parts(&self) -> (bool, Nullability) {
668 match self.as_ref().dtype() {
669 DType::Utf8(nullability) => (true, *nullability),
670 DType::Binary(nullability) => (false, *nullability),
671 _ => unreachable!("VarBinViewArrayExt requires a utf8 or binary dtype"),
672 }
673 }
674
675 fn varbinview_validity(&self) -> Validity {
676 child_to_validity(
677 self.as_ref().slots()[VarBinViewSlots::VALIDITY].as_ref(),
678 self.dtype_parts().1,
679 )
680 }
681}
682impl<T: TypedArrayRef<VarBinView>> VarBinViewArrayExt for T {}
683
684impl Array<VarBinView> {
685 #[inline]
686 fn from_prevalidated_data(dtype: DType, data: VarBinViewData, slots: ArraySlots) -> Self {
687 let len = data.len();
688 unsafe {
689 Array::from_parts_unchecked(
690 ArrayParts::new(VarBinView, dtype, len, data).with_slots(slots),
691 )
692 }
693 }
694
695 #[expect(
697 clippy::same_name_method,
698 reason = "intentionally named from_iter like Iterator::from_iter"
699 )]
700 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
701 iter: I,
702 dtype: DType,
703 ) -> Self {
704 let iter = iter.into_iter();
705 let mut builder = VarBinViewBuilder::with_capacity_in(
706 dtype,
707 iter.size_hint().0,
708 BufferAllocatorRef::statically_allocated(),
709 );
710 for value in iter {
711 match value {
712 Some(value) => builder.append_value(value),
713 None => builder.append_null(),
714 }
715 }
716 builder.finish_into_varbinview()
717 }
718
719 pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
720 let iter = iter.into_iter();
721 let mut builder = VarBinViewBuilder::with_capacity_in(
722 DType::Utf8(Nullability::NonNullable),
723 iter.size_hint().0,
724 BufferAllocatorRef::statically_allocated(),
725 );
726 for value in iter {
727 builder.append_value(value.as_ref());
728 }
729 builder.finish_into_varbinview()
730 }
731
732 pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
733 iter: I,
734 ) -> Self {
735 let iter = iter.into_iter();
736 let mut builder = VarBinViewBuilder::with_capacity_in(
737 DType::Utf8(Nullability::Nullable),
738 iter.size_hint().0,
739 BufferAllocatorRef::statically_allocated(),
740 );
741 for value in iter {
742 match value {
743 Some(value) => builder.append_value(value.as_ref()),
744 None => builder.append_null(),
745 }
746 }
747 builder.finish_into_varbinview()
748 }
749
750 pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
751 let iter = iter.into_iter();
752 let mut builder = VarBinViewBuilder::with_capacity_in(
753 DType::Binary(Nullability::NonNullable),
754 iter.size_hint().0,
755 BufferAllocatorRef::statically_allocated(),
756 );
757 for value in iter {
758 builder.append_value(value.as_ref());
759 }
760 builder.finish_into_varbinview()
761 }
762
763 pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
764 iter: I,
765 ) -> Self {
766 let iter = iter.into_iter();
767 let mut builder = VarBinViewBuilder::with_capacity_in(
768 DType::Binary(Nullability::Nullable),
769 iter.size_hint().0,
770 BufferAllocatorRef::statically_allocated(),
771 );
772 for value in iter {
773 match value {
774 Some(value) => builder.append_value(value.as_ref()),
775 None => builder.append_null(),
776 }
777 }
778 builder.finish_into_varbinview()
779 }
780
781 pub fn try_new(
783 views: Buffer<BinaryView>,
784 buffers: Arc<[ByteBuffer]>,
785 dtype: DType,
786 validity: Validity,
787 ctx: &mut ExecutionCtx,
788 ) -> VortexResult<Self> {
789 let data = VarBinViewData::try_new(views, buffers, dtype.clone(), validity.clone(), ctx)?;
790 let slots = VarBinViewData::make_slots(&validity, data.len());
791 Ok(Self::from_prevalidated_data(dtype, data, slots))
792 }
793
794 pub unsafe fn new_unchecked(
800 views: Buffer<BinaryView>,
801 buffers: Arc<[ByteBuffer]>,
802 dtype: DType,
803 validity: Validity,
804 ) -> Self {
805 let data = unsafe {
806 VarBinViewData::new_unchecked(views, buffers, dtype.clone(), validity.clone())
807 };
808 let slots = VarBinViewData::make_slots(&validity, data.len());
809 Self::from_prevalidated_data(dtype, data, slots)
810 }
811
812 pub fn new_handle(
814 views: BufferHandle,
815 buffers: Arc<[BufferHandle]>,
816 dtype: DType,
817 validity: Validity,
818 ) -> Self {
819 let data = VarBinViewData::new_handle(views, buffers, dtype.clone(), validity.clone());
820 let slots = VarBinViewData::make_slots(&validity, data.len());
821 Self::from_prevalidated_data(dtype, data, slots)
822 }
823
824 pub unsafe fn new_handle_unchecked(
830 views: BufferHandle,
831 buffers: Arc<[BufferHandle]>,
832 dtype: DType,
833 validity: Validity,
834 ) -> Self {
835 let data = unsafe {
836 VarBinViewData::new_handle_unchecked(views, buffers, dtype.clone(), validity.clone())
837 };
838 let slots = VarBinViewData::make_slots(&validity, data.len());
839 Self::from_prevalidated_data(dtype, data, slots)
840 }
841
842 pub fn into_data_parts(self) -> VarBinViewDataParts {
843 let dtype = self.dtype().clone();
844 let validity = self.varbinview_validity();
845 let data = self.into_data();
846 VarBinViewDataParts {
847 dtype,
848 buffers: data.buffers,
849 views: data.views,
850 validity,
851 }
852 }
853}
854
855impl<'a> FromIterator<Option<&'a [u8]>> for VarBinViewData {
856 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
857 Self::from_iter_nullable_bin(iter)
858 }
859}
860
861impl FromIterator<Option<Vec<u8>>> for VarBinViewData {
862 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
863 Self::from_iter_nullable_bin(iter)
864 }
865}
866
867impl FromIterator<Option<String>> for VarBinViewData {
868 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
869 Self::from_iter_nullable_str(iter)
870 }
871}
872
873impl<'a> FromIterator<Option<&'a str>> for VarBinViewData {
874 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
875 Self::from_iter_nullable_str(iter)
876 }
877}
878
879impl<'a> FromIterator<Option<&'a [u8]>> for Array<VarBinView> {
882 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
883 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
884 }
885}
886
887impl FromIterator<Option<Vec<u8>>> for Array<VarBinView> {
888 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
889 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
890 }
891}
892
893impl FromIterator<Option<String>> for Array<VarBinView> {
894 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
895 Self::from_iter_nullable_str(iter)
896 }
897}
898
899impl<'a> FromIterator<Option<&'a str>> for Array<VarBinView> {
900 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
901 Self::from_iter_nullable_str(iter)
902 }
903}