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