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