Skip to main content

lance_encoding/
data.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4//! Data layouts to represent encoded data in a sub-Arrow format
5//!
6//! These [`DataBlock`] structures represent physical layouts.  They fill a gap somewhere
7//! between [`arrow_data::ArrayData`] (which, as a collection of buffers, is too
8//! generic because it doesn't give us enough information about what those buffers represent)
9//! and [`arrow_array::array::Array`] (which is too specific, because it cares about the
10//! logical data type).
11//!
12//! In addition, the layouts represented here are slightly stricter than Arrow's layout rules.
13//! For example, offset buffers MUST start with 0.  These additional restrictions impose a
14//! slight penalty on encode (to normalize arrow data) but make the development of encoders
15//! and decoders easier (since they can rely on a normalized representation)
16
17use 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/// A data block with no buffers where everything is null
41///
42/// Note: this data block should not be used for future work.  It will be deprecated
43/// in the 2.1 version of the format where nullability will be handled by the structural
44/// encoders.
45#[derive(Debug, Clone)]
46pub struct AllNullDataBlock {
47    /// The number of values represented by this block
48    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// `BlockInfo` stores the statistics of this `DataBlock`, such as `NullCount` for `NullableDataBlock`,
64// `BitWidth` for `FixedWidthDataBlock`, `Cardinality` for all `DataBlock`
65#[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/// Wraps a data block and adds nullability information to it
89///
90/// Note: this data block should not be used for future work.  It will be deprecated
91/// in the 2.1 version of the format where nullability will be handled by the structural
92/// encoders.
93#[derive(Debug, Clone)]
94pub struct NullableDataBlock {
95    /// The underlying data
96    pub data: Box<DataBlock>,
97    /// A bitmap of validity for each value
98    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        let nulls = self.nulls.into_buffer();
106        let data = self.data.into_arrow(data_type, validate)?.into_builder();
107        let data = data.null_bit_buffer(Some(nulls));
108        if validate {
109            Ok(data.build()?)
110        } else {
111            Ok(unsafe { data.build_unchecked() })
112        }
113    }
114
115    fn into_buffers(self) -> Vec<LanceBuffer> {
116        let mut buffers = vec![self.nulls];
117        buffers.extend(self.data.into_buffers());
118        buffers
119    }
120
121    pub fn data_size(&self) -> u64 {
122        self.data.data_size() + self.nulls.len() as u64
123    }
124}
125
126/// A block representing the same constant value repeated many times
127#[derive(Debug, PartialEq, Clone)]
128pub struct ConstantDataBlock {
129    /// Data buffer containing the value
130    pub data: LanceBuffer,
131    /// The number of values
132    pub num_values: u64,
133}
134
135impl ConstantDataBlock {
136    fn into_buffers(self) -> Vec<LanceBuffer> {
137        vec![self.data]
138    }
139
140    fn into_arrow(self, _data_type: DataType, _validate: bool) -> Result<ArrayData> {
141        // We don't need this yet but if we come up with some way of serializing
142        // scalars to/from bytes then we could implement it.
143        todo!()
144    }
145
146    pub fn try_clone(&self) -> Result<Self> {
147        Ok(Self {
148            data: self.data.clone(),
149            num_values: self.num_values,
150        })
151    }
152
153    pub fn data_size(&self) -> u64 {
154        self.data.len() as u64
155    }
156}
157
158/// A data block for a single buffer of data where each element has a fixed number of bits
159#[derive(Debug, PartialEq, Clone)]
160pub struct FixedWidthDataBlock {
161    /// The data buffer
162    pub data: LanceBuffer,
163    /// The number of bits per value
164    pub bits_per_value: u64,
165    /// The number of values represented by this block
166    pub num_values: u64,
167
168    pub block_info: BlockInfo,
169}
170
171impl FixedWidthDataBlock {
172    fn do_into_arrow(
173        self,
174        data_type: DataType,
175        num_values: u64,
176        validate: bool,
177    ) -> Result<ArrayData> {
178        // Booleans expanded for full-zip (bits_per_value==8, one byte each) need re-packing to
179        // Arrow's bit-packed format.
180        let data_buffer = if matches!(data_type, DataType::Boolean) && self.bits_per_value == 8 {
181            let mut builder = BooleanBufferBuilder::new(num_values as usize);
182            for &byte in self.data.as_ref().iter().take(num_values as usize) {
183                builder.append(byte != 0);
184            }
185            builder.finish().into_inner()
186        } else {
187            self.data.into_buffer()
188        };
189        let builder = ArrayDataBuilder::new(data_type)
190            .add_buffer(data_buffer)
191            .len(num_values as usize)
192            .null_count(0);
193        if validate {
194            Ok(builder.build()?)
195        } else {
196            Ok(unsafe { builder.build_unchecked() })
197        }
198    }
199
200    pub fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
201        let root_num_values = self.num_values;
202        self.do_into_arrow(data_type, root_num_values, validate)
203    }
204
205    pub fn into_buffers(self) -> Vec<LanceBuffer> {
206        vec![self.data]
207    }
208
209    pub fn try_clone(&self) -> Result<Self> {
210        Ok(Self {
211            data: self.data.clone(),
212            bits_per_value: self.bits_per_value,
213            num_values: self.num_values,
214            block_info: self.block_info.clone(),
215        })
216    }
217
218    pub fn data_size(&self) -> u64 {
219        self.data.len() as u64
220    }
221}
222
223#[derive(Debug)]
224pub struct VariableWidthDataBlockBuilder<T: OffsetSizeTrait> {
225    offsets: Vec<T>,
226    bytes: Vec<u8>,
227}
228
229impl<T: OffsetSizeTrait> VariableWidthDataBlockBuilder<T> {
230    fn new(estimated_size_bytes: u64) -> Self {
231        Self {
232            offsets: vec![T::from_usize(0).unwrap()],
233            bytes: Vec::with_capacity(estimated_size_bytes as usize),
234        }
235    }
236}
237
238impl<T: OffsetSizeTrait + bytemuck::Pod> DataBlockBuilderImpl for VariableWidthDataBlockBuilder<T> {
239    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
240        let block = data_block.as_variable_width_ref().unwrap();
241        assert!(block.bits_per_offset == T::get_byte_width() as u8 * 8);
242        let offsets = block.offsets.borrow_to_typed_view::<T>();
243
244        let start_offset = offsets[selection.start as usize];
245        let end_offset = offsets[selection.end as usize];
246        let mut previous_len = self.bytes.len();
247
248        self.bytes
249            .extend_from_slice(&block.data[start_offset.as_usize()..end_offset.as_usize()]);
250
251        self.offsets.extend(
252            offsets[selection.start as usize..selection.end as usize]
253                .iter()
254                .zip(&offsets[selection.start as usize + 1..=selection.end as usize])
255                .map(|(&current, &next)| {
256                    let this_value_len = next - current;
257                    previous_len += this_value_len.as_usize();
258                    T::from_usize(previous_len).unwrap()
259                }),
260        );
261    }
262
263    fn finish(self: Box<Self>) -> DataBlock {
264        let num_values = (self.offsets.len() - 1) as u64;
265        DataBlock::VariableWidth(VariableWidthBlock {
266            data: LanceBuffer::from(self.bytes),
267            offsets: LanceBuffer::reinterpret_vec(self.offsets),
268            bits_per_offset: T::get_byte_width() as u8 * 8,
269            num_values,
270            block_info: BlockInfo::new(),
271        })
272    }
273}
274
275#[derive(Debug)]
276struct BitmapDataBlockBuilder {
277    values: BooleanBufferBuilder,
278}
279
280impl BitmapDataBlockBuilder {
281    fn new(estimated_size_bytes: u64) -> Self {
282        Self {
283            values: BooleanBufferBuilder::new(estimated_size_bytes as usize * 8),
284        }
285    }
286}
287
288impl DataBlockBuilderImpl for BitmapDataBlockBuilder {
289    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
290        let bitmap_blk = data_block.as_fixed_width_ref().unwrap();
291        self.values.append_packed_range(
292            selection.start as usize..selection.end as usize,
293            &bitmap_blk.data,
294        );
295    }
296
297    fn finish(mut self: Box<Self>) -> DataBlock {
298        let bool_buf = self.values.finish();
299        let num_values = bool_buf.len() as u64;
300        let bits_buf = bool_buf.into_inner();
301        DataBlock::FixedWidth(FixedWidthDataBlock {
302            data: LanceBuffer::from(bits_buf),
303            bits_per_value: 1,
304            num_values,
305            block_info: BlockInfo::new(),
306        })
307    }
308}
309
310#[derive(Debug)]
311struct FixedWidthDataBlockBuilder {
312    bits_per_value: u64,
313    bytes_per_value: u64,
314    values: Vec<u8>,
315}
316
317impl FixedWidthDataBlockBuilder {
318    fn new(bits_per_value: u64, estimated_size_bytes: u64) -> Self {
319        assert!(bits_per_value.is_multiple_of(8));
320        Self {
321            bits_per_value,
322            bytes_per_value: bits_per_value / 8,
323            values: Vec::with_capacity(estimated_size_bytes as usize),
324        }
325    }
326}
327
328impl DataBlockBuilderImpl for FixedWidthDataBlockBuilder {
329    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
330        let block = data_block.as_fixed_width_ref().unwrap();
331        assert_eq!(self.bits_per_value, block.bits_per_value);
332        let start = selection.start as usize * self.bytes_per_value as usize;
333        let end = selection.end as usize * self.bytes_per_value as usize;
334        self.values.extend_from_slice(&block.data[start..end]);
335    }
336
337    fn finish(self: Box<Self>) -> DataBlock {
338        let num_values = (self.values.len() / self.bytes_per_value as usize) as u64;
339        DataBlock::FixedWidth(FixedWidthDataBlock {
340            data: LanceBuffer::from(self.values),
341            bits_per_value: self.bits_per_value,
342            num_values,
343            block_info: BlockInfo::new(),
344        })
345    }
346}
347
348#[derive(Debug)]
349struct StructDataBlockBuilder {
350    children: Vec<Box<dyn DataBlockBuilderImpl>>,
351}
352
353impl StructDataBlockBuilder {
354    fn new(children: Vec<Box<dyn DataBlockBuilderImpl>>) -> Self {
355        Self { children }
356    }
357}
358
359impl DataBlockBuilderImpl for StructDataBlockBuilder {
360    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
361        let data_block = data_block.as_struct_ref().unwrap();
362        for i in 0..self.children.len() {
363            self.children[i].append(&data_block.children[i], selection.clone());
364        }
365    }
366
367    fn finish(self: Box<Self>) -> DataBlock {
368        let mut children_data_block = Vec::new();
369        for child in self.children {
370            let child_data_block = child.finish();
371            children_data_block.push(child_data_block);
372        }
373        DataBlock::Struct(StructDataBlock {
374            children: children_data_block,
375            block_info: BlockInfo::new(),
376            validity: None,
377        })
378    }
379}
380
381#[derive(Debug, Default)]
382struct AllNullDataBlockBuilder {
383    num_values: u64,
384}
385
386impl DataBlockBuilderImpl for AllNullDataBlockBuilder {
387    fn append(&mut self, _data_block: &DataBlock, selection: Range<u64>) {
388        self.num_values += selection.end - selection.start;
389    }
390
391    fn finish(self: Box<Self>) -> DataBlock {
392        DataBlock::AllNull(AllNullDataBlock {
393            num_values: self.num_values,
394        })
395    }
396}
397
398/// A data block to represent a fixed size list
399#[derive(Debug, Clone)]
400pub struct FixedSizeListBlock {
401    /// The child data block
402    pub child: Box<DataBlock>,
403    /// The number of items in each list
404    pub dimension: u64,
405}
406
407impl FixedSizeListBlock {
408    pub fn num_values(&self) -> u64 {
409        self.child.num_values() / self.dimension
410    }
411
412    /// Try to flatten a FixedSizeListBlock into a FixedWidthDataBlock
413    ///
414    /// Returns None if any children are nullable
415    pub fn try_into_flat(self) -> Option<FixedWidthDataBlock> {
416        match *self.child {
417            // Cannot flatten a nullable child
418            DataBlock::Nullable(_) => None,
419            DataBlock::FixedSizeList(inner) => {
420                let mut flat = inner.try_into_flat()?;
421                flat.bits_per_value *= self.dimension;
422                flat.num_values /= self.dimension;
423                Some(flat)
424            }
425            DataBlock::FixedWidth(mut inner) => {
426                inner.bits_per_value *= self.dimension;
427                inner.num_values /= self.dimension;
428                Some(inner)
429            }
430            _ => panic!(
431                "Expected FixedSizeList or FixedWidth data block but found {:?}",
432                self
433            ),
434        }
435    }
436
437    pub fn flatten_as_fixed(&mut self) -> FixedWidthDataBlock {
438        match self.child.as_mut() {
439            DataBlock::FixedSizeList(fsl) => fsl.flatten_as_fixed(),
440            DataBlock::FixedWidth(fw) => fw.clone(),
441            _ => panic!("Expected FixedSizeList or FixedWidth data block"),
442        }
443    }
444
445    /// Convert a flattened values block into a FixedSizeListBlock
446    pub fn from_flat(data: FixedWidthDataBlock, data_type: &DataType) -> DataBlock {
447        match data_type {
448            DataType::FixedSizeList(child_field, dimension) => {
449                let mut data = data;
450                data.bits_per_value /= *dimension as u64;
451                data.num_values *= *dimension as u64;
452                let child_data = Self::from_flat(data, child_field.data_type());
453                DataBlock::FixedSizeList(Self {
454                    child: Box::new(child_data),
455                    dimension: *dimension as u64,
456                })
457            }
458            // Base case, we've hit a non-list type
459            _ => DataBlock::FixedWidth(data),
460        }
461    }
462
463    fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
464        let num_values = self.num_values();
465        let builder = match &data_type {
466            DataType::FixedSizeList(child_field, _) => {
467                let child_data = self
468                    .child
469                    .into_arrow(child_field.data_type().clone(), validate)?;
470                ArrayDataBuilder::new(data_type)
471                    .add_child_data(child_data)
472                    .len(num_values as usize)
473                    .null_count(0)
474            }
475            _ => panic!("Expected FixedSizeList data type and got {:?}", data_type),
476        };
477        if validate {
478            Ok(builder.build()?)
479        } else {
480            Ok(unsafe { builder.build_unchecked() })
481        }
482    }
483
484    fn into_buffers(self) -> Vec<LanceBuffer> {
485        self.child.into_buffers()
486    }
487
488    fn data_size(&self) -> u64 {
489        self.child.data_size()
490    }
491}
492
493#[derive(Debug)]
494struct FixedSizeListBlockBuilder {
495    inner: Box<dyn DataBlockBuilderImpl>,
496    dimension: u64,
497}
498
499impl FixedSizeListBlockBuilder {
500    fn new(inner: Box<dyn DataBlockBuilderImpl>, dimension: u64) -> Self {
501        Self { inner, dimension }
502    }
503}
504
505impl DataBlockBuilderImpl for FixedSizeListBlockBuilder {
506    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
507        let selection = selection.start * self.dimension..selection.end * self.dimension;
508        let fsl = data_block.as_fixed_size_list_ref().unwrap();
509        self.inner.append(fsl.child.as_ref(), selection);
510    }
511
512    fn finish(self: Box<Self>) -> DataBlock {
513        let inner_block = self.inner.finish();
514        DataBlock::FixedSizeList(FixedSizeListBlock {
515            child: Box::new(inner_block),
516            dimension: self.dimension,
517        })
518    }
519}
520
521#[derive(Debug)]
522struct NullableDataBlockBuilder {
523    inner: Box<dyn DataBlockBuilderImpl>,
524    validity: BooleanBufferBuilder,
525}
526
527impl NullableDataBlockBuilder {
528    fn new(inner: Box<dyn DataBlockBuilderImpl>, estimated_size_bytes: usize) -> Self {
529        Self {
530            inner,
531            validity: BooleanBufferBuilder::new(estimated_size_bytes * 8),
532        }
533    }
534}
535
536impl DataBlockBuilderImpl for NullableDataBlockBuilder {
537    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
538        let nullable = data_block.as_nullable_ref().unwrap();
539        let bool_buf = BooleanBuffer::new(
540            nullable.nulls.clone().into_buffer(),
541            selection.start as usize,
542            (selection.end - selection.start) as usize,
543        );
544        self.validity.append_buffer(&bool_buf);
545        self.inner.append(nullable.data.as_ref(), selection);
546    }
547
548    fn finish(mut self: Box<Self>) -> DataBlock {
549        let inner_block = self.inner.finish();
550        DataBlock::Nullable(NullableDataBlock {
551            data: Box::new(inner_block),
552            nulls: LanceBuffer::from(self.validity.finish().into_inner()),
553            block_info: BlockInfo::new(),
554        })
555    }
556}
557
558/// A data block with no regular structure.  There is no available spot to attach
559/// validity / repdef information and it cannot be converted to Arrow without being
560/// decoded
561#[derive(Debug, Clone)]
562pub struct OpaqueBlock {
563    pub buffers: Vec<LanceBuffer>,
564    pub num_values: u64,
565    pub block_info: BlockInfo,
566}
567
568impl OpaqueBlock {
569    pub fn data_size(&self) -> u64 {
570        self.buffers.iter().map(|b| b.len() as u64).sum()
571    }
572}
573
574/// A data block for variable-width data (e.g. strings, packed rows, etc.)
575#[derive(Debug, Clone)]
576pub struct VariableWidthBlock {
577    /// The data buffer
578    pub data: LanceBuffer,
579    /// The offsets buffer (contains num_values + 1 offsets)
580    ///
581    /// Offsets MUST start at 0
582    pub offsets: LanceBuffer,
583    /// The number of bits per offset
584    pub bits_per_offset: u8,
585    /// The number of values represented by this block
586    pub num_values: u64,
587
588    pub block_info: BlockInfo,
589}
590
591/// Proof that a [`VariableWidthBlock`] satisfies the Arrow layout contract for
592/// its target data type (offsets buffer long enough, offsets monotonic and
593/// within the data buffer, values valid UTF-8 where required).
594///
595/// Only [`VariableWidthBlock::validate_layout`] can construct it, which ties the
596/// unchecked Arrow build below to an actual validation pass instead of a
597/// caller-controlled flag.
598struct ValidVariableWidthLayout;
599
600impl VariableWidthBlock {
601    // The offsets buffer comes straight from file bytes, so an unchecked build would
602    // let a corrupt file smuggle out-of-bounds offsets into an Arrow array whose
603    // consumers then read (or crash on) memory outside the data buffer.  This
604    // boundary therefore always validates the layout, ignoring the optional
605    // `validate` flag.  Lance validates the common layouts itself (a branchless
606    // scan, measurably cheaper than Arrow's element-wise checked build) and only
607    // falls back to Arrow's checked build for the cold cases.
608    fn into_arrow(self, data_type: DataType, _validate: bool) -> Result<ArrayData> {
609        let Some(expected_bits_per_offset) = Self::expected_bits_per_offset(&data_type) else {
610            // Not an [offsets, bytes] layout we know how to prove; let Arrow
611            // check it.
612            return self.into_arrow_checked(data_type);
613        };
614        if self.bits_per_offset != expected_bits_per_offset {
615            return Err(self.layout_error(
616                &data_type,
617                format!(
618                    "expected {}-bit offsets but got {}-bit offsets",
619                    expected_bits_per_offset, self.bits_per_offset
620                ),
621            ));
622        }
623        if self.num_values == 0 {
624            // Cold path; Arrow handles the empty-offsets special cases.
625            return self.into_arrow_checked(data_type);
626        }
627        let proof = self.validate_layout(&data_type)?;
628        Ok(self.into_arrow_unchecked(data_type, proof))
629    }
630
631    /// The offset width Arrow mandates for `data_type`, or `None` if the type
632    /// does not use the `[offsets, bytes]` layout this block represents.
633    fn expected_bits_per_offset(data_type: &DataType) -> Option<u8> {
634        match data_type {
635            DataType::Binary | DataType::Utf8 => Some(32),
636            DataType::LargeBinary | DataType::LargeUtf8 => Some(64),
637            _ => None,
638        }
639    }
640
641    fn layout_error(&self, data_type: &DataType, detail: impl std::fmt::Display) -> Error {
642        Self::format_layout_error(
643            data_type,
644            detail,
645            self.num_values,
646            self.bits_per_offset,
647            self.offsets.len(),
648            self.data.len(),
649        )
650    }
651
652    fn format_layout_error(
653        data_type: &DataType,
654        detail: impl std::fmt::Display,
655        num_values: u64,
656        bits_per_offset: u8,
657        offsets_size: usize,
658        data_size: usize,
659    ) -> Error {
660        Error::corrupt_file_named(
661            "variable width data block",
662            format!(
663                "invalid variable-width layout for {}: {} (num_values: {}, bits_per_offset: {}, \
664                 offsets buffer size: {} bytes, data buffer size: {} bytes)",
665                data_type, detail, num_values, bits_per_offset, offsets_size, data_size,
666            ),
667        )
668    }
669
670    fn validate_layout(&self, data_type: &DataType) -> Result<ValidVariableWidthLayout> {
671        let bytes_per_offset = (self.bits_per_offset / 8) as u64;
672        let required_bytes = self
673            .num_values
674            .checked_add(1)
675            .and_then(|num_offsets| num_offsets.checked_mul(bytes_per_offset))
676            .ok_or_else(|| self.layout_error(data_type, "offsets buffer size overflows"))?;
677        if (self.offsets.len() as u64) < required_bytes {
678            return Err(self.layout_error(
679                data_type,
680                format!(
681                    "offsets buffer must hold at least {} offsets ({} bytes)",
682                    self.num_values + 1,
683                    required_bytes
684                ),
685            ));
686        }
687        let validate_utf8 = matches!(data_type, DataType::Utf8 | DataType::LargeUtf8);
688        match self.bits_per_offset {
689            32 => self.validate_offsets_and_values::<i32>(data_type, validate_utf8),
690            64 => self.validate_offsets_and_values::<i64>(data_type, validate_utf8),
691            other => Err(self.layout_error(
692                data_type,
693                format!("unsupported offset width: {} bits", other),
694            )),
695        }
696    }
697
698    fn validate_offsets_and_values<T: ArrowNativeType + Ord>(
699        &self,
700        data_type: &DataType,
701        validate_utf8: bool,
702    ) -> Result<ValidVariableWidthLayout> {
703        let num_offsets = self.num_values as usize + 1;
704        // Slice before borrowing: the buffer may carry padding that is not a
705        // multiple of the offset width.
706        let offsets = self
707            .offsets
708            .slice_with_length(0, num_offsets * std::mem::size_of::<T>());
709        let offsets = offsets.borrow_to_typed_slice::<T>();
710        let offsets: &[T] = offsets.as_ref();
711        let data = self.data.as_ref();
712
713        // A monotonic sequence with a non-negative first offset and an
714        // in-bounds last offset is entirely within [0, data.len()], so the hot
715        // loop only proves monotonicity; everything else is O(1) at the ends.
716        // The `&=` accumulation keeps the loop branchless so it vectorizes.
717        let mut is_monotonic = true;
718        for window in offsets.windows(2) {
719            is_monotonic &= window[0] <= window[1];
720        }
721        let first = offsets[0];
722        let last = offsets[num_offsets - 1];
723        let bounds_ok =
724            first >= T::usize_as(0) && last.to_usize().is_some_and(|last| last <= data.len());
725        if !is_monotonic || !bounds_ok {
726            return Err(self.offset_violation_error::<T>(data_type, offsets));
727        }
728
729        if validate_utf8 {
730            let (first, last) = (first.as_usize(), last.as_usize());
731            let values = std::str::from_utf8(&data[first..last])
732                .map_err(|utf8_err| self.layout_error(data_type, utf8_err))?;
733            let mut on_char_boundaries = true;
734            for &offset in offsets {
735                on_char_boundaries &= values.is_char_boundary(offset.as_usize() - first);
736            }
737            if !on_char_boundaries {
738                // Cold path: rescan to pinpoint the offending offset.
739                let position = offsets
740                    .iter()
741                    .position(|offset| !values.is_char_boundary(offset.as_usize() - first))
742                    .expect("the fast scan found a non-boundary offset");
743                return Err(self.layout_error(
744                    data_type,
745                    format!("offset at position {position} splits a UTF-8 character"),
746                ));
747            }
748        }
749
750        Ok(ValidVariableWidthLayout)
751    }
752
753    /// Cold path: pinpoint the first offending offset for the error message.
754    fn offset_violation_error<T: ArrowNativeType + Ord>(
755        &self,
756        data_type: &DataType,
757        offsets: &[T],
758    ) -> Error {
759        let data_size = self.data.len();
760        for (position, window) in offsets.windows(2).enumerate() {
761            if window[0] > window[1] {
762                return self.layout_error(
763                    data_type,
764                    format!(
765                        "non-monotonic offset at position {}: {:?} > {:?}",
766                        position, window[0], window[1]
767                    ),
768                );
769            }
770        }
771        for (position, offset) in offsets.iter().enumerate() {
772            match offset.to_usize() {
773                None => {
774                    return self.layout_error(
775                        data_type,
776                        format!("negative offset at position {}: {:?}", position, offset),
777                    );
778                }
779                Some(offset) if offset > data_size => {
780                    return self.layout_error(
781                        data_type,
782                        format!(
783                            "offset at position {} out of bounds: {} > {}",
784                            position, offset, data_size
785                        ),
786                    );
787                }
788                Some(_) => {}
789            }
790        }
791        // The fast scan only fails when one of the loops above finds the
792        // culprit; reaching here would be a bug in the fast scan itself.
793        self.layout_error(data_type, "offsets failed validation")
794    }
795
796    fn into_arrow_checked(self, data_type: DataType) -> Result<ArrayData> {
797        let num_values = self.num_values;
798        let bits_per_offset = self.bits_per_offset;
799        let offsets_size = self.offsets.len();
800        let data_size = self.data.len();
801        let builder = self.into_arrow_builder(data_type.clone());
802        builder.build().map_err(|arrow_err| {
803            Self::format_layout_error(
804                &data_type,
805                arrow_err,
806                num_values,
807                bits_per_offset,
808                offsets_size,
809                data_size,
810            )
811        })
812    }
813
814    fn into_arrow_unchecked(
815        self,
816        data_type: DataType,
817        _proof: ValidVariableWidthLayout,
818    ) -> ArrayData {
819        let builder = self.into_arrow_builder(data_type);
820        // SAFETY: `_proof` witnesses that `validate_layout` proved this block
821        // satisfies the Arrow layout contract for `data_type`.
822        unsafe { builder.build_unchecked() }
823    }
824
825    fn into_arrow_builder(self, data_type: DataType) -> ArrayDataBuilder {
826        let num_values = self.num_values;
827        let data_buffer = self.data.into_buffer();
828        let offsets_buffer = self.offsets.into_buffer();
829        ArrayDataBuilder::new(data_type)
830            .add_buffer(offsets_buffer)
831            .add_buffer(data_buffer)
832            .len(num_values as usize)
833            .null_count(0)
834    }
835
836    fn into_buffers(self) -> Vec<LanceBuffer> {
837        vec![self.offsets, self.data]
838    }
839
840    pub fn offsets_as_block(&mut self) -> DataBlock {
841        let offsets = self.offsets.clone();
842        DataBlock::FixedWidth(FixedWidthDataBlock {
843            data: offsets,
844            bits_per_value: self.bits_per_offset as u64,
845            num_values: self.num_values + 1,
846            block_info: BlockInfo::new(),
847        })
848    }
849
850    pub fn data_size(&self) -> u64 {
851        (self.data.len() + self.offsets.len()) as u64
852    }
853}
854
855/// A data block representing a struct
856#[derive(Debug, Clone)]
857pub struct StructDataBlock {
858    /// The child arrays
859    pub children: Vec<DataBlock>,
860    pub block_info: BlockInfo,
861    /// The validity bitmap for the struct (None means all valid)
862    pub validity: Option<NullBuffer>,
863}
864
865impl StructDataBlock {
866    fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
867        if let DataType::Struct(fields) = &data_type {
868            let mut builder = ArrayDataBuilder::new(DataType::Struct(fields.clone()));
869            let mut num_rows = 0;
870            for (field, child) in fields.iter().zip(self.children) {
871                let child_data = child.into_arrow(field.data_type().clone(), validate)?;
872                num_rows = child_data.len();
873                builder = builder.add_child_data(child_data);
874            }
875
876            // Apply validity if present
877            let builder = if let Some(validity) = self.validity {
878                let null_count = validity.null_count();
879                builder
880                    .null_bit_buffer(Some(validity.into_inner().into_inner()))
881                    .null_count(null_count)
882            } else {
883                builder.null_count(0)
884            };
885
886            let builder = builder.len(num_rows);
887            if validate {
888                Ok(builder.build()?)
889            } else {
890                Ok(unsafe { builder.build_unchecked() })
891            }
892        } else {
893            Err(Error::internal(format!(
894                "Expected Struct, got {:?}",
895                data_type
896            )))
897        }
898    }
899
900    fn remove_outer_validity(self) -> Self {
901        Self {
902            children: self
903                .children
904                .into_iter()
905                .map(|c| c.remove_outer_validity())
906                .collect(),
907            block_info: self.block_info,
908            validity: None, // Remove the validity
909        }
910    }
911
912    fn into_buffers(self) -> Vec<LanceBuffer> {
913        self.children
914            .into_iter()
915            .flat_map(|c| c.into_buffers())
916            .collect()
917    }
918
919    pub fn has_variable_width_child(&self) -> bool {
920        self.children
921            .iter()
922            .any(|child| !matches!(child, DataBlock::FixedWidth(_)))
923    }
924
925    pub fn data_size(&self) -> u64 {
926        self.children
927            .iter()
928            .map(|data_block| data_block.data_size())
929            .sum()
930    }
931}
932
933/// A data block for dictionary encoded data
934#[derive(Debug, Clone)]
935pub struct DictionaryDataBlock {
936    /// The indices buffer
937    pub indices: FixedWidthDataBlock,
938    /// The dictionary itself
939    pub dictionary: Box<DataBlock>,
940}
941
942impl DictionaryDataBlock {
943    fn decode_helper<K: ArrowDictionaryKeyType>(self) -> Result<DataBlock> {
944        // Handle empty batch - this can happen when decoding a range that contains
945        // only empty/null lists, or when reading sparse data
946        if self.indices.num_values == 0 {
947            return Ok(DataBlock::AllNull(AllNullDataBlock { num_values: 0 }));
948        }
949
950        // assume the indices are uniformly distributed.
951        let estimated_size_bytes = self.dictionary.data_size()
952            * (self.indices.num_values + self.dictionary.num_values() - 1)
953            / self.dictionary.num_values();
954        let mut data_builder = DataBlockBuilder::with_capacity_estimate(estimated_size_bytes);
955
956        let indices = self.indices.data.borrow_to_typed_slice::<K::Native>();
957        let indices = indices.as_ref();
958
959        indices
960            .iter()
961            .map(|idx| idx.to_usize().unwrap() as u64)
962            .for_each(|idx| {
963                data_builder.append(&self.dictionary, idx..idx + 1);
964            });
965
966        Ok(data_builder.finish())
967    }
968
969    pub fn decode(self) -> Result<DataBlock> {
970        match self.indices.bits_per_value {
971            8 => self.decode_helper::<UInt8Type>(),
972            16 => self.decode_helper::<UInt16Type>(),
973            32 => self.decode_helper::<UInt32Type>(),
974            64 => self.decode_helper::<UInt64Type>(),
975            _ => Err(lance_core::Error::internal(format!(
976                "Unsupported dictionary index bit width: {} bits",
977                self.indices.bits_per_value
978            ))),
979        }
980    }
981
982    fn into_arrow_dict(
983        self,
984        key_type: Box<DataType>,
985        value_type: Box<DataType>,
986        validate: bool,
987    ) -> Result<ArrayData> {
988        let indices = self.indices.into_arrow((*key_type).clone(), validate)?;
989        let dictionary = self
990            .dictionary
991            .into_arrow((*value_type).clone(), validate)?;
992
993        let builder = indices
994            .into_builder()
995            .add_child_data(dictionary)
996            .data_type(DataType::Dictionary(key_type, value_type));
997
998        if validate {
999            Ok(builder.build()?)
1000        } else {
1001            Ok(unsafe { builder.build_unchecked() })
1002        }
1003    }
1004
1005    fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
1006        if let DataType::Dictionary(key_type, value_type) = data_type {
1007            self.into_arrow_dict(key_type, value_type, validate)
1008        } else {
1009            self.decode()?.into_arrow(data_type, validate)
1010        }
1011    }
1012
1013    fn into_buffers(self) -> Vec<LanceBuffer> {
1014        let mut buffers = self.indices.into_buffers();
1015        buffers.extend(self.dictionary.into_buffers());
1016        buffers
1017    }
1018
1019    pub fn into_parts(self) -> (DataBlock, DataBlock) {
1020        (DataBlock::FixedWidth(self.indices), *self.dictionary)
1021    }
1022
1023    pub fn from_parts(indices: FixedWidthDataBlock, dictionary: DataBlock) -> Self {
1024        Self {
1025            indices,
1026            dictionary: Box::new(dictionary),
1027        }
1028    }
1029}
1030
1031/// A DataBlock is a collection of buffers that represents an "array" of data in very generic terms
1032///
1033/// The output of each decoder is a DataBlock.  Decoders can be chained together to transform
1034/// one DataBlock into a different kind of DataBlock.
1035///
1036/// The DataBlock is somewhere in between Arrow's ArrayData and Array and represents a physical
1037/// layout of the data.
1038///
1039/// A DataBlock can be converted into an Arrow ArrayData (and then Array) for a given array type.
1040/// For example, a FixedWidthDataBlock can be converted into any primitive type or a fixed size
1041/// list of a primitive type.  This is a zero-copy operation.
1042///
1043/// In addition, a DataBlock can be created from an Arrow array or arrays.  This is not a zero-copy
1044/// operation as some normalization may be required.
1045#[derive(Debug, Clone)]
1046pub enum DataBlock {
1047    Empty(),
1048    Constant(ConstantDataBlock),
1049    AllNull(AllNullDataBlock),
1050    Nullable(NullableDataBlock),
1051    FixedWidth(FixedWidthDataBlock),
1052    FixedSizeList(FixedSizeListBlock),
1053    VariableWidth(VariableWidthBlock),
1054    Opaque(OpaqueBlock),
1055    Struct(StructDataBlock),
1056    Dictionary(DictionaryDataBlock),
1057}
1058
1059impl DataBlock {
1060    /// Convert self into an Arrow ArrayData
1061    pub fn into_arrow(self, data_type: DataType, validate: bool) -> Result<ArrayData> {
1062        match self {
1063            Self::Empty() => Ok(new_empty_array(&data_type).to_data()),
1064            Self::Constant(inner) => inner.into_arrow(data_type, validate),
1065            Self::AllNull(inner) => inner.into_arrow(data_type, validate),
1066            Self::Nullable(inner) => inner.into_arrow(data_type, validate),
1067            Self::FixedWidth(inner) => inner.into_arrow(data_type, validate),
1068            Self::FixedSizeList(inner) => inner.into_arrow(data_type, validate),
1069            Self::VariableWidth(inner) => inner.into_arrow(data_type, validate),
1070            Self::Struct(inner) => inner.into_arrow(data_type, validate),
1071            Self::Dictionary(inner) => inner.into_arrow(data_type, validate),
1072            Self::Opaque(_) => Err(Error::internal(
1073                "Cannot convert OpaqueBlock to Arrow".to_string(),
1074            )),
1075        }
1076    }
1077
1078    /// Convert the data block into a collection of buffers for serialization
1079    ///
1080    /// The order matters and will be used to reconstruct the data block at read time.
1081    pub fn into_buffers(self) -> Vec<LanceBuffer> {
1082        match self {
1083            Self::Empty() => Vec::default(),
1084            Self::Constant(inner) => inner.into_buffers(),
1085            Self::AllNull(inner) => inner.into_buffers(),
1086            Self::Nullable(inner) => inner.into_buffers(),
1087            Self::FixedWidth(inner) => inner.into_buffers(),
1088            Self::FixedSizeList(inner) => inner.into_buffers(),
1089            Self::VariableWidth(inner) => inner.into_buffers(),
1090            Self::Struct(inner) => inner.into_buffers(),
1091            Self::Dictionary(inner) => inner.into_buffers(),
1092            Self::Opaque(inner) => inner.buffers,
1093        }
1094    }
1095
1096    /// Converts the data buffers into borrowed mode and clones the block
1097    ///
1098    /// This is a zero-copy operation but requires a mutable reference to self and, afterwards,
1099    /// all buffers will be in Borrowed mode.
1100    /// Try and clone the block
1101    ///
1102    /// This will fail if any buffers are in owned mode.  You can call borrow_and_clone() to
1103    /// ensure that all buffers are in borrowed mode before calling this method.
1104    pub fn try_clone(&self) -> Result<Self> {
1105        match self {
1106            Self::Empty() => Ok(Self::Empty()),
1107            Self::Constant(inner) => Ok(Self::Constant(inner.clone())),
1108            Self::AllNull(inner) => Ok(Self::AllNull(inner.clone())),
1109            Self::Nullable(inner) => Ok(Self::Nullable(inner.clone())),
1110            Self::FixedWidth(inner) => Ok(Self::FixedWidth(inner.clone())),
1111            Self::FixedSizeList(inner) => Ok(Self::FixedSizeList(inner.clone())),
1112            Self::VariableWidth(inner) => Ok(Self::VariableWidth(inner.clone())),
1113            Self::Struct(inner) => Ok(Self::Struct(inner.clone())),
1114            Self::Dictionary(inner) => Ok(Self::Dictionary(inner.clone())),
1115            Self::Opaque(inner) => Ok(Self::Opaque(inner.clone())),
1116        }
1117    }
1118
1119    pub fn name(&self) -> &'static str {
1120        match self {
1121            Self::Constant(_) => "Constant",
1122            Self::Empty() => "Empty",
1123            Self::AllNull(_) => "AllNull",
1124            Self::Nullable(_) => "Nullable",
1125            Self::FixedWidth(_) => "FixedWidth",
1126            Self::FixedSizeList(_) => "FixedSizeList",
1127            Self::VariableWidth(_) => "VariableWidth",
1128            Self::Struct(_) => "Struct",
1129            Self::Dictionary(_) => "Dictionary",
1130            Self::Opaque(_) => "Opaque",
1131        }
1132    }
1133
1134    pub fn is_variable(&self) -> bool {
1135        match self {
1136            Self::Constant(_) => false,
1137            Self::Empty() => false,
1138            Self::AllNull(_) => false,
1139            Self::Nullable(nullable) => nullable.data.is_variable(),
1140            Self::FixedWidth(_) => false,
1141            Self::FixedSizeList(fsl) => fsl.child.is_variable(),
1142            Self::VariableWidth(_) => true,
1143            Self::Struct(strct) => strct.children.iter().any(|c| c.is_variable()),
1144            Self::Dictionary(_) => {
1145                todo!("is_variable for DictionaryDataBlock is not implemented yet")
1146            }
1147            Self::Opaque(_) => panic!("Does not make sense to ask if an Opaque block is variable"),
1148        }
1149    }
1150
1151    pub fn is_nullable(&self) -> bool {
1152        match self {
1153            Self::AllNull(_) => true,
1154            Self::Nullable(_) => true,
1155            Self::FixedSizeList(fsl) => fsl.child.is_nullable(),
1156            Self::Struct(strct) => strct.children.iter().any(|c| c.is_nullable()),
1157            Self::Dictionary(_) => {
1158                todo!("is_nullable for DictionaryDataBlock is not implemented yet")
1159            }
1160            Self::Opaque(_) => panic!("Does not make sense to ask if an Opaque block is nullable"),
1161            _ => false,
1162        }
1163    }
1164
1165    /// The number of values in the block
1166    ///
1167    /// This function does not recurse into child blocks.  If this is a FSL then it will
1168    /// be the number of lists and not the number of items.
1169    pub fn num_values(&self) -> u64 {
1170        match self {
1171            Self::Empty() => 0,
1172            Self::Constant(inner) => inner.num_values,
1173            Self::AllNull(inner) => inner.num_values,
1174            Self::Nullable(inner) => inner.data.num_values(),
1175            Self::FixedWidth(inner) => inner.num_values,
1176            Self::FixedSizeList(inner) => inner.num_values(),
1177            Self::VariableWidth(inner) => inner.num_values,
1178            Self::Struct(inner) => inner.children[0].num_values(),
1179            Self::Dictionary(inner) => inner.indices.num_values,
1180            Self::Opaque(inner) => inner.num_values,
1181        }
1182    }
1183
1184    /// The number of items in a single row
1185    ///
1186    /// This is always 1 unless there are layers of FSL
1187    pub fn items_per_row(&self) -> u64 {
1188        match self {
1189            Self::Empty() => todo!(),     // Leave undefined until needed
1190            Self::Constant(_) => todo!(), // Leave undefined until needed
1191            Self::AllNull(_) => todo!(),  // Leave undefined until needed
1192            Self::Nullable(nullable) => nullable.data.items_per_row(),
1193            Self::FixedWidth(_) => 1,
1194            Self::FixedSizeList(fsl) => fsl.dimension * fsl.child.items_per_row(),
1195            Self::VariableWidth(_) => 1,
1196            Self::Struct(_) => todo!(), // Leave undefined until needed
1197            Self::Dictionary(_) => 1,
1198            Self::Opaque(_) => 1,
1199        }
1200    }
1201
1202    /// The number of bytes in the data block (including any child blocks)
1203    pub fn data_size(&self) -> u64 {
1204        match self {
1205            Self::Empty() => 0,
1206            Self::Constant(inner) => inner.data_size(),
1207            Self::AllNull(_) => 0,
1208            Self::Nullable(inner) => inner.data_size(),
1209            Self::FixedWidth(inner) => inner.data_size(),
1210            Self::FixedSizeList(inner) => inner.data_size(),
1211            Self::VariableWidth(inner) => inner.data_size(),
1212            Self::Struct(inner) => inner.children.iter().map(|child| child.data_size()).sum(),
1213            Self::Dictionary(inner) => inner.indices.data_size() + inner.dictionary.data_size(),
1214            Self::Opaque(inner) => inner.data_size(),
1215        }
1216    }
1217
1218    /// Removes any validity information from the block
1219    ///
1220    /// This does not filter the block (e.g. remove rows).  It only removes
1221    /// the validity bitmaps (if present).  Any garbage masked by null bits
1222    /// will now appear as proper values.
1223    ///
1224    /// If `recurse` is true, then this will also remove validity from any child blocks.
1225    pub fn remove_outer_validity(self) -> Self {
1226        match self {
1227            Self::AllNull(_) => panic!("Cannot remove validity on all-null data"),
1228            Self::Nullable(inner) => *inner.data,
1229            Self::Struct(inner) => Self::Struct(inner.remove_outer_validity()),
1230            other => other,
1231        }
1232    }
1233
1234    pub fn make_builder(&self, estimated_size_bytes: u64) -> Box<dyn DataBlockBuilderImpl> {
1235        match self {
1236            Self::FixedWidth(inner) => {
1237                if inner.bits_per_value == 1 {
1238                    Box::new(BitmapDataBlockBuilder::new(estimated_size_bytes))
1239                } else {
1240                    Box::new(FixedWidthDataBlockBuilder::new(
1241                        inner.bits_per_value,
1242                        estimated_size_bytes,
1243                    ))
1244                }
1245            }
1246            Self::VariableWidth(inner) => {
1247                if inner.bits_per_offset == 32 {
1248                    Box::new(VariableWidthDataBlockBuilder::<i32>::new(
1249                        estimated_size_bytes,
1250                    ))
1251                } else if inner.bits_per_offset == 64 {
1252                    Box::new(VariableWidthDataBlockBuilder::<i64>::new(
1253                        estimated_size_bytes,
1254                    ))
1255                } else {
1256                    todo!()
1257                }
1258            }
1259            Self::FixedSizeList(inner) => {
1260                let inner_builder = inner.child.make_builder(estimated_size_bytes);
1261                Box::new(FixedSizeListBlockBuilder::new(
1262                    inner_builder,
1263                    inner.dimension,
1264                ))
1265            }
1266            Self::Nullable(nullable) => {
1267                // There's no easy way to know what percentage of the data is in the valiidty buffer
1268                // but 1/16th seems like a reasonable guess.
1269                let estimated_validity_size_bytes = estimated_size_bytes / 16;
1270                let inner_builder = nullable
1271                    .data
1272                    .make_builder(estimated_size_bytes - estimated_validity_size_bytes);
1273                Box::new(NullableDataBlockBuilder::new(
1274                    inner_builder,
1275                    estimated_validity_size_bytes as usize,
1276                ))
1277            }
1278            Self::Struct(struct_data_block) => {
1279                let num_children = struct_data_block.children.len();
1280                let per_child_estimate = if num_children == 0 {
1281                    0
1282                } else {
1283                    estimated_size_bytes / num_children as u64
1284                };
1285                let child_builders = struct_data_block
1286                    .children
1287                    .iter()
1288                    .map(|child| child.make_builder(per_child_estimate))
1289                    .collect();
1290                Box::new(StructDataBlockBuilder::new(child_builders))
1291            }
1292            Self::AllNull(_) => Box::new(AllNullDataBlockBuilder::default()),
1293            _ => todo!("make_builder for {:?}", self),
1294        }
1295    }
1296}
1297
1298macro_rules! as_type {
1299    ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1300        pub fn $fn_name(self) -> Option<$inner_type> {
1301            match self {
1302                Self::$inner(inner) => Some(inner),
1303                _ => None,
1304            }
1305        }
1306    };
1307}
1308
1309macro_rules! as_type_ref {
1310    ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1311        pub fn $fn_name(&self) -> Option<&$inner_type> {
1312            match self {
1313                Self::$inner(inner) => Some(inner),
1314                _ => None,
1315            }
1316        }
1317    };
1318}
1319
1320macro_rules! as_type_ref_mut {
1321    ($fn_name:ident, $inner:tt, $inner_type:ident) => {
1322        pub fn $fn_name(&mut self) -> Option<&mut $inner_type> {
1323            match self {
1324                Self::$inner(inner) => Some(inner),
1325                _ => None,
1326            }
1327        }
1328    };
1329}
1330
1331// Cast implementations
1332impl DataBlock {
1333    as_type!(as_all_null, AllNull, AllNullDataBlock);
1334    as_type!(as_nullable, Nullable, NullableDataBlock);
1335    as_type!(as_fixed_width, FixedWidth, FixedWidthDataBlock);
1336    as_type!(as_fixed_size_list, FixedSizeList, FixedSizeListBlock);
1337    as_type!(as_variable_width, VariableWidth, VariableWidthBlock);
1338    as_type!(as_struct, Struct, StructDataBlock);
1339    as_type!(as_dictionary, Dictionary, DictionaryDataBlock);
1340    as_type_ref!(as_all_null_ref, AllNull, AllNullDataBlock);
1341    as_type_ref!(as_nullable_ref, Nullable, NullableDataBlock);
1342    as_type_ref!(as_fixed_width_ref, FixedWidth, FixedWidthDataBlock);
1343    as_type_ref!(as_fixed_size_list_ref, FixedSizeList, FixedSizeListBlock);
1344    as_type_ref!(as_variable_width_ref, VariableWidth, VariableWidthBlock);
1345    as_type_ref!(as_struct_ref, Struct, StructDataBlock);
1346    as_type_ref!(as_dictionary_ref, Dictionary, DictionaryDataBlock);
1347    as_type_ref_mut!(as_all_null_ref_mut, AllNull, AllNullDataBlock);
1348    as_type_ref_mut!(as_nullable_ref_mut, Nullable, NullableDataBlock);
1349    as_type_ref_mut!(as_fixed_width_ref_mut, FixedWidth, FixedWidthDataBlock);
1350    as_type_ref_mut!(
1351        as_fixed_size_list_ref_mut,
1352        FixedSizeList,
1353        FixedSizeListBlock
1354    );
1355    as_type_ref_mut!(as_variable_width_ref_mut, VariableWidth, VariableWidthBlock);
1356    as_type_ref_mut!(as_struct_ref_mut, Struct, StructDataBlock);
1357    as_type_ref_mut!(as_dictionary_ref_mut, Dictionary, DictionaryDataBlock);
1358}
1359
1360// Methods to convert from Arrow -> DataBlock
1361
1362fn get_byte_range<T: ArrowNativeType>(offsets: &mut LanceBuffer) -> Range<usize> {
1363    let offsets = offsets.borrow_to_typed_slice::<T>();
1364    if offsets.as_ref().is_empty() {
1365        0..0
1366    } else {
1367        offsets.as_ref().first().unwrap().as_usize()..offsets.as_ref().last().unwrap().as_usize()
1368    }
1369}
1370
1371// Given multiple offsets arrays [0, 5, 10], [0, 3, 7], etc. stitch
1372// them together to get [0, 5, 10, 13, 20, ...]
1373//
1374// Also returns the data range referenced by each offset array (may
1375// not be 0..len if there is slicing involved)
1376fn stitch_offsets<T: ArrowNativeType + std::ops::Add<Output = T> + std::ops::Sub<Output = T>>(
1377    offsets: Vec<LanceBuffer>,
1378) -> (LanceBuffer, Vec<Range<usize>>) {
1379    if offsets.is_empty() {
1380        return (LanceBuffer::empty(), Vec::default());
1381    }
1382    let len = offsets.iter().map(|b| b.len()).sum::<usize>();
1383    // Note: we are making a copy here, even if there is only one input, because we want to
1384    // normalize that input if it doesn't start with zero.  This could be micro-optimized out
1385    // if needed.
1386    let mut dest = Vec::with_capacity(len);
1387    let mut byte_ranges = Vec::with_capacity(offsets.len());
1388
1389    // We insert one leading 0 before processing any of the inputs
1390    dest.push(T::from_usize(0).unwrap());
1391
1392    for mut o in offsets.into_iter() {
1393        if !o.is_empty() {
1394            let last_offset = *dest.last().unwrap();
1395            let o = o.borrow_to_typed_slice::<T>();
1396            let start = *o.as_ref().first().unwrap();
1397            // First, we skip the first offset
1398            // Then, we subtract that first offset from each remaining offset
1399            //
1400            // This gives us a 0-based offset array (minus the leading 0)
1401            //
1402            // Then we add the last offset from the previous array to each offset
1403            // which shifts our offset array to the correct position
1404            //
1405            // For example, let's assume the last offset from the previous array
1406            // was 10 and we are given [13, 17, 22].  This means we have two values with
1407            // length 4 (17 - 13) and 5 (22 - 17).  The output from this step will be
1408            // [14, 19].  Combined with our last offset of 10, this gives us [10, 14, 19]
1409            // which is our same two values of length 4 and 5.
1410            dest.extend(o.as_ref()[1..].iter().map(|&x| x + last_offset - start));
1411        }
1412        byte_ranges.push(get_byte_range::<T>(&mut o));
1413    }
1414    (LanceBuffer::reinterpret_vec(dest), byte_ranges)
1415}
1416
1417fn arrow_binary_to_data_block(
1418    arrays: &[ArrayRef],
1419    num_values: u64,
1420    bits_per_offset: u8,
1421) -> DataBlock {
1422    let data_vec = arrays.iter().map(|arr| arr.to_data()).collect::<Vec<_>>();
1423    let bytes_per_offset = bits_per_offset as usize / 8;
1424    let offsets = data_vec
1425        .iter()
1426        .map(|d| {
1427            LanceBuffer::from(
1428                d.buffers()[0].slice_with_length(d.offset(), (d.len() + 1) * bytes_per_offset),
1429            )
1430        })
1431        .collect::<Vec<_>>();
1432    let (offsets, data_ranges) = if bits_per_offset == 32 {
1433        stitch_offsets::<i32>(offsets)
1434    } else {
1435        stitch_offsets::<i64>(offsets)
1436    };
1437    let data = data_vec
1438        .iter()
1439        .zip(data_ranges)
1440        .map(|(d, byte_range)| {
1441            LanceBuffer::from(
1442                d.buffers()[1]
1443                    .slice_with_length(byte_range.start, byte_range.end - byte_range.start),
1444            )
1445        })
1446        .collect::<Vec<_>>();
1447    let data = LanceBuffer::concat_into_one(data);
1448    DataBlock::VariableWidth(VariableWidthBlock {
1449        data,
1450        offsets,
1451        bits_per_offset,
1452        num_values,
1453        block_info: BlockInfo::new(),
1454    })
1455}
1456
1457fn encode_flat_data(arrays: &[ArrayRef], num_values: u64) -> LanceBuffer {
1458    let bytes_per_value = arrays[0].data_type().byte_width();
1459    let mut buffer = Vec::with_capacity(num_values as usize * bytes_per_value);
1460    for arr in arrays {
1461        let data = arr.to_data();
1462        buffer.extend_from_slice(data.buffers()[0].as_slice());
1463    }
1464    LanceBuffer::from(buffer)
1465}
1466
1467fn do_encode_bitmap_data(bitmaps: &[BooleanBuffer], num_values: u64) -> LanceBuffer {
1468    let mut builder = BooleanBufferBuilder::new(num_values as usize);
1469
1470    for buf in bitmaps {
1471        builder.append_buffer(buf);
1472    }
1473
1474    let buffer = builder.finish().into_inner();
1475    LanceBuffer::from(buffer)
1476}
1477
1478fn encode_bitmap_data(arrays: &[ArrayRef], num_values: u64) -> LanceBuffer {
1479    let bitmaps = arrays
1480        .iter()
1481        .map(|arr| arr.as_boolean().values().clone())
1482        .collect::<Vec<_>>();
1483    do_encode_bitmap_data(&bitmaps, num_values)
1484}
1485
1486// Concatenate dictionary arrays.  This is a bit tricky because we might overflow the
1487// index type.  If we do, we need to upscale the indices to a larger type.
1488fn concat_dict_arrays(arrays: &[ArrayRef]) -> ArrayRef {
1489    let value_type = arrays[0].as_any_dictionary().values().data_type();
1490    let array_refs = arrays.iter().map(|arr| arr.as_ref()).collect::<Vec<_>>();
1491    match arrow_select::concat::concat(&array_refs) {
1492        Ok(array) => array,
1493        Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1494            // Slow, but hopefully a corner case.  Optimize later
1495            let upscaled = array_refs
1496                .iter()
1497                .map(|arr| {
1498                    match arrow_cast::cast(
1499                        *arr,
1500                        &DataType::Dictionary(
1501                            Box::new(DataType::UInt32),
1502                            Box::new(value_type.clone()),
1503                        ),
1504                    ) {
1505                        Ok(arr) => arr,
1506                        Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1507                            // Technically I think this means the input type was u64 already
1508                            unimplemented!("Dictionary arrays with more than 2^32 unique values")
1509                        }
1510                        err => err.unwrap(),
1511                    }
1512                })
1513                .collect::<Vec<_>>();
1514            let array_refs = upscaled.iter().map(|arr| arr.as_ref()).collect::<Vec<_>>();
1515            // Can still fail if concat pushes over u32 boundary
1516            match arrow_select::concat::concat(&array_refs) {
1517                Ok(array) => array,
1518                Err(arrow_schema::ArrowError::DictionaryKeyOverflowError) => {
1519                    unimplemented!("Dictionary arrays with more than 2^32 unique values")
1520                }
1521                err => err.unwrap(),
1522            }
1523        }
1524        // Shouldn't be any other possible errors in concat
1525        err => err.unwrap(),
1526    }
1527}
1528
1529fn max_index_val(index_type: &DataType) -> u64 {
1530    match index_type {
1531        DataType::Int8 => i8::MAX as u64,
1532        DataType::Int16 => i16::MAX as u64,
1533        DataType::Int32 => i32::MAX as u64,
1534        DataType::Int64 => i64::MAX as u64,
1535        DataType::UInt8 => u8::MAX as u64,
1536        DataType::UInt16 => u16::MAX as u64,
1537        DataType::UInt32 => u32::MAX as u64,
1538        DataType::UInt64 => u64::MAX,
1539        _ => panic!("Invalid dictionary index type"),
1540    }
1541}
1542
1543// If we get multiple dictionary arrays and they don't all have the same dictionary
1544// then we need to normalize the indices.  Otherwise we might have something like:
1545//
1546// First chunk ["hello", "foo"], [0, 0, 1, 1, 1]
1547// Second chunk ["bar", "world"], [0, 1, 0, 1, 1]
1548//
1549// If we simply encode as ["hello", "foo", "bar", "world"], [0, 0, 1, 1, 1, 0, 1, 0, 1, 1]
1550// then we will get the wrong answer because the dictionaries were not merged and the indices
1551// were not remapped.
1552//
1553// A simple way to do this today is to just concatenate all the arrays.  This is because
1554// arrow's dictionary concatenation function already has the logic to merge dictionaries.
1555//
1556// TODO: We could be more efficient here by checking if the dictionaries are the same
1557//       Also, if they aren't, we can possibly do something cheaper than concatenating
1558//
1559// In addition, we want to normalize the representation of nulls.  The cheapest thing to
1560// do (space-wise) is to put the nulls in the dictionary.
1561fn arrow_dictionary_to_data_block(arrays: &[ArrayRef], validity: Option<NullBuffer>) -> DataBlock {
1562    let array = concat_dict_arrays(arrays);
1563    let array_dict = array.as_any_dictionary();
1564    let mut indices = array_dict.keys();
1565    let num_values = indices.len() as u64;
1566    let mut values = array_dict.values().clone();
1567    // Placeholder, if we need to upcast, we will initialize this and set `indices` to refer to it
1568    let mut upcast = None;
1569
1570    // TODO: Should we just always normalize indices to u32?  That would make logic simpler
1571    // and we're going to bitpack them soon anyways
1572
1573    let indices_block = if let Some(validity) = validity {
1574        // If there is validity then we find the first invalid index in the dictionary values, inserting
1575        // a new value if we need to.  Then we change all indices to point to that value.  This way we
1576        // never need to store nullability of the indices.
1577        let mut first_invalid_index = None;
1578        if let Some(values_validity) = values.nulls() {
1579            first_invalid_index = (!values_validity.inner()).set_indices().next();
1580        }
1581        let first_invalid_index = first_invalid_index.unwrap_or_else(|| {
1582            let null_arr = new_null_array(values.data_type(), 1);
1583            values = arrow_select::concat::concat(&[values.as_ref(), null_arr.as_ref()]).unwrap();
1584            let null_index = values.len() - 1;
1585            let max_index_val = max_index_val(indices.data_type());
1586            if null_index as u64 > max_index_val {
1587                // Widen the index type
1588                if max_index_val >= u32::MAX as u64 {
1589                    unimplemented!("Dictionary arrays with 2^32 unique value (or more) and a null")
1590                }
1591                upcast = Some(arrow_cast::cast(indices, &DataType::UInt32).unwrap());
1592                indices = upcast.as_ref().unwrap();
1593            }
1594            null_index
1595        });
1596        // This can't fail since we already checked for fit
1597        let null_index_arr = arrow_cast::cast(
1598            &UInt64Array::from(vec![first_invalid_index as u64]),
1599            indices.data_type(),
1600        )
1601        .unwrap();
1602
1603        let bytes_per_index = indices.data_type().byte_width();
1604        let bits_per_index = bytes_per_index as u64 * 8;
1605
1606        let null_index_arr = null_index_arr.into_data();
1607        let null_index_bytes = &null_index_arr.buffers()[0];
1608        // Need to make a copy here since indices isn't mutable, could be avoided in theory
1609        let mut indices_bytes = indices.to_data().buffers()[0].to_vec();
1610        for invalid_idx in (!validity.inner()).set_indices() {
1611            indices_bytes[invalid_idx * bytes_per_index..(invalid_idx + 1) * bytes_per_index]
1612                .copy_from_slice(null_index_bytes.as_slice());
1613        }
1614        FixedWidthDataBlock {
1615            data: LanceBuffer::from(indices_bytes),
1616            bits_per_value: bits_per_index,
1617            num_values,
1618            block_info: BlockInfo::new(),
1619        }
1620    } else {
1621        FixedWidthDataBlock {
1622            data: LanceBuffer::from(indices.to_data().buffers()[0].clone()),
1623            bits_per_value: indices.data_type().byte_width() as u64 * 8,
1624            num_values,
1625            block_info: BlockInfo::new(),
1626        }
1627    };
1628
1629    let items = DataBlock::from(values);
1630    DataBlock::Dictionary(DictionaryDataBlock {
1631        indices: indices_block,
1632        dictionary: Box::new(items),
1633    })
1634}
1635
1636enum Nullability {
1637    None,
1638    All,
1639    Some(NullBuffer),
1640}
1641
1642impl Nullability {
1643    fn to_option(&self) -> Option<NullBuffer> {
1644        match self {
1645            Self::Some(nulls) => Some(nulls.clone()),
1646            _ => None,
1647        }
1648    }
1649}
1650
1651fn extract_nulls(arrays: &[ArrayRef], num_values: u64) -> Nullability {
1652    let mut has_nulls = false;
1653    let nulls_and_lens = arrays
1654        .iter()
1655        .map(|arr| {
1656            let nulls = arr.logical_nulls();
1657            has_nulls |= nulls.is_some();
1658            (nulls, arr.len())
1659        })
1660        .collect::<Vec<_>>();
1661    if !has_nulls {
1662        return Nullability::None;
1663    }
1664    let mut builder = BooleanBufferBuilder::new(num_values as usize);
1665    let mut num_nulls = 0;
1666    for (null, len) in nulls_and_lens {
1667        if let Some(null) = null {
1668            num_nulls += null.null_count();
1669            builder.append_buffer(&null.into_inner());
1670        } else {
1671            builder.append_n(len, true);
1672        }
1673    }
1674    if num_nulls == num_values as usize {
1675        Nullability::All
1676    } else {
1677        Nullability::Some(NullBuffer::new(builder.finish()))
1678    }
1679}
1680
1681impl DataBlock {
1682    pub fn from_arrays(arrays: &[ArrayRef], num_values: u64) -> Self {
1683        if arrays.is_empty() || num_values == 0 {
1684            return Self::AllNull(AllNullDataBlock { num_values: 0 });
1685        }
1686
1687        let data_type = arrays[0].data_type();
1688        let nulls = extract_nulls(arrays, num_values);
1689
1690        if let Nullability::All = nulls {
1691            return Self::AllNull(AllNullDataBlock { num_values });
1692        }
1693
1694        let mut encoded = match data_type {
1695            DataType::Binary | DataType::Utf8 => arrow_binary_to_data_block(arrays, num_values, 32),
1696            // View types have no Lance disk representation; cast to the classic offset layout.
1697            DataType::Utf8View => {
1698                let casted: Vec<ArrayRef> = arrays
1699                    .iter()
1700                    .map(|a| {
1701                        arrow_cast::cast(a.as_ref(), &DataType::Utf8)
1702                            .expect("Utf8View to Utf8 cast is always valid")
1703                    })
1704                    .collect();
1705                arrow_binary_to_data_block(&casted, num_values, 32)
1706            }
1707            DataType::BinaryView => {
1708                let casted: Vec<ArrayRef> = arrays
1709                    .iter()
1710                    .map(|a| {
1711                        arrow_cast::cast(a.as_ref(), &DataType::Binary)
1712                            .expect("BinaryView to Binary cast is always valid")
1713                    })
1714                    .collect();
1715                arrow_binary_to_data_block(&casted, num_values, 32)
1716            }
1717            DataType::LargeBinary | DataType::LargeUtf8 => {
1718                arrow_binary_to_data_block(arrays, num_values, 64)
1719            }
1720            DataType::Boolean => {
1721                let data = encode_bitmap_data(arrays, num_values);
1722                Self::FixedWidth(FixedWidthDataBlock {
1723                    data,
1724                    bits_per_value: 1,
1725                    num_values,
1726                    block_info: BlockInfo::new(),
1727                })
1728            }
1729            DataType::Date32
1730            | DataType::Date64
1731            | DataType::Decimal32(_, _)
1732            | DataType::Decimal64(_, _)
1733            | DataType::Decimal128(_, _)
1734            | DataType::Decimal256(_, _)
1735            | DataType::Duration(_)
1736            | DataType::FixedSizeBinary(_)
1737            | DataType::Float16
1738            | DataType::Float32
1739            | DataType::Float64
1740            | DataType::Int16
1741            | DataType::Int32
1742            | DataType::Int64
1743            | DataType::Int8
1744            | DataType::Interval(_)
1745            | DataType::Time32(_)
1746            | DataType::Time64(_)
1747            | DataType::Timestamp(_, _)
1748            | DataType::UInt16
1749            | DataType::UInt32
1750            | DataType::UInt64
1751            | DataType::UInt8 => {
1752                let data = encode_flat_data(arrays, num_values);
1753                Self::FixedWidth(FixedWidthDataBlock {
1754                    data,
1755                    bits_per_value: data_type.byte_width() as u64 * 8,
1756                    num_values,
1757                    block_info: BlockInfo::new(),
1758                })
1759            }
1760            DataType::Null => Self::AllNull(AllNullDataBlock { num_values }),
1761            DataType::Dictionary(_, _) => arrow_dictionary_to_data_block(arrays, nulls.to_option()),
1762            DataType::Struct(fields) => {
1763                let structs = arrays.iter().map(|arr| arr.as_struct()).collect::<Vec<_>>();
1764                let mut children = Vec::with_capacity(fields.len());
1765                for child_idx in 0..fields.len() {
1766                    let child_vec = structs
1767                        .iter()
1768                        .map(|s| s.column(child_idx).clone())
1769                        .collect::<Vec<_>>();
1770                    children.push(Self::from_arrays(&child_vec, num_values));
1771                }
1772
1773                // Extract validity for the struct array
1774                let validity = match &nulls {
1775                    Nullability::None => None,
1776                    Nullability::Some(null_buffer) => Some(null_buffer.clone()),
1777                    Nullability::All => unreachable!("Should have returned AllNull earlier"),
1778                };
1779
1780                Self::Struct(StructDataBlock {
1781                    children,
1782                    block_info: BlockInfo::default(),
1783                    validity,
1784                })
1785            }
1786            DataType::FixedSizeList(_, dim) => {
1787                let children = arrays
1788                    .iter()
1789                    .map(|arr| arr.as_fixed_size_list().values().clone())
1790                    .collect::<Vec<_>>();
1791                let child_block = Self::from_arrays(&children, num_values * *dim as u64);
1792                Self::FixedSizeList(FixedSizeListBlock {
1793                    child: Box::new(child_block),
1794                    dimension: *dim as u64,
1795                })
1796            }
1797            DataType::LargeList(_)
1798            | DataType::List(_)
1799            | DataType::ListView(_)
1800            | DataType::LargeListView(_)
1801            | DataType::Map(_, _)
1802            | DataType::RunEndEncoded(_, _)
1803            | DataType::Union(_, _) => {
1804                panic!(
1805                    "Field with data type {} cannot be converted to data block",
1806                    data_type
1807                )
1808            }
1809        };
1810
1811        // compute statistics
1812        encoded.compute_stat();
1813
1814        if !matches!(data_type, DataType::Dictionary(_, _)) {
1815            match nulls {
1816                Nullability::None => encoded,
1817                Nullability::Some(nulls) => Self::Nullable(NullableDataBlock {
1818                    data: Box::new(encoded),
1819                    nulls: LanceBuffer::from(nulls.into_inner().into_inner()),
1820                    block_info: BlockInfo::new(),
1821                }),
1822                _ => unreachable!(),
1823            }
1824        } else {
1825            // Dictionaries already insert the nulls into the dictionary items
1826            encoded
1827        }
1828    }
1829
1830    pub fn from_array<T: Array + 'static>(array: T) -> Self {
1831        let num_values = array.len();
1832        Self::from_arrays(&[Arc::new(array)], num_values as u64)
1833    }
1834}
1835
1836impl From<ArrayRef> for DataBlock {
1837    fn from(array: ArrayRef) -> Self {
1838        let num_values = array.len() as u64;
1839        Self::from_arrays(&[array], num_values)
1840    }
1841}
1842
1843pub trait DataBlockBuilderImpl: std::fmt::Debug {
1844    fn append(&mut self, data_block: &DataBlock, selection: Range<u64>);
1845    fn finish(self: Box<Self>) -> DataBlock;
1846}
1847
1848#[derive(Debug)]
1849pub struct DataBlockBuilder {
1850    estimated_size_bytes: u64,
1851    builder: Option<Box<dyn DataBlockBuilderImpl>>,
1852}
1853
1854impl DataBlockBuilder {
1855    pub fn with_capacity_estimate(estimated_size_bytes: u64) -> Self {
1856        Self {
1857            estimated_size_bytes,
1858            builder: None,
1859        }
1860    }
1861
1862    fn get_builder(&mut self, block: &DataBlock) -> &mut dyn DataBlockBuilderImpl {
1863        if self.builder.is_none() {
1864            self.builder = Some(block.make_builder(self.estimated_size_bytes));
1865        }
1866        self.builder.as_mut().unwrap().as_mut()
1867    }
1868
1869    pub fn append(&mut self, data_block: &DataBlock, selection: Range<u64>) {
1870        self.get_builder(data_block).append(data_block, selection);
1871    }
1872
1873    pub fn finish(self) -> DataBlock {
1874        let builder = self.builder.expect("DataBlockBuilder didn't see any data");
1875        builder.finish()
1876    }
1877}
1878
1879#[cfg(test)]
1880mod tests {
1881    use std::sync::Arc;
1882
1883    use arrow_array::{
1884        ArrayRef, BinaryArray, BinaryViewArray, DictionaryArray, Int8Array, LargeBinaryArray,
1885        LargeStringArray, StringArray, StringViewArray, UInt8Array, UInt16Array, make_array,
1886        new_null_array,
1887        types::{Int8Type, Int32Type},
1888    };
1889    use arrow_buffer::{BooleanBuffer, NullBuffer};
1890
1891    use arrow_schema::{DataType, Field, Fields};
1892    use lance_core::Error;
1893    use lance_datagen::{ArrayGeneratorExt, DEFAULT_SEED, RowCount, array};
1894    use rand::SeedableRng;
1895    use rstest::rstest;
1896
1897    use crate::buffer::LanceBuffer;
1898
1899    use super::{
1900        AllNullDataBlock, BlockInfo, DataBlock, DictionaryDataBlock, FixedWidthDataBlock,
1901        VariableWidthBlock,
1902    };
1903
1904    use arrow_array::Array;
1905
1906    #[test]
1907    fn test_sliced_to_data_block() {
1908        let ints = UInt16Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8]);
1909        let ints = ints.slice(2, 4);
1910        let data = DataBlock::from_array(ints);
1911
1912        let fixed_data = data.as_fixed_width().unwrap();
1913        assert_eq!(fixed_data.num_values, 4);
1914        assert_eq!(fixed_data.data.len(), 8);
1915
1916        let nullable_ints =
1917            UInt16Array::from(vec![Some(0), None, Some(2), None, Some(4), None, Some(6)]);
1918        let nullable_ints = nullable_ints.slice(1, 3);
1919        let data = DataBlock::from_array(nullable_ints);
1920
1921        let nullable = data.as_nullable().unwrap();
1922        assert_eq!(nullable.nulls, LanceBuffer::from(vec![0b00000010]));
1923    }
1924
1925    #[test]
1926    fn test_string_to_data_block() {
1927        // Converting string arrays that contain nulls to DataBlock
1928        let strings1 = StringArray::from(vec![Some("hello"), None, Some("world")]);
1929        let strings2 = StringArray::from(vec![Some("a"), Some("b")]);
1930        let strings3 = StringArray::from(vec![Option::<&'static str>::None, None]);
1931
1932        let arrays = &[strings1, strings2, strings3]
1933            .iter()
1934            .map(|arr| Arc::new(arr.clone()) as ArrayRef)
1935            .collect::<Vec<_>>();
1936
1937        let block = DataBlock::from_arrays(arrays, 7);
1938
1939        assert_eq!(block.num_values(), 7);
1940        let block = block.as_nullable().unwrap();
1941
1942        assert_eq!(block.nulls, LanceBuffer::from(vec![0b00011101]));
1943
1944        let data = block.data.as_variable_width().unwrap();
1945        assert_eq!(
1946            data.offsets,
1947            LanceBuffer::reinterpret_vec(vec![0, 5, 5, 10, 11, 12, 12, 12])
1948        );
1949
1950        assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworldab"));
1951
1952        // Converting string arrays that do not contain nulls to DataBlock
1953        let strings1 = StringArray::from(vec![Some("a"), Some("bc")]);
1954        let strings2 = StringArray::from(vec![Some("def")]);
1955
1956        let arrays = &[strings1, strings2]
1957            .iter()
1958            .map(|arr| Arc::new(arr.clone()) as ArrayRef)
1959            .collect::<Vec<_>>();
1960
1961        let block = DataBlock::from_arrays(arrays, 3);
1962
1963        assert_eq!(block.num_values(), 3);
1964        // Should be no nullable wrapper
1965        let data = block.as_variable_width().unwrap();
1966        assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(vec![0, 1, 3, 6]));
1967        assert_eq!(data.data, LanceBuffer::copy_slice(b"abcdef"));
1968    }
1969
1970    #[test]
1971    fn test_string_view_to_data_block() {
1972        let views1 = StringViewArray::from(vec![Some("hello"), None, Some("world")]);
1973        let views2 = StringViewArray::from(vec![Some("a"), Some("b")]);
1974        let views3 = StringViewArray::from(vec![Option::<&'static str>::None, None]);
1975
1976        let arrays = &[views1, views2, views3]
1977            .iter()
1978            .map(|arr| Arc::new(arr.clone()) as ArrayRef)
1979            .collect::<Vec<_>>();
1980
1981        let block = DataBlock::from_arrays(arrays, 7);
1982
1983        assert_eq!(block.num_values(), 7);
1984        let block = block.as_nullable().unwrap();
1985        assert_eq!(block.nulls, LanceBuffer::from(vec![0b00011101]));
1986        let data = block.data.as_variable_width().unwrap();
1987        assert_eq!(
1988            data.offsets,
1989            LanceBuffer::reinterpret_vec(vec![0, 5, 5, 10, 11, 12, 12, 12])
1990        );
1991        assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworldab"));
1992
1993        let views1 = StringViewArray::from(vec![Some("a"), Some("bc")]);
1994        let views2 = StringViewArray::from(vec![Some("def")]);
1995
1996        let arrays = &[views1, views2]
1997            .iter()
1998            .map(|arr| Arc::new(arr.clone()) as ArrayRef)
1999            .collect::<Vec<_>>();
2000
2001        let block = DataBlock::from_arrays(arrays, 3);
2002
2003        assert_eq!(block.num_values(), 3);
2004        let data = block.as_variable_width().unwrap();
2005        assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(vec![0, 1, 3, 6]));
2006        assert_eq!(data.data, LanceBuffer::copy_slice(b"abcdef"));
2007    }
2008
2009    #[test]
2010    fn test_binary_view_to_data_block() {
2011        let arr: ArrayRef = Arc::new(BinaryViewArray::from(vec![
2012            Some(b"foo".as_slice()),
2013            None,
2014            Some(b"bar".as_slice()),
2015        ]));
2016        let block = DataBlock::from_arrays(&[arr], 3);
2017        let block = block.as_nullable().unwrap();
2018        let data = block.data.as_variable_width().unwrap();
2019        assert_eq!(data.data, LanceBuffer::copy_slice(b"foobar"));
2020    }
2021
2022    #[test]
2023    fn test_string_sliced() {
2024        let check = |arr: Vec<StringArray>, expected_off: Vec<i32>, expected_data: &[u8]| {
2025            let arrs = arr
2026                .into_iter()
2027                .map(|a| Arc::new(a) as ArrayRef)
2028                .collect::<Vec<_>>();
2029            let num_rows = arrs.iter().map(|a| a.len()).sum::<usize>() as u64;
2030            let data = DataBlock::from_arrays(&arrs, num_rows);
2031
2032            assert_eq!(data.num_values(), num_rows);
2033
2034            let data = data.as_variable_width().unwrap();
2035            assert_eq!(data.offsets, LanceBuffer::reinterpret_vec(expected_off));
2036            assert_eq!(data.data, LanceBuffer::copy_slice(expected_data));
2037        };
2038
2039        let string = StringArray::from(vec![Some("hello"), Some("world")]);
2040        check(vec![string.slice(1, 1)], vec![0, 5], b"world");
2041        check(vec![string.slice(0, 1)], vec![0, 5], b"hello");
2042        check(
2043            vec![string.slice(0, 1), string.slice(1, 1)],
2044            vec![0, 5, 10],
2045            b"helloworld",
2046        );
2047
2048        let string2 = StringArray::from(vec![Some("foo"), Some("bar")]);
2049        check(
2050            vec![string.slice(0, 1), string2.slice(0, 1)],
2051            vec![0, 5, 8],
2052            b"hellofoo",
2053        );
2054    }
2055
2056    #[test]
2057    fn test_large() {
2058        let arr = LargeBinaryArray::from_vec(vec![b"hello", b"world"]);
2059        let data = DataBlock::from_array(arr);
2060
2061        assert_eq!(data.num_values(), 2);
2062        let data = data.as_variable_width().unwrap();
2063        assert_eq!(data.bits_per_offset, 64);
2064        assert_eq!(data.num_values, 2);
2065        assert_eq!(data.data, LanceBuffer::copy_slice(b"helloworld"));
2066        assert_eq!(
2067            data.offsets,
2068            LanceBuffer::reinterpret_vec(vec![0_u64, 5, 10])
2069        );
2070    }
2071
2072    #[test]
2073    fn test_dictionary_indices_normalized() {
2074        let arr1 = DictionaryArray::<Int8Type>::from_iter([Some("a"), Some("a"), Some("b")]);
2075        let arr2 = DictionaryArray::<Int8Type>::from_iter([Some("b"), Some("c")]);
2076
2077        let data = DataBlock::from_arrays(&[Arc::new(arr1), Arc::new(arr2)], 5);
2078
2079        assert_eq!(data.num_values(), 5);
2080        let data = data.as_dictionary().unwrap();
2081        let indices = data.indices;
2082        assert_eq!(indices.bits_per_value, 8);
2083        assert_eq!(indices.num_values, 5);
2084        assert_eq!(
2085            indices.data,
2086            // You might expect 0, 0, 1, 1, 2 but it seems that arrow's dictionary concat does
2087            // not actually collapse dictionaries.  This is an arrow problem however, and we don't
2088            // need to fix it here.
2089            LanceBuffer::reinterpret_vec::<i8>(vec![0, 0, 1, 2, 3])
2090        );
2091
2092        let items = data.dictionary.as_variable_width().unwrap();
2093        assert_eq!(items.bits_per_offset, 32);
2094        assert_eq!(items.num_values, 4);
2095        assert_eq!(items.data, LanceBuffer::copy_slice(b"abbc"));
2096        assert_eq!(
2097            items.offsets,
2098            LanceBuffer::reinterpret_vec(vec![0, 1, 2, 3, 4],)
2099        );
2100    }
2101
2102    #[test]
2103    fn test_dictionary_nulls() {
2104        // Test both ways of encoding nulls
2105
2106        // By default, nulls get encoded into the indices
2107        let arr1 = DictionaryArray::<Int8Type>::from_iter([None, Some("a"), Some("b")]);
2108        let arr2 = DictionaryArray::<Int8Type>::from_iter([Some("c"), None]);
2109
2110        let data = DataBlock::from_arrays(&[Arc::new(arr1), Arc::new(arr2)], 5);
2111
2112        let check_common = |data: DataBlock| {
2113            assert_eq!(data.num_values(), 5);
2114            let dict = data.as_dictionary().unwrap();
2115
2116            let nullable_items = dict.dictionary.as_nullable().unwrap();
2117            assert_eq!(nullable_items.nulls, LanceBuffer::from(vec![0b00000111]));
2118            assert_eq!(nullable_items.data.num_values(), 4);
2119
2120            let items = nullable_items.data.as_variable_width().unwrap();
2121            assert_eq!(items.bits_per_offset, 32);
2122            assert_eq!(items.num_values, 4);
2123            assert_eq!(items.data, LanceBuffer::copy_slice(b"abc"));
2124            assert_eq!(
2125                items.offsets,
2126                LanceBuffer::reinterpret_vec(vec![0, 1, 2, 3, 3],)
2127            );
2128
2129            let indices = dict.indices;
2130            assert_eq!(indices.bits_per_value, 8);
2131            assert_eq!(indices.num_values, 5);
2132            assert_eq!(
2133                indices.data,
2134                LanceBuffer::reinterpret_vec::<i8>(vec![3, 0, 1, 2, 3])
2135            );
2136        };
2137        check_common(data);
2138
2139        // However, we can manually create a dictionary where nulls are in the dictionary
2140        let items = StringArray::from(vec![Some("a"), Some("b"), Some("c"), None]);
2141        let indices = Int8Array::from(vec![Some(3), Some(0), Some(1), Some(2), Some(3)]);
2142        let dict = DictionaryArray::new(indices, Arc::new(items));
2143
2144        let data = DataBlock::from_array(dict);
2145
2146        check_common(data);
2147    }
2148
2149    #[test]
2150    fn test_dictionary_cannot_add_null() {
2151        // 256 unique strings
2152        let items = StringArray::from(
2153            (0..256)
2154                .map(|i| Some(String::from_utf8(vec![0; i]).unwrap()))
2155                .collect::<Vec<_>>(),
2156        );
2157        // 257 indices, covering the whole range, plus one null
2158        let indices = UInt8Array::from(
2159            (0..=256)
2160                .map(|i| if i == 256 { None } else { Some(i as u8) })
2161                .collect::<Vec<_>>(),
2162        );
2163        // We want to normalize this by pushing nulls into the dictionary, but we cannot because
2164        // the dictionary is too large for the index type
2165        let dict = DictionaryArray::new(indices, Arc::new(items));
2166        let data = DataBlock::from_array(dict);
2167
2168        assert_eq!(data.num_values(), 257);
2169
2170        let dict = data.as_dictionary().unwrap();
2171
2172        assert_eq!(dict.indices.bits_per_value, 32);
2173        assert_eq!(
2174            dict.indices.data,
2175            LanceBuffer::reinterpret_vec((0_u32..257).collect::<Vec<_>>())
2176        );
2177
2178        let nullable_items = dict.dictionary.as_nullable().unwrap();
2179        let null_buffer = NullBuffer::new(BooleanBuffer::new(
2180            nullable_items.nulls.into_buffer(),
2181            0,
2182            257,
2183        ));
2184        for i in 0..256 {
2185            assert!(!null_buffer.is_null(i));
2186        }
2187        assert!(null_buffer.is_null(256));
2188
2189        assert_eq!(
2190            nullable_items.data.as_variable_width().unwrap().data.len(),
2191            32640
2192        );
2193    }
2194
2195    #[test]
2196    fn test_all_null() {
2197        for data_type in [
2198            DataType::UInt32,
2199            DataType::FixedSizeBinary(2),
2200            DataType::List(Arc::new(Field::new("item", DataType::UInt32, true))),
2201            DataType::Struct(Fields::from(vec![Field::new("a", DataType::UInt32, true)])),
2202        ] {
2203            let block = DataBlock::AllNull(AllNullDataBlock { num_values: 10 });
2204            let arr = block.into_arrow(data_type.clone(), true).unwrap();
2205            let arr = make_array(arr);
2206            let expected = new_null_array(&data_type, 10);
2207            assert_eq!(&arr, &expected);
2208        }
2209    }
2210
2211    #[test]
2212    fn test_dictionary_cannot_concatenate() {
2213        // 256 unique strings
2214        let items = StringArray::from(
2215            (0..256)
2216                .map(|i| Some(String::from_utf8(vec![0; i]).unwrap()))
2217                .collect::<Vec<_>>(),
2218        );
2219        // 256 different unique strings
2220        let other_items = StringArray::from(
2221            (0..256)
2222                .map(|i| Some(String::from_utf8(vec![1; i + 1]).unwrap()))
2223                .collect::<Vec<_>>(),
2224        );
2225        let indices = UInt8Array::from_iter_values(0..=255);
2226        let dict1 = DictionaryArray::new(indices.clone(), Arc::new(items));
2227        let dict2 = DictionaryArray::new(indices, Arc::new(other_items));
2228        let data = DataBlock::from_arrays(&[Arc::new(dict1), Arc::new(dict2)], 512);
2229        assert_eq!(data.num_values(), 512);
2230
2231        let dict = data.as_dictionary().unwrap();
2232
2233        assert_eq!(dict.indices.bits_per_value, 32);
2234        assert_eq!(
2235            dict.indices.data,
2236            LanceBuffer::reinterpret_vec::<u32>((0..512).collect::<Vec<_>>())
2237        );
2238        // What fun: 0 + 1 + .. + 255 + 1 + 2 + .. + 256 = 2^16
2239        assert_eq!(
2240            dict.dictionary.as_variable_width().unwrap().data.len(),
2241            65536
2242        );
2243    }
2244
2245    #[test]
2246    fn test_data_size() {
2247        let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(DEFAULT_SEED.0);
2248        // test data_size() when input has no nulls
2249        let mut genn = array::rand::<Int32Type>().with_nulls(&[false, false, false]);
2250
2251        let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2252        let block = DataBlock::from_array(arr.clone());
2253        assert!(block.data_size() == arr.get_buffer_memory_size() as u64);
2254
2255        let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2256        let block = DataBlock::from_array(arr.clone());
2257        assert!(block.data_size() == arr.get_buffer_memory_size() as u64);
2258
2259        // test data_size() when input has nulls
2260        let mut genn = array::rand::<Int32Type>().with_nulls(&[false, true, false]);
2261        let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2262        let block = DataBlock::from_array(arr.clone());
2263
2264        let array_data = arr.to_data();
2265        let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2266        // the NullBuffer.len() returns the length in bits so we divide_round_up by 8
2267        let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2268        assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2269
2270        let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2271        let block = DataBlock::from_array(arr.clone());
2272
2273        let array_data = arr.to_data();
2274        let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2275        let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2276        assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2277
2278        let mut genn = array::rand::<Int32Type>().with_nulls(&[true, true, false]);
2279        let arr = genn.generate(RowCount::from(3), &mut rng).unwrap();
2280        let block = DataBlock::from_array(arr.clone());
2281
2282        let array_data = arr.to_data();
2283        let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2284        let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2285        assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2286
2287        let arr = genn.generate(RowCount::from(400), &mut rng).unwrap();
2288        let block = DataBlock::from_array(arr.clone());
2289
2290        let array_data = arr.to_data();
2291        let total_buffer_size: usize = array_data.buffers().iter().map(|buffer| buffer.len()).sum();
2292        let array_nulls_size_in_bytes = arr.nulls().unwrap().len().div_ceil(8);
2293        assert!(block.data_size() == (total_buffer_size + array_nulls_size_in_bytes) as u64);
2294
2295        let mut genn = array::rand::<Int32Type>().with_nulls(&[false, true, false]);
2296        let arr1 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2297        let arr2 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2298        let arr3 = genn.generate(RowCount::from(3), &mut rng).unwrap();
2299        let block = DataBlock::from_arrays(&[arr1.clone(), arr2.clone(), arr3.clone()], 9);
2300
2301        let concatenated_array = arrow_select::concat::concat(&[
2302            &*Arc::new(arr1.clone()) as &dyn Array,
2303            &*Arc::new(arr2.clone()) as &dyn Array,
2304            &*Arc::new(arr3.clone()) as &dyn Array,
2305        ])
2306        .unwrap();
2307        let total_buffer_size: usize = concatenated_array
2308            .to_data()
2309            .buffers()
2310            .iter()
2311            .map(|buffer| buffer.len())
2312            .sum();
2313
2314        let total_nulls_size_in_bytes = concatenated_array.nulls().unwrap().len().div_ceil(8);
2315        assert!(block.data_size() == (total_buffer_size + total_nulls_size_in_bytes) as u64);
2316    }
2317
2318    #[test]
2319    fn variable_width_rejects_out_of_bounds_offsets_without_optional_validation() {
2320        let block = VariableWidthBlock {
2321            data: LanceBuffer::copy_slice(b"alphabetagamma"),
2322            offsets: LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2323            bits_per_offset: 32,
2324            num_values: 3,
2325            block_info: BlockInfo::new(),
2326        };
2327
2328        let error = block
2329            .into_arrow(DataType::Binary, false)
2330            .expect_err("out-of-bounds offsets must be rejected");
2331        assert!(
2332            matches!(error, Error::CorruptFile { .. }),
2333            "expected CorruptFile, got: {error}"
2334        );
2335        let message = error.to_string();
2336        assert!(
2337            message.contains("100000") && message.contains("data buffer size: 14 bytes"),
2338            "error must report the offending offset and the data buffer size: {message}"
2339        );
2340    }
2341
2342    #[rstest]
2343    #[case::binary_i32_tail_out_of_bounds(
2344        DataType::Binary,
2345        LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2346        32,
2347        3,
2348        b"alphabetagamma".as_slice()
2349    )]
2350    #[case::utf8_i32_tail_out_of_bounds(
2351        DataType::Utf8,
2352        LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2353        32,
2354        3,
2355        b"alphabetagamma".as_slice()
2356    )]
2357    #[case::large_binary_i64_tail_out_of_bounds(
2358        DataType::LargeBinary,
2359        LanceBuffer::reinterpret_vec(vec![0_i64, 5, 9, 100_000]),
2360        64,
2361        3,
2362        b"alphabetagamma".as_slice()
2363    )]
2364    #[case::large_utf8_i64_tail_out_of_bounds(
2365        DataType::LargeUtf8,
2366        LanceBuffer::reinterpret_vec(vec![0_i64, 5, 9, 100_000]),
2367        64,
2368        3,
2369        b"alphabetagamma".as_slice()
2370    )]
2371    #[case::binary_negative_offset(
2372        DataType::Binary,
2373        LanceBuffer::reinterpret_vec(vec![0_i32, -1, 9, 14]),
2374        32,
2375        3,
2376        b"alphabetagamma".as_slice()
2377    )]
2378    #[case::binary_non_monotonic_offsets(
2379        DataType::Binary,
2380        LanceBuffer::reinterpret_vec(vec![0_i32, 9, 5, 14]),
2381        32,
2382        3,
2383        b"alphabetagamma".as_slice()
2384    )]
2385    #[case::binary_interior_offset_out_of_bounds(
2386        DataType::Binary,
2387        LanceBuffer::reinterpret_vec(vec![0_i32, 100_000, 100_000, 14]),
2388        32,
2389        3,
2390        b"alphabetagamma".as_slice()
2391    )]
2392    #[case::binary_offsets_buffer_too_short(
2393        DataType::Binary,
2394        LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9]),
2395        32,
2396        3,
2397        b"alphabetagamma".as_slice()
2398    )]
2399    #[case::utf8_invalid_byte_sequence(
2400        DataType::Utf8,
2401        LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2, 3]),
2402        32,
2403        3,
2404        &[b'a', 0xFF, b'b']
2405    )]
2406    #[case::utf8_offset_splits_multibyte_char(
2407        DataType::Utf8,
2408        LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2]),
2409        32,
2410        2,
2411        "é".as_bytes()
2412    )]
2413    #[case::large_utf8_invalid_byte_sequence(
2414        DataType::LargeUtf8,
2415        LanceBuffer::reinterpret_vec(vec![0_i64, 1, 2, 3]),
2416        64,
2417        3,
2418        &[b'a', 0xFF, b'b']
2419    )]
2420    fn variable_width_rejects_malformed_layout(
2421        #[case] data_type: DataType,
2422        #[case] offsets: LanceBuffer,
2423        #[case] bits_per_offset: u8,
2424        #[case] num_values: u64,
2425        #[case] data: &[u8],
2426    ) {
2427        let block = VariableWidthBlock {
2428            data: LanceBuffer::copy_slice(data),
2429            offsets,
2430            bits_per_offset,
2431            num_values,
2432            block_info: BlockInfo::new(),
2433        };
2434
2435        // The malformed layout must be rejected regardless of the optional
2436        // `validate` flag: the flag selects extra validation, not the memory
2437        // safety proof required to construct an Arrow array.
2438        for validate in [false, true] {
2439            let error = DataBlock::VariableWidth(block.clone())
2440                .into_arrow(data_type.clone(), validate)
2441                .expect_err("malformed variable-width layout must be rejected");
2442            assert!(
2443                matches!(error, Error::CorruptFile { .. }),
2444                "expected CorruptFile with validate={validate}, got: {error}"
2445            );
2446        }
2447    }
2448
2449    #[test]
2450    fn dictionary_rejects_malformed_variable_width_values_without_optional_validation() {
2451        let values = VariableWidthBlock {
2452            data: LanceBuffer::copy_slice(b"alphabetagamma"),
2453            offsets: LanceBuffer::reinterpret_vec(vec![0_i32, 5, 9, 100_000]),
2454            bits_per_offset: 32,
2455            num_values: 3,
2456            block_info: BlockInfo::new(),
2457        };
2458        let dictionary = DataBlock::Dictionary(DictionaryDataBlock {
2459            indices: FixedWidthDataBlock {
2460                data: LanceBuffer::reinterpret_vec(vec![0_i32, 1, 2]),
2461                bits_per_value: 32,
2462                num_values: 3,
2463                block_info: BlockInfo::new(),
2464            },
2465            dictionary: Box::new(DataBlock::VariableWidth(values)),
2466        });
2467
2468        let data_type = DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Binary));
2469        let error = dictionary
2470            .into_arrow(data_type, false)
2471            .expect_err("dictionary with out-of-bounds value offsets must be rejected");
2472        assert!(
2473            matches!(error, Error::CorruptFile { .. }),
2474            "expected CorruptFile, got: {error}"
2475        );
2476    }
2477
2478    #[rstest]
2479    #[case::binary(Arc::new(BinaryArray::from_vec(vec![b"alpha", b"", b"gamma"])) as ArrayRef)]
2480    #[case::large_binary(
2481        Arc::new(LargeBinaryArray::from_vec(vec![b"alpha", b"", b"gamma"])) as ArrayRef
2482    )]
2483    #[case::utf8(Arc::new(StringArray::from(vec!["héllo", "", "world"])) as ArrayRef)]
2484    #[case::large_utf8(Arc::new(LargeStringArray::from(vec!["héllo", "", "world"])) as ArrayRef)]
2485    fn variable_width_valid_data_survives_mandatory_validation(#[case] array: ArrayRef) {
2486        let block = DataBlock::from_array(array.clone());
2487        for validate in [false, true] {
2488            let round_tripped = make_array(
2489                block
2490                    .clone()
2491                    .into_arrow(array.data_type().clone(), validate)
2492                    .unwrap(),
2493            );
2494            assert_eq!(&round_tripped, &array);
2495        }
2496    }
2497}