1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use num_traits::AsPrimitive;
8use smallvec::smallvec;
9use vortex_array::arrays::PrimitiveArray;
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::arrays::VarBin;
25use crate::arrays::varbin::builder::VarBinBuilder;
26use crate::buffer::BufferHandle;
27use crate::dtype::DType;
28use crate::dtype::IntegerPType;
29use crate::dtype::Nullability;
30use crate::legacy_session;
31use crate::match_each_integer_ptype;
32use crate::validity::Validity;
33
34pub(super) const OFFSETS_SLOT: usize = 0;
36pub(super) const VALIDITY_SLOT: usize = 1;
38pub(super) const NUM_SLOTS: usize = 2;
39pub(super) const SLOT_NAMES: [&str; NUM_SLOTS] = ["offsets", "validity"];
40
41#[derive(Clone, Debug)]
42pub struct VarBinData {
43 pub(super) bytes: BufferHandle,
44}
45
46impl Display for VarBinData {
47 fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
48 Ok(())
49 }
50}
51
52pub struct VarBinDataParts {
53 pub dtype: DType,
54 pub bytes: BufferHandle,
55 pub offsets: ArrayRef,
56 pub validity: Validity,
57}
58
59impl VarBinData {
60 pub fn build(offsets: ArrayRef, bytes: ByteBuffer, dtype: DType, validity: Validity) -> Self {
67 Self::try_build(offsets, bytes, dtype, validity).vortex_expect("VarBinArray new")
68 }
69
70 pub fn build_from_handle(
77 offset: ArrayRef,
78 bytes: BufferHandle,
79 dtype: DType,
80 validity: Validity,
81 ) -> Self {
82 Self::try_build_from_handle(offset, bytes, dtype, validity).vortex_expect("VarBinArray new")
83 }
84
85 pub(crate) fn make_slots(offsets: ArrayRef, validity: &Validity, len: usize) -> ArraySlots {
86 smallvec![Some(offsets), validity_to_child(validity, len)]
87 }
88
89 pub fn try_build(
98 offsets: ArrayRef,
99 bytes: ByteBuffer,
100 dtype: DType,
101 validity: Validity,
102 ) -> VortexResult<Self> {
103 let bytes = BufferHandle::new_host(bytes);
104 Self::validate(&offsets, &bytes, &dtype, &validity)?;
105
106 Ok(unsafe { Self::new_unchecked_from_handle(bytes) })
108 }
109
110 pub fn try_build_from_handle(
120 offsets: ArrayRef,
121 bytes: BufferHandle,
122 dtype: DType,
123 validity: Validity,
124 ) -> VortexResult<Self> {
125 Self::validate(&offsets, &bytes, &dtype, &validity)?;
126
127 Ok(unsafe { Self::new_unchecked_from_handle(bytes) })
129 }
130
131 pub unsafe fn new_unchecked(bytes: ByteBuffer) -> Self {
160 unsafe { Self::new_unchecked_from_handle(BufferHandle::new_host(bytes)) }
163 }
164
165 pub unsafe fn new_unchecked_from_handle(bytes: BufferHandle) -> Self {
172 Self { bytes }
173 }
174
175 pub fn validate(
179 offsets: &ArrayRef,
180 bytes: &BufferHandle,
181 dtype: &DType,
182 validity: &Validity,
183 ) -> VortexResult<()> {
184 vortex_ensure!(
186 offsets.dtype().is_int() && !offsets.dtype().is_nullable(),
187 MismatchedTypes: "non nullable int", offsets.dtype()
188 );
189
190 vortex_ensure!(
192 matches!(dtype, DType::Binary(_) | DType::Utf8(_)),
193 MismatchedTypes: "utf8 or binary", dtype
194 );
195
196 vortex_ensure!(
198 dtype.is_nullable() != matches!(validity, Validity::NonNullable),
199 InvalidArgument: "incorrect validity {:?} for dtype {}",
200 validity,
201 dtype
202 );
203
204 vortex_ensure!(
206 !offsets.is_empty(),
207 InvalidArgument: "Offsets must have at least one element"
208 );
209
210 if let Some(validity_len) = validity.maybe_len() {
212 vortex_ensure!(
213 validity_len == offsets.len() - 1,
214 "Validity length {} doesn't match array length {}",
215 validity_len,
216 offsets.len() - 1
217 );
218 }
219
220 if offsets.is_host()
222 && bytes.is_on_host()
223 && matches!(dtype, DType::Utf8(_))
224 && let Some(bytes) = bytes.as_host_opt()
225 {
226 Self::validate_utf8(offsets, bytes.as_ref(), validity)?;
227 }
228
229 Ok(())
230 }
231
232 #[allow(clippy::disallowed_methods)]
234 fn validate_utf8(offsets: &ArrayRef, bytes: &[u8], validity: &Validity) -> VortexResult<()> {
235 let validate_at = |i: usize, start: usize, end: usize| -> VortexResult<()> {
236 let string_bytes = &bytes[start..end];
237 simdutf8::basic::from_utf8(string_bytes).map_err(|_| {
238 #[expect(clippy::unwrap_used)]
239 let err = simdutf8::compat::from_utf8(string_bytes).unwrap_err();
241 vortex_err!("invalid utf-8: {err} at index {i}")
242 })?;
243 Ok(())
244 };
245
246 let mut ctx = legacy_session().create_execution_ctx();
247 let primitive_offsets = offsets.clone().execute::<PrimitiveArray>(&mut ctx)?;
249
250 let mask = match validity {
254 Validity::Array(_) => {
255 Some(validity.execute_mask(primitive_offsets.len().saturating_sub(1), &mut ctx)?)
256 }
257 _ => None,
258 };
259 let all_invalid = validity.definitely_all_null();
260
261 match_each_integer_ptype!(primitive_offsets.dtype().as_ptype(), |O| {
262 let offsets_slice = primitive_offsets.as_slice::<O>();
263
264 let last_offset: usize = offsets_slice[offsets_slice.len() - 1].as_();
265 vortex_ensure!(
266 last_offset <= bytes.len(),
267 InvalidArgument: "Last offset {} exceeds bytes length {}",
268 last_offset,
269 bytes.len()
270 );
271
272 for (i, (start, end)) in offsets_slice
273 .windows(2)
274 .map(|o| (o[0].as_(), o[1].as_()))
275 .enumerate()
276 {
277 let valid = mask.as_ref().map_or(!all_invalid, |mask| mask.value(i));
278 if valid {
279 validate_at(i, start, end)?;
280 }
281 }
282 });
283 Ok(())
284 }
285
286 #[inline]
294 pub fn bytes(&self) -> &ByteBuffer {
295 self.bytes.as_host()
296 }
297
298 #[inline]
300 pub fn bytes_handle(&self) -> &BufferHandle {
301 &self.bytes
302 }
303}
304
305pub trait VarBinArrayExt: TypedArrayRef<VarBin> {
306 fn offsets(&self) -> &ArrayRef {
307 self.as_ref().slots()[OFFSETS_SLOT]
308 .as_ref()
309 .vortex_expect("VarBinArray offsets slot")
310 }
311
312 fn validity_child(&self) -> Option<&ArrayRef> {
313 self.as_ref().slots()[VALIDITY_SLOT].as_ref()
314 }
315
316 fn dtype_parts(&self) -> (bool, Nullability) {
317 match self.as_ref().dtype() {
318 DType::Utf8(nullability) => (true, *nullability),
319 DType::Binary(nullability) => (false, *nullability),
320 _ => unreachable!("VarBinArrayExt requires a utf8 or binary dtype"),
321 }
322 }
323
324 fn is_utf8(&self) -> bool {
325 self.dtype_parts().0
326 }
327
328 fn nullability(&self) -> Nullability {
329 self.dtype_parts().1
330 }
331
332 fn varbin_validity(&self) -> Validity {
333 child_to_validity(
334 self.as_ref().slots()[VALIDITY_SLOT].as_ref(),
335 self.nullability(),
336 )
337 }
338
339 #[allow(clippy::disallowed_methods)]
340 fn offset_at(&self, index: usize) -> usize {
341 assert!(
342 index <= self.as_ref().len(),
343 "Index {index} out of bounds 0..={}",
344 self.as_ref().len()
345 );
346
347 (&self
348 .offsets()
349 .execute_scalar(index, &mut legacy_session().create_execution_ctx())
350 .vortex_expect("offsets must support execute_scalar"))
351 .try_into()
352 .vortex_expect("Failed to convert offset to usize")
353 }
354
355 fn bytes_at(&self, index: usize) -> ByteBuffer {
356 let start = self.offset_at(index);
357 let end = self.offset_at(index + 1);
358 self.bytes().slice(start..end)
359 }
360
361 fn sliced_bytes(&self) -> ByteBuffer {
362 let first_offset: usize = self.offset_at(0);
363 let last_offset = self.offset_at(self.as_ref().len());
364 self.bytes().slice(first_offset..last_offset)
365 }
366}
367impl<T: TypedArrayRef<VarBin>> VarBinArrayExt for T {}
368
369impl Array<VarBin> {
371 pub fn from_vec<T: AsRef<[u8]>>(vec: Vec<T>, dtype: DType) -> Self {
372 let size: usize = vec.iter().map(|v| v.as_ref().len()).sum();
373 if size < u32::MAX as usize {
374 Self::from_vec_sized::<u32, T>(vec, dtype)
375 } else {
376 Self::from_vec_sized::<u64, T>(vec, dtype)
377 }
378 }
379
380 #[expect(
381 clippy::same_name_method,
382 reason = "intentionally named from_iter like Iterator::from_iter"
383 )]
384 pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
385 iter: I,
386 dtype: DType,
387 ) -> Self {
388 let iter = iter.into_iter();
389 let mut builder = VarBinBuilder::<u32>::with_capacity(iter.size_hint().0);
390 for v in iter {
391 builder.append(v.as_ref().map(|o| o.as_ref()));
392 }
393 builder.finish(dtype)
394 }
395
396 pub fn from_iter_nonnull<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(
397 iter: I,
398 dtype: DType,
399 ) -> Self {
400 let iter = iter.into_iter();
401 let mut builder = VarBinBuilder::<u32>::with_capacity(iter.size_hint().0);
402 for v in iter {
403 builder.append_value(v);
404 }
405 builder.finish(dtype)
406 }
407
408 fn from_vec_sized<O, T>(vec: Vec<T>, dtype: DType) -> Self
409 where
410 O: IntegerPType,
411 T: AsRef<[u8]>,
412 {
413 let mut builder = VarBinBuilder::<O>::with_capacity(vec.len());
414 for v in vec {
415 builder.append_value(v.as_ref());
416 }
417 builder.finish(dtype)
418 }
419
420 pub fn from_strs(value: Vec<&str>) -> Self {
422 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
423 }
424
425 pub fn from_nullable_strs(value: Vec<Option<&str>>) -> Self {
427 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
428 }
429
430 pub fn from_bytes(value: Vec<&[u8]>) -> Self {
432 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
433 }
434
435 pub fn from_nullable_bytes(value: Vec<Option<&[u8]>>) -> Self {
437 Self::from_iter(value, DType::Binary(Nullability::Nullable))
438 }
439
440 pub fn into_data_parts(self) -> VarBinDataParts {
441 let dtype = self.dtype().clone();
442 let validity = self.varbin_validity();
443 let offsets = self.offsets().clone();
444 let data = self.into_data();
445 VarBinDataParts {
446 dtype,
447 bytes: data.bytes,
448 offsets,
449 validity,
450 }
451 }
452}
453
454impl Array<VarBin> {
455 pub fn new(offsets: ArrayRef, bytes: ByteBuffer, dtype: DType, validity: Validity) -> Self {
457 let len = offsets.len().saturating_sub(1);
458 let slots = VarBinData::make_slots(offsets, &validity, len);
459 let data = VarBinData::build(
460 slots[OFFSETS_SLOT]
461 .as_ref()
462 .vortex_expect("VarBinArray offsets slot")
463 .clone(),
464 bytes,
465 dtype.clone(),
466 validity,
467 );
468 unsafe {
469 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
470 }
471 }
472
473 pub unsafe fn new_unchecked(
479 offsets: ArrayRef,
480 bytes: ByteBuffer,
481 dtype: DType,
482 validity: Validity,
483 ) -> Self {
484 let len = offsets.len().saturating_sub(1);
485 let slots = VarBinData::make_slots(offsets, &validity, len);
486 let data = unsafe { VarBinData::new_unchecked(bytes) };
487 unsafe {
488 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
489 }
490 }
491
492 pub unsafe fn new_unchecked_from_handle(
498 offsets: ArrayRef,
499 bytes: BufferHandle,
500 dtype: DType,
501 validity: Validity,
502 ) -> Self {
503 let len = offsets.len().saturating_sub(1);
504 let slots = VarBinData::make_slots(offsets, &validity, len);
505 let data = unsafe { VarBinData::new_unchecked_from_handle(bytes) };
506 unsafe {
507 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
508 }
509 }
510
511 pub fn try_new(
513 offsets: ArrayRef,
514 bytes: ByteBuffer,
515 dtype: DType,
516 validity: Validity,
517 ) -> VortexResult<Self> {
518 let len = offsets.len() - 1;
519 let bytes = BufferHandle::new_host(bytes);
520 VarBinData::validate(&offsets, &bytes, &dtype, &validity)?;
521 let slots = VarBinData::make_slots(offsets, &validity, len);
522 let data = unsafe { VarBinData::new_unchecked_from_handle(bytes) };
524 Ok(unsafe {
525 Array::from_parts_unchecked(ArrayParts::new(VarBin, dtype, len, data).with_slots(slots))
526 })
527 }
528}
529
530impl From<Vec<&[u8]>> for Array<VarBin> {
531 fn from(value: Vec<&[u8]>) -> Self {
532 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
533 }
534}
535
536impl From<Vec<Vec<u8>>> for Array<VarBin> {
537 fn from(value: Vec<Vec<u8>>) -> Self {
538 Self::from_vec(value, DType::Binary(Nullability::NonNullable))
539 }
540}
541
542impl From<Vec<String>> for Array<VarBin> {
543 fn from(value: Vec<String>) -> Self {
544 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
545 }
546}
547
548impl From<Vec<&str>> for Array<VarBin> {
549 fn from(value: Vec<&str>) -> Self {
550 Self::from_vec(value, DType::Utf8(Nullability::NonNullable))
551 }
552}
553
554impl From<Vec<Option<&[u8]>>> for Array<VarBin> {
555 fn from(value: Vec<Option<&[u8]>>) -> Self {
556 Self::from_iter(value, DType::Binary(Nullability::Nullable))
557 }
558}
559
560impl From<Vec<Option<Vec<u8>>>> for Array<VarBin> {
561 fn from(value: Vec<Option<Vec<u8>>>) -> Self {
562 Self::from_iter(value, DType::Binary(Nullability::Nullable))
563 }
564}
565
566impl From<Vec<Option<String>>> for Array<VarBin> {
567 fn from(value: Vec<Option<String>>) -> Self {
568 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
569 }
570}
571
572impl From<Vec<Option<&str>>> for Array<VarBin> {
573 fn from(value: Vec<Option<&str>>) -> Self {
574 Self::from_iter(value, DType::Utf8(Nullability::Nullable))
575 }
576}
577
578impl<'a> FromIterator<Option<&'a [u8]>> for Array<VarBin> {
579 fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
580 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
581 }
582}
583
584impl FromIterator<Option<Vec<u8>>> for Array<VarBin> {
585 fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
586 Self::from_iter(iter, DType::Binary(Nullability::Nullable))
587 }
588}
589
590impl FromIterator<Option<String>> for Array<VarBin> {
591 fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
592 Self::from_iter(iter, DType::Utf8(Nullability::Nullable))
593 }
594}
595
596impl<'a> FromIterator<Option<&'a str>> for Array<VarBin> {
597 fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
598 Self::from_iter(iter, DType::Utf8(Nullability::Nullable))
599 }
600}