1use std::{
18 ops::Range,
19 sync::{Arc, RwLock},
20};
21
22use arrow_array::{
23 Array, ArrayRef, OffsetSizeTrait, UInt64Array,
24 cast::AsArray,
25 new_empty_array, new_null_array,
26 types::{ArrowDictionaryKeyType, UInt8Type, UInt16Type, UInt32Type, UInt64Type},
27};
28use arrow_buffer::{ArrowNativeType, BooleanBuffer, BooleanBufferBuilder, NullBuffer};
29use arrow_data::{ArrayData, ArrayDataBuilder};
30use arrow_schema::DataType;
31use lance_arrow::DataTypeExt;
32
33use lance_core::{Error, Result};
34
35use crate::{
36 buffer::LanceBuffer,
37 statistics::{ComputeStat, Stat},
38};
39
40#[derive(Debug, Clone)]
46pub struct AllNullDataBlock {
47 pub num_values: u64,
49}
50
51impl AllNullDataBlock {
52 fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
53 Ok(ArrayData::new_null(&data_type, self.num_values as usize))
54 }
55
56 fn into_buffers(self) -> Vec<LanceBuffer> {
57 vec![]
58 }
59}
60
61use std::collections::HashMap;
62
63#[derive(Debug, Clone)]
66pub struct BlockInfo(pub Arc<RwLock<HashMap<Stat, Arc<dyn Array>>>>);
67
68impl Default for BlockInfo {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl BlockInfo {
75 pub fn new() -> Self {
76 Self(Arc::new(RwLock::new(HashMap::new())))
77 }
78}
79
80impl PartialEq for BlockInfo {
81 fn eq(&self, other: &Self) -> bool {
82 let self_info = self.0.read().unwrap();
83 let other_info = other.0.read().unwrap();
84 *self_info == *other_info
85 }
86}
87
88#[derive(Debug, Clone)]
94pub struct NullableDataBlock {
95 pub data: Box<DataBlock>,
97 pub nulls: LanceBuffer,
99
100 pub block_info: BlockInfo,
101}
102
103impl NullableDataBlock {
104 fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
105 if data_type == DataType::Null {
106 return Ok(ArrayData::new_null(
108 &data_type,
109 self.data.num_values() as usize,
110 ));
111 }
112 let nulls = self.nulls.into_buffer();
113 let data = self.data.into_arrow_impl(data_type, true)?.into_builder();
114 let data = data.null_bit_buffer(Some(nulls));
115 Ok(data.build()?)
116 }
117
118 fn into_buffers(self) -> Vec<LanceBuffer> {
119 let mut buffers = vec![self.nulls];
120 buffers.extend(self.data.into_buffers());
121 buffers
122 }
123
124 pub fn data_size(&self) -> u64 {
125 self.data.data_size() + self.nulls.len() as u64
126 }
127}
128
129#[derive(Debug, PartialEq, Clone)]
131pub struct ConstantDataBlock {
132 pub data: LanceBuffer,
134 pub num_values: u64,
136}
137
138impl ConstantDataBlock {
139 fn into_buffers(self) -> Vec<LanceBuffer> {
140 vec![self.data]
141 }
142
143 fn into_arrow(self, _data_type: DataType, _validate: bool) -> Result<ArrayData> {
144 todo!()
147 }
148
149 pub fn try_clone(&self) -> Result<Self> {
150 Ok(Self {
151 data: self.data.clone(),
152 num_values: self.num_values,
153 })
154 }
155
156 pub fn data_size(&self) -> u64 {
157 self.data.len() as u64
158 }
159}
160
161#[derive(Debug, PartialEq, Clone)]
163pub struct FixedWidthDataBlock {
164 pub data: LanceBuffer,
166 pub bits_per_value: u64,
168 pub num_values: u64,
170
171 pub block_info: BlockInfo,
172}
173
174impl FixedWidthDataBlock {
175 fn do_into_arrow(
176 self,
177 data_type: DataType,
178 num_values: u64,
179 _validate: bool,
180 ) -> Result<ArrayData> {
181 let data_buffer = if matches!(data_type, DataType::Boolean) && self.bits_per_value == 8 {
184 let mut builder = BooleanBufferBuilder::new(num_values as usize);
185 for &byte in self.data.as_ref().iter().take(num_values as usize) {
186 builder.append(byte != 0);
187 }
188 builder.finish().into_inner()
189 } else {
190 self.data.into_buffer()
191 };
192 let builder = ArrayDataBuilder::new(data_type)
193 .add_buffer(data_buffer)
194 .len(num_values as usize)
195 .null_count(0);
196 Ok(builder.build()?)
197 }
198
199 pub fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
204 let root_num_values = self.num_values;
205 self.do_into_arrow(data_type, root_num_values, true)
206 }
207
208 pub fn into_buffers(self) -> Vec<LanceBuffer> {
209 vec![self.data]
210 }
211
212 pub fn try_clone(&self) -> Result<Self> {
213 Ok(Self {
214 data: self.data.clone(),
215 bits_per_value: self.bits_per_value,
216 num_values: self.num_values,
217 block_info: self.block_info.clone(),
218 })
219 }
220
221 pub fn data_size(&self) -> u64 {
222 self.data.len() as u64
223 }
224}
225
226#[derive(Debug)]
227struct VariableWidthDataBlockBuilder<T: OffsetSizeTrait> {
228 offsets: Vec<T>,
229 bytes: Vec<u8>,
230}
231
232impl<T: OffsetSizeTrait> VariableWidthDataBlockBuilder<T> {
233 fn new(estimated_size_bytes: u64) -> Self {
234 Self {
235 offsets: vec![T::from_usize(0).unwrap()],
236 bytes: Vec::with_capacity(estimated_size_bytes as usize),
237 }
238 }
239}
240
241impl<T: OffsetSizeTrait + bytemuck::Pod> DataBlockBuilderImpl for VariableWidthDataBlockBuilder<T> {
242 fn validate_append(&self, data_block: &DataBlock, selection: &Range<u64>) -> Result<()> {
243 let block = data_block.as_variable_width_ref().unwrap();
244 block.validate_offsets_for_append::<T>(selection)
245 }
246
247 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
248 let block = data_block.as_variable_width_ref().unwrap();
249 debug_assert_eq!(block.bits_per_offset, T::get_byte_width() as u8 * 8);
250 let offsets = block.offsets.borrow_to_typed_view::<T>();
251
252 let start_offset = offsets[selection.start as usize];
253 let end_offset = offsets[selection.end as usize];
254 let selected_data_len = end_offset.as_usize() - start_offset.as_usize();
255 let new_data_len = self
256 .bytes
257 .len()
258 .checked_add(selected_data_len)
259 .ok_or_else(|| {
260 Error::not_supported_source(
261 "appending variable-width data would overflow usize".into(),
262 )
263 })?;
264 if T::from_usize(new_data_len).is_none() {
265 return Err(Error::not_supported_source(
266 format!(
267 "appending variable-width data would require {} bytes, which exceeds the \
268 capacity of {}-bit offsets",
269 new_data_len,
270 T::get_byte_width() * 8
271 )
272 .into(),
273 ));
274 }
275 let previous_len = self.bytes.len();
276
277 self.bytes
278 .extend_from_slice(&block.data[start_offset.as_usize()..end_offset.as_usize()]);
279
280 self.offsets.extend(
281 offsets[selection.start as usize + 1..=selection.end as usize]
282 .iter()
283 .map(|&offset| {
284 let rebased_offset =
285 previous_len + (offset.as_usize() - start_offset.as_usize());
286 T::from_usize(rebased_offset).unwrap()
287 }),
288 );
289 Ok(())
290 }
291
292 fn finish(self: Box<Self>) -> DataBlock {
293 let num_values = (self.offsets.len() - 1) as u64;
294 DataBlock::VariableWidth(VariableWidthBlock {
295 data: LanceBuffer::from(self.bytes),
296 offsets: LanceBuffer::reinterpret_vec(self.offsets),
297 bits_per_offset: T::get_byte_width() as u8 * 8,
298 num_values,
299 block_info: BlockInfo::new(),
300 })
301 }
302}
303
304#[derive(Debug)]
305struct BitmapDataBlockBuilder {
306 values: BooleanBufferBuilder,
307}
308
309impl BitmapDataBlockBuilder {
310 fn new(estimated_size_bytes: u64) -> Self {
311 Self {
312 values: BooleanBufferBuilder::new(estimated_size_bytes as usize * 8),
313 }
314 }
315}
316
317impl DataBlockBuilderImpl for BitmapDataBlockBuilder {
318 fn validate_append(&self, _data_block: &DataBlock, _selection: &Range<u64>) -> Result<()> {
319 Ok(())
320 }
321
322 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
323 let bitmap_blk = data_block.as_fixed_width_ref().unwrap();
324 self.values.append_packed_range(
325 selection.start as usize..selection.end as usize,
326 &bitmap_blk.data,
327 );
328 Ok(())
329 }
330
331 fn finish(mut self: Box<Self>) -> DataBlock {
332 let bool_buf = self.values.finish();
333 let num_values = bool_buf.len() as u64;
334 let bits_buf = bool_buf.into_inner();
335 DataBlock::FixedWidth(FixedWidthDataBlock {
336 data: LanceBuffer::from(bits_buf),
337 bits_per_value: 1,
338 num_values,
339 block_info: BlockInfo::new(),
340 })
341 }
342}
343
344#[derive(Debug)]
345struct FixedWidthDataBlockBuilder {
346 bits_per_value: u64,
347 bytes_per_value: u64,
348 values: Vec<u8>,
349}
350
351impl FixedWidthDataBlockBuilder {
352 fn new(bits_per_value: u64, estimated_size_bytes: u64) -> Self {
353 assert!(bits_per_value.is_multiple_of(8));
354 Self {
355 bits_per_value,
356 bytes_per_value: bits_per_value / 8,
357 values: Vec::with_capacity(estimated_size_bytes as usize),
358 }
359 }
360}
361
362impl DataBlockBuilderImpl for FixedWidthDataBlockBuilder {
363 fn validate_append(&self, _data_block: &DataBlock, _selection: &Range<u64>) -> Result<()> {
364 Ok(())
365 }
366
367 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
368 let block = data_block.as_fixed_width_ref().unwrap();
369 assert_eq!(self.bits_per_value, block.bits_per_value);
370 let start = selection.start as usize * self.bytes_per_value as usize;
371 let end = selection.end as usize * self.bytes_per_value as usize;
372 self.values.extend_from_slice(&block.data[start..end]);
373 Ok(())
374 }
375
376 fn finish(self: Box<Self>) -> DataBlock {
377 let num_values = (self.values.len() / self.bytes_per_value as usize) as u64;
378 DataBlock::FixedWidth(FixedWidthDataBlock {
379 data: LanceBuffer::from(self.values),
380 bits_per_value: self.bits_per_value,
381 num_values,
382 block_info: BlockInfo::new(),
383 })
384 }
385}
386
387#[derive(Debug)]
388struct StructDataBlockBuilder {
389 children: Vec<Box<dyn DataBlockBuilderImpl>>,
390}
391
392impl StructDataBlockBuilder {
393 fn new(children: Vec<Box<dyn DataBlockBuilderImpl>>) -> Self {
394 Self { children }
395 }
396}
397
398impl DataBlockBuilderImpl for StructDataBlockBuilder {
399 fn validate_append(&self, data_block: &DataBlock, selection: &Range<u64>) -> Result<()> {
400 let data_block = data_block.as_struct_ref().unwrap();
401 for i in 0..self.children.len() {
402 self.children[i].validate_append(&data_block.children[i], selection)?;
403 }
404 Ok(())
405 }
406
407 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
408 let data_block = data_block.as_struct_ref().unwrap();
409 for i in 0..self.children.len() {
410 self.children[i].append_validated(&data_block.children[i], selection.clone())?;
411 }
412 Ok(())
413 }
414
415 fn finish(self: Box<Self>) -> DataBlock {
416 let mut children_data_block = Vec::new();
417 for child in self.children {
418 let child_data_block = child.finish();
419 children_data_block.push(child_data_block);
420 }
421 DataBlock::Struct(StructDataBlock {
422 children: children_data_block,
423 block_info: BlockInfo::new(),
424 validity: None,
425 })
426 }
427}
428
429#[derive(Debug, Default)]
430struct AllNullDataBlockBuilder {
431 num_values: u64,
432}
433
434impl DataBlockBuilderImpl for AllNullDataBlockBuilder {
435 fn validate_append(&self, _data_block: &DataBlock, _selection: &Range<u64>) -> Result<()> {
436 Ok(())
437 }
438
439 fn append_validated(&mut self, _data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
440 self.num_values += selection.end - selection.start;
441 Ok(())
442 }
443
444 fn finish(self: Box<Self>) -> DataBlock {
445 DataBlock::AllNull(AllNullDataBlock {
446 num_values: self.num_values,
447 })
448 }
449}
450
451#[derive(Debug, Clone)]
453pub struct FixedSizeListBlock {
454 pub child: Box<DataBlock>,
456 pub dimension: u64,
458}
459
460impl FixedSizeListBlock {
461 pub fn num_values(&self) -> u64 {
462 self.child.num_values() / self.dimension
463 }
464
465 pub fn try_into_flat(self) -> Option<FixedWidthDataBlock> {
469 match *self.child {
470 DataBlock::Nullable(_) => None,
472 DataBlock::FixedSizeList(inner) => {
473 let mut flat = inner.try_into_flat()?;
474 flat.bits_per_value *= self.dimension;
475 flat.num_values /= self.dimension;
476 Some(flat)
477 }
478 DataBlock::FixedWidth(mut inner) => {
479 inner.bits_per_value *= self.dimension;
480 inner.num_values /= self.dimension;
481 Some(inner)
482 }
483 _ => panic!(
484 "Expected FixedSizeList or FixedWidth data block but found {:?}",
485 self
486 ),
487 }
488 }
489
490 pub fn flatten_as_fixed(&mut self) -> FixedWidthDataBlock {
491 match self.child.as_mut() {
492 DataBlock::FixedSizeList(fsl) => fsl.flatten_as_fixed(),
493 DataBlock::FixedWidth(fw) => fw.clone(),
494 _ => panic!("Expected FixedSizeList or FixedWidth data block"),
495 }
496 }
497
498 pub fn from_flat(data: FixedWidthDataBlock, data_type: &DataType) -> DataBlock {
500 match data_type {
501 DataType::FixedSizeList(child_field, dimension) => {
502 let mut data = data;
503 data.bits_per_value /= *dimension as u64;
504 data.num_values *= *dimension as u64;
505 let child_data = Self::from_flat(data, child_field.data_type());
506 DataBlock::FixedSizeList(Self {
507 child: Box::new(child_data),
508 dimension: *dimension as u64,
509 })
510 }
511 _ => DataBlock::FixedWidth(data),
513 }
514 }
515
516 fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
517 let num_values = self.num_values();
518 let builder = match &data_type {
519 DataType::FixedSizeList(child_field, _) => {
520 let child_data = self
521 .child
522 .into_arrow_impl(child_field.data_type().clone(), true)?;
523 ArrayDataBuilder::new(data_type)
524 .add_child_data(child_data)
525 .len(num_values as usize)
526 .null_count(0)
527 }
528 _ => panic!("Expected FixedSizeList data type and got {:?}", data_type),
529 };
530 Ok(builder.build()?)
531 }
532
533 fn into_buffers(self) -> Vec<LanceBuffer> {
534 self.child.into_buffers()
535 }
536
537 fn data_size(&self) -> u64 {
538 self.child.data_size()
539 }
540}
541
542#[derive(Debug)]
543struct FixedSizeListBlockBuilder {
544 inner: Box<dyn DataBlockBuilderImpl>,
545 dimension: u64,
546}
547
548impl FixedSizeListBlockBuilder {
549 fn new(inner: Box<dyn DataBlockBuilderImpl>, dimension: u64) -> Self {
550 Self { inner, dimension }
551 }
552}
553
554impl DataBlockBuilderImpl for FixedSizeListBlockBuilder {
555 fn validate_append(&self, data_block: &DataBlock, selection: &Range<u64>) -> Result<()> {
556 let selection = selection.start * self.dimension..selection.end * self.dimension;
557 let fsl = data_block.as_fixed_size_list_ref().unwrap();
558 self.inner.validate_append(fsl.child.as_ref(), &selection)
559 }
560
561 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
562 let selection = selection.start * self.dimension..selection.end * self.dimension;
563 let fsl = data_block.as_fixed_size_list_ref().unwrap();
564 self.inner.append_validated(fsl.child.as_ref(), selection)
565 }
566
567 fn finish(self: Box<Self>) -> DataBlock {
568 let inner_block = self.inner.finish();
569 DataBlock::FixedSizeList(FixedSizeListBlock {
570 child: Box::new(inner_block),
571 dimension: self.dimension,
572 })
573 }
574}
575
576#[derive(Debug)]
577struct NullableDataBlockBuilder {
578 inner: Box<dyn DataBlockBuilderImpl>,
579 validity: BooleanBufferBuilder,
580}
581
582impl NullableDataBlockBuilder {
583 fn new(inner: Box<dyn DataBlockBuilderImpl>, estimated_size_bytes: usize) -> Self {
584 Self {
585 inner,
586 validity: BooleanBufferBuilder::new(estimated_size_bytes * 8),
587 }
588 }
589}
590
591impl DataBlockBuilderImpl for NullableDataBlockBuilder {
592 fn validate_append(&self, data_block: &DataBlock, selection: &Range<u64>) -> Result<()> {
593 let nullable = data_block.as_nullable_ref().unwrap();
594 self.inner
595 .validate_append(nullable.data.as_ref(), selection)
596 }
597
598 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
599 let nullable = data_block.as_nullable_ref().unwrap();
600 self.inner
601 .append_validated(nullable.data.as_ref(), selection.clone())?;
602 let bool_buf = BooleanBuffer::new(
603 nullable.nulls.clone().into_buffer(),
604 selection.start as usize,
605 (selection.end - selection.start) as usize,
606 );
607 self.validity.append_buffer(&bool_buf);
608 Ok(())
609 }
610
611 fn finish(mut self: Box<Self>) -> DataBlock {
612 let inner_block = self.inner.finish();
613 DataBlock::Nullable(NullableDataBlock {
614 data: Box::new(inner_block),
615 nulls: LanceBuffer::from(self.validity.finish().into_inner()),
616 block_info: BlockInfo::new(),
617 })
618 }
619}
620
621#[derive(Debug, Clone)]
625pub struct OpaqueBlock {
626 pub buffers: Vec<LanceBuffer>,
627 pub num_values: u64,
628 pub block_info: BlockInfo,
629}
630
631impl OpaqueBlock {
632 pub fn data_size(&self) -> u64 {
633 self.buffers.iter().map(|b| b.len() as u64).sum()
634 }
635}
636
637#[derive(Debug, Clone)]
639pub struct VariableWidthBlock {
640 pub data: LanceBuffer,
642 pub offsets: LanceBuffer,
646 pub bits_per_offset: u8,
648 pub num_values: u64,
650
651 pub block_info: BlockInfo,
652}
653
654struct ValidVariableWidthLayout;
662
663impl VariableWidthBlock {
664 fn append_error(&self, selection: &Range<u64>, detail: impl std::fmt::Display) -> Error {
665 Error::corrupt_file_named(
666 "variable width data block",
667 format!(
668 "cannot append offsets for selection {}..{}: {} (num_values: {}, \
669 bits_per_offset: {}, offsets buffer size: {} bytes, data buffer size: {} bytes)",
670 selection.start,
671 selection.end,
672 detail,
673 self.num_values,
674 self.bits_per_offset,
675 self.offsets.len(),
676 self.data.len(),
677 ),
678 )
679 }
680
681 fn validate_offsets_for_append<T>(&self, selection: &Range<u64>) -> Result<()>
682 where
683 T: OffsetSizeTrait + bytemuck::Pod,
684 {
685 let expected_bits_per_offset = T::get_byte_width() as u8 * 8;
686 if self.bits_per_offset != expected_bits_per_offset {
687 return Err(self.append_error(
688 selection,
689 format!(
690 "expected {}-bit offsets but found {}-bit offsets",
691 expected_bits_per_offset, self.bits_per_offset
692 ),
693 ));
694 }
695 let offset_size = std::mem::size_of::<T>();
696 if !self.offsets.len().is_multiple_of(offset_size) {
697 return Err(self.append_error(
698 selection,
699 format!(
700 "offsets buffer length {} is not a multiple of the {}-byte offset width",
701 self.offsets.len(),
702 offset_size
703 ),
704 ));
705 }
706 if selection.start > selection.end || selection.end > self.num_values {
707 return Err(
708 self.append_error(selection, "selection is outside the block's value range")
709 );
710 }
711 let selection_start = usize::try_from(selection.start)
712 .map_err(|_| self.append_error(selection, "selection start does not fit in usize"))?;
713 let selection_end = usize::try_from(selection.end)
714 .map_err(|_| self.append_error(selection, "selection end does not fit in usize"))?;
715 let offsets = self.offsets.borrow_to_typed_view::<T>();
716 if selection_end >= offsets.len() {
717 return Err(self.append_error(
718 selection,
719 format!(
720 "selection requires offset {} but the buffer holds {} offsets",
721 selection_end,
722 offsets.len()
723 ),
724 ));
725 }
726 let selected_offsets = &offsets[selection_start..=selection_end];
727 if let Some(detail) =
728 Self::offset_violation_detail(selected_offsets, self.data.len(), selection_start)
729 {
730 return Err(self.append_error(selection, detail));
731 }
732 Ok(())
733 }
734
735 fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
743 let Some(expected_bits_per_offset) = Self::expected_bits_per_offset(&data_type) else {
744 return self.into_arrow_checked(data_type);
747 };
748 if self.bits_per_offset != expected_bits_per_offset {
749 return Err(self.layout_error(
750 &data_type,
751 format!(
752 "expected {}-bit offsets but got {}-bit offsets",
753 expected_bits_per_offset, self.bits_per_offset
754 ),
755 ));
756 }
757 if self.num_values == 0 {
758 return self.into_arrow_checked(data_type);
760 }
761 let proof = self.validate_layout(&data_type)?;
762 Ok(self.into_arrow_unchecked(data_type, proof))
763 }
764
765 fn expected_bits_per_offset(data_type: &DataType) -> Option<u8> {
768 match data_type {
769 DataType::Binary | DataType::Utf8 => Some(32),
770 DataType::LargeBinary | DataType::LargeUtf8 => Some(64),
771 _ => None,
772 }
773 }
774
775 fn layout_error(&self, data_type: &DataType, detail: impl std::fmt::Display) -> Error {
776 Self::format_layout_error(
777 data_type,
778 detail,
779 self.num_values,
780 self.bits_per_offset,
781 self.offsets.len(),
782 self.data.len(),
783 )
784 }
785
786 fn format_layout_error(
787 data_type: &DataType,
788 detail: impl std::fmt::Display,
789 num_values: u64,
790 bits_per_offset: u8,
791 offsets_size: usize,
792 data_size: usize,
793 ) -> Error {
794 Error::corrupt_file_named(
795 "variable width data block",
796 format!(
797 "invalid variable-width layout for {}: {} (num_values: {}, bits_per_offset: {}, \
798 offsets buffer size: {} bytes, data buffer size: {} bytes)",
799 data_type, detail, num_values, bits_per_offset, offsets_size, data_size,
800 ),
801 )
802 }
803
804 fn validate_layout(&self, data_type: &DataType) -> Result<ValidVariableWidthLayout> {
805 let bytes_per_offset = (self.bits_per_offset / 8) as u64;
806 let required_bytes = self
807 .num_values
808 .checked_add(1)
809 .and_then(|num_offsets| num_offsets.checked_mul(bytes_per_offset))
810 .ok_or_else(|| self.layout_error(data_type, "offsets buffer size overflows"))?;
811 if (self.offsets.len() as u64) < required_bytes {
812 return Err(self.layout_error(
813 data_type,
814 format!(
815 "offsets buffer must hold at least {} offsets ({} bytes)",
816 self.num_values + 1,
817 required_bytes
818 ),
819 ));
820 }
821 let validate_utf8 = matches!(data_type, DataType::Utf8 | DataType::LargeUtf8);
822 match self.bits_per_offset {
823 32 => self.validate_offsets_and_values::<i32>(data_type, validate_utf8),
824 64 => self.validate_offsets_and_values::<i64>(data_type, validate_utf8),
825 other => Err(self.layout_error(
826 data_type,
827 format!("unsupported offset width: {} bits", other),
828 )),
829 }
830 }
831
832 fn validate_offsets_and_values<T: ArrowNativeType + Ord>(
833 &self,
834 data_type: &DataType,
835 validate_utf8: bool,
836 ) -> Result<ValidVariableWidthLayout> {
837 let num_offsets = self.num_values as usize + 1;
838 let offsets = self
841 .offsets
842 .slice_with_length(0, num_offsets * std::mem::size_of::<T>());
843 let offsets = offsets.borrow_to_typed_slice::<T>();
844 let offsets: &[T] = offsets.as_ref();
845 let data = self.data.as_ref();
846
847 if let Some(detail) = Self::offset_violation_detail(offsets, data.len(), 0) {
848 return Err(self.layout_error(data_type, detail));
849 }
850
851 if validate_utf8 {
852 let (first, last) = (offsets[0].as_usize(), offsets[num_offsets - 1].as_usize());
853 let values = std::str::from_utf8(&data[first..last])
854 .map_err(|utf8_err| self.layout_error(data_type, utf8_err))?;
855 let mut on_char_boundaries = true;
856 for &offset in offsets {
857 on_char_boundaries &= values.is_char_boundary(offset.as_usize() - first);
858 }
859 if !on_char_boundaries {
860 let position = offsets
862 .iter()
863 .position(|offset| !values.is_char_boundary(offset.as_usize() - first))
864 .expect("the fast scan found a non-boundary offset");
865 return Err(self.layout_error(
866 data_type,
867 format!("offset at position {position} splits a UTF-8 character"),
868 ));
869 }
870 }
871
872 Ok(ValidVariableWidthLayout)
873 }
874
875 fn offset_violation_detail<T: ArrowNativeType + Ord>(
876 offsets: &[T],
877 data_size: usize,
878 position_base: usize,
879 ) -> Option<String> {
880 let mut is_monotonic = true;
884 for window in offsets.windows(2) {
885 is_monotonic &= window[0] <= window[1];
886 }
887 let first = offsets[0];
888 let last = offsets[offsets.len() - 1];
889 let bounds_ok =
890 first >= T::usize_as(0) && last.to_usize().is_some_and(|last| last <= data_size);
891 if is_monotonic && bounds_ok {
892 return None;
893 }
894
895 for (relative_position, window) in offsets.windows(2).enumerate() {
896 if window[0] > window[1] {
897 let position = position_base + relative_position + 1;
898 return Some(format!(
899 "non-monotonic offset at position {}: {:?} decreases from {:?}",
900 position, window[1], window[0]
901 ));
902 }
903 }
904 for (relative_position, offset) in offsets.iter().enumerate() {
905 let position = position_base + relative_position;
906 match offset.to_usize() {
907 None => {
908 return Some(format!(
909 "offset at position {} is negative: {:?}",
910 position, offset
911 ));
912 }
913 Some(offset) if offset > data_size => {
914 return Some(format!(
915 "offset at position {} is out of bounds: {} > {}",
916 position, offset, data_size
917 ));
918 }
919 Some(_) => {}
920 }
921 }
922 Some("offsets failed validation".to_string())
923 }
924
925 fn into_arrow_checked(self, data_type: DataType) -> Result<ArrayData> {
926 let num_values = self.num_values;
927 let bits_per_offset = self.bits_per_offset;
928 let offsets_size = self.offsets.len();
929 let data_size = self.data.len();
930 let builder = self.into_arrow_builder(data_type.clone());
931 builder.build().map_err(|arrow_err| {
932 Self::format_layout_error(
933 &data_type,
934 arrow_err,
935 num_values,
936 bits_per_offset,
937 offsets_size,
938 data_size,
939 )
940 })
941 }
942
943 fn into_arrow_unchecked(
944 self,
945 data_type: DataType,
946 _proof: ValidVariableWidthLayout,
947 ) -> ArrayData {
948 let builder = self.into_arrow_builder(data_type);
949 unsafe { builder.build_unchecked() }
952 }
953
954 fn into_arrow_builder(self, data_type: DataType) -> ArrayDataBuilder {
955 let num_values = self.num_values;
956 let data_buffer = self.data.into_buffer();
957 let offsets_buffer = self.offsets.into_buffer();
958 ArrayDataBuilder::new(data_type)
959 .add_buffer(offsets_buffer)
960 .add_buffer(data_buffer)
961 .len(num_values as usize)
962 .null_count(0)
963 }
964
965 fn into_buffers(self) -> Vec<LanceBuffer> {
966 vec![self.offsets, self.data]
967 }
968
969 pub fn offsets_as_block(&mut self) -> DataBlock {
970 let offsets = self.offsets.clone();
971 DataBlock::FixedWidth(FixedWidthDataBlock {
972 data: offsets,
973 bits_per_value: self.bits_per_offset as u64,
974 num_values: self.num_values + 1,
975 block_info: BlockInfo::new(),
976 })
977 }
978
979 pub fn data_size(&self) -> u64 {
980 (self.data.len() + self.offsets.len()) as u64
981 }
982}
983
984#[derive(Debug, Clone)]
986pub struct StructDataBlock {
987 pub children: Vec<DataBlock>,
989 pub block_info: BlockInfo,
990 pub validity: Option<NullBuffer>,
992}
993
994impl StructDataBlock {
995 fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
996 if let DataType::Struct(fields) = &data_type {
997 let mut builder = ArrayDataBuilder::new(DataType::Struct(fields.clone()));
998 let mut num_rows = 0;
999 for (field, child) in fields.iter().zip(self.children) {
1000 let child_data = child.into_arrow_impl(field.data_type().clone(), true)?;
1001 num_rows = child_data.len();
1002 builder = builder.add_child_data(child_data);
1003 }
1004
1005 let builder = if let Some(validity) = self.validity {
1007 let null_count = validity.null_count();
1008 builder
1009 .null_bit_buffer(Some(validity.into_inner().into_inner()))
1010 .null_count(null_count)
1011 } else {
1012 builder.null_count(0)
1013 };
1014
1015 let builder = builder.len(num_rows);
1016 Ok(builder.build()?)
1017 } else {
1018 Err(Error::internal(format!(
1019 "Expected Struct, got {:?}",
1020 data_type
1021 )))
1022 }
1023 }
1024
1025 fn remove_outer_validity(self) -> Self {
1026 Self {
1027 children: self
1028 .children
1029 .into_iter()
1030 .map(|c| c.remove_outer_validity())
1031 .collect(),
1032 block_info: self.block_info,
1033 validity: None, }
1035 }
1036
1037 fn into_buffers(self) -> Vec<LanceBuffer> {
1038 self.children
1039 .into_iter()
1040 .flat_map(|c| c.into_buffers())
1041 .collect()
1042 }
1043
1044 pub fn has_variable_width_child(&self) -> bool {
1045 self.children
1046 .iter()
1047 .any(|child| !matches!(child, DataBlock::FixedWidth(_)))
1048 }
1049
1050 pub fn data_size(&self) -> u64 {
1051 self.children
1052 .iter()
1053 .map(|data_block| data_block.data_size())
1054 .sum()
1055 }
1056}
1057
1058#[derive(Debug, Clone)]
1060pub struct DictionaryDataBlock {
1061 pub indices: FixedWidthDataBlock,
1063 pub dictionary: Box<DataBlock>,
1065}
1066
1067impl DictionaryDataBlock {
1068 fn decode_helper<K: ArrowDictionaryKeyType>(self) -> Result<DataBlock> {
1069 if self.indices.num_values == 0 {
1072 return Ok(DataBlock::AllNull(AllNullDataBlock { num_values: 0 }));
1073 }
1074
1075 let estimated_size_bytes = self.dictionary.data_size()
1077 * (self.indices.num_values + self.dictionary.num_values() - 1)
1078 / self.dictionary.num_values();
1079 let mut data_builder = DataBlockBuilder::with_capacity_estimate(estimated_size_bytes);
1080
1081 let indices = self.indices.data.borrow_to_typed_slice::<K::Native>();
1082 let indices = indices.as_ref();
1083
1084 let selections = indices.iter().map(|idx| {
1085 let idx = idx.to_usize().unwrap() as u64;
1086 idx..idx + 1
1087 });
1088 data_builder.append_ranges(&self.dictionary, selections)?;
1089
1090 Ok(data_builder.finish())
1091 }
1092
1093 pub fn decode(self) -> Result<DataBlock> {
1094 match self.indices.bits_per_value {
1095 8 => self.decode_helper::<UInt8Type>(),
1096 16 => self.decode_helper::<UInt16Type>(),
1097 32 => self.decode_helper::<UInt32Type>(),
1098 64 => self.decode_helper::<UInt64Type>(),
1099 _ => Err(lance_core::Error::internal(format!(
1100 "Unsupported dictionary index bit width: {} bits",
1101 self.indices.bits_per_value
1102 ))),
1103 }
1104 }
1105
1106 fn into_arrow_dict(
1107 self,
1108 key_type: Box<DataType>,
1109 value_type: Box<DataType>,
1110 _validate: bool,
1111 ) -> Result<ArrayData> {
1112 let declared_key_bits = key_type.byte_width() as u64 * 8;
1113 if self.indices.bits_per_value != declared_key_bits {
1114 return Err(lance_core::Error::corrupt_file_named(
1115 "dictionary",
1116 format!(
1117 "dictionary indices use {} bits but the declared {} key type uses {} bits",
1118 self.indices.bits_per_value, key_type, declared_key_bits
1119 ),
1120 ));
1121 }
1122 let indices_num_values = self.indices.num_values;
1123 let indices = self
1124 .indices
1125 .do_into_arrow((*key_type).clone(), indices_num_values, true)?;
1126 let dictionary = self
1127 .dictionary
1128 .into_arrow_impl((*value_type).clone(), true)?;
1129
1130 let builder = indices
1131 .into_builder()
1132 .add_child_data(dictionary)
1133 .data_type(DataType::Dictionary(key_type, value_type));
1134
1135 Ok(builder.build()?)
1136 }
1137
1138 fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
1139 if let DataType::Dictionary(key_type, value_type) = data_type {
1140 self.into_arrow_dict(key_type, value_type, validate)
1141 } else {
1142 self.decode()?.into_arrow_impl(data_type, validate)
1143 }
1144 }
1145
1146 fn into_buffers(self) -> Vec<LanceBuffer> {
1147 let mut buffers = self.indices.into_buffers();
1148 buffers.extend(self.dictionary.into_buffers());
1149 buffers
1150 }
1151
1152 pub fn into_parts(self) -> (DataBlock, DataBlock) {
1153 (DataBlock::FixedWidth(self.indices), *self.dictionary)
1154 }
1155
1156 pub fn from_parts(indices: FixedWidthDataBlock, dictionary: DataBlock) -> Self {
1157 Self {
1158 indices,
1159 dictionary: Box::new(dictionary),
1160 }
1161 }
1162}
1163
1164#[derive(Debug, Clone)]
1179pub enum DataBlock {
1180 Empty(),
1181 Constant(ConstantDataBlock),
1182 AllNull(AllNullDataBlock),
1183 Nullable(NullableDataBlock),
1184 FixedWidth(FixedWidthDataBlock),
1185 FixedSizeList(FixedSizeListBlock),
1186 VariableWidth(VariableWidthBlock),
1187 Opaque(OpaqueBlock),
1188 Struct(StructDataBlock),
1189 Dictionary(DictionaryDataBlock),
1190}
1191
1192impl DataBlock {
1193 pub fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
1198 self.into_arrow_impl(data_type, true)
1199 }
1200
1201 fn into_arrow_impl(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
1202 match self {
1203 Self::Empty() => Ok(new_empty_array(&data_type).to_data()),
1204 Self::Constant(inner) => inner.into_arrow(data_type, validate),
1205 Self::AllNull(inner) => inner.into_arrow(data_type, validate),
1206 Self::Nullable(inner) => inner.into_arrow(data_type, validate),
1207 Self::FixedWidth(inner) => inner.into_arrow(data_type, validate),
1208 Self::FixedSizeList(inner) => inner.into_arrow(data_type, validate),
1209 Self::VariableWidth(inner) => inner.into_arrow(data_type, validate),
1210 Self::Struct(inner) => inner.into_arrow(data_type, validate),
1211 Self::Dictionary(inner) => inner.into_arrow(data_type, validate),
1212 Self::Opaque(_) => Err(Error::internal(
1213 "Cannot convert OpaqueBlock to Arrow".to_string(),
1214 )),
1215 }
1216 }
1217
1218 pub fn into_buffers(self) -> Vec<LanceBuffer> {
1222 match self {
1223 Self::Empty() => Vec::default(),
1224 Self::Constant(inner) => inner.into_buffers(),
1225 Self::AllNull(inner) => inner.into_buffers(),
1226 Self::Nullable(inner) => inner.into_buffers(),
1227 Self::FixedWidth(inner) => inner.into_buffers(),
1228 Self::FixedSizeList(inner) => inner.into_buffers(),
1229 Self::VariableWidth(inner) => inner.into_buffers(),
1230 Self::Struct(inner) => inner.into_buffers(),
1231 Self::Dictionary(inner) => inner.into_buffers(),
1232 Self::Opaque(inner) => inner.buffers,
1233 }
1234 }
1235
1236 pub fn try_clone(&self) -> Result<Self> {
1245 match self {
1246 Self::Empty() => Ok(Self::Empty()),
1247 Self::Constant(inner) => Ok(Self::Constant(inner.clone())),
1248 Self::AllNull(inner) => Ok(Self::AllNull(inner.clone())),
1249 Self::Nullable(inner) => Ok(Self::Nullable(inner.clone())),
1250 Self::FixedWidth(inner) => Ok(Self::FixedWidth(inner.clone())),
1251 Self::FixedSizeList(inner) => Ok(Self::FixedSizeList(inner.clone())),
1252 Self::VariableWidth(inner) => Ok(Self::VariableWidth(inner.clone())),
1253 Self::Struct(inner) => Ok(Self::Struct(inner.clone())),
1254 Self::Dictionary(inner) => Ok(Self::Dictionary(inner.clone())),
1255 Self::Opaque(inner) => Ok(Self::Opaque(inner.clone())),
1256 }
1257 }
1258
1259 pub fn name(&self) -> &'static str {
1260 match self {
1261 Self::Constant(_) => "Constant",
1262 Self::Empty() => "Empty",
1263 Self::AllNull(_) => "AllNull",
1264 Self::Nullable(_) => "Nullable",
1265 Self::FixedWidth(_) => "FixedWidth",
1266 Self::FixedSizeList(_) => "FixedSizeList",
1267 Self::VariableWidth(_) => "VariableWidth",
1268 Self::Struct(_) => "Struct",
1269 Self::Dictionary(_) => "Dictionary",
1270 Self::Opaque(_) => "Opaque",
1271 }
1272 }
1273
1274 pub fn is_variable(&self) -> bool {
1275 match self {
1276 Self::Constant(_) => false,
1277 Self::Empty() => false,
1278 Self::AllNull(_) => false,
1279 Self::Nullable(nullable) => nullable.data.is_variable(),
1280 Self::FixedWidth(_) => false,
1281 Self::FixedSizeList(fsl) => fsl.child.is_variable(),
1282 Self::VariableWidth(_) => true,
1283 Self::Struct(strct) => strct.children.iter().any(|c| c.is_variable()),
1284 Self::Dictionary(_) => {
1285 todo!("is_variable for DictionaryDataBlock is not implemented yet")
1286 }
1287 Self::Opaque(_) => panic!("Does not make sense to ask if an Opaque block is variable"),
1288 }
1289 }
1290
1291 pub fn is_nullable(&self) -> bool {
1292 match self {
1293 Self::AllNull(_) => true,
1294 Self::Nullable(_) => true,
1295 Self::FixedSizeList(fsl) => fsl.child.is_nullable(),
1296 Self::Struct(strct) => strct.children.iter().any(|c| c.is_nullable()),
1297 Self::Dictionary(_) => {
1298 todo!("is_nullable for DictionaryDataBlock is not implemented yet")
1299 }
1300 Self::Opaque(_) => panic!("Does not make sense to ask if an Opaque block is nullable"),
1301 _ => false,
1302 }
1303 }
1304
1305 pub fn num_values(&self) -> u64 {
1310 match self {
1311 Self::Empty() => 0,
1312 Self::Constant(inner) => inner.num_values,
1313 Self::AllNull(inner) => inner.num_values,
1314 Self::Nullable(inner) => inner.data.num_values(),
1315 Self::FixedWidth(inner) => inner.num_values,
1316 Self::FixedSizeList(inner) => inner.num_values(),
1317 Self::VariableWidth(inner) => inner.num_values,
1318 Self::Struct(inner) => inner.children[0].num_values(),
1319 Self::Dictionary(inner) => inner.indices.num_values,
1320 Self::Opaque(inner) => inner.num_values,
1321 }
1322 }
1323
1324 pub fn items_per_row(&self) -> u64 {
1328 match self {
1329 Self::Empty() => todo!(), Self::Constant(_) => todo!(), Self::AllNull(_) => todo!(), Self::Nullable(nullable) => nullable.data.items_per_row(),
1333 Self::FixedWidth(_) => 1,
1334 Self::FixedSizeList(fsl) => fsl.dimension * fsl.child.items_per_row(),
1335 Self::VariableWidth(_) => 1,
1336 Self::Struct(_) => todo!(), Self::Dictionary(_) => 1,
1338 Self::Opaque(_) => 1,
1339 }
1340 }
1341
1342 pub fn data_size(&self) -> u64 {
1344 match self {
1345 Self::Empty() => 0,
1346 Self::Constant(inner) => inner.data_size(),
1347 Self::AllNull(_) => 0,
1348 Self::Nullable(inner) => inner.data_size(),
1349 Self::FixedWidth(inner) => inner.data_size(),
1350 Self::FixedSizeList(inner) => inner.data_size(),
1351 Self::VariableWidth(inner) => inner.data_size(),
1352 Self::Struct(inner) => inner.children.iter().map(|child| child.data_size()).sum(),
1353 Self::Dictionary(inner) => inner.indices.data_size() + inner.dictionary.data_size(),
1354 Self::Opaque(inner) => inner.data_size(),
1355 }
1356 }
1357
1358 pub fn remove_outer_validity(self) -> Self {
1366 match self {
1367 Self::AllNull(_) => panic!("Cannot remove validity on all-null data"),
1368 Self::Nullable(inner) => *inner.data,
1369 Self::Struct(inner) => Self::Struct(inner.remove_outer_validity()),
1370 other => other,
1371 }
1372 }
1373
1374 fn make_builder(&self, estimated_size_bytes: u64) -> Box<dyn DataBlockBuilderImpl> {
1375 match self {
1376 Self::FixedWidth(inner) => {
1377 if inner.bits_per_value == 1 {
1378 Box::new(BitmapDataBlockBuilder::new(estimated_size_bytes))
1379 } else {
1380 Box::new(FixedWidthDataBlockBuilder::new(
1381 inner.bits_per_value,
1382 estimated_size_bytes,
1383 ))
1384 }
1385 }
1386 Self::VariableWidth(inner) => {
1387 if inner.bits_per_offset == 32 {
1388 Box::new(VariableWidthDataBlockBuilder::<i32>::new(
1389 estimated_size_bytes,
1390 ))
1391 } else if inner.bits_per_offset == 64 {
1392 Box::new(VariableWidthDataBlockBuilder::<i64>::new(
1393 estimated_size_bytes,
1394 ))
1395 } else {
1396 todo!()
1397 }
1398 }
1399 Self::FixedSizeList(inner) => {
1400 let inner_builder = inner.child.make_builder(estimated_size_bytes);
1401 Box::new(FixedSizeListBlockBuilder::new(
1402 inner_builder,
1403 inner.dimension,
1404 ))
1405 }
1406 Self::Nullable(nullable) => {
1407 let estimated_validity_size_bytes = estimated_size_bytes / 16;
1410 let inner_builder = nullable
1411 .data
1412 .make_builder(estimated_size_bytes - estimated_validity_size_bytes);
1413 Box::new(NullableDataBlockBuilder::new(
1414 inner_builder,
1415 estimated_validity_size_bytes as usize,
1416 ))
1417 }
1418 Self::Struct(struct_data_block) => {
1419 let num_children = struct_data_block.children.len();
1420 let per_child_estimate = if num_children == 0 {
1421 0
1422 } else {
1423 estimated_size_bytes / num_children as u64
1424 };
1425 let child_builders = struct_data_block
1426 .children
1427 .iter()
1428 .map(|child| child.make_builder(per_child_estimate))
1429 .collect();
1430 Box::new(StructDataBlockBuilder::new(child_builders))
1431 }
1432 Self::AllNull(_) => Box::new(AllNullDataBlockBuilder::default()),
1433 _ => todo!("make_builder for {:?}", self),
1434 }
1435 }
1436}
1437
1438macro_rules! as_type {
1439 ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1440 pub fn $fn_name(self) -> Option<$inner_type> {
1441 match self {
1442 Self::$inner(inner) => Some(inner),
1443 _ => None,
1444 }
1445 }
1446 };
1447}
1448
1449macro_rules! as_type_ref {
1450 ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1451 pub fn $fn_name(&self) -> Option<&$inner_type> {
1452 match self {
1453 Self::$inner(inner) => Some(inner),
1454 _ => None,
1455 }
1456 }
1457 };
1458}
1459
1460macro_rules! as_type_ref_mut {
1461 ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1462 pub fn $fn_name(&mut self) -> Option<&mut $inner_type> {
1463 match self {
1464 Self::$inner(inner) => Some(inner),
1465 _ => None,
1466 }
1467 }
1468 };
1469}
1470
1471impl DataBlock {
1473 as_type!(as_all_null, AllNull, AllNullDataBlock);
1474 as_type!(as_nullable, Nullable, NullableDataBlock);
1475 as_type!(as_fixed_width, FixedWidth, FixedWidthDataBlock);
1476 as_type!(as_fixed_size_list, FixedSizeList, FixedSizeListBlock);
1477 as_type!(as_variable_width, VariableWidth, VariableWidthBlock);
1478 as_type!(as_struct, Struct, StructDataBlock);
1479 as_type!(as_dictionary, Dictionary, DictionaryDataBlock);
1480 as_type_ref!(as_all_null_ref, AllNull, AllNullDataBlock);
1481 as_type_ref!(as_nullable_ref, Nullable, NullableDataBlock);
1482 as_type_ref!(as_fixed_width_ref, FixedWidth, FixedWidthDataBlock);
1483 as_type_ref!(as_fixed_size_list_ref, FixedSizeList, FixedSizeListBlock);
1484 as_type_ref!(as_variable_width_ref, VariableWidth, VariableWidthBlock);
1485 as_type_ref!(as_struct_ref, Struct, StructDataBlock);
1486 as_type_ref!(as_dictionary_ref, Dictionary, DictionaryDataBlock);
1487 as_type_ref_mut!(as_all_null_ref_mut, AllNull, AllNullDataBlock);
1488 as_type_ref_mut!(as_nullable_ref_mut, Nullable, NullableDataBlock);
1489 as_type_ref_mut!(as_fixed_width_ref_mut, FixedWidth, FixedWidthDataBlock);
1490 as_type_ref_mut!(
1491 as_fixed_size_list_ref_mut,
1492 FixedSizeList,
1493 FixedSizeListBlock
1494 );
1495 as_type_ref_mut!(as_variable_width_ref_mut, VariableWidth, VariableWidthBlock);
1496 as_type_ref_mut!(as_struct_ref_mut, Struct, StructDataBlock);
1497 as_type_ref_mut!(as_dictionary_ref_mut, Dictionary, DictionaryDataBlock);
1498}
1499
1500fn get_byte_range<T: ArrowNativeType>(offsets: &mut LanceBuffer) -> Range<usize> {
1503 let offsets = offsets.borrow_to_typed_slice::<T>();
1504 if offsets.as_ref().is_empty() {
1505 0..0
1506 } else {
1507 offsets.as_ref().first().unwrap().as_usize()..offsets.as_ref().last().unwrap().as_usize()
1508 }
1509}
1510
1511fn stitch_offsets<T: ArrowNativeType + std::ops::Add<Output = T> + std::ops::Sub<Output = T>>(
1517 offsets: Vec<LanceBuffer>,
1518) -> (LanceBuffer, Vec<Range<usize>>) {
1519 if offsets.is_empty() {
1520 return (LanceBuffer::empty(), Vec::default());
1521 }
1522 let len = offsets.iter().map(|b| b.len()).sum::<usize>();
1523 let mut dest = Vec::with_capacity(len);
1527 let mut byte_ranges = Vec::with_capacity(offsets.len());
1528
1529 dest.push(T::from_usize(0).unwrap());
1531
1532 for mut o in offsets.into_iter() {
1533 if !o.is_empty() {
1534 let last_offset = *dest.last().unwrap();
1535 let o = o.borrow_to_typed_slice::<T>();
1536 let start = *o.as_ref().first().unwrap();
1537 dest.extend(o.as_ref()[1..].iter().map(|&x| x + last_offset - start));
1551 }
1552 byte_ranges.push(get_byte_range::<T>(&mut o));
1553 }
1554 (LanceBuffer::reinterpret_vec(dest), byte_ranges)
1555}
1556
1557fn arrow_binary_to_data_block(
1558 arrays: &[ArrayRef],
1559 num_values: u64,
1560 bits_per_offset: u8,
1561) -> DataBlock {
1562 let data_vec = arrays.iter().map(|arr| arr.to_data()).collect::<Vec<_>>();
1563 arrow_binary_array_data_to_data_block(&data_vec, num_values, bits_per_offset)
1564}
1565
1566fn arrow_binary_array_data_to_data_block(
1567 data_vec: &[ArrayData],
1568 num_values: u64,
1569 bits_per_offset: u8,
1570) -> DataBlock {
1571 let bytes_per_offset = bits_per_offset as usize / 8;
1572 let offsets = data_vec
1573 .iter()
1574 .map(|d| {
1575 LanceBuffer::from(d.buffers()[0].slice_with_length(
1576 d.offset() * bytes_per_offset,
1577 (d.len() + 1) * bytes_per_offset,
1578 ))
1579 })
1580 .collect::<Vec<_>>();
1581 let (offsets, data_ranges) = if bits_per_offset == 32 {
1582 stitch_offsets::<i32>(offsets)
1583 } else {
1584 stitch_offsets::<i64>(offsets)
1585 };
1586 let data = data_vec
1587 .iter()
1588 .zip(data_ranges)
1589 .map(|(d, byte_range)| {
1590 LanceBuffer::from(
1591 d.buffers()[1]
1592 .slice_with_length(byte_range.start, byte_range.end - byte_range.start),
1593 )
1594 })
1595 .collect::<Vec<_>>();
1596 let data = LanceBuffer::concat_into_one(data);
1597 DataBlock::VariableWidth(VariableWidthBlock {
1598 data,
1599 offsets,
1600 bits_per_offset,
1601 num_values,
1602 block_info: BlockInfo::new(),
1603 })
1604}
1605
1606fn encode_flat_data(arrays: &[ArrayRef], num_values: u64) -> LanceBuffer {
1607 let bytes_per_value = arrays[0].data_type().byte_width();
1608 let mut buffer = Vec::with_capacity(num_values as usize * bytes_per_value);
1609 for arr in arrays {
1610 let data = arr.to_data();
1611 buffer.extend_from_slice(data.buffers()[0].as_slice());
1612 }
1613 LanceBuffer::from(buffer)
1614}
1615
1616fn do_encode_bitmap_data(bitmaps: &[BooleanBuffer], num_values: u64) -> LanceBuffer {
1617 let mut builder = BooleanBufferBuilder::new(num_values as usize);
1618
1619 for buf in bitmaps {
1620 builder.append_buffer(buf);
1621 }
1622
1623 let buffer = builder.finish().into_inner();
1624 LanceBuffer::from(buffer)
1625}
1626
1627fn encode_bitmap_data(arrays: &[ArrayRef], num_values: u64) -> LanceBuffer {
1628 let bitmaps = arrays
1629 .iter()
1630 .map(|arr| arr.as_boolean().values().clone())
1631 .collect::<Vec<_>>();
1632 do_encode_bitmap_data(&bitmaps, num_values)
1633}
1634
1635fn concat_dict_arrays(arrays: &[ArrayRef]) -> ArrayRef {
1638 let value_type = arrays[0].as_any_dictionary().values().data_type();
1639 let array_refs = arrays.iter().map(|arr| arr.as_ref()).collect::<Vec<_>>();
1640 match arrow_select::concat::concat(&array_refs) {
1641 Ok(array) => array,
1642 Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1643 let upscaled = array_refs
1645 .iter()
1646 .map(|arr| {
1647 match arrow_cast::cast(
1648 *arr,
1649 &DataType::Dictionary(
1650 Box::new(DataType::UInt32),
1651 Box::new(value_type.clone()),
1652 ),
1653 ) {
1654 Ok(arr) => arr,
1655 Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1656 unimplemented!("Dictionary arrays with more than 2^32 unique values")
1658 }
1659 err => err.unwrap(),
1660 }
1661 })
1662 .collect::<Vec<_>>();
1663 let array_refs = upscaled.iter().map(|arr| arr.as_ref()).collect::<Vec<_>>();
1664 match arrow_select::concat::concat(&array_refs) {
1666 Ok(array) => array,
1667 Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1668 unimplemented!("Dictionary arrays with more than 2^32 unique values")
1669 }
1670 err => err.unwrap(),
1671 }
1672 }
1673 err => err.unwrap(),
1675 }
1676}
1677
1678fn max_index_val(index_type: &DataType) -> u64 {
1679 match index_type {
1680 DataType::Int8 => i8::MAX as u64,
1681 DataType::Int16 => i16::MAX as u64,
1682 DataType::Int32 => i32::MAX as u64,
1683 DataType::Int64 => i64::MAX as u64,
1684 DataType::UInt8 => u8::MAX as u64,
1685 DataType::UInt16 => u16::MAX as u64,
1686 DataType::UInt32 => u32::MAX as u64,
1687 DataType::UInt64 => u64::MAX,
1688 _ => panic!("Invalid dictionary index type"),
1689 }
1690}
1691
1692fn arrow_dictionary_to_data_block(arrays: &[ArrayRef], validity: Option<NullBuffer>) -> DataBlock {
1711 let array = concat_dict_arrays(arrays);
1712 let array_dict = array.as_any_dictionary();
1713 let mut indices = array_dict.keys();
1714 let num_values = indices.len() as u64;
1715 let mut values = array_dict.values().clone();
1716
1717 let indices_block = if let Some(validity) = validity {
1721 let mut first_invalid_index = None;
1725 if let Some(values_validity) = values.nulls() {
1726 first_invalid_index = (!values_validity.inner()).set_indices().next();
1727 }
1728 let first_invalid_index = first_invalid_index.unwrap_or_else(|| {
1729 let null_arr = new_null_array(values.data_type(), 1);
1730 values = arrow_select::concat::concat(&[values.as_ref(), null_arr.as_ref()]).unwrap();
1731 values.len() - 1
1732 });
1733 let max_index_val = max_index_val(indices.data_type());
1734 let upcast = if first_invalid_index as u64 > max_index_val {
1735 if max_index_val >= u32::MAX as u64 {
1738 unimplemented!("Dictionary arrays with 2^32 unique value (or more) and a null")
1739 }
1740 Some(arrow_cast::cast(indices, &DataType::UInt32).unwrap())
1741 } else {
1742 None
1743 };
1744 if let Some(upcast) = upcast.as_ref() {
1745 indices = upcast;
1746 }
1747 let null_index_arr = arrow_cast::cast(
1749 &UInt64Array::from(vec![first_invalid_index as u64]),
1750 indices.data_type(),
1751 )
1752 .unwrap();
1753
1754 let bytes_per_index = indices.data_type().byte_width();
1755 let bits_per_index = bytes_per_index as u64 * 8;
1756
1757 let null_index_arr = null_index_arr.into_data();
1758 let null_index_bytes = &null_index_arr.buffers()[0];
1759 let mut indices_bytes = indices.to_data().buffers()[0].to_vec();
1761 for invalid_idx in (!validity.inner()).set_indices() {
1762 indices_bytes[invalid_idx * bytes_per_index..(invalid_idx + 1) * bytes_per_index]
1763 .copy_from_slice(null_index_bytes.as_slice());
1764 }
1765 FixedWidthDataBlock {
1766 data: LanceBuffer::from(indices_bytes),
1767 bits_per_value: bits_per_index,
1768 num_values,
1769 block_info: BlockInfo::new(),
1770 }
1771 } else {
1772 FixedWidthDataBlock {
1773 data: LanceBuffer::from(indices.to_data().buffers()[0].clone()),
1774 bits_per_value: indices.data_type().byte_width() as u64 * 8,
1775 num_values,
1776 block_info: BlockInfo::new(),
1777 }
1778 };
1779
1780 let items = DataBlock::from(values);
1781 DataBlock::Dictionary(DictionaryDataBlock {
1782 indices: indices_block,
1783 dictionary: Box::new(items),
1784 })
1785}
1786
1787enum Nullability {
1788 None,
1789 All,
1790 Some(NullBuffer),
1791}
1792
1793impl Nullability {
1794 fn to_option(&self) -> Option<NullBuffer> {
1795 match self {
1796 Self::Some(nulls) => Some(nulls.clone()),
1797 _ => None,
1798 }
1799 }
1800}
1801
1802fn extract_nulls(arrays: &[ArrayRef], num_values: u64) -> Nullability {
1803 let mut has_nulls = false;
1804 let nulls_and_lens = arrays
1805 .iter()
1806 .map(|arr| {
1807 let nulls = arr.logical_nulls();
1808 has_nulls |= nulls.is_some();
1809 (nulls, arr.len())
1810 })
1811 .collect::<Vec<_>>();
1812 if !has_nulls {
1813 return Nullability::None;
1814 }
1815 let mut builder = BooleanBufferBuilder::new(num_values as usize);
1816 let mut num_nulls = 0;
1817 for (null, len) in nulls_and_lens {
1818 if let Some(null) = null {
1819 num_nulls += null.null_count();
1820 builder.append_buffer(&null.into_inner());
1821 } else {
1822 builder.append_n(len, true);
1823 }
1824 }
1825 if num_nulls == num_values as usize {
1826 Nullability::All
1827 } else {
1828 Nullability::Some(NullBuffer::new(builder.finish()))
1829 }
1830}
1831
1832impl DataBlock {
1833 fn validate_variable_width_offsets<T: ArrowNativeType + Ord>(
1834 array_data: &ArrayData,
1835 ) -> std::result::Result<(), String> {
1836 if array_data.is_empty() && array_data.buffers()[0].is_empty() {
1837 return Ok(());
1838 }
1839 let offset_size = std::mem::size_of::<T>();
1840 let offset_start = array_data.offset() * offset_size;
1841 let offset_len = (array_data.len() + 1) * offset_size;
1842 let offset_buffer =
1843 LanceBuffer::from(array_data.buffers()[0].slice_with_length(offset_start, offset_len));
1844 let offsets = offset_buffer.borrow_to_typed_slice::<T>();
1845 VariableWidthBlock::offset_violation_detail(
1846 offsets.as_ref(),
1847 array_data.buffers()[1].len(),
1848 0,
1849 )
1850 .map_or(Ok(()), Err)
1851 }
1852
1853 fn validate_variable_width_layouts(array_data: &ArrayData) -> std::result::Result<(), String> {
1856 match array_data.data_type() {
1857 DataType::Binary | DataType::Utf8 => {
1858 Self::validate_variable_width_offsets::<i32>(array_data)?;
1859 }
1860 DataType::LargeBinary | DataType::LargeUtf8 => {
1861 Self::validate_variable_width_offsets::<i64>(array_data)?;
1862 }
1863 _ => {}
1864 }
1865 for child_data in array_data.child_data() {
1866 Self::validate_variable_width_layouts(child_data)?;
1867 }
1868 Ok(())
1869 }
1870
1871 fn validate_array_data(
1872 array_data: &ArrayData,
1873 field_name: &str,
1874 array_index: usize,
1875 ) -> Result<()> {
1876 let validation = array_data
1877 .validate()
1878 .map_err(|error| error.to_string())
1879 .and_then(|_| Self::validate_variable_width_layouts(array_data));
1880 validation.map_err(|error| {
1881 Error::invalid_input_source(
1882 format!(
1883 "Invalid Arrow array for field '{}' at buffered array {}: {}",
1884 field_name, array_index, error
1885 )
1886 .into(),
1887 )
1888 })
1889 }
1890
1891 pub(crate) fn validate_arrays(arrays: &[ArrayRef], field_name: &str) -> Result<()> {
1892 for (array_index, array) in arrays.iter().enumerate() {
1893 Self::validate_array_data(&array.to_data(), field_name, array_index)?;
1894 }
1895 Ok(())
1896 }
1897
1898 pub fn from_arrays(arrays: &[ArrayRef], num_values: u64) -> Self {
1899 Self::from_arrays_impl(arrays, num_values, true)
1900 }
1901
1902 pub(crate) fn from_decoded_array(array: ArrayRef) -> Self {
1904 let len = array.len() as u64;
1905 Self::from_arrays_impl(&[array], len, false)
1906 }
1907
1908 fn from_arrays_impl(arrays: &[ArrayRef], num_values: u64, encoding: bool) -> Self {
1909 if arrays.is_empty() || num_values == 0 {
1910 return Self::AllNull(AllNullDataBlock { num_values: 0 });
1911 }
1912
1913 let data_type = arrays[0].data_type();
1914 let nulls = extract_nulls(arrays, num_values);
1915
1916 if let Nullability::All = nulls {
1917 return Self::AllNull(AllNullDataBlock { num_values });
1918 }
1919
1920 let mut encoded = match data_type {
1921 DataType::Binary | DataType::Utf8 => arrow_binary_to_data_block(arrays, num_values, 32),
1922 DataType::Utf8View => {
1924 let casted: Vec<ArrayRef> = arrays
1925 .iter()
1926 .map(|a| {
1927 arrow_cast::cast(a.as_ref(), &DataType::Utf8)
1928 .expect("Utf8View to Utf8 cast is always valid")
1929 })
1930 .collect();
1931 arrow_binary_to_data_block(&casted, num_values, 32)
1932 }
1933 DataType::BinaryView => {
1934 let casted: Vec<ArrayRef> = arrays
1935 .iter()
1936 .map(|a| {
1937 arrow_cast::cast(a.as_ref(), &DataType::Binary)
1938 .expect("BinaryView to Binary cast is always valid")
1939 })
1940 .collect();
1941 arrow_binary_to_data_block(&casted, num_values, 32)
1942 }
1943 DataType::LargeBinary | DataType::LargeUtf8 => {
1944 arrow_binary_to_data_block(arrays, num_values, 64)
1945 }
1946 DataType::Boolean => {
1947 let data = encode_bitmap_data(arrays, num_values);
1948 Self::FixedWidth(FixedWidthDataBlock {
1949 data,
1950 bits_per_value: 1,
1951 num_values,
1952 block_info: BlockInfo::new(),
1953 })
1954 }
1955 DataType::Date32
1956 | DataType::Date64
1957 | DataType::Decimal32(_, _)
1958 | DataType::Decimal64(_, _)
1959 | DataType::Decimal128(_, _)
1960 | DataType::Decimal256(_, _)
1961 | DataType::Duration(_)
1962 | DataType::FixedSizeBinary(_)
1963 | DataType::Float16
1964 | DataType::Float32
1965 | DataType::Float64
1966 | DataType::Int16
1967 | DataType::Int32
1968 | DataType::Int64
1969 | DataType::Int8
1970 | DataType::Interval(_)
1971 | DataType::Time32(_)
1972 | DataType::Time64(_)
1973 | DataType::Timestamp(_, _)
1974 | DataType::UInt16
1975 | DataType::UInt32
1976 | DataType::UInt64
1977 | DataType::UInt8 => {
1978 let data = if !encoding && arrays.len() == 1 {
1979 let data = arrays[0].to_data();
1980 let width = data_type.byte_width();
1981 LanceBuffer::from(
1982 data.buffers()[0]
1983 .slice_with_length(data.offset() * width, num_values as usize * width),
1984 )
1985 } else {
1986 encode_flat_data(arrays, num_values)
1987 };
1988 Self::FixedWidth(FixedWidthDataBlock {
1989 data,
1990 bits_per_value: data_type.byte_width() as u64 * 8,
1991 num_values,
1992 block_info: BlockInfo::new(),
1993 })
1994 }
1995 DataType::Null => Self::AllNull(AllNullDataBlock { num_values }),
1996 DataType::Dictionary(_, _) => arrow_dictionary_to_data_block(arrays, nulls.to_option()),
1997 DataType::Struct(fields) => {
1998 let structs = arrays.iter().map(|arr| arr.as_struct()).collect::<Vec<_>>();
1999 let mut children = Vec::with_capacity(fields.len());
2000 for child_idx in 0..fields.len() {
2001 let child_vec = structs
2002 .iter()
2003 .map(|s| s.column(child_idx).clone())
2004 .collect::<Vec<_>>();
2005 children.push(Self::from_arrays_impl(&child_vec, num_values, encoding));
2006 }
2007
2008 let validity = match &nulls {
2010 Nullability::None => None,
2011 Nullability::Some(null_buffer) => Some(null_buffer.clone()),
2012 Nullability::All => unreachable!("Should have returned AllNull earlier"),
2013 };
2014
2015 Self::Struct(StructDataBlock {
2016 children,
2017 block_info: BlockInfo::default(),
2018 validity,
2019 })
2020 }
2021 DataType::FixedSizeList(_, dim) => {
2022 let children = arrays
2023 .iter()
2024 .map(|arr| arr.as_fixed_size_list().values().clone())
2025 .collect::<Vec<_>>();
2026 let child_block =
2027 Self::from_arrays_impl(&children, num_values * *dim as u64, encoding);
2028 Self::FixedSizeList(FixedSizeListBlock {
2029 child: Box::new(child_block),
2030 dimension: *dim as u64,
2031 })
2032 }
2033 DataType::LargeList(_)
2034 | DataType::List(_)
2035 | DataType::ListView(_)
2036 | DataType::LargeListView(_)
2037 | DataType::Map(_, _)
2038 | DataType::RunEndEncoded(_, _)
2039 | DataType::Union(_, _) => {
2040 panic!(
2041 "Field with data type {} cannot be converted to data block",
2042 data_type
2043 )
2044 }
2045 };
2046
2047 if encoding {
2049 encoded.compute_stat();
2050 }
2051
2052 if !matches!(data_type, DataType::Dictionary(_, _)) {
2053 match nulls {
2054 Nullability::None => encoded,
2055 Nullability::Some(nulls) => Self::Nullable(NullableDataBlock {
2056 data: Box::new(encoded),
2057 nulls: LanceBuffer::from(nulls.into_inner().into_inner()),
2058 block_info: BlockInfo::new(),
2059 }),
2060 _ => unreachable!(),
2061 }
2062 } else {
2063 encoded
2065 }
2066 }
2067
2068 pub fn from_array<T: Array + 'static>(array: T) -> Self {
2069 let num_values = array.len();
2070 Self::from_arrays(&[Arc::new(array)], num_values as u64)
2071 }
2072}
2073
2074impl From<ArrayRef> for DataBlock {
2075 fn from(array: ArrayRef) -> Self {
2076 let num_values = array.len() as u64;
2077 Self::from_arrays(&[array], num_values)
2078 }
2079}
2080
2081trait DataBlockBuilderImpl: std::fmt::Debug {
2082 fn validate_append(&self, data_block: &DataBlock, selection: &Range<u64>) -> Result<()>;
2083
2084 fn append_validated(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()>;
2085
2086 fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
2087 self.validate_append(data_block, &selection)?;
2088 self.append_validated(data_block, selection)
2089 }
2090
2091 fn finish(self: Box<Self>) -> DataBlock;
2092}
2093
2094#[derive(Debug)]
2095pub struct DataBlockBuilder {
2096 estimated_size_bytes: u64,
2097 builder: Option<Box<dyn DataBlockBuilderImpl>>,
2098}
2099
2100impl DataBlockBuilder {
2101 pub fn with_capacity_estimate(estimated_size_bytes: u64) -> Self {
2102 Self {
2103 estimated_size_bytes,
2104 builder: None,
2105 }
2106 }
2107
2108 fn get_builder(&mut self, block: &DataBlock) -> &mut dyn DataBlockBuilderImpl {
2109 if self.builder.is_none() {
2110 self.builder = Some(block.make_builder(self.estimated_size_bytes));
2111 }
2112 self.builder.as_mut().unwrap().as_mut()
2113 }
2114
2115 pub fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) -> Result<()> {
2116 self.get_builder(data_block).append(data_block, selection)
2117 }
2118
2119 fn append_ranges(
2120 &mut self,
2121 data_block: &DataBlock,
2122 selections: impl IntoIterator<Item = Range<u64>>,
2123 ) -> Result<()> {
2124 let full_selection = 0..data_block.num_values();
2125 let builder = self.get_builder(data_block);
2126 builder.validate_append(data_block, &full_selection)?;
2127 for selection in selections {
2128 if selection.start > selection.end || selection.end > full_selection.end {
2129 return Err(Error::corrupt_file_named(
2130 "data block",
2131 format!(
2132 "cannot append selection {}..{} from a block with {} values",
2133 selection.start, selection.end, full_selection.end
2134 ),
2135 ));
2136 }
2137 builder.append_validated(data_block, selection)?;
2138 }
2139 Ok(())
2140 }
2141
2142 pub fn finish(self) -> DataBlock {
2143 let builder = self.builder.expect("DataBlockBuilder didn't see any data");
2144 builder.finish()
2145 }
2146}
2147
2148#[cfg(test)]
2149mod tests {
2150 use std::sync::Arc;
2151
2152 use arrow_array::{
2153 ArrayRef, BinaryArray, BinaryViewArray, DictionaryArray, Int8Array, LargeBinaryArray,
2154 LargeStringArray, StringArray, StringViewArray, UInt8Array, UInt16Array, make_array,
2155 new_null_array,
2156 types::{Int8Type, Int32Type},
2157 };
2158 use arrow_buffer::{BooleanBuffer, Buffer, NullBuffer};
2159 use arrow_data::ArrayData;
2160
2161 use arrow_schema::{DataType, Field, Fields};
2162 use lance_core::Error;
2163 use lance_datagen::{ArrayGeneratorExt, DEFAULT_SEED, RowCount, array};
2164 use rand::SeedableRng;
2165 use rstest::rstest;
2166
2167 use crate::buffer::LanceBuffer;
2168
2169 use super::{
2170 AllNullDataBlock, BlockInfo, DataBlock, DataBlockBuilder, DictionaryDataBlock,
2171 FixedWidthDataBlock, VariableWidthBlock,
2172 };
2173
2174 use arrow_array::Array;
2175
2176 #[test]
2177 fn test_sliced_to_data_block() {
2178 let ints = UInt16Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]);
2179 let ints = ints.slice(2, 4);
2180 let data = DataBlock::from_array(ints);
2181
2182 let fixed_data = data.as_fixed_width().unwrap();
2183 assert_eq!(fixed_data.num_values, 4);
2184 assert_eq!(fixed_data.data.len(), 8);
2185
2186 let nullable_ints =
2187 UInt16Array::from(vec![Some(0), None, Some(2), None, Some(4), None, Some(6)]);
2188 let nullable_ints = nullable_ints.slice(1, 3);
2189 let data = DataBlock::from_array(nullable_ints);
2190
2191 let nullable = data.as_nullable().unwrap();
2192 assert_eq!(nullable.nulls, LanceBuffer::from(vec![0b00000010]));
2193 }
2194
2195 #[test]
2196 fn test_string_to_data_block() {
2197 let strings1 = StringArray::from(vec![Some("hello"), None, Some("world")]);
2199 let strings2 = StringArray::from(vec![Some("a"), Some("b")]);
2200 let strings3 = StringArray::from(vec![Option::<&'static str>::None, None]);
2201
2202 let arrays = &[strings1, strings2, strings3]
2203 .iter()
2204 .map(|arr| Arc::new(arr.clone()) as ArrayRef)
2205 .collect::<Vec<_>>();
2206
2207 let block = DataBlock::from_arrays(arrays, 7);
2208
2209 assert_eq!(block.num_values(), 7);
2210 let block = block.as_nullable().unwrap();
2211
2212 assert_eq!(block.nulls, LanceBuffer::from(vec![0b00011101]));
2213
2214 let data = block.data.as_variable_width().unwrap();
2215 assert_eq!(
2216 data.offsets,
2217 LanceBuffer::reinterpret_vec(vec![0, 5, 5, 10, 11, 12, 12, 12])
2218 );
2219
2220 assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworldab"));
2221
2222 let strings1 = StringArray::from(vec![Some("a"), Some("bc")]);
2224 let strings2 = StringArray::from(vec![Some("def")]);
2225
2226 let arrays = &[strings1, strings2]
2227 .iter()
2228 .map(|arr| Arc::new(arr.clone()) as ArrayRef)
2229 .collect::<Vec<_>>();
2230
2231 let block = DataBlock::from_arrays(arrays, 3);
2232
2233 assert_eq!(block.num_values(), 3);
2234 let data = block.as_variable_width().unwrap();
2236 assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(vec![0, 1, 3, 6]));
2237 assert_eq!(data.data, LanceBuffer::copy_slice(b"abcdef"));
2238 }
2239
2240 #[test]
2241 fn test_string_view_to_data_block() {
2242 let views1 = StringViewArray::from(vec![Some("hello"), None, Some("world")]);
2243 let views2 = StringViewArray::from(vec![Some("a"), Some("b")]);
2244 let views3 = StringViewArray::from(vec![Option::<&'static str>::None, None]);
2245
2246 let arrays = &[views1, views2, views3]
2247 .iter()
2248 .map(|arr| Arc::new(arr.clone()) as ArrayRef)
2249 .collect::<Vec<_>>();
2250
2251 let block = DataBlock::from_arrays(arrays, 7);
2252
2253 assert_eq!(block.num_values(), 7);
2254 let block = block.as_nullable().unwrap();
2255 assert_eq!(block.nulls, LanceBuffer::from(vec![0b00011101]));
2256 let data = block.data.as_variable_width().unwrap();
2257 assert_eq!(
2258 data.offsets,
2259 LanceBuffer::reinterpret_vec(vec![0, 5, 5, 10, 11, 12, 12, 12])
2260 );
2261 assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworldab"));
2262
2263 let views1 = StringViewArray::from(vec![Some("a"), Some("bc")]);
2264 let views2 = StringViewArray::from(vec![Some("def")]);
2265
2266 let arrays = &[views1, views2]
2267 .iter()
2268 .map(|arr| Arc::new(arr.clone()) as ArrayRef)
2269 .collect::<Vec<_>>();
2270
2271 let block = DataBlock::from_arrays(arrays, 3);
2272
2273 assert_eq!(block.num_values(), 3);
2274 let data = block.as_variable_width().unwrap();
2275 assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(vec![0, 1, 3, 6]));
2276 assert_eq!(data.data, LanceBuffer::copy_slice(b"abcdef"));
2277 }
2278
2279 #[test]
2280 fn test_binary_view_to_data_block() {
2281 let arr: ArrayRef = Arc::new(BinaryViewArray::from(vec![
2282 Some(b"foo".as_slice()),
2283 None,
2284 Some(b"bar".as_slice()),
2285 ]));
2286 let block = DataBlock::from_arrays(&[arr], 3);
2287 let block = block.as_nullable().unwrap();
2288 let data = block.data.as_variable_width().unwrap();
2289 assert_eq!(data.data, LanceBuffer::copy_slice(b"foobar"));
2290 }
2291
2292 #[test]
2293 fn test_string_sliced() {
2294 let check = |arr: Vec<StringArray>, expected_off: Vec<i32>, expected_data: &[u8]| {
2295 let arrs = arr
2296 .into_iter()
2297 .map(|a| Arc::new(a) as ArrayRef)
2298 .collect::<Vec<_>>();
2299 let num_rows = arrs.iter().map(|a| a.len()).sum::<usize>() as u64;
2300 let data = DataBlock::from_arrays(&arrs, num_rows);
2301
2302 assert_eq!(data.num_values(), num_rows);
2303
2304 let data = data.as_variable_width().unwrap();
2305 assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(expected_off));
2306 assert_eq!(data.data, LanceBuffer::copy_slice(expected_data));
2307 };
2308
2309 let string = StringArray::from(vec![Some("hello"), Some("world")]);
2310 check(vec![string.slice(1, 1)], vec![0, 5], b"world");
2311 check(vec![string.slice(0, 1)], vec![0, 5], b"hello");
2312 check(
2313 vec![string.slice(0, 1), string.slice(1, 1)],
2314 vec![0, 5, 10],
2315 b"helloworld",
2316 );
2317
2318 let string2 = StringArray::from(vec![Some("foo"), Some("bar")]);
2319 check(
2320 vec![string.slice(0, 1), string2.slice(0, 1)],
2321 vec![0, 5, 8],
2322 b"hellofoo",
2323 );
2324 }
2325
2326 #[rstest]
2327 #[case::utf8(
2328 DataType::Utf8,
2329 Buffer::from_slice_ref([0_i32, 5, 10]),
2330 32,
2331 LanceBuffer::reinterpret_vec(vec![0_i32, 5])
2332 )]
2333 #[case::large_utf8(
2334 DataType::LargeUtf8,
2335 Buffer::from_slice_ref([0_i64, 5, 10]),
2336 64,
2337 LanceBuffer::reinterpret_vec(vec![0_i64, 5])
2338 )]
2339 fn test_variable_width_array_data_offset(
2340 #[case] data_type: DataType,
2341 #[case] offsets: Buffer,
2342 #[case] bits_per_offset: u8,
2343 #[case] expected_offsets: LanceBuffer,
2344 ) {
2345 let array_data = ArrayData::builder(data_type)
2346 .len(1)
2347 .offset(1)
2348 .add_buffer(offsets)
2349 .add_buffer(Buffer::from(b"helloworld"))
2350 .build()
2351 .unwrap();
2352
2353 DataBlock::validate_array_data(&array_data, "text", 0).unwrap();
2354 let data = super::arrow_binary_array_data_to_data_block(&[array_data], 1, bits_per_offset);
2355
2356 let data = data.as_variable_width().unwrap();
2357 assert_eq!(data.offsets, expected_offsets);
2358 assert_eq!(data.data, LanceBuffer::copy_slice(b"world"));
2359 }
2360
2361 #[rstest]
2362 #[case::utf8(DataType::Utf8, Buffer::from_slice_ref([0_i32, -1]))]
2363 #[case::large_utf8(DataType::LargeUtf8, Buffer::from_slice_ref([0_i64, -1]))]
2364 fn test_invalid_string_offsets_rejected_before_encoding(
2365 #[case] data_type: DataType,
2366 #[case] offsets: Buffer,
2367 ) {
2368 let array_data = unsafe {
2369 ArrayData::builder(data_type)
2370 .len(1)
2371 .add_buffer(offsets)
2372 .add_buffer(Buffer::from(b""))
2373 .build_unchecked()
2374 };
2375
2376 let error = DataBlock::validate_array_data(&array_data, "text", 0).unwrap_err();
2377
2378 assert!(matches!(error, Error::InvalidInput { .. }));
2379 let message = error.to_string();
2380 assert!(
2381 message.contains("field 'text'"),
2382 "unexpected message: {message}"
2383 );
2384 assert!(
2385 message.contains("offset[1] (-1)"),
2386 "unexpected message: {message}"
2387 );
2388 }
2389
2390 #[test]
2391 fn test_large() {
2392 let arr = LargeBinaryArray::from_vec(vec![b"hello", b"world"]);
2393 let data = DataBlock::from_array(arr);
2394
2395 assert_eq!(data.num_values(), 2);
2396 let data = data.as_variable_width().unwrap();
2397 assert_eq!(data.bits_per_offset, 64);
2398 assert_eq!(data.num_values, 2);
2399 assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworld"));
2400 assert_eq!(
2401 data.offsets,
2402 LanceBuffer::reinterpret_vec(vec![0_u64, 5, 10])
2403 );
2404 }
2405
2406 #[test]
2407 fn test_dictionary_indices_normalized() {
2408 let arr1 = DictionaryArray::<Int8Type>::from_iter([Some("a"), Some("a"), Some("b")]);
2409 let arr2 = DictionaryArray::<Int8Type>::from_iter([Some("b"), Some("c")]);
2410
2411 let data = DataBlock::from_arrays(&[Arc::new(arr1), Arc::new(arr2)], 5);
2412
2413 assert_eq!(data.num_values(), 5);
2414 let data = data.as_dictionary().unwrap();
2415 let indices = data.indices;
2416 assert_eq!(indices.bits_per_value, 8);
2417 assert_eq!(indices.num_values, 5);
2418 assert_eq!(
2419 indices.data,
2420 LanceBuffer::reinterpret_vec::<i8>(vec![0, 0, 1, 2, 3])
2424 );
2425
2426 let items = data.dictionary.as_variable_width().unwrap();
2427 assert_eq!(items.bits_per_offset, 32);
2428 assert_eq!(items.num_values, 4);
2429 assert_eq!(items.data, LanceBuffer::copy_slice(b"abbc"));
2430 assert_eq!(
2431 items.offsets,
2432 LanceBuffer::reinterpret_vec(vec![0, 1, 2, 3, 4],)
2433 );
2434 }
2435
2436 #[test]
2437 fn test_dictionary_nulls() {
2438 let arr1 = DictionaryArray::<Int8Type>::from_iter([None, Some("a"), Some("b")]);
2442 let arr2 = DictionaryArray::<Int8Type>::from_iter([Some("c"), None]);
2443
2444 let data = DataBlock::from_arrays(&[Arc::new(arr1), Arc::new(arr2)], 5);
2445
2446 let check_common = |data: DataBlock| {
2447 assert_eq!(data.num_values(), 5);
2448 let dict = data.as_dictionary().unwrap();
2449
2450 let nullable_items = dict.dictionary.as_nullable().unwrap();
2451 assert_eq!(nullable_items.nulls, LanceBuffer::from(vec![0b00000111]));
2452 assert_eq!(nullable_items.data.num_values(), 4);
2453
2454 let items = nullable_items.data.as_variable_width().unwrap();
2455 assert_eq!(items.bits_per_offset, 32);
2456 assert_eq!(items.num_values, 4);
2457 assert_eq!(items.data, LanceBuffer::copy_slice(b"abc"));
2458 assert_eq!(
2459 items.offsets,
2460 LanceBuffer::reinterpret_vec(vec![0, 1, 2, 3, 3],)
2461 );
2462
2463 let indices = dict.indices;
2464 assert_eq!(indices.bits_per_value, 8);
2465 assert_eq!(indices.num_values, 5);
2466 assert_eq!(
2467 indices.data,
2468 LanceBuffer::reinterpret_vec::<i8>(vec![3, 0, 1, 2, 3])
2469 );
2470 };
2471 check_common(data);
2472
2473 let items = StringArray::from(vec![Some("a"), Some("b"), Some("c"), None]);
2475 let indices = Int8Array::from(vec![Some(3), Some(0), Some(1), Some(2), Some(3)]);
2476 let dict = DictionaryArray::new(indices, Arc::new(items));
2477
2478 let data = DataBlock::from_array(dict);
2479
2480 check_common(data);
2481 }
2482
2483 #[test]
2484 fn test_dictionary_cannot_add_null() {
2485 let items = StringArray::from(
2487 (0..256)
2488 .map(|i| Some(String::from_utf8(vec![0; i]).unwrap()))
2489 .collect::<Vec<_>>(),
2490 );
2491 let indices = UInt8Array::from(
2493 (0..=256)
2494 .map(|i| if i == 256 { None } else { Some(i as u8) })
2495 .collect::<Vec<_>>(),
2496 );
2497 let dict = DictionaryArray::new(indices, Arc::new(items));
2500 let data = DataBlock::from_array(dict);
2501
2502 assert_eq!(data.num_values(), 257);
2503
2504 let dict = data.as_dictionary().unwrap();
2505
2506 assert_eq!(dict.indices.bits_per_value, 32);
2507 assert_eq!(
2508 dict.indices.data,
2509 LanceBuffer::reinterpret_vec((0_u32..257).collect::<Vec<_>>())
2510 );
2511
2512 let nullable_items = dict.dictionary.as_nullable().unwrap();
2513 let null_buffer = NullBuffer::new(BooleanBuffer::new(
2514 nullable_items.nulls.into_buffer(),
2515 0,
2516 257,
2517 ));
2518 for i in 0..256 {
2519 assert!(!null_buffer.is_null(i));
2520 }
2521 assert!(null_buffer.is_null(256));
2522
2523 assert_eq!(
2524 nullable_items.data.as_variable_width().unwrap().data.len(),
2525 32640
2526 );
2527 }
2528
2529 #[test]
2530 fn test_all_null() {
2531 for data_type in [
2532 DataType::UInt32,
2533 DataType::FixedSizeBinary(2),
2534 DataType::List(Arc::new(Field::new("item", DataType::UInt32, true))),
2535 DataType::Struct(Fields::from(vec![Field::new("a", DataType::UInt32, true)])),
2536 ] {
2537 let block = DataBlock::AllNull(AllNullDataBlock { num_values: 10 });
2538 let arr = block.into_arrow(data_type.clone(), true).unwrap();
2539 let arr = make_array(arr);
2540 let expected = new_null_array(&data_type, 10);
2541 assert_eq!(&arr, &expected);
2542 }
2543 }
2544
2545 #[test]
2546 fn test_dictionary_cannot_concatenate() {
2547 let items = StringArray::from(
2549 (0..256)
2550 .map(|i| Some(String::from_utf8(vec![0; i]).unwrap()))
2551 .collect::<Vec<_>>(),
2552 );
2553 let other_items = StringArray::from(
2555 (0..256)
2556 .map(|i| Some(String::from_utf8(vec![1; i + 1]).unwrap()))
2557 .collect::<Vec<_>>(),
2558 );
2559 let indices = UInt8Array::from_iter_values(0..=255);
2560 let dict1 = DictionaryArray::new(indices.clone(), Arc::new(items));
2561 let dict2 = DictionaryArray::new(indices, Arc::new(other_items));
2562 let data = DataBlock::from_arrays(&[Arc::new(dict1), Arc::new(dict2)], 512);
2563 assert_eq!(data.num_values(), 512);
2564
2565 let dict = data.as_dictionary().unwrap();
2566
2567 assert_eq!(dict.indices.bits_per_value, 32);
2568 assert_eq!(
2569 dict.indices.data,
2570 LanceBuffer::reinterpret_vec::<u32>((0..512).collect::<Vec<_>>())
2571 );
2572 assert_eq!(
2574 dict.dictionary.as_variable_width().unwrap().data.len(),
2575 65536
2576 );
2577 }
2578
2579 #[test]
2580 fn test_data_size() {
2581 let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(DEFAULT_SEED.0);
2582 let mut genn = array::rand::<Int32Type>().with_nulls(&[false, false, false]);
2584
2585 let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2586 let block = DataBlock::from_array(arr.clone());
2587 assert!(block.data_size() == arr.get_buffer_memory_size() as u64);
2588
2589 let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2590 let block = DataBlock::from_array(arr.clone());
2591 assert!(block.data_size() == arr.get_buffer_memory_size() as u64);
2592
2593 let mut genn = array::rand::<Int32Type>().with_nulls(&[false, true, false]);
2595 let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2596 let block = DataBlock::from_array(arr.clone());
2597
2598 let array_data = arr.to_data();
2599 let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2600 let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2602 assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2603
2604 let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2605 let block = DataBlock::from_array(arr.clone());
2606
2607 let array_data = arr.to_data();
2608 let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2609 let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2610 assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2611
2612 let mut genn = array::rand::<Int32Type>().with_nulls(&[true, true, false]);
2613 let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2614 let block = DataBlock::from_array(arr.clone());
2615
2616 let array_data = arr.to_data();
2617 let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2618 let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2619 assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2620
2621 let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2622 let block = DataBlock::from_array(arr.clone());
2623
2624 let array_data = arr.to_data();
2625 let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2626 let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2627 assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2628
2629 let mut genn = array::rand::<Int32Type>().with_nulls(&[false, true, false]);
2630 let arr1 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2631 let arr2 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2632 let arr3 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2633 let block = DataBlock::from_arrays(&[arr1.clone(), arr2.clone(), arr3.clone()], 9);
2634
2635 let concatenated_array = arrow_select::concat::concat(&[
2636 &*Arc::new(arr1.clone()) as &dyn Array,
2637 &*Arc::new(arr2.clone()) as &dyn Array,
2638 &*Arc::new(arr3.clone()) as &dyn Array,
2639 ])
2640 .unwrap();
2641 let total_buffer_size: usize = concatenated_array
2642 .to_data()
2643 .buffers()
2644 .iter()
2645 .map(|buffer| buffer.len())
2646 .sum();
2647
2648 let total_nulls_size_in_bytes = concatenated_array.nulls().unwrap().len().div_ceil(8);
2649 assert!(block.data_size() == (total_buffer_size + total_nulls_size_in_bytes) as u64);
2650 }
2651
2652 #[test]
2653 fn variable_width_rejects_out_of_bounds_offsets_without_optional_validation() {
2654 let block = VariableWidthBlock {
2655 data: LanceBuffer::copy_slice(b"alphabetagamma"),
2656 offsets: LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2657 bits_per_offset: 32,
2658 num_values: 3,
2659 block_info: BlockInfo::new(),
2660 };
2661
2662 let error = block
2663 .into_arrow(DataType::Binary, false)
2664 .expect_err("out-of-bounds offsets must be rejected");
2665 assert!(
2666 matches!(error, Error::CorruptFile { .. }),
2667 "expected CorruptFile, got: {error}"
2668 );
2669 let message = error.to_string();
2670 assert!(
2671 message.contains("100000") && message.contains("data buffer size: 14 bytes"),
2672 "error must report the offending offset and the data buffer size: {message}"
2673 );
2674 }
2675
2676 #[test]
2677 fn public_fixed_width_conversion_always_validates_layout() {
2678 let block = FixedWidthDataBlock {
2679 data: LanceBuffer::from(vec![0_u8; 4]),
2680 bits_per_value: 32,
2681 num_values: 2,
2682 block_info: BlockInfo::new(),
2683 };
2684
2685 for validate in [false, true] {
2686 block
2687 .clone()
2688 .into_arrow(DataType::Int32, validate)
2689 .expect_err("a short values buffer must be rejected");
2690 DataBlock::FixedWidth(block.clone())
2691 .into_arrow(DataType::Int32, validate)
2692 .expect_err("a short values buffer must be rejected");
2693 }
2694 }
2695
2696 #[rstest]
2697 #[case::i32_decreasing(
2698 LanceBuffer::reinterpret_vec(vec![0_i32, 5, 2]),
2699 32,
2700 2,
2701 "decreases"
2702 )]
2703 #[case::i64_decreasing(
2704 LanceBuffer::reinterpret_vec(vec![0_i64, 5, 2]),
2705 64,
2706 2,
2707 "decreases"
2708 )]
2709 #[case::i32_out_of_bounds(
2710 LanceBuffer::reinterpret_vec(vec![0_i32, 6]),
2711 32,
2712 1,
2713 "out of bounds"
2714 )]
2715 fn variable_width_builder_rejects_malformed_offsets(
2716 #[case] offsets: LanceBuffer,
2717 #[case] bits_per_offset: u8,
2718 #[case] num_values: u64,
2719 #[case] expected_message: &str,
2720 ) {
2721 let block = DataBlock::VariableWidth(VariableWidthBlock {
2722 data: LanceBuffer::copy_slice(b"abcde"),
2723 offsets,
2724 bits_per_offset,
2725 num_values,
2726 block_info: BlockInfo::new(),
2727 });
2728 let mut builder = DataBlockBuilder::with_capacity_estimate(5);
2729
2730 let error = builder
2731 .append(&block, 0..num_values)
2732 .expect_err("malformed offsets must fail concatenation");
2733 assert!(
2734 matches!(error, Error::CorruptFile { .. }),
2735 "expected CorruptFile, got: {error}"
2736 );
2737 assert!(
2738 error.to_string().contains(expected_message),
2739 "unexpected message: {error}"
2740 );
2741 }
2742
2743 #[rstest]
2744 #[case::binary_i32_tail_out_of_bounds(
2745 DataType::Binary,
2746 LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2747 32,
2748 3,
2749 b"alphabetagamma".as_slice()
2750 )]
2751 #[case::utf8_i32_tail_out_of_bounds(
2752 DataType::Utf8,
2753 LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2754 32,
2755 3,
2756 b"alphabetagamma".as_slice()
2757 )]
2758 #[case::large_binary_i64_tail_out_of_bounds(
2759 DataType::LargeBinary,
2760 LanceBuffer::reinterpret_vec(vec![0_i64, 5, 9, 100_000]),
2761 64,
2762 3,
2763 b"alphabetagamma".as_slice()
2764 )]
2765 #[case::large_utf8_i64_tail_out_of_bounds(
2766 DataType::LargeUtf8,
2767 LanceBuffer::reinterpret_vec(vec![0_i64, 5, 9, 100_000]),
2768 64,
2769 3,
2770 b"alphabetagamma".as_slice()
2771 )]
2772 #[case::binary_negative_offset(
2773 DataType::Binary,
2774 LanceBuffer::reinterpret_vec(vec![0_i32, -1, 9, 14]),
2775 32,
2776 3,
2777 b"alphabetagamma".as_slice()
2778 )]
2779 #[case::binary_non_monotonic_offsets(
2780 DataType::Binary,
2781 LanceBuffer::reinterpret_vec(vec![0_i32, 9, 5, 14]),
2782 32,
2783 3,
2784 b"alphabetagamma".as_slice()
2785 )]
2786 #[case::binary_interior_offset_out_of_bounds(
2787 DataType::Binary,
2788 LanceBuffer::reinterpret_vec(vec![0_i32, 100_000, 100_000, 14]),
2789 32,
2790 3,
2791 b"alphabetagamma".as_slice()
2792 )]
2793 #[case::binary_offsets_buffer_too_short(
2794 DataType::Binary,
2795 LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9]),
2796 32,
2797 3,
2798 b"alphabetagamma".as_slice()
2799 )]
2800 #[case::utf8_invalid_byte_sequence(
2801 DataType::Utf8,
2802 LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2, 3]),
2803 32,
2804 3,
2805 &[b'a', 0xFF, b'b']
2806 )]
2807 #[case::utf8_offset_splits_multibyte_char(
2808 DataType::Utf8,
2809 LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2]),
2810 32,
2811 2,
2812 "é".as_bytes()
2813 )]
2814 #[case::large_utf8_invalid_byte_sequence(
2815 DataType::LargeUtf8,
2816 LanceBuffer::reinterpret_vec(vec![0_i64, 1, 2, 3]),
2817 64,
2818 3,
2819 &[b'a', 0xFF, b'b']
2820 )]
2821 fn variable_width_rejects_malformed_layout(
2822 #[case] data_type: DataType,
2823 #[case] offsets: LanceBuffer,
2824 #[case] bits_per_offset: u8,
2825 #[case] num_values: u64,
2826 #[case] data: &[u8],
2827 ) {
2828 let block = VariableWidthBlock {
2829 data: LanceBuffer::copy_slice(data),
2830 offsets,
2831 bits_per_offset,
2832 num_values,
2833 block_info: BlockInfo::new(),
2834 };
2835
2836 for validate in [false, true] {
2840 let error = DataBlock::VariableWidth(block.clone())
2841 .into_arrow(data_type.clone(), validate)
2842 .expect_err("malformed variable-width layout must be rejected");
2843 assert!(
2844 matches!(error, Error::CorruptFile { .. }),
2845 "expected CorruptFile with validate={validate}, got: {error}"
2846 );
2847 }
2848 }
2849
2850 #[test]
2851 fn dictionary_rejects_malformed_variable_width_values_without_optional_validation() {
2852 let values = VariableWidthBlock {
2853 data: LanceBuffer::copy_slice(b"alphabetagamma"),
2854 offsets: LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2855 bits_per_offset: 32,
2856 num_values: 3,
2857 block_info: BlockInfo::new(),
2858 };
2859 let dictionary = DataBlock::Dictionary(DictionaryDataBlock {
2860 indices: FixedWidthDataBlock {
2861 data: LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2]),
2862 bits_per_value: 32,
2863 num_values: 3,
2864 block_info: BlockInfo::new(),
2865 },
2866 dictionary: Box::new(DataBlock::VariableWidth(values)),
2867 });
2868
2869 let data_type = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Binary));
2870 let error = dictionary
2871 .into_arrow(data_type, false)
2872 .expect_err("dictionary with out-of-bounds value offsets must be rejected");
2873 assert!(
2874 matches!(error, Error::CorruptFile { .. }),
2875 "expected CorruptFile, got: {error}"
2876 );
2877 }
2878
2879 #[test]
2880 fn dictionary_rejects_indices_wider_than_declared_key_type() {
2881 let dictionary = DataBlock::Dictionary(DictionaryDataBlock {
2882 indices: FixedWidthDataBlock {
2883 data: LanceBuffer::reinterpret_vec(vec![0_u32, 1, 128]),
2884 bits_per_value: 32,
2885 num_values: 3,
2886 block_info: BlockInfo::new(),
2887 },
2888 dictionary: Box::new(DataBlock::from_array(StringArray::from(vec![
2889 Some("zero"),
2890 Some("one"),
2891 None,
2892 ]))),
2893 });
2894
2895 let data_type = DataType::Dictionary(Box::new(DataType::Int8), Box::new(DataType::Utf8));
2896 let error = dictionary
2897 .into_arrow(data_type, false)
2898 .expect_err("mismatched dictionary index widths must be rejected");
2899
2900 assert!(matches!(error, Error::CorruptFile { .. }));
2901 assert!(
2902 error.to_string().contains(
2903 "dictionary indices use 32 bits but the declared Int8 key type uses 8 bits"
2904 )
2905 );
2906 }
2907
2908 #[rstest]
2909 #[case::binary(Arc::new(BinaryArray::from_vec(vec![b"alpha", b"", b"gamma"])) as ArrayRef)]
2910 #[case::large_binary(
2911 Arc::new(LargeBinaryArray::from_vec(vec![b"alpha", b"", b"gamma"])) as ArrayRef
2912 )]
2913 #[case::utf8(Arc::new(StringArray::from(vec!["héllo", "", "world"])) as ArrayRef)]
2914 #[case::large_utf8(Arc::new(LargeStringArray::from(vec!["héllo", "", "world"])) as ArrayRef)]
2915 fn variable_width_valid_data_survives_mandatory_validation(#[case] array: ArrayRef) {
2916 let block = DataBlock::from_array(array.clone());
2917 for validate in [false, true] {
2918 let round_tripped = make_array(
2919 block
2920 .clone()
2921 .into_arrow(array.data_type().clone(), validate)
2922 .unwrap(),
2923 );
2924 assert_eq!(&round_tripped, &array);
2925 }
2926 }
2927}