Skip to main content

arrow_row/
list.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::{LengthTracker, RowConverter, Rows, SortField, fixed, null_sentinel};
19use arrow_array::{
20    Array, ArrayRef, FixedSizeListArray, GenericListArray, GenericListViewArray, MapArray,
21    OffsetSizeTrait, StructArray, new_null_array,
22};
23use arrow_buffer::{
24    ArrowNativeType, BooleanBuffer, MutableBuffer, NullBuffer, OffsetBuffer, ScalarBuffer,
25};
26use arrow_schema::{ArrowError, DataType, Fields, SortOptions};
27use std::{ops::Range, sync::Arc};
28
29pub(crate) trait GenericListArrayOrMap: Array {
30    type Offset: OffsetSizeTrait;
31
32    fn offsets(&self) -> &[Self::Offset];
33
34    unsafe fn from_parts_unchecked(
35        data_type: DataType,
36        offsets: Vec<Self::Offset>,
37        children: Vec<ArrayRef>,
38        null_buffer: Option<NullBuffer>,
39    ) -> Self
40    where
41        Self: Sized;
42}
43
44impl<O: OffsetSizeTrait> GenericListArrayOrMap for GenericListArray<O> {
45    type Offset = O;
46
47    fn offsets(&self) -> &[Self::Offset] {
48        self.value_offsets()
49    }
50
51    unsafe fn from_parts_unchecked(
52        data_type: DataType,
53        offsets: Vec<Self::Offset>,
54        children: Vec<ArrayRef>,
55        null_buffer: Option<NullBuffer>,
56    ) -> Self
57    where
58        Self: Sized,
59    {
60        let (DataType::List(field) | DataType::LargeList(field)) = data_type else {
61            unreachable!()
62        };
63
64        let child = children
65            .into_iter()
66            .next()
67            .expect("List arrays must have exactly one child array");
68
69        // SAFETY: Caller must ensure offsets are valid and correctly correspond to the children and null buffer
70        // the benefit here is to avoid validating that the offsets are monotonically increasing
71        let offset_buffer = unsafe { OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets)) };
72        GenericListArray::<Self::Offset>::new(field, offset_buffer, child, null_buffer)
73    }
74}
75
76impl GenericListArrayOrMap for MapArray {
77    type Offset = i32;
78
79    fn offsets(&self) -> &[Self::Offset] {
80        self.value_offsets()
81    }
82
83    unsafe fn from_parts_unchecked(
84        data_type: DataType,
85        offsets: Vec<Self::Offset>,
86        children: Vec<ArrayRef>,
87        null_buffer: Option<NullBuffer>,
88    ) -> Self
89    where
90        Self: Sized,
91    {
92        let DataType::Map(entries_field, ordered) = data_type else {
93            unreachable!("data type must be Map for MapArray");
94        };
95
96        assert_eq!(
97            children.len(),
98            2,
99            "Map arrays must have exactly two child arrays for keys and values"
100        );
101
102        let DataType::Struct(fields) = entries_field.data_type() else {
103            unreachable!("Map entry type must be Struct");
104        };
105
106        let entries = StructArray::new(
107            fields.clone(),
108            children,
109            // Entries StructArray cannot have NullBuffer since nulls are represented at the Map level
110            None,
111        );
112
113        // SAFETY: Caller must ensure offsets are valid and correctly correspond to the children and null buffer
114        // the benefit here is to avoid validating that the offsets are monotonically increasing
115        let offset_buffer = unsafe { OffsetBuffer::new_unchecked(ScalarBuffer::from(offsets)) };
116
117        MapArray::new(entries_field, offset_buffer, entries, null_buffer, ordered)
118    }
119}
120
121pub(crate) fn compute_lengths<L: GenericListArrayOrMap>(
122    lengths: &mut [usize],
123    rows: &Rows,
124    array: &L,
125) {
126    let shift = array.offsets()[0].as_usize();
127
128    lengths
129        .iter_mut()
130        .zip(array.offsets().windows(2))
131        .enumerate()
132        .for_each(|(idx, (length, offsets))| {
133            let start = offsets[0].as_usize() - shift;
134            let end = offsets[1].as_usize() - shift;
135            let range = array.is_valid(idx).then_some(start..end);
136            *length += list_like_element_encoded_len(rows, range);
137        });
138}
139
140/// Encodes the provided [`GenericListArrayOrMap`] to `out` with the provided `SortOptions`
141///
142/// `rows` should contain the encoded child elements
143pub(crate) fn encode<L: GenericListArrayOrMap>(
144    data: &mut [u8],
145    offsets: &mut [usize],
146    rows: &Rows,
147    opts: SortOptions,
148    array: &L,
149) {
150    let shift = array.offsets()[0].as_usize();
151
152    offsets
153        .iter_mut()
154        .skip(1)
155        .zip(array.offsets().windows(2))
156        .enumerate()
157        .for_each(|(idx, (offset, offsets))| {
158            let start = offsets[0].as_usize() - shift;
159            let end = offsets[1].as_usize() - shift;
160            let range = array.is_valid(idx).then_some(start..end);
161            let out = &mut data[*offset..];
162            *offset += encode_one(out, rows, range, opts)
163        });
164}
165
166#[inline]
167fn encode_one(
168    out: &mut [u8],
169    rows: &Rows,
170    range: Option<Range<usize>>,
171    opts: SortOptions,
172) -> usize {
173    match range {
174        None => super::variable::encode_null(out, opts),
175        Some(range) if range.start == range.end => super::variable::encode_empty(out, opts),
176        Some(range) => {
177            let mut offset = 0;
178            for i in range {
179                let row = rows.row(i);
180                offset += super::variable::encode_one(&mut out[offset..], Some(row.data), opts);
181            }
182            offset += super::variable::encode_empty(&mut out[offset..], opts);
183            offset
184        }
185    }
186}
187
188/// Decodes an array from `rows` with the provided `options`
189///
190/// # Safety
191///
192/// `rows` must contain valid data for the provided `converter`
193pub(crate) unsafe fn decode<ListLikeImpl: GenericListArrayOrMap>(
194    converter: &RowConverter,
195    rows: &mut [&[u8]],
196    field: &SortField,
197    validate_utf8: bool,
198) -> Result<ListLikeImpl, ArrowError> {
199    let opts = field.options;
200
201    let mut values_bytes = 0;
202
203    let mut offset = 0;
204    let mut offsets = Vec::with_capacity(rows.len() + 1);
205    offsets.push(ListLikeImpl::Offset::usize_as(0));
206
207    for row in rows.iter_mut() {
208        let mut row_offset = 0;
209        loop {
210            let decoded = super::variable::decode_blocks(&row[row_offset..], opts, |x| {
211                values_bytes += x.len();
212            });
213            if decoded <= 1 {
214                offsets.push(ListLikeImpl::Offset::usize_as(offset));
215                break;
216            }
217            row_offset += decoded;
218            offset += 1;
219        }
220    }
221    ListLikeImpl::Offset::from_usize(offset).expect("overflow");
222
223    let nulls = crate::variable::decode_nulls_sentinel(rows, opts);
224
225    let mut values_offsets = Vec::with_capacity(offset);
226    let mut values_bytes = Vec::with_capacity(values_bytes);
227    for row in rows.iter_mut() {
228        let mut row_offset = 0;
229        loop {
230            let decoded = super::variable::decode_blocks(&row[row_offset..], opts, |x| {
231                values_bytes.extend_from_slice(x)
232            });
233            row_offset += decoded;
234            if decoded <= 1 {
235                break;
236            }
237            values_offsets.push(values_bytes.len());
238        }
239        *row = &row[row_offset..];
240    }
241
242    if opts.descending {
243        values_bytes.iter_mut().for_each(|o| *o = !*o);
244    }
245
246    let mut last_value_offset = 0;
247    let mut child_rows: Vec<_> = values_offsets
248        .into_iter()
249        .map(|offset| {
250            let v = &values_bytes[last_value_offset..offset];
251            last_value_offset = offset;
252            v
253        })
254        .collect();
255
256    let children = unsafe { converter.convert_raw(&mut child_rows, validate_utf8) }?;
257
258    // Since RowConverter flattens certain data types (i.e. Dictionary),
259    // we need to use updated data type instead of original field
260    let corrected_type = match &field.data_type {
261        DataType::List(inner_field) => {
262            assert_eq!(children.len(), 1);
263            DataType::List(Arc::new(
264                inner_field
265                    .as_ref()
266                    .clone()
267                    .with_data_type(children[0].data_type().clone()),
268            ))
269        }
270        DataType::LargeList(inner_field) => {
271            assert_eq!(children.len(), 1);
272            DataType::LargeList(Arc::new(
273                inner_field
274                    .as_ref()
275                    .clone()
276                    .with_data_type(children[0].data_type().clone()),
277            ))
278        }
279        DataType::Map(inner_field, ordered) => {
280            let DataType::Struct(entries_field) = inner_field.data_type() else {
281                return Err(ArrowError::InvalidArgumentError(format!(
282                    "Expected Map entry type to be Struct, found: {}",
283                    inner_field.data_type()
284                )));
285            };
286            assert_eq!(
287                children.len(),
288                2,
289                "Map arrays must have exactly two child arrays for keys and values"
290            );
291            let key_field = entries_field[0]
292                .as_ref()
293                .clone()
294                .with_data_type(children[0].data_type().clone());
295            let value_field = entries_field[1]
296                .as_ref()
297                .clone()
298                .with_data_type(children[1].data_type().clone());
299
300            let entries_fields = Fields::from(vec![key_field, value_field]);
301
302            DataType::Map(
303                Arc::new(
304                    inner_field
305                        .as_ref()
306                        .clone()
307                        .with_data_type(DataType::Struct(entries_fields)),
308                ),
309                *ordered,
310            )
311        }
312        _ => unreachable!(),
313    };
314
315    Ok(unsafe { ListLikeImpl::from_parts_unchecked(corrected_type, offsets, children, nulls) })
316}
317
318pub fn compute_lengths_fixed_size_list(
319    tracker: &mut LengthTracker,
320    rows: &Rows,
321    array: &FixedSizeListArray,
322) {
323    let value_length = array.value_length().as_usize();
324    tracker.push_variable((0..array.len()).map(|idx| {
325        match array.is_valid(idx) {
326            true => {
327                1 + ((idx * value_length)..(idx + 1) * value_length)
328                    .map(|child_idx| rows.row(child_idx).as_ref().len())
329                    .sum::<usize>()
330            }
331            false => 1,
332        }
333    }))
334}
335
336/// Encodes the provided `FixedSizeListArray` to `out` with the provided `SortOptions`
337///
338/// `rows` should contain the encoded child elements
339pub fn encode_fixed_size_list(
340    data: &mut [u8],
341    offsets: &mut [usize],
342    rows: &Rows,
343    opts: SortOptions,
344    array: &FixedSizeListArray,
345) {
346    let null_sentinel = null_sentinel(opts);
347    offsets
348        .iter_mut()
349        .skip(1)
350        .enumerate()
351        .for_each(|(idx, offset)| {
352            let value_length = array.value_length().as_usize();
353            match array.is_valid(idx) {
354                true => {
355                    data[*offset] = 0x01;
356                    *offset += 1;
357                    for child_idx in (idx * value_length)..(idx + 1) * value_length {
358                        let row = rows.row(child_idx);
359                        let end_offset = *offset + row.as_ref().len();
360                        data[*offset..end_offset].copy_from_slice(row.as_ref());
361                        *offset = end_offset;
362                    }
363                }
364                false => {
365                    data[*offset] = null_sentinel;
366                    *offset += 1;
367                }
368            }
369        })
370}
371
372/// Decodes a fixed size list array from `rows` with the provided `options`
373///
374/// # Safety
375///
376/// `rows` must contain valid data for the provided `converter`
377pub unsafe fn decode_fixed_size_list(
378    converter: &RowConverter,
379    rows: &mut [&[u8]],
380    field: &SortField,
381    validate_utf8: bool,
382    value_length: usize,
383) -> Result<FixedSizeListArray, ArrowError> {
384    let list_type = &field.data_type;
385    let DataType::FixedSizeList(element_field, size) = list_type else {
386        return Err(ArrowError::InvalidArgumentError(format!(
387            "Expected FixedSizeListArray, found: {list_type}",
388        )));
389    };
390
391    let num_rows = rows.len();
392    let nulls = fixed::decode_nulls(rows);
393
394    let null_element_encoded =
395        converter.convert_columns(&[new_null_array(element_field.data_type(), 1)])?;
396    let null_element_encoded = null_element_encoded.row(0);
397    let null_element_slice = null_element_encoded.as_ref();
398
399    let mut child_rows = Vec::new();
400    for row in rows {
401        let valid = row[0] == 1;
402        let mut row_offset = 1;
403        if !valid {
404            for _ in 0..value_length {
405                child_rows.push(null_element_slice);
406            }
407        } else {
408            for _ in 0..value_length {
409                let mut temp_child_rows = vec![&row[row_offset..]];
410                unsafe { converter.convert_raw(&mut temp_child_rows, validate_utf8) }?;
411                let decoded_bytes = row.len() - row_offset - temp_child_rows[0].len();
412                let next_offset = row_offset + decoded_bytes;
413                child_rows.push(&row[row_offset..next_offset]);
414                row_offset = next_offset;
415            }
416        }
417        *row = &row[row_offset..]; // Update row for the next decoder
418    }
419
420    let mut children = unsafe { converter.convert_raw(&mut child_rows, validate_utf8) }?;
421    assert_eq!(children.len(), 1);
422
423    // Since RowConverter flattens certain data types (i.e. Dictionary),
424    // we need to use updated data type instead of original field
425    let corrected_element_field = Arc::new(
426        element_field
427            .as_ref()
428            .clone()
429            .with_data_type(children[0].data_type().clone()),
430    );
431
432    FixedSizeListArray::try_new_with_length(
433        corrected_element_field,
434        *size,
435        children.pop().unwrap(),
436        nulls,
437        num_rows,
438    )
439}
440
441/// Computes the encoded length for a single list/map element given its child rows.
442///
443/// This is used by list types (List, LargeList, ListView, LargeListView) and by Map to determine
444/// the encoded length of a list element/map entry. For null elements, returns 1 (null sentinel only).
445/// For valid elements, returns 1 + the sum of padded lengths for each child row.
446#[inline]
447fn list_like_element_encoded_len(rows: &Rows, range: Option<Range<usize>>) -> usize {
448    match range {
449        None => 1,
450        Some(range) => {
451            1 + range
452                .map(|i| super::variable::padded_length(Some(rows.row(i).as_ref().len())))
453                .sum::<usize>()
454        }
455    }
456}
457
458/// Computes the encoded lengths for a `GenericListViewArray`
459///
460/// `rows` should contain the encoded child elements
461pub fn compute_lengths_list_view<O: OffsetSizeTrait>(
462    lengths: &mut [usize],
463    rows: &Rows,
464    array: &GenericListViewArray<O>,
465    shift: usize,
466) {
467    let offsets = array.value_offsets();
468    let sizes = array.value_sizes();
469
470    lengths.iter_mut().enumerate().for_each(|(idx, length)| {
471        let size = sizes[idx].as_usize();
472        let range = array.is_valid(idx).then(|| {
473            // For empty lists (size=0), offset may be arbitrary and could underflow when shifted.
474            // Use 0 as start since the range is empty anyway.
475            let start = if size > 0 {
476                offsets[idx].as_usize() - shift
477            } else {
478                0
479            };
480            start..start + size
481        });
482        *length += list_like_element_encoded_len(rows, range);
483    });
484}
485
486/// Encodes the provided `GenericListViewArray` to `out` with the provided `SortOptions`
487///
488/// `rows` should contain the encoded child elements
489pub fn encode_list_view<O: OffsetSizeTrait>(
490    data: &mut [u8],
491    out_offsets: &mut [usize],
492    rows: &Rows,
493    opts: SortOptions,
494    array: &GenericListViewArray<O>,
495    shift: usize,
496) {
497    let offsets = array.value_offsets();
498    let sizes = array.value_sizes();
499
500    out_offsets
501        .iter_mut()
502        .skip(1)
503        .enumerate()
504        .for_each(|(idx, offset)| {
505            let size = sizes[idx].as_usize();
506            let range = array.is_valid(idx).then(|| {
507                // For empty lists (size=0), offset may be arbitrary and could underflow when shifted.
508                // Use 0 as start since the range is empty anyway.
509                let start = if size > 0 {
510                    offsets[idx].as_usize() - shift
511                } else {
512                    0
513                };
514                start..start + size
515            });
516            let out = &mut data[*offset..];
517            *offset += encode_one(out, rows, range, opts)
518        });
519}
520
521/// Decodes a `GenericListViewArray` from `rows` with the provided `options`
522///
523/// # Safety
524///
525/// `rows` must contain valid data for the provided `converter`
526pub unsafe fn decode_list_view<O: OffsetSizeTrait>(
527    converter: &RowConverter,
528    rows: &mut [&[u8]],
529    field: &SortField,
530    validate_utf8: bool,
531) -> Result<GenericListViewArray<O>, ArrowError> {
532    let opts = field.options;
533
534    let mut values_bytes = 0;
535
536    let mut child_count = 0usize;
537    let mut list_sizes: Vec<O> = Vec::with_capacity(rows.len());
538
539    // First pass: count children and compute sizes
540    for row in rows.iter_mut() {
541        let mut row_offset = 0;
542        let mut list_size = 0usize;
543        loop {
544            let decoded = super::variable::decode_blocks(&row[row_offset..], opts, |x| {
545                values_bytes += x.len();
546            });
547            if decoded <= 1 {
548                list_sizes.push(O::usize_as(list_size));
549                break;
550            }
551            row_offset += decoded;
552            child_count += 1;
553            list_size += 1;
554        }
555    }
556    O::from_usize(child_count).expect("overflow");
557
558    let null_sentinel = null_sentinel(opts);
559    let mut null_count = 0;
560    let nulls = MutableBuffer::collect_bool(rows.len(), |x| {
561        let valid = rows[x][0] != null_sentinel;
562        null_count += !valid as usize;
563        valid
564    });
565
566    let mut values_offsets_vec = Vec::with_capacity(child_count);
567    let mut values_bytes = Vec::with_capacity(values_bytes);
568    for row in rows.iter_mut() {
569        let mut row_offset = 0;
570        loop {
571            let decoded = super::variable::decode_blocks(&row[row_offset..], opts, |x| {
572                values_bytes.extend_from_slice(x)
573            });
574            row_offset += decoded;
575            if decoded <= 1 {
576                break;
577            }
578            values_offsets_vec.push(values_bytes.len());
579        }
580        *row = &row[row_offset..];
581    }
582
583    if opts.descending {
584        values_bytes.iter_mut().for_each(|o| *o = !*o);
585    }
586
587    let mut last_value_offset = 0;
588    let mut child_rows: Vec<_> = values_offsets_vec
589        .into_iter()
590        .map(|offset| {
591            let v = &values_bytes[last_value_offset..offset];
592            last_value_offset = offset;
593            v
594        })
595        .collect();
596
597    let child = unsafe { converter.convert_raw(&mut child_rows, validate_utf8) }?;
598    assert_eq!(child.len(), 1);
599
600    let child_data = child[0].to_data();
601
602    // Technically ListViews don't have to have offsets follow each other precisely, but can be
603    // reused. However, because we cannot preserve that sharing within the row format, this is the
604    // best we can do.
605    let mut list_offsets: Vec<O> = Vec::with_capacity(rows.len());
606    let mut current_offset = O::usize_as(0);
607    for size in &list_sizes {
608        list_offsets.push(current_offset);
609        current_offset += *size;
610    }
611
612    // Since RowConverter flattens certain data types (i.e. Dictionary),
613    // we need to use updated data type instead of original field
614    let corrected_inner_field = match &field.data_type {
615        DataType::ListView(inner_field) | DataType::LargeListView(inner_field) => Arc::new(
616            inner_field
617                .as_ref()
618                .clone()
619                .with_data_type(child_data.data_type().clone()),
620        ),
621        _ => unreachable!(),
622    };
623
624    // SAFETY: null_count was computed correctly when building the nulls buffer above
625    let null_buffer = unsafe {
626        NullBuffer::new_unchecked(BooleanBuffer::new(nulls.into(), 0, rows.len()), null_count)
627    };
628
629    GenericListViewArray::try_new(
630        corrected_inner_field,
631        ScalarBuffer::from(list_offsets),
632        ScalarBuffer::from(list_sizes),
633        child[0].clone(),
634        Some(null_buffer).filter(|n| n.null_count() > 0),
635    )
636}