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::ByteBuffer;
13use vortex_error::VortexExpect;
14use vortex_error::VortexResult;
15use vortex_error::vortex_bail;
16use vortex_error::vortex_ensure;
17use vortex_error::vortex_err;
18use vortex_error::vortex_panic;
19
20use crate::ArrayRef;
21use crate::ArraySlots;
22use crate::VortexSessionExecute;
23use crate::array::Array;
24use crate::array::ArrayParts;
25use crate::array::TypedArrayRef;
26use crate::array::child_to_validity;
27use crate::array::validity_to_child;
28use crate::array_slots;
29use crate::arrays::VarBinView;
30use crate::arrays::varbinview::BinaryView;
31use crate::buffer::BufferHandle;
32use crate::builders::ArrayBuilder;
33use crate::builders::VarBinViewBuilder;
34use crate::dtype::DType;
35use crate::dtype::Nullability;
36use crate::legacy_session;
37use crate::validity::Validity;
38
39#[array_slots(VarBinView)]
40pub struct VarBinViewSlots {
41 pub validity: Option<ArrayRef>,
43}
44
45#[derive(Clone, Debug)]
105pub struct VarBinViewData {
106 pub(super) buffers: Arc<[BufferHandle]>,
107 pub(super) views: BufferHandle,
108}
109
110impl Display for VarBinViewData {
111 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
112 Ok(())
113 }
114}
115
116pub struct VarBinViewDataParts {
117 pub dtype: DType,
118 pub buffers: Arc<[BufferHandle]>,
119 pub views: BufferHandle,
120 pub validity: Validity,
121}
122
123impl VarBinViewData {
124 fn dtype_parts(dtype: &DType) -> VortexResult<(bool, Nullability)> {
125 match dtype {
126 DType::Utf8(nullability) => Ok((true, *nullability)),
127 DType::Binary(nullability) => Ok((false, *nullability)),
128 _ => vortex_bail!(InvalidArgument: "invalid DType {dtype} for `VarBinViewArray`"),
129 }
130 }
131
132 pub(super) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
134 smallvec![validity_to_child(validity, len)]
135 }
136
137 pub fn new(
144 views: Buffer<BinaryView>,
145 buffers: Arc<[ByteBuffer]>,
146 dtype: DType,
147 validity: Validity,
148 ) -> Self {
149 Self::try_new(views, buffers, dtype, validity)
150 .vortex_expect("VarBinViewArray construction failed")
151 }
152
153 pub fn new_handle(
160 views: BufferHandle,
161 buffers: Arc<[BufferHandle]>,
162 dtype: DType,
163 validity: Validity,
164 ) -> Self {
165 Self::try_new_handle(views, buffers, dtype, validity)
166 .vortex_expect("VarbinViewArray construction failed")
167 }
168
169 pub fn try_new(
178 views: Buffer<BinaryView>,
179 buffers: Arc<[ByteBuffer]>,
180 dtype: DType,
181 validity: Validity,
182 ) -> VortexResult<Self> {
183 Self::validate(&views, &buffers, &dtype, &validity)?;
184
185 Ok(unsafe { Self::new_unchecked(views, buffers, dtype, validity) })
187 }
188
189 pub fn try_new_handle(
198 views: BufferHandle,
199 buffers: Arc<[BufferHandle]>,
200 dtype: DType,
201 validity: Validity,
202 ) -> VortexResult<Self> {
203 let views_nbytes = views.len();
204 vortex_ensure!(
205 views_nbytes.is_multiple_of(size_of::<BinaryView>()),
206 "Expected views buffer length ({views_nbytes}) to be a multiple of {}",
207 size_of::<BinaryView>()
208 );
209
210 if let Some(host) = views.as_host_opt() {
212 vortex_ensure!(
213 host.is_aligned(Alignment::of::<BinaryView>()),
214 "Views on host must be 16 byte aligned"
215 );
216 }
217
218 Ok(unsafe { Self::new_handle_unchecked(views, buffers, dtype, validity) })
220 }
221
222 pub unsafe fn new_unchecked(
252 views: Buffer<BinaryView>,
253 buffers: Arc<[ByteBuffer]>,
254 dtype: DType,
255 validity: Validity,
256 ) -> Self {
257 #[cfg(debug_assertions)]
258 Self::validate(&views, &buffers, &dtype, &validity)
259 .vortex_expect("[Debug Assertion]: Invalid `VarBinViewArray` parameters");
260
261 let handles: Vec<BufferHandle> = buffers
262 .iter()
263 .cloned()
264 .map(BufferHandle::new_host)
265 .collect();
266
267 let handles = Arc::from(handles);
268 let view_handle = BufferHandle::new_host(views.into_byte_buffer());
269 unsafe { Self::new_handle_unchecked(view_handle, handles, dtype, validity) }
270 }
271
272 pub unsafe fn new_handle_unchecked(
278 views: BufferHandle,
279 buffers: Arc<[BufferHandle]>,
280 dtype: DType,
281 _validity: Validity,
282 ) -> Self {
283 let _ =
284 Self::dtype_parts(&dtype).vortex_expect("VarBinViewArray dtype must be utf8 or binary");
285 Self { buffers, views }
286 }
287
288 pub fn validate(
292 views: &Buffer<BinaryView>,
293 buffers: &Arc<[ByteBuffer]>,
294 dtype: &DType,
295 validity: &Validity,
296 ) -> VortexResult<()> {
297 vortex_ensure!(
298 validity.nullability() == dtype.nullability(),
299 InvalidArgument: "validity {:?} incompatible with nullability {:?}",
300 validity,
301 dtype.nullability()
302 );
303
304 match dtype {
305 DType::Utf8(_) => Self::validate_views(views, buffers, validity, |string| {
306 simdutf8::basic::from_utf8(string).is_ok()
307 })?,
308 DType::Binary(_) => Self::validate_views(views, buffers, validity, |_| true)?,
309 _ => vortex_bail!(InvalidArgument: "invalid DType {dtype} for `VarBinViewArray`"),
310 }
311
312 Ok(())
313 }
314
315 #[allow(clippy::disallowed_methods)]
316 fn validate_views<F>(
317 views: &Buffer<BinaryView>,
318 buffers: &Arc<[ByteBuffer]>,
319 validity: &Validity,
320 validator: F,
321 ) -> VortexResult<()>
322 where
323 F: Fn(&[u8]) -> bool,
324 {
325 let validate_view = |idx: usize, view: &BinaryView| -> VortexResult<()> {
326 if view.is_inlined() {
327 let bytes = &view.as_inlined().data[..view.len() as usize];
329 vortex_ensure!(
330 validator(bytes),
331 InvalidArgument: "view at index {idx}: inlined bytes failed utf-8 validation"
332 );
333 } else {
334 let view = view.as_view();
336 let buf_index = view.buffer_index as usize;
337 let start_offset = view.offset as usize;
338 let end_offset = start_offset.saturating_add(view.size as usize);
339
340 let buf = buffers.get(buf_index).ok_or_else(||
341 vortex_err!(InvalidArgument: "view at index {idx} references invalid buffer: {buf_index} out of bounds for VarBinViewData with {} buffers",
342 buffers.len()))?;
343
344 vortex_ensure!(
345 start_offset < buf.len(),
346 InvalidArgument: "start offset {start_offset} out of bounds for buffer {buf_index} with size {}",
347 buf.len(),
348 );
349
350 vortex_ensure!(
351 end_offset <= buf.len(),
352 InvalidArgument: "end offset {end_offset} out of bounds for buffer {buf_index} with size {}",
353 buf.len(),
354 );
355
356 let bytes = &buf[start_offset..end_offset];
358 vortex_ensure!(
359 view.prefix == bytes[..4],
360 InvalidArgument: "VarBinView prefix does not match full string"
361 );
362
363 vortex_ensure!(
365 validator(bytes),
366 InvalidArgument: "view at index {idx}: outlined bytes fails utf-8 validation"
367 );
368 }
369 Ok(())
370 };
371
372 match validity {
373 Validity::Array(_) => {
377 let mut ctx = legacy_session().create_execution_ctx();
378 let mask = validity.execute_mask(views.len(), &mut ctx)?;
379 for ((idx, view), valid) in views.iter().enumerate().zip(mask.iter()) {
380 if valid {
381 validate_view(idx, view)?;
382 }
383 }
384 }
385 Validity::AllInvalid => {}
387 Validity::NonNullable | Validity::AllValid => {
389 for (idx, view) in views.iter().enumerate() {
390 validate_view(idx, view)?;
391 }
392 }
393 }
394
395 Ok(())
396 }
397
398 pub fn len(&self) -> usize {
400 self.views.len() / size_of::<BinaryView>()
401 }
402
403 pub fn is_empty(&self) -> bool {
405 self.len() == 0
406 }
407
408 #[inline]
414 pub fn views(&self) -> &[BinaryView] {
415 let host_views = self.views.as_host();
416 let len = host_views.len() / size_of::<BinaryView>();
417
418 unsafe { std::slice::from_raw_parts(host_views.as_ptr().cast(), len) }
420 }
421
422 pub fn views_handle(&self) -> &BufferHandle {
424 &self.views
425 }
426
427 #[inline]
431 pub fn bytes_at(&self, index: usize) -> ByteBuffer {
432 let views = self.views();
433 let view = &views[index];
434 if !view.is_inlined() {
436 let view_ref = view.as_view();
437 self.buffer(view_ref.buffer_index as usize)
438 .slice(view_ref.as_range())
439 } else {
440 self.views_handle()
442 .as_host()
443 .clone()
444 .into_byte_buffer()
445 .slice_ref(view.as_inlined().value())
446 }
447 }
448
449 #[inline]
456 pub fn buffer(&self, idx: usize) -> &ByteBuffer {
457 if idx >= self.data_buffers().len() {
458 vortex_panic!(
459 "{idx} buffer index out of bounds, there are {} buffers",
460 self.data_buffers().len()
461 );
462 }
463 self.buffers[idx].as_host()
464 }
465
466 #[inline]
468 pub fn data_buffers(&self) -> &Arc<[BufferHandle]> {
469 &self.buffers
470 }
471
472 #[expect(
474 clippy::same_name_method,
475 reason = "intentionally named from_iter like Iterator::from_iter"
476 )]
477 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
478 iter: I,
479 dtype: DType,
480 ) -> Self {
481 let iter = iter.into_iter();
482 let mut builder = VarBinViewBuilder::with_capacity(dtype, iter.size_hint().0);
483
484 for item in iter {
485 match item {
486 None => builder.append_null(),
487 Some(v) => builder.append_value(v),
488 }
489 }
490
491 builder.finish_into_varbinview().into_data()
492 }
493
494 pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
495 let iter = iter.into_iter();
496 let mut builder = VarBinViewBuilder::with_capacity(
497 DType::Utf8(Nullability::NonNullable),
498 iter.size_hint().0,
499 );
500
501 for item in iter {
502 builder.append_value(item.as_ref());
503 }
504
505 builder.finish_into_varbinview().into_data()
506 }
507
508 pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
509 iter: I,
510 ) -> Self {
511 let iter = iter.into_iter();
512 let mut builder = VarBinViewBuilder::with_capacity(
513 DType::Utf8(Nullability::Nullable),
514 iter.size_hint().0,
515 );
516
517 for item in iter {
518 match item {
519 None => builder.append_null(),
520 Some(v) => builder.append_value(v.as_ref()),
521 }
522 }
523
524 builder.finish_into_varbinview().into_data()
525 }
526
527 pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
528 let iter = iter.into_iter();
529 let mut builder = VarBinViewBuilder::with_capacity(
530 DType::Binary(Nullability::NonNullable),
531 iter.size_hint().0,
532 );
533
534 for item in iter {
535 builder.append_value(item.as_ref());
536 }
537
538 builder.finish_into_varbinview().into_data()
539 }
540
541 pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
542 iter: I,
543 ) -> Self {
544 let iter = iter.into_iter();
545 let mut builder = VarBinViewBuilder::with_capacity(
546 DType::Binary(Nullability::Nullable),
547 iter.size_hint().0,
548 );
549
550 for item in iter {
551 match item {
552 None => builder.append_null(),
553 Some(v) => builder.append_value(v.as_ref()),
554 }
555 }
556
557 builder.finish_into_varbinview().into_data()
558 }
559}
560
561pub trait VarBinViewArrayExt: TypedArrayRef<VarBinView> {
562 fn dtype_parts(&self) -> (bool, Nullability) {
563 match self.as_ref().dtype() {
564 DType::Utf8(nullability) => (true, *nullability),
565 DType::Binary(nullability) => (false, *nullability),
566 _ => unreachable!("VarBinViewArrayExt requires a utf8 or binary dtype"),
567 }
568 }
569
570 fn varbinview_validity(&self) -> Validity {
571 child_to_validity(
572 self.as_ref().slots()[VarBinViewSlots::VALIDITY].as_ref(),
573 self.dtype_parts().1,
574 )
575 }
576}
577impl<T: TypedArrayRef<VarBinView>> VarBinViewArrayExt for T {}
578
579impl Array<VarBinView> {
580 #[inline]
581 fn from_prevalidated_data(dtype: DType, data: VarBinViewData, slots: ArraySlots) -> Self {
582 let len = data.len();
583 unsafe {
584 Array::from_parts_unchecked(
585 ArrayParts::new(VarBinView, dtype, len, data).with_slots(slots),
586 )
587 }
588 }
589
590 #[expect(
592 clippy::same_name_method,
593 reason = "intentionally named from_iter like Iterator::from_iter"
594 )]
595 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
596 iter: I,
597 dtype: DType,
598 ) -> Self {
599 let iter = iter.into_iter();
600 let mut builder = VarBinViewBuilder::with_capacity(dtype, iter.size_hint().0);
601 for value in iter {
602 match value {
603 Some(value) => builder.append_value(value),
604 None => builder.append_null(),
605 }
606 }
607 builder.finish_into_varbinview()
608 }
609
610 pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
611 let iter = iter.into_iter();
612 let mut builder = VarBinViewBuilder::with_capacity(
613 DType::Utf8(Nullability::NonNullable),
614 iter.size_hint().0,
615 );
616 for value in iter {
617 builder.append_value(value.as_ref());
618 }
619 builder.finish_into_varbinview()
620 }
621
622 pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
623 iter: I,
624 ) -> Self {
625 let iter = iter.into_iter();
626 let mut builder = VarBinViewBuilder::with_capacity(
627 DType::Utf8(Nullability::Nullable),
628 iter.size_hint().0,
629 );
630 for value in iter {
631 match value {
632 Some(value) => builder.append_value(value.as_ref()),
633 None => builder.append_null(),
634 }
635 }
636 builder.finish_into_varbinview()
637 }
638
639 pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
640 let iter = iter.into_iter();
641 let mut builder = VarBinViewBuilder::with_capacity(
642 DType::Binary(Nullability::NonNullable),
643 iter.size_hint().0,
644 );
645 for value in iter {
646 builder.append_value(value.as_ref());
647 }
648 builder.finish_into_varbinview()
649 }
650
651 pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
652 iter: I,
653 ) -> Self {
654 let iter = iter.into_iter();
655 let mut builder = VarBinViewBuilder::with_capacity(
656 DType::Binary(Nullability::Nullable),
657 iter.size_hint().0,
658 );
659 for value in iter {
660 match value {
661 Some(value) => builder.append_value(value.as_ref()),
662 None => builder.append_null(),
663 }
664 }
665 builder.finish_into_varbinview()
666 }
667
668 pub fn try_new(
670 views: Buffer<BinaryView>,
671 buffers: Arc<[ByteBuffer]>,
672 dtype: DType,
673 validity: Validity,
674 ) -> VortexResult<Self> {
675 let data = VarBinViewData::try_new(views, buffers, dtype.clone(), validity.clone())?;
676 let slots = VarBinViewData::make_slots(&validity, data.len());
677 Ok(Self::from_prevalidated_data(dtype, data, slots))
678 }
679
680 pub unsafe fn new_unchecked(
686 views: Buffer<BinaryView>,
687 buffers: Arc<[ByteBuffer]>,
688 dtype: DType,
689 validity: Validity,
690 ) -> Self {
691 let data = unsafe {
692 VarBinViewData::new_unchecked(views, buffers, dtype.clone(), validity.clone())
693 };
694 let slots = VarBinViewData::make_slots(&validity, data.len());
695 Self::from_prevalidated_data(dtype, data, slots)
696 }
697
698 pub fn new_handle(
700 views: BufferHandle,
701 buffers: Arc<[BufferHandle]>,
702 dtype: DType,
703 validity: Validity,
704 ) -> Self {
705 let data = VarBinViewData::new_handle(views, buffers, dtype.clone(), validity.clone());
706 let slots = VarBinViewData::make_slots(&validity, data.len());
707 Self::from_prevalidated_data(dtype, data, slots)
708 }
709
710 pub unsafe fn new_handle_unchecked(
716 views: BufferHandle,
717 buffers: Arc<[BufferHandle]>,
718 dtype: DType,
719 validity: Validity,
720 ) -> Self {
721 let data = unsafe {
722 VarBinViewData::new_handle_unchecked(views, buffers, dtype.clone(), validity.clone())
723 };
724 let slots = VarBinViewData::make_slots(&validity, data.len());
725 Self::from_prevalidated_data(dtype, data, slots)
726 }
727
728 pub fn into_data_parts(self) -> VarBinViewDataParts {
729 let dtype = self.dtype().clone();
730 let validity = self.varbinview_validity();
731 let data = self.into_data();
732 VarBinViewDataParts {
733 dtype,
734 buffers: data.buffers,
735 views: data.views,
736 validity,
737 }
738 }
739}
740
741impl<'a> FromIterator<Option<&'a [u8]>> for VarBinViewData {
742 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
743 Self::from_iter_nullable_bin(iter)
744 }
745}
746
747impl FromIterator<Option<Vec<u8>>> for VarBinViewData {
748 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
749 Self::from_iter_nullable_bin(iter)
750 }
751}
752
753impl FromIterator<Option<String>> for VarBinViewData {
754 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
755 Self::from_iter_nullable_str(iter)
756 }
757}
758
759impl<'a> FromIterator<Option<&'a str>> for VarBinViewData {
760 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
761 Self::from_iter_nullable_str(iter)
762 }
763}
764
765impl<'a> FromIterator<Option<&'a [u8]>> for Array<VarBinView> {
768 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
769 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
770 }
771}
772
773impl FromIterator<Option<Vec<u8>>> for Array<VarBinView> {
774 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
775 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
776 }
777}
778
779impl FromIterator<Option<String>> for Array<VarBinView> {
780 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
781 Self::from_iter_nullable_str(iter)
782 }
783}
784
785impl<'a> FromIterator<Option<&'a str>> for Array<VarBinView> {
786 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
787 Self::from_iter_nullable_str(iter)
788 }
789}