Skip to main content

lance_encoding/
data.rs

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