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