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