1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use num_traits::AsPrimitive;
8use vortex_array::arrays::PrimitiveArray;
9use vortex_buffer::BufferAllocatorRef;
10use vortex_buffer::ByteBuffer;
11use vortex_error::VortexExpect;
12use vortex_error::VortexResult;
13use vortex_error::vortex_ensure;
14use vortex_error::vortex_err;
15
16use crate::ArrayRef;
17use crate::ArraySlots;
18use crate::VortexSessionExecute;
19use crate::array::Array;
20use crate::array::ArrayParts;
21use crate::array::TypedArrayRef;
22use crate::array::child_to_validity;
23use crate::array::validity_to_child;
24use crate::array_slots;
25use crate::arrays::VarBin;
26use crate::arrays::varbin::builder::VarBinBuilder;
27use crate::buffer::BufferHandle;
28use crate::dtype::DType;
29use crate::dtype::Nullability;
30use crate::dtype::OffsetBuilderPType;
31use crate::legacy_session;
32use crate::match_each_integer_ptype;
33use crate::validity::Validity;
34
35#[array_slots(VarBin)]
36pub struct VarBinSlots {
37 #[slot(0)]
39 pub offsets: ArrayRef,
40 #[slot(1)]
42 pub validity: Option<ArrayRef>,
43}
44
45#[derive(Clone, Debug)]
46pub struct VarBinData {
47 pub(super) bytes: BufferHandle,
48}
49
50impl Display for VarBinData {
51 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
52 Ok(())
53 }
54}
55
56pub struct VarBinDataParts {
57 pub dtype: DType,
58 pub bytes: BufferHandle,
59 pub offsets: ArrayRef,
60 pub validity: Validity,
61}
62
63impl VarBinData {
64 pub fn build(offsets: ArrayRef, bytes: ByteBuffer, dtype: DType, validity: Validity) -> Self {
71 Self::try_build(offsets, bytes, dtype, validity).vortex_expect("VarBinArray new")
72 }
73
74 pub fn build_from_handle(
81 offset: ArrayRef,
82 bytes: BufferHandle,
83 dtype: DType,
84 validity: Validity,
85 ) -> Self {
86 Self::try_build_from_handle(offset, bytes, dtype, validity).vortex_expect("VarBinArray new")
87 }
88
89 pub(crate) fn make_slots(offsets: ArrayRef, validity: &Validity, len: usize) -> ArraySlots {
90 VarBinSlots {
91 offsets,
92 validity: validity_to_child(validity, len),
93 }
94 .into_slots()
95 }
96
97 pub fn try_build(
106 offsets: ArrayRef,
107 bytes: ByteBuffer,
108 dtype: DType,
109 validity: Validity,
110 ) -> VortexResult<Self> {
111 let bytes = BufferHandle::new_host(bytes);
112 Self::validate(&offsets, &bytes, &dtype, &validity)?;
113
114 Ok(unsafe { Self::new_unchecked_from_handle(bytes) })
116 }
117
118 pub fn try_build_from_handle(
128 offsets: ArrayRef,
129 bytes: BufferHandle,
130 dtype: DType,
131 validity: Validity,
132 ) -> VortexResult<Self> {
133 Self::validate(&offsets, &bytes, &dtype, &validity)?;
134
135 Ok(unsafe { Self::new_unchecked_from_handle(bytes) })
137 }
138
139 pub unsafe fn new_unchecked(bytes: ByteBuffer) -> Self {
168 unsafe { Self::new_unchecked_from_handle(BufferHandle::new_host(bytes)) }
171 }
172
173 pub unsafe fn new_unchecked_from_handle(bytes: BufferHandle) -> Self {
180 Self { bytes }
181 }
182
183 pub fn validate(
187 offsets: &ArrayRef,
188 bytes: &BufferHandle,
189 dtype: &DType,
190 validity: &Validity,
191 ) -> VortexResult<()> {
192 vortex_ensure!(
194 offsets.dtype().is_int() && !offsets.dtype().is_nullable(),
195 MismatchedTypes: "non nullable int", offsets.dtype()
196 );
197
198 vortex_ensure!(
200 matches!(dtype, DType::Binary(_) | DType::Utf8(_)),
201 MismatchedTypes: "utf8 or binary", dtype
202 );
203
204 vortex_ensure!(
206 dtype.is_nullable() != matches!(validity, Validity::NonNullable),
207 InvalidArgument: "incorrect validity {:?} for dtype {}",
208 validity,
209 dtype
210 );
211
212 vortex_ensure!(
214 !offsets.is_empty(),
215 InvalidArgument: "Offsets must have at least one element"
216 );
217
218 if let Some(validity_len) = validity.maybe_len() {
220 vortex_ensure!(
221 validity_len == offsets.len() - 1,
222 "Validity length {} doesn't match array length {}",
223 validity_len,
224 offsets.len() - 1
225 );
226 }
227
228 if offsets.is_host()
230 && bytes.is_on_host()
231 && matches!(dtype, DType::Utf8(_))
232 && let Some(bytes) = bytes.as_host_opt()
233 {
234 Self::validate_utf8(offsets, bytes.as_ref(), validity)?;
235 }
236
237 Ok(())
238 }
239
240 #[allow(clippy::disallowed_methods)]
242 fn validate_utf8(offsets: &ArrayRef, bytes: &[u8], validity: &Validity) -> VortexResult<()> {
243 let validate_at = |i: usize, start: usize, end: usize| -> VortexResult<()> {
244 let string_bytes = &bytes[start..end];
245 simdutf8::basic::from_utf8(string_bytes).map_err(|_| {
246 #[expect(clippy::unwrap_used)]
247 let err = simdutf8::compat::from_utf8(string_bytes).unwrap_err();
249 vortex_err!("invalid utf-8: {err} at index {i}")
250 })?;
251 Ok(())
252 };
253
254 let mut ctx = legacy_session().create_execution_ctx();
255 let primitive_offsets = offsets.clone().execute::<PrimitiveArray>(&mut ctx)?;
257
258 let mask = match validity {
262 Validity::Array(_) => {
263 Some(validity.execute_mask(primitive_offsets.len().saturating_sub(1), &mut ctx)?)
264 }
265 _ => None,
266 };
267 let all_invalid = validity.definitely_all_null();
268
269 match_each_integer_ptype!(primitive_offsets.dtype().as_ptype(), |O| {
270 let offsets_slice = primitive_offsets.as_slice::<O>();
271
272 let last_offset: usize = offsets_slice[offsets_slice.len() - 1].as_();
273 vortex_ensure!(
274 last_offset <= bytes.len(),
275 InvalidArgument: "Last offset {} exceeds bytes length {}",
276 last_offset,
277 bytes.len()
278 );
279
280 for (i, (start, end)) in offsets_slice
281 .windows(2)
282 .map(|o| (o[0].as_(), o[1].as_()))
283 .enumerate()
284 {
285 let valid = mask.as_ref().map_or(!all_invalid, |mask| mask.value(i));
286 if valid {
287 validate_at(i, start, end)?;
288 }
289 }
290 });
291 Ok(())
292 }
293
294 #[inline]
302 pub fn bytes(&self) -> &ByteBuffer {
303 self.bytes.as_host()
304 }
305
306 #[inline]
308 pub fn bytes_handle(&self) -> &BufferHandle {
309 &self.bytes
310 }
311}
312
313pub trait VarBinArrayExt: VarBinArraySlotsExt {
314 fn dtype_parts(&self) -> (bool, Nullability) {
315 match self.as_ref().dtype() {
316 DType::Utf8(nullability) => (true, *nullability),
317 DType::Binary(nullability) => (false, *nullability),
318 _ => unreachable!("VarBinArrayExt requires a utf8 or binary dtype"),
319 }
320 }
321
322 fn is_utf8(&self) -> bool {
323 self.dtype_parts().0
324 }
325
326 fn nullability(&self) -> Nullability {
327 self.dtype_parts().1
328 }
329
330 fn varbin_validity(&self) -> Validity {
331 child_to_validity(
332 self.as_ref().slots()[VarBinSlots::VALIDITY].as_ref(),
333 self.nullability(),
334 )
335 }
336
337 #[allow(clippy::disallowed_methods)]
338 fn offset_at(&self, index: usize) -> usize {
339 assert!(
340 index <= self.as_ref().len(),
341 "Index {index} out of bounds 0..={}",
342 self.as_ref().len()
343 );
344
345 (&self
346 .offsets()
347 .execute_scalar(index, &mut legacy_session().create_execution_ctx())
348 .vortex_expect("offsets must support execute_scalar"))
349 .try_into()
350 .vortex_expect("Failed to convert offset to usize")
351 }
352
353 fn bytes_at(&self, index: usize) -> ByteBuffer {
354 let start = self.offset_at(index);
355 let end = self.offset_at(index + 1);
356 self.bytes().slice(start..end)
357 }
358
359 fn sliced_bytes(&self) -> ByteBuffer {
360 let first_offset: usize = self.offset_at(0);
361 let last_offset = self.offset_at(self.as_ref().len());
362 self.bytes().slice(first_offset..last_offset)
363 }
364}
365impl<T: TypedArrayRef<VarBin>> VarBinArrayExt for T {}
366
367impl Array<VarBin> {
369 pub fn from_vec<T: AsRef<[u8]>>(vec: Vec<T>, dtype: DType) -> Self {
370 let size: usize = vec.iter().map(|v| v.as_ref().len()).sum();
371 if size < u32::MAX as usize {
372 Self::from_vec_sized::<u32, T>(vec, dtype)
373 } else {
374 Self::from_vec_sized::<u64, T>(vec, dtype)
375 }
376 }
377
378 #[expect(
379 clippy::same_name_method,
380 reason = "intentionally named from_iter like Iterator::from_iter"
381 )]
382 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
383 iter: I,
384 dtype: DType,
385 ) -> Self {
386 let iter = iter.into_iter();
387 let mut builder = VarBinBuilder::<u32>::with_capacity_in(
388 dtype,
389 iter.size_hint().0,
390 BufferAllocatorRef::static_ref(),
391 );
392 for v in iter {
393 builder.append(v.as_ref().map(|o| o.as_ref()));
394 }
395 builder.finish_into_varbin()
396 }
397
398 pub fn from_iter_nonnull<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(
399 iter: I,
400 dtype: DType,
401 ) -> Self {
402 let iter = iter.into_iter();
403 let mut builder = VarBinBuilder::<u32>::with_capacity_in(
404 dtype,
405 iter.size_hint().0,
406 BufferAllocatorRef::static_ref(),
407 );
408 for v in iter {
409 builder.append_value(v);
410 }
411 builder.finish_into_varbin()
412 }
413
414 fn from_vec_sized<O, T>(vec: Vec<T>, dtype: DType) -> Self
415 where
416 O: OffsetBuilderPType,
417 T: AsRef<[u8]>,
418 {
419 let mut builder = VarBinBuilder::<O>::with_capacity_in(
420 dtype,
421 vec.len(),
422 BufferAllocatorRef::static_ref(),
423 );
424 for v in vec {
425 builder.append_value(v.as_ref());
426 }
427 builder.finish_into_varbin()
428 }
429
430 pub fn from_strs(value: Vec<&str>) -> Self {
432 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
433 }
434
435 pub fn from_nullable_strs(value: Vec<Option<&str>>) -> Self {
437 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
438 }
439
440 pub fn from_bytes(value: Vec<&[u8]>) -> Self {
442 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
443 }
444
445 pub fn from_nullable_bytes(value: Vec<Option<&[u8]>>) -> Self {
447 Self::from_iter(value, DType::Binary(Nullability::Nullable))
448 }
449
450 pub fn into_data_parts(self) -> VarBinDataParts {
451 let dtype = self.dtype().clone();
452 let validity = self.varbin_validity();
453 let offsets = self.offsets().clone();
454 let data = self.into_data();
455 VarBinDataParts {
456 dtype,
457 bytes: data.bytes,
458 offsets,
459 validity,
460 }
461 }
462}
463
464impl Array<VarBin> {
465 pub fn new(offsets: ArrayRef, bytes: ByteBuffer, dtype: DType, validity: Validity) -> Self {
467 let len = offsets.len().saturating_sub(1);
468 let slots = VarBinData::make_slots(offsets, &validity, len);
469 let data = VarBinData::build(
470 slots[VarBinSlots::OFFSETS]
471 .as_ref()
472 .vortex_expect("VarBinArray offsets slot")
473 .clone(),
474 bytes,
475 dtype.clone(),
476 validity,
477 );
478 unsafe {
479 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
480 }
481 }
482
483 pub unsafe fn new_unchecked(
489 offsets: ArrayRef,
490 bytes: ByteBuffer,
491 dtype: DType,
492 validity: Validity,
493 ) -> Self {
494 let len = offsets.len().saturating_sub(1);
495 let slots = VarBinData::make_slots(offsets, &validity, len);
496 let data = unsafe { VarBinData::new_unchecked(bytes) };
497 unsafe {
498 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
499 }
500 }
501
502 pub unsafe fn new_unchecked_from_handle(
508 offsets: ArrayRef,
509 bytes: BufferHandle,
510 dtype: DType,
511 validity: Validity,
512 ) -> Self {
513 let len = offsets.len().saturating_sub(1);
514 let slots = VarBinData::make_slots(offsets, &validity, len);
515 let data = unsafe { VarBinData::new_unchecked_from_handle(bytes) };
516 unsafe {
517 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
518 }
519 }
520
521 pub fn try_new(
523 offsets: ArrayRef,
524 bytes: ByteBuffer,
525 dtype: DType,
526 validity: Validity,
527 ) -> VortexResult<Self> {
528 let len = offsets.len() - 1;
529 let bytes = BufferHandle::new_host(bytes);
530 VarBinData::validate(&offsets, &bytes, &dtype, &validity)?;
531 let slots = VarBinData::make_slots(offsets, &validity, len);
532 let data = unsafe { VarBinData::new_unchecked_from_handle(bytes) };
534 Ok(unsafe {
535 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
536 })
537 }
538}
539
540impl From<Vec<&[u8]>> for Array<VarBin> {
541 fn from(value: Vec<&[u8]>) -> Self {
542 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
543 }
544}
545
546impl From<Vec<Vec<u8>>> for Array<VarBin> {
547 fn from(value: Vec<Vec<u8>>) -> Self {
548 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
549 }
550}
551
552impl From<Vec<String>> for Array<VarBin> {
553 fn from(value: Vec<String>) -> Self {
554 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
555 }
556}
557
558impl From<Vec<&str>> for Array<VarBin> {
559 fn from(value: Vec<&str>) -> Self {
560 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
561 }
562}
563
564impl From<Vec<Option<&[u8]>>> for Array<VarBin> {
565 fn from(value: Vec<Option<&[u8]>>) -> Self {
566 Self::from_iter(value, DType::Binary(Nullability::Nullable))
567 }
568}
569
570impl From<Vec<Option<Vec<u8>>>> for Array<VarBin> {
571 fn from(value: Vec<Option<Vec<u8>>>) -> Self {
572 Self::from_iter(value, DType::Binary(Nullability::Nullable))
573 }
574}
575
576impl From<Vec<Option<String>>> for Array<VarBin> {
577 fn from(value: Vec<Option<String>>) -> Self {
578 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
579 }
580}
581
582impl From<Vec<Option<&str>>> for Array<VarBin> {
583 fn from(value: Vec<Option<&str>>) -> Self {
584 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
585 }
586}
587
588impl<'a> FromIterator<Option<&'a [u8]>> for Array<VarBin> {
589 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
590 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
591 }
592}
593
594impl FromIterator<Option<Vec<u8>>> for Array<VarBin> {
595 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
596 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
597 }
598}
599
600impl FromIterator<Option<String>> for Array<VarBin> {
601 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
602 Self::from_iter(iter, DType::Utf8(Nullability::Nullable))
603 }
604}
605
606impl<'a> FromIterator<Option<&'a str>> for Array<VarBin> {
607 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
608 Self::from_iter(iter, DType::Utf8(Nullability::Nullable))
609 }
610}