Skip to main content

lance_encoding/encodings/logical/primitive/
sparse.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright The Lance Authors
3
4use super::structural_encoder::{
5    DictionaryEncodingPolicy, MiniblockChunkSize, PrimitiveEncodeContext,
6};
7use super::*;
8use crate::compression::CompressionStrategy;
9use crate::data::{BlockInfo, FixedWidthDataBlock};
10use crate::decoder::{PageEncoding, PageInfo};
11use crate::encoder::EncodedPage;
12use crate::encodings::physical::sequence::{SequenceDecoder, SequenceMetadata};
13use crate::format::pb23;
14use crate::repdef::{DefinitionInterpretation, SerializedRepDefs};
15use crate::statistics::ComputeStat;
16use arrow_array::{Array, ArrayRef, make_array};
17use arrow_buffer::ArrowNativeType;
18use arrow_buffer::BooleanBuffer;
19use lance_arrow::DataTypeExt;
20use lance_core::datatypes::Field;
21
22fn invalid_enum<T>(
23    value: std::result::Result<T, prost::UnknownEnumValue>,
24    label: &str,
25) -> Result<T> {
26    value.map_err(|error| {
27        Error::invalid_input_source(
28            format!("Sparse structural {label} has an invalid enum value: {error}").into(),
29        )
30    })
31}
32
33pub(super) mod dictionary;
34pub(super) mod planar;
35pub(super) mod writer;
36
37fn usize_from_u64(value: u64, label: &str) -> Result<usize> {
38    usize::try_from(value).map_err(|_| {
39        Error::invalid_input_source(
40            format!("Sparse structural {label} {value} exceeds usize::MAX").into(),
41        )
42    })
43}
44
45/// Native sparse structural representation used by the 2.3 sparse layout.
46///
47/// Layers are stored from outer-most to inner-most, matching the order Arrow structural
48/// encoders record offsets and validity.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct SparseStructuralPlan {
51    pub(crate) layers: Vec<SparseStructuralLayerPlan>,
52    pub(crate) num_visible_items: u64,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum SparsePositionPlan {
57    Empty,
58    All { len: u64 },
59    Range { start: u64, len: u64 },
60    Arithmetic { start: u64, step: u64, len: u64 },
61    Explicit(Vec<u64>),
62}
63
64impl SparsePositionPlan {
65    pub(crate) fn from_positions(
66        positions: Vec<u64>,
67        domain_len: u64,
68        label: &str,
69    ) -> Result<Self> {
70        if positions.is_empty() {
71            return Ok(Self::Empty);
72        }
73        for window in positions.windows(2) {
74            let [previous, current] = window else {
75                continue;
76            };
77            if previous >= current {
78                return Err(Error::invalid_input_source(
79                    format!("Sparse structural {label} positions must be strictly increasing")
80                        .into(),
81                ));
82            }
83        }
84        if let Some(position) = positions.iter().find(|position| **position >= domain_len) {
85            return Err(Error::invalid_input_source(
86                format!(
87                    "Sparse structural {label} position {} is outside layer with {} slots",
88                    position, domain_len
89                )
90                .into(),
91            ));
92        }
93
94        let len = u64::try_from(positions.len()).map_err(|_| {
95            Error::invalid_input_source(
96                format!("Sparse structural {label} position count exceeds u64::MAX").into(),
97            )
98        })?;
99        let first = positions.first().copied().ok_or_else(|| {
100            Error::invalid_input_source(
101                format!("Sparse structural {label} positions are unexpectedly empty").into(),
102            )
103        })?;
104        let last = positions.last().copied().ok_or_else(|| {
105            Error::invalid_input_source(
106                format!("Sparse structural {label} positions are unexpectedly empty").into(),
107            )
108        })?;
109        if first == 0 && len == domain_len && domain_len > 0 && last == domain_len - 1 {
110            return Ok(Self::All { len: domain_len });
111        }
112        if last - first + 1 == len {
113            return Ok(Self::Range { start: first, len });
114        }
115        if len >= 2 {
116            let step = positions[1] - first;
117            if positions.windows(2).all(|pair| pair[1] - pair[0] == step) {
118                return Ok(Self::Arithmetic {
119                    start: first,
120                    step,
121                    len,
122                });
123            }
124        }
125        Ok(Self::Explicit(positions))
126    }
127
128    pub(crate) fn empty() -> Self {
129        Self::Empty
130    }
131
132    pub(crate) fn all(len: u64) -> Self {
133        if len == 0 {
134            Self::Empty
135        } else {
136            Self::All { len }
137        }
138    }
139
140    pub(crate) fn range(start: u64, len: u64) -> Self {
141        if len == 0 {
142            Self::Empty
143        } else {
144            Self::Range { start, len }
145        }
146    }
147
148    pub(crate) fn len(&self) -> u64 {
149        match self {
150            Self::Empty => 0,
151            Self::All { len } | Self::Range { len, .. } | Self::Arithmetic { len, .. } => *len,
152            Self::Explicit(positions) => positions.len() as u64,
153        }
154    }
155
156    pub(crate) fn is_empty(&self) -> bool {
157        self.len() == 0
158    }
159
160    pub(crate) fn deep_size(&self) -> usize {
161        match self {
162            Self::Explicit(positions) => positions.len() * std::mem::size_of::<u64>(),
163            Self::Empty | Self::All { .. } | Self::Range { .. } | Self::Arithmetic { .. } => 0,
164        }
165    }
166
167    pub(crate) fn materialize(&self) -> Result<Vec<u64>> {
168        match self {
169            Self::Empty => Ok(Vec::new()),
170            Self::All { len } => Self::materialize_range(0, *len),
171            Self::Range { start, len } => Self::materialize_range(*start, *len),
172            Self::Arithmetic { start, step, len } => {
173                let mut values = crate::encodings::physical::try_vec_with_capacity::<u64>(
174                    *len,
175                    "Sparse positions",
176                )?;
177                values.extend((0..*len).map(|i| start + step * i));
178                Ok(values)
179            }
180            Self::Explicit(positions) => Ok(positions.clone()),
181        }
182    }
183
184    fn materialize_range(start: u64, len: u64) -> Result<Vec<u64>> {
185        let len_usize = usize::try_from(len).map_err(|_| {
186            Error::invalid_input_source(
187                format!(
188                    "Sparse structural position range length {} exceeds usize::MAX",
189                    len
190                )
191                .into(),
192            )
193        })?;
194        let end = start.checked_add(len).ok_or_else(|| {
195            Error::invalid_input_source("Sparse structural position range overflows".into())
196        })?;
197        let mut positions = Vec::with_capacity(len_usize);
198        positions.extend(start..end);
199        Ok(positions)
200    }
201
202    fn contains(&self, position: u64) -> bool {
203        match self {
204            Self::Empty => false,
205            Self::All { len } => position < *len,
206            Self::Range { start, len } => {
207                position >= *start && position < start.saturating_add(*len)
208            }
209            Self::Arithmetic { start, step, len } => {
210                position >= *start
211                    && (position - start).is_multiple_of(*step)
212                    && (position - start) / step < *len
213            }
214            Self::Explicit(positions) => positions.binary_search(&position).is_ok(),
215        }
216    }
217
218    fn progression(&self) -> Option<(u64, u64, u64)> {
219        match self {
220            Self::All { len } => Some((0, 1, *len)),
221            Self::Range { start, len } => Some((*start, 1, *len)),
222            Self::Arithmetic { start, step, len } => Some((*start, *step, *len)),
223            _ => None,
224        }
225    }
226
227    fn rank(&self, position: u64) -> u64 {
228        if let Some((start, step, len)) = self.progression() {
229            position.saturating_sub(start).div_ceil(step).min(len)
230        } else if let Self::Explicit(values) = self {
231            values.partition_point(|value| *value < position) as u64
232        } else {
233            0
234        }
235    }
236
237    fn is_subset_of(&self, other: &Self, domain_len: u64) -> Result<bool> {
238        self.validate_domain(domain_len, "subset")?;
239        other.validate_domain(domain_len, "superset")?;
240        if self.is_empty() {
241            return Ok(true);
242        }
243        if self.len() > other.len() {
244            return Ok(false);
245        }
246        if let Some((start, 1, len)) = self.progression() {
247            return Ok(other.rank(start + len) - other.rank(start) == len);
248        }
249        if let (Some((start, step, len)), Some((_, other_step, _))) =
250            (self.progression(), other.progression())
251        {
252            return Ok(other.contains(start)
253                && other.contains(start + step * (len - 1))
254                && (len == 1 || step.is_multiple_of(other_step)));
255        }
256        Ok(match self {
257            Self::Explicit(values) => values.iter().all(|value| other.contains(*value)),
258            _ => {
259                let (start, step, len) = self.progression().expect("non-empty metadata positions");
260                (0..len).all(|i| other.contains(start + step * i))
261            }
262        })
263    }
264
265    fn is_disjoint(&self, other: &Self, domain_len: u64) -> Result<bool> {
266        self.validate_domain(domain_len, "first disjoint set")?;
267        other.validate_domain(domain_len, "second disjoint set")?;
268        if self.is_empty() || other.is_empty() {
269            return Ok(true);
270        }
271        if let Some((start, 1, len)) = self.progression() {
272            return Ok(other.rank(start + len) == other.rank(start));
273        }
274        if let Some((start, 1, len)) = other.progression() {
275            return Ok(self.rank(start + len) == self.rank(start));
276        }
277        if let (Self::Explicit(left), Self::Explicit(right)) = (self, other) {
278            let (smaller, larger) = if left.len() <= right.len() {
279                (left, right)
280            } else {
281                (right, left)
282            };
283            return Ok(smaller
284                .iter()
285                .all(|value| larger.binary_search(value).is_err()));
286        }
287        if let Self::Explicit(values) = self {
288            return Ok(values.iter().all(|value| !other.contains(*value)));
289        }
290        if let Self::Explicit(values) = other {
291            return Ok(values.iter().all(|value| !self.contains(*value)));
292        }
293        let (a, s, n) = self.progression().expect("non-empty metadata positions");
294        let (b, t, m) = other.progression().expect("non-empty metadata positions");
295        Ok(!progressions_intersect(a, s, n, b, t, m))
296    }
297}
298
299// Solve a + s*k = b (mod t), then test the first common value in both domains.
300fn progressions_intersect(a: u64, s: u64, n: u64, b: u64, t: u64, m: u64) -> bool {
301    let (mut r, mut next_r) = (i128::from(s), i128::from(t));
302    let (mut inverse, mut next_inverse) = (1_i128, 0_i128);
303    while next_r != 0 {
304        let q = r / next_r;
305        (r, next_r) = (next_r, r - q * next_r);
306        (inverse, next_inverse) = (next_inverse, inverse - q * next_inverse);
307    }
308    let difference = i128::from(b) - i128::from(a);
309    if difference % r != 0 {
310        return false;
311    }
312    let modulus = i128::from(t) / r;
313    let k = ((difference / r).rem_euclid(modulus) as u128 * inverse.rem_euclid(modulus) as u128)
314        % modulus as u128;
315    let mut first = u128::from(a) + u128::from(s) * k;
316    let lower = u128::from(a.max(b));
317    let period = u128::from(s) * (u128::from(t) / r as u128);
318    if first < lower {
319        first += (lower - first).div_ceil(period) * period;
320    }
321    first <= u128::from((a + s * (n - 1)).min(b + t * (m - 1)))
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq)]
325pub enum SparseValidityMeaning {
326    NullPositions,
327    ValidPositions,
328}
329
330#[derive(Debug, Clone, PartialEq, Eq)]
331pub struct SparseValiditySet {
332    pub(crate) meaning: SparseValidityMeaning,
333    pub(crate) positions: SparsePositionPlan,
334}
335
336impl SparseValiditySet {
337    pub(crate) fn deep_size(&self) -> usize {
338        self.positions.deep_size()
339    }
340
341    fn contains_only_valid_positions(
342        &self,
343        positions: &SparsePositionPlan,
344        num_slots: u64,
345    ) -> Result<bool> {
346        match self.meaning {
347            SparseValidityMeaning::NullPositions => {
348                positions.is_disjoint(&self.positions, num_slots)
349            }
350            SparseValidityMeaning::ValidPositions => {
351                positions.is_subset_of(&self.positions, num_slots)
352            }
353        }
354    }
355
356    fn append_to(&self, validity: &mut BooleanBufferBuilder, num_slots: u64) -> Result<()> {
357        self.positions.validate_domain(num_slots, "validity")?;
358        let num_slots_usize = usize_from_u64(num_slots, "validity slot count")?;
359        match (self.meaning, &self.positions) {
360            (SparseValidityMeaning::NullPositions, SparsePositionPlan::Empty) => {
361                validity.append_n(num_slots_usize, true);
362            }
363            (SparseValidityMeaning::ValidPositions, SparsePositionPlan::Empty) => {
364                validity.append_n(num_slots_usize, false);
365            }
366            (SparseValidityMeaning::NullPositions, SparsePositionPlan::All { .. }) => {
367                validity.append_n(num_slots_usize, false);
368            }
369            (SparseValidityMeaning::ValidPositions, SparsePositionPlan::All { .. }) => {
370                validity.append_n(num_slots_usize, true);
371            }
372            (meaning, SparsePositionPlan::Range { start, len }) => {
373                let range_end = start.checked_add(*len).ok_or_else(|| {
374                    Error::invalid_input_source("Sparse structural validity range overflows".into())
375                })?;
376                let default_valid = matches!(meaning, SparseValidityMeaning::NullPositions);
377                let range_valid = !default_valid;
378                validity.append_n(
379                    usize_from_u64(*start, "validity range start")?,
380                    default_valid,
381                );
382                validity.append_n(usize_from_u64(*len, "validity range length")?, range_valid);
383                validity.append_n(
384                    usize_from_u64(num_slots - range_end, "validity range tail")?,
385                    default_valid,
386                );
387            }
388            (_, SparsePositionPlan::Explicit(_) | SparsePositionPlan::Arithmetic { .. }) => {
389                let mut cursor = SparseValidityCursor::new(self, num_slots, "validity")?;
390                for slot in 0..num_slots {
391                    validity.append(cursor.is_valid(slot)?);
392                }
393                cursor.finish()?;
394            }
395        }
396        Ok(())
397    }
398}
399
400impl SparsePositionPlan {
401    fn validate_domain(&self, domain_len: u64, label: &str) -> Result<()> {
402        match self {
403            Self::Empty => {}
404            Self::All { len } => {
405                if *len != domain_len {
406                    return Err(Error::invalid_input_source(
407                        format!(
408                            "Sparse structural {label} all set length {} does not match domain {}",
409                            len, domain_len
410                        )
411                        .into(),
412                    ));
413                }
414            }
415            Self::Range { start, len } => {
416                let end = start.checked_add(*len).ok_or_else(|| {
417                    Error::invalid_input_source(
418                        format!("Sparse structural {label} range overflows").into(),
419                    )
420                })?;
421                if end > domain_len {
422                    return Err(Error::invalid_input_source(
423                        format!(
424                            "Sparse structural {label} range {}..{} is outside domain {}",
425                            start, end, domain_len
426                        )
427                        .into(),
428                    ));
429                }
430            }
431            Self::Arithmetic { start, step, len } => {
432                let last =
433                    crate::encodings::physical::range::checked_range_last(64, *start, *step, *len)?;
434                if last >= domain_len {
435                    return Err(Error::invalid_input(format!(
436                        "Sparse structural {label} position {last} is outside layer with {domain_len} slots"
437                    )));
438                }
439            }
440            Self::Explicit(positions) => {
441                for window in positions.windows(2) {
442                    let [previous, current] = window else {
443                        continue;
444                    };
445                    if previous >= current {
446                        return Err(Error::invalid_input_source(
447                            format!(
448                                "Sparse structural {label} positions must be strictly increasing"
449                            )
450                            .into(),
451                        ));
452                    }
453                }
454                if let Some(position) = positions.last()
455                    && *position >= domain_len
456                {
457                    return Err(Error::invalid_input_source(
458                        format!(
459                            "Sparse structural {label} position {} is outside layer with {} slots",
460                            position, domain_len
461                        )
462                        .into(),
463                    ));
464                }
465            }
466        }
467        Ok(())
468    }
469}
470
471struct SparsePositionCursor<'a> {
472    set: &'a SparsePositionPlan,
473    explicit: Option<std::iter::Peekable<std::slice::Iter<'a, u64>>>,
474}
475
476impl<'a> SparsePositionCursor<'a> {
477    fn new(set: &'a SparsePositionPlan, domain_len: u64, label: &str) -> Result<Self> {
478        set.validate_domain(domain_len, label)?;
479        let explicit = match set {
480            SparsePositionPlan::Explicit(positions) => Some(positions.iter().peekable()),
481            _ => None,
482        };
483        Ok(Self { set, explicit })
484    }
485
486    fn contains(&mut self, slot: u64) -> Result<bool> {
487        Ok(match self.set {
488            SparsePositionPlan::Empty => false,
489            SparsePositionPlan::All { .. } => true,
490            SparsePositionPlan::Range { start, len } => {
491                slot >= *start && slot < start.saturating_add(*len)
492            }
493            SparsePositionPlan::Arithmetic { .. } => self.set.contains(slot),
494            SparsePositionPlan::Explicit(_) => {
495                let iter = self.explicit.as_mut().ok_or_else(|| {
496                    Error::internal("Sparse structural explicit cursor is missing".to_string())
497                })?;
498                if let Some(position) = iter.peek()
499                    && **position < slot
500                {
501                    return Err(Error::invalid_input_source(
502                        format!(
503                            "Sparse structural explicit position {} was skipped before slot {}",
504                            **position, slot
505                        )
506                        .into(),
507                    ));
508                }
509                if iter.peek().is_some_and(|position| **position == slot) {
510                    iter.next();
511                    true
512                } else {
513                    false
514                }
515            }
516        })
517    }
518
519    fn finish(&mut self) -> Result<()> {
520        if let Some(iter) = self.explicit.as_mut()
521            && let Some(position) = iter.next()
522        {
523            return Err(Error::invalid_input_source(
524                format!(
525                    "Sparse structural explicit position {} was not consumed",
526                    position
527                )
528                .into(),
529            ));
530        }
531        Ok(())
532    }
533}
534
535struct SparseValidityCursor<'a> {
536    meaning: SparseValidityMeaning,
537    positions: SparsePositionCursor<'a>,
538}
539
540impl<'a> SparseValidityCursor<'a> {
541    fn new(validity: &'a SparseValiditySet, domain_len: u64, label: &str) -> Result<Self> {
542        Ok(Self {
543            meaning: validity.meaning,
544            positions: SparsePositionCursor::new(&validity.positions, domain_len, label)?,
545        })
546    }
547
548    fn is_valid(&mut self, slot: u64) -> Result<bool> {
549        let is_stored = self.positions.contains(slot)?;
550        Ok(match self.meaning {
551            SparseValidityMeaning::NullPositions => !is_stored,
552            SparseValidityMeaning::ValidPositions => is_stored,
553        })
554    }
555
556    fn finish(&mut self) -> Result<()> {
557        self.positions.finish()
558    }
559}
560
561#[derive(Debug, Clone, PartialEq, Eq)]
562pub enum SparseCountPlan {
563    Empty,
564    Constant {
565        value: u64,
566        len: u64,
567    },
568    Explicit {
569        counts: Arc<[u64]>,
570        offsets: Arc<[u64]>,
571    },
572}
573
574impl SparseCountPlan {
575    pub(crate) fn from_counts(counts: Vec<u64>) -> Result<Self> {
576        if counts.is_empty() {
577            return Ok(Self::Empty);
578        }
579        if let Some(first) = counts.first().copied()
580            && counts.iter().all(|count| *count == first)
581        {
582            return Ok(Self::Constant {
583                value: first,
584                len: counts.len() as u64,
585            });
586        }
587        let offsets = offsets_from_counts(&counts)?;
588        Ok(Self::Explicit {
589            counts: counts.into(),
590            offsets: offsets.into(),
591        })
592    }
593
594    pub(crate) fn constant(value: u64, len: u64) -> Self {
595        if len == 0 {
596            Self::Empty
597        } else {
598            Self::Constant { value, len }
599        }
600    }
601
602    pub(crate) fn len(&self) -> u64 {
603        match self {
604            Self::Empty => 0,
605            Self::Constant { len, .. } => *len,
606            Self::Explicit { counts, .. } => counts.len() as u64,
607        }
608    }
609
610    pub(crate) fn deep_size(&self) -> usize {
611        match self {
612            Self::Explicit { counts, offsets } => {
613                counts.len() * std::mem::size_of::<u64>()
614                    + offsets.len() * std::mem::size_of::<u64>()
615            }
616            Self::Empty | Self::Constant { .. } => 0,
617        }
618    }
619
620    pub(crate) fn materialize(&self) -> Result<Vec<u64>> {
621        match self {
622            Self::Empty => Ok(Vec::new()),
623            Self::Constant { value, len } => {
624                let len = usize::try_from(*len).map_err(|_| {
625                    Error::invalid_input_source(
626                        "Sparse structural count set length exceeds usize::MAX".into(),
627                    )
628                })?;
629                Ok(vec![*value; len])
630            }
631            Self::Explicit { counts, .. } => Ok(counts.to_vec()),
632        }
633    }
634
635    pub(crate) fn sum(&self) -> Result<u64> {
636        match self {
637            Self::Empty => Ok(0),
638            Self::Constant { value, len } => value.checked_mul(*len).ok_or_else(|| {
639                Error::invalid_input_source(
640                    format!(
641                        "Sparse structural constant count sum overflows: value={}, len={}",
642                        value, len
643                    )
644                    .into(),
645                )
646            }),
647            Self::Explicit { offsets, .. } => offsets.last().copied().ok_or_else(|| {
648                Error::invalid_input_source(
649                    "Sparse structural explicit count offsets are empty".into(),
650                )
651            }),
652        }
653    }
654
655    fn validate_positive(&self) -> Result<()> {
656        let has_zero = match self {
657            Self::Empty => false,
658            Self::Constant { value, .. } => *value == 0,
659            Self::Explicit { counts, .. } => counts.contains(&0),
660        };
661        if has_zero {
662            return Err(Error::invalid_input_source(
663                "Sparse structural non-empty list count is zero".into(),
664            ));
665        }
666        Ok(())
667    }
668}
669
670#[derive(Debug, Clone, PartialEq, Eq)]
671pub enum SparseStructuralLayerPlan {
672    Validity {
673        num_slots: u64,
674        validity: SparseValiditySet,
675    },
676    List {
677        num_slots: u64,
678        num_child_slots: u64,
679        non_empty_positions: SparsePositionPlan,
680        counts: SparseCountPlan,
681        validity: SparseValiditySet,
682    },
683    FixedSizeList {
684        num_slots: u64,
685        dimension: u64,
686        validity: SparseValiditySet,
687    },
688}
689
690impl SparseStructuralPlan {
691    fn validate(&self, row_domain: u64) -> Result<()> {
692        usize_from_u64(self.num_visible_items, "visible item count")?;
693        let mut expected_slots = row_domain;
694        for (layer_index, layer) in self.layers.iter().enumerate() {
695            let (num_slots, num_child_slots, validity) = match layer {
696                SparseStructuralLayerPlan::Validity {
697                    num_slots,
698                    validity,
699                } => (*num_slots, *num_slots, validity),
700                SparseStructuralLayerPlan::List {
701                    num_slots,
702                    num_child_slots,
703                    non_empty_positions,
704                    counts,
705                    validity,
706                } => {
707                    counts.validate_positive()?;
708                    if non_empty_positions.len() != counts.len() {
709                        return Err(Error::invalid_input_source(
710                            format!(
711                                "Sparse structural list layer {} has {} non-empty positions but {} counts",
712                                layer_index,
713                                non_empty_positions.len(),
714                                counts.len()
715                            )
716                            .into(),
717                        ));
718                    }
719                    if counts.sum()? != *num_child_slots {
720                        return Err(Error::invalid_input_source(
721                            format!(
722                                "Sparse structural list layer {} count sum does not match {} child slots",
723                                layer_index, num_child_slots
724                            )
725                            .into(),
726                        ));
727                    }
728                    if !validity.contains_only_valid_positions(non_empty_positions, *num_slots)? {
729                        return Err(Error::invalid_input_source(
730                            format!(
731                                "Sparse structural list layer {} contains a non-empty null slot",
732                                layer_index
733                            )
734                            .into(),
735                        ));
736                    }
737                    (*num_slots, *num_child_slots, validity)
738                }
739                SparseStructuralLayerPlan::FixedSizeList {
740                    num_slots,
741                    dimension,
742                    validity,
743                } => {
744                    if *dimension == 0 {
745                        return Err(Error::invalid_input_source(
746                            format!(
747                                "Sparse structural fixed-size-list layer {} has dimension zero",
748                                layer_index
749                            )
750                            .into(),
751                        ));
752                    }
753                    let num_child_slots = num_slots.checked_mul(*dimension).ok_or_else(|| {
754                        Error::invalid_input_source(
755                            "Sparse structural fixed-size-list child domain overflows".into(),
756                        )
757                    })?;
758                    (*num_slots, num_child_slots, validity)
759                }
760            };
761            usize_from_u64(num_slots, "layer slot count")?;
762            usize_from_u64(num_child_slots, "layer child slot count")?;
763            if num_slots != expected_slots {
764                return Err(Error::invalid_input_source(
765                    format!(
766                        "Sparse structural layer {} has {} slots, expected {}",
767                        layer_index, num_slots, expected_slots
768                    )
769                    .into(),
770                ));
771            }
772            validity.positions.validate_domain(num_slots, "validity")?;
773            expected_slots = num_child_slots;
774        }
775        if expected_slots != self.num_visible_items {
776            return Err(Error::invalid_input_source(
777                format!(
778                    "Sparse structural terminal domain has {} slots, expected {} visible items",
779                    expected_slots, self.num_visible_items
780                )
781                .into(),
782            ));
783        }
784        Ok(())
785    }
786}
787
788#[derive(Debug)]
789pub struct SparseStructuralUnraveler {
790    layers: Vec<SparseStructuralLayerPlan>,
791    next_layer: usize,
792    pending_fixed_size_list: bool,
793}
794
795impl SparseStructuralUnraveler {
796    pub(crate) fn new(plan: SparseStructuralPlan) -> Self {
797        let next_layer = plan.layers.len();
798        Self {
799            layers: plan.layers,
800            next_layer,
801            pending_fixed_size_list: false,
802        }
803    }
804
805    fn current_layer(&self) -> Option<&SparseStructuralLayerPlan> {
806        self.next_layer
807            .checked_sub(1)
808            .and_then(|idx| self.layers.get(idx))
809    }
810
811    fn consume_current_layer(&mut self) -> Result<()> {
812        self.next_layer = self.next_layer.checked_sub(1).ok_or_else(|| {
813            Error::invalid_input_source(
814                "Sparse structural metadata has fewer layers than the Arrow schema".into(),
815            )
816        })?;
817        Ok(())
818    }
819
820    pub(crate) fn ensure_exhausted(&self) -> Result<()> {
821        if self.pending_fixed_size_list {
822            return Err(Error::invalid_input_source(
823                "Sparse structural metadata has an unconsumed fixed-size-list layer".into(),
824            ));
825        }
826        if self.next_layer != 0 {
827            return Err(Error::invalid_input_source(
828                format!(
829                    "Sparse structural metadata has {} unconsumed layer(s)",
830                    self.next_layer
831                )
832                .into(),
833            ));
834        }
835        Ok(())
836    }
837
838    pub(crate) fn is_all_valid(&self) -> bool {
839        match self.current_layer() {
840            Some(SparseStructuralLayerPlan::Validity {
841                num_slots,
842                validity,
843            })
844            | Some(SparseStructuralLayerPlan::FixedSizeList {
845                num_slots,
846                validity,
847                ..
848            })
849            | Some(SparseStructuralLayerPlan::List {
850                num_slots,
851                validity,
852                ..
853            }) => match validity.meaning {
854                SparseValidityMeaning::NullPositions => validity.positions.is_empty(),
855                SparseValidityMeaning::ValidPositions => validity.positions.len() == *num_slots,
856            },
857            None => true,
858        }
859    }
860
861    pub(crate) fn max_lists(&self) -> Result<usize> {
862        match self.current_layer() {
863            Some(SparseStructuralLayerPlan::List { num_slots, .. }) => {
864                usize_from_u64(*num_slots, "list slot count")
865            }
866            _ => Ok(0),
867        }
868    }
869
870    pub(crate) fn skip_validity(&mut self) -> Result<()> {
871        match self.current_layer() {
872            Some(SparseStructuralLayerPlan::Validity { .. }) => {
873                if self.pending_fixed_size_list {
874                    return Err(Error::invalid_input_source(
875                        "Sparse fixed-size-list schema does not match a validity layer".into(),
876                    ));
877                }
878                self.consume_current_layer()?;
879            }
880            Some(SparseStructuralLayerPlan::FixedSizeList { .. }) => {
881                if !self.pending_fixed_size_list {
882                    return Err(Error::invalid_input_source(
883                        "Sparse fixed-size-list layer does not match the Arrow schema".into(),
884                    ));
885                }
886                self.pending_fixed_size_list = false;
887                self.consume_current_layer()?;
888            }
889            None => {
890                return Err(Error::invalid_input_source(
891                    "Sparse structural metadata has fewer layers than the Arrow schema".into(),
892                ));
893            }
894            Some(SparseStructuralLayerPlan::List { .. }) => {
895                return Err(Error::invalid_input_source(
896                    "Sparse structural list layer does not match an Arrow validity layer".into(),
897                ));
898            }
899        }
900        Ok(())
901    }
902
903    pub(crate) fn unravel_validity(&mut self, validity: &mut BooleanBufferBuilder) -> Result<()> {
904        match self.current_layer() {
905            Some(SparseStructuralLayerPlan::Validity {
906                num_slots,
907                validity: layer_validity,
908            }) => {
909                if self.pending_fixed_size_list {
910                    return Err(Error::invalid_input_source(
911                        "Sparse fixed-size-list schema does not match a validity layer".into(),
912                    ));
913                }
914                layer_validity.append_to(validity, *num_slots)?;
915                self.consume_current_layer()?;
916            }
917            Some(SparseStructuralLayerPlan::FixedSizeList {
918                num_slots,
919                validity: layer_validity,
920                ..
921            }) => {
922                if !self.pending_fixed_size_list {
923                    return Err(Error::invalid_input_source(
924                        "Sparse fixed-size-list layer does not match the Arrow schema".into(),
925                    ));
926                }
927                layer_validity.append_to(validity, *num_slots)?;
928                self.pending_fixed_size_list = false;
929                self.consume_current_layer()?;
930            }
931            None => {
932                return Err(Error::invalid_input_source(
933                    "Sparse structural metadata has fewer layers than the Arrow schema".into(),
934                ));
935            }
936            Some(SparseStructuralLayerPlan::List { .. }) => {
937                return Err(Error::invalid_input_source(
938                    "Sparse structural list layer does not match an Arrow validity layer".into(),
939                ));
940            }
941        }
942        Ok(())
943    }
944
945    pub(crate) fn decimate(&mut self, dimension: usize) -> Result<()> {
946        if self.pending_fixed_size_list {
947            return Err(Error::invalid_input_source(
948                "Sparse fixed-size-list layer was decimated more than once".into(),
949            ));
950        }
951        let Some(SparseStructuralLayerPlan::FixedSizeList {
952            dimension: actual_dimension,
953            ..
954        }) = self.current_layer()
955        else {
956            return Err(Error::invalid_input_source(
957                "Sparse structural layer does not match an Arrow fixed-size-list layer".into(),
958            ));
959        };
960        if usize_from_u64(*actual_dimension, "fixed-size-list dimension")? != dimension {
961            return Err(Error::invalid_input_source(
962                format!(
963                    "Sparse structural fixed-size-list dimension {} does not match Arrow dimension {}",
964                    actual_dimension, dimension
965                )
966                .into(),
967            ));
968        }
969        self.pending_fixed_size_list = true;
970        Ok(())
971    }
972
973    fn to_offset<T: ArrowNativeType>(value: u64) -> Result<T> {
974        let value = usize::try_from(value).map_err(|_| {
975            Error::invalid_input_source(
976                format!("Sparse structural offset {} exceeds usize::MAX", value).into(),
977            )
978        })?;
979        T::from_usize(value).ok_or_else(|| {
980            Error::invalid_input_source(
981                "Sparse structural offset does not fit the Arrow offset type".into(),
982            )
983        })
984    }
985
986    pub(crate) fn unravel_offsets<T: ArrowNativeType>(
987        &mut self,
988        offsets: &mut Vec<T>,
989        validity: Option<&mut BooleanBufferBuilder>,
990    ) -> Result<()> {
991        let Some(SparseStructuralLayerPlan::List {
992            num_slots,
993            num_child_slots,
994            non_empty_positions,
995            counts,
996            validity: layer_validity,
997            ..
998        }) = self.current_layer()
999        else {
1000            return Err(Error::invalid_input_source(
1001                "Sparse structural layer does not match an Arrow list layer".into(),
1002            ));
1003        };
1004        if self.pending_fixed_size_list {
1005            return Err(Error::invalid_input_source(
1006                "Sparse fixed-size-list schema does not match an Arrow list layer".into(),
1007            ));
1008        }
1009
1010        if non_empty_positions.len() != counts.len() {
1011            return Err(Error::invalid_input_source(
1012                format!(
1013                    "Sparse structural list has {} non-empty positions but {} counts",
1014                    non_empty_positions.len(),
1015                    counts.len()
1016                )
1017                .into(),
1018            ));
1019        }
1020        let actual_child_slots = counts.sum()?;
1021        if actual_child_slots != *num_child_slots {
1022            return Err(Error::invalid_input_source(
1023                format!(
1024                    "Sparse structural list count sum {} does not match child slots {}",
1025                    actual_child_slots, num_child_slots
1026                )
1027                .into(),
1028            ));
1029        }
1030
1031        let mut current_offset = offsets
1032            .last()
1033            .map(|offset| offset.as_usize() as u64)
1034            .unwrap_or(0);
1035        if offsets.is_empty() {
1036            offsets.push(Self::to_offset(current_offset)?);
1037        }
1038
1039        if non_empty_positions.is_empty() {
1040            if let Some(validity) = validity {
1041                layer_validity.append_to(validity, *num_slots)?;
1042            }
1043            let offset = Self::to_offset(current_offset)?;
1044            let new_len = offsets
1045                .len()
1046                .checked_add(usize_from_u64(*num_slots, "list slot count")?)
1047                .ok_or_else(|| {
1048                    Error::invalid_input_source(
1049                        "Sparse structural list offset length overflows usize".into(),
1050                    )
1051                })?;
1052            offsets.resize(new_len, offset);
1053            self.consume_current_layer()?;
1054            return Ok(());
1055        }
1056
1057        let non_empty_positions = non_empty_positions.materialize()?;
1058        let counts = counts.materialize()?;
1059        let mut non_empty_iter = non_empty_positions
1060            .iter()
1061            .copied()
1062            .zip(counts.iter().copied())
1063            .peekable();
1064        let mut validity_cursor =
1065            SparseValidityCursor::new(layer_validity, *num_slots, "list validity")?;
1066        let mut validity = validity;
1067        for slot in 0..*num_slots {
1068            let is_valid = validity_cursor.is_valid(slot)?;
1069            if let Some(validity) = validity.as_mut() {
1070                validity.append(is_valid);
1071            }
1072
1073            if non_empty_iter
1074                .peek()
1075                .is_some_and(|(non_empty_pos, _)| *non_empty_pos == slot)
1076            {
1077                let (_, count) = non_empty_iter.next().ok_or_else(|| {
1078                    Error::invalid_input_source(
1079                        "Sparse structural list position is missing its child count".into(),
1080                    )
1081                })?;
1082                if !is_valid {
1083                    return Err(Error::invalid_input_source(
1084                        format!(
1085                            "Sparse structural list slot {} is both invalid and non-empty",
1086                            slot
1087                        )
1088                        .into(),
1089                    ));
1090                }
1091                current_offset = current_offset.checked_add(count).ok_or_else(|| {
1092                    Error::invalid_input_source(
1093                        "Sparse structural list offsets overflow u64".into(),
1094                    )
1095                })?;
1096            }
1097            offsets.push(Self::to_offset(current_offset)?);
1098        }
1099        validity_cursor.finish()?;
1100        if let Some((extra_pos, _)) = non_empty_iter.next() {
1101            return Err(Error::invalid_input_source(
1102                format!(
1103                    "Sparse structural non-empty position {} is outside layer with {} slots",
1104                    extra_pos, num_slots
1105                )
1106                .into(),
1107            ));
1108        }
1109
1110        self.consume_current_layer()?;
1111        Ok(())
1112    }
1113}
1114
1115#[derive(Debug, Clone)]
1116enum SparsePositionDecoder {
1117    Ready(SparsePositionPlan),
1118    Encoded {
1119        sequence: SequenceDecoder,
1120        domain_len: u64,
1121    },
1122}
1123
1124#[derive(Debug, Clone)]
1125enum SparseCountDecoder {
1126    Ready(SparseCountPlan),
1127    Encoded(SequenceDecoder),
1128}
1129
1130#[derive(Debug, Clone)]
1131enum SparseLayerDecompressors {
1132    Validity {
1133        num_slots: u64,
1134        validity: SparseValiditySetDecoder,
1135    },
1136    List {
1137        num_slots: u64,
1138        num_child_slots: u64,
1139        non_empty_positions: SparsePositionDecoder,
1140        counts: SparseCountDecoder,
1141        validity: SparseValiditySetDecoder,
1142    },
1143    FixedSizeList {
1144        num_slots: u64,
1145        num_child_slots: u64,
1146        dimension: u64,
1147        validity: SparseValiditySetDecoder,
1148    },
1149}
1150
1151impl SparseLayerDecompressors {
1152    fn num_buffers(&self) -> usize {
1153        let position_buffers = |positions: &SparsePositionDecoder| match positions {
1154            SparsePositionDecoder::Ready(_) => 0,
1155            SparsePositionDecoder::Encoded { sequence, .. } => {
1156                usize::from(sequence.requires_payload())
1157            }
1158        };
1159        match self {
1160            Self::Validity { validity, .. } | Self::FixedSizeList { validity, .. } => {
1161                position_buffers(&validity.positions)
1162            }
1163            Self::List {
1164                non_empty_positions,
1165                counts,
1166                validity,
1167                ..
1168            } => {
1169                let count_buffers = match counts {
1170                    SparseCountDecoder::Ready(_) => 0,
1171                    SparseCountDecoder::Encoded(sequence) => {
1172                        usize::from(sequence.requires_payload())
1173                    }
1174                };
1175                position_buffers(non_empty_positions)
1176                    + count_buffers
1177                    + position_buffers(&validity.positions)
1178            }
1179        }
1180    }
1181}
1182
1183#[derive(Debug, Clone)]
1184struct SparseValiditySetDecoder {
1185    meaning: SparseValidityMeaning,
1186    positions: SparsePositionDecoder,
1187}
1188
1189impl pb23::SparseStructure {
1190    /// Derive the logical page row count and fixed-size-list scheduling scale.
1191    ///
1192    /// Page metadata counts rows multiplied by every structural fixed-size-list
1193    /// dimension, including dimensions below variable-size lists.
1194    ///
1195    /// ```
1196    /// let layout = lance_encoding::format::pb23::SparseStructure::default();
1197    /// assert_eq!(layout.row_count_and_scale(5)?, (5, 1));
1198    /// # Ok::<(), lance_core::Error>(())
1199    /// ```
1200    pub fn row_count_and_scale(&self, encoded_rows: u64) -> Result<(u64, u64)> {
1201        let scale =
1202            self.layers
1203                .iter()
1204                .try_fold(1_u64, |scale, layer| match layer.layer.as_ref() {
1205                    Some(pb23::sparse_structural_layer::Layer::FixedSizeList(layer)) => {
1206                        if layer.dimension == 0 {
1207                            return Err(Error::invalid_input(
1208                                "Sparse structural fixed-size-list dimension is zero",
1209                            ));
1210                        }
1211                        scale.checked_mul(layer.dimension).ok_or_else(|| {
1212                            Error::invalid_input(
1213                                "Sparse structural fixed-size-list row scale overflows",
1214                            )
1215                        })
1216                    }
1217                    Some(_) => Ok(scale),
1218                    None => Err(Error::invalid_input(
1219                        "Sparse structural layer is missing its layer variant",
1220                    )),
1221                })?;
1222        if !encoded_rows.is_multiple_of(scale) {
1223            return Err(Error::invalid_input(format!(
1224                "Sparse encoded row domain {encoded_rows} is not divisible by fixed-size-list scale {scale}"
1225            )));
1226        }
1227        Ok((encoded_rows / scale, scale))
1228    }
1229}
1230
1231fn require_layer(
1232    layer: &pb23::SparseStructuralLayer,
1233) -> Result<&pb23::sparse_structural_layer::Layer> {
1234    layer.layer.as_ref().ok_or_else(|| {
1235        Error::invalid_input_source("Sparse structural layer is missing its layer variant".into())
1236    })
1237}
1238
1239fn child_domain(layer: &pb23::SparseStructuralLayer, num_slots: u64) -> Result<u64> {
1240    let child_slots = match require_layer(layer)? {
1241        pb23::sparse_structural_layer::Layer::Validity(_) => num_slots,
1242        pb23::sparse_structural_layer::Layer::List(layer) => layer.num_child_slots,
1243        pb23::sparse_structural_layer::Layer::FixedSizeList(layer) => {
1244            num_slots.checked_mul(layer.dimension).ok_or_else(|| {
1245                Error::invalid_input("Sparse structural fixed-size-list child slot count overflows")
1246            })?
1247        }
1248    };
1249    usize_from_u64(child_slots, "layer child slot count")?;
1250    Ok(child_slots)
1251}
1252
1253fn checked_buffer_range(position: u64, size: u64, label: &str) -> Result<Range<u64>> {
1254    let end = position.checked_add(size).ok_or_else(|| {
1255        Error::invalid_input_source(
1256            format!("Sparse structural {label} buffer range overflows").into(),
1257        )
1258    })?;
1259    Ok(position..end)
1260}
1261
1262fn require_sequence_encoding<'a>(
1263    encoding: &'a Option<CompressiveEncoding>,
1264    label: &str,
1265) -> Result<&'a CompressiveEncoding> {
1266    encoding.as_ref().ok_or_else(|| {
1267        Error::invalid_input(format!("Sparse structural {label} encoding is required"))
1268    })
1269}
1270
1271fn require_validity_set<'a>(
1272    set: &'a Option<pb23::SparseValiditySet>,
1273    label: &str,
1274) -> Result<&'a pb23::SparseValiditySet> {
1275    set.as_ref().ok_or_else(|| {
1276        Error::invalid_input_source(
1277            format!("Sparse structural {label} validity set is required").into(),
1278        )
1279    })
1280}
1281
1282fn validity_meaning(
1283    validity_set: &pb23::SparseValiditySet,
1284    label: &str,
1285) -> Result<SparseValidityMeaning> {
1286    match invalid_enum(
1287        pb23::sparse_validity_set::Meaning::try_from(validity_set.meaning),
1288        "validity meaning",
1289    )? {
1290        pb23::sparse_validity_set::Meaning::SparseValidityUnspecified => {
1291            Err(Error::invalid_input_source(
1292                format!("Sparse structural {label} meaning is unspecified").into(),
1293            ))
1294        }
1295        pb23::sparse_validity_set::Meaning::SparseValidityNullPositions => {
1296            Ok(SparseValidityMeaning::NullPositions)
1297        }
1298        pb23::sparse_validity_set::Meaning::SparseValidityValidPositions => {
1299            Ok(SparseValidityMeaning::ValidPositions)
1300        }
1301    }
1302}
1303
1304fn position_decoder(
1305    encoding: &CompressiveEncoding,
1306    cardinality: u64,
1307    domain_len: u64,
1308    label: &str,
1309) -> Result<SparsePositionDecoder> {
1310    if cardinality > domain_len {
1311        return Err(Error::invalid_input(format!(
1312            "Sparse structural {label} cardinality {cardinality} exceeds domain {domain_len}"
1313        )));
1314    }
1315    let sequence = SequenceDecoder::try_new(encoding, 64, cardinality)?;
1316    let positions = match sequence.metadata() {
1317        Some(SequenceMetadata::Empty) => SparsePositionPlan::Empty,
1318        Some(SequenceMetadata::Constant(value)) => {
1319            if cardinality != 1 {
1320                return Err(Error::invalid_input(format!(
1321                    "Sparse structural {label} positions must be strictly increasing"
1322                )));
1323            }
1324            SparsePositionPlan::range(value, 1)
1325        }
1326        Some(SequenceMetadata::Range { start, step: 1 }) => {
1327            if start == 0 && cardinality == domain_len {
1328                SparsePositionPlan::all(cardinality)
1329            } else {
1330                SparsePositionPlan::range(start, cardinality)
1331            }
1332        }
1333        Some(SequenceMetadata::Range { start, step }) => SparsePositionPlan::Arithmetic {
1334            start,
1335            step,
1336            len: cardinality,
1337        },
1338        None => {
1339            return Ok(SparsePositionDecoder::Encoded {
1340                sequence,
1341                domain_len,
1342            });
1343        }
1344    };
1345    positions.validate_domain(domain_len, label)?;
1346    Ok(SparsePositionDecoder::Ready(positions))
1347}
1348
1349fn validity_set_decoder(
1350    validity: &pb23::SparseValiditySet,
1351    domain_len: u64,
1352    label: &str,
1353) -> Result<(SparseValiditySetDecoder, u64)> {
1354    let meaning = validity_meaning(validity, label)?;
1355    let encoding = require_sequence_encoding(&validity.positions, label)?;
1356    let positions = position_decoder(encoding, validity.num_positions, domain_len, label)?;
1357    Ok((
1358        SparseValiditySetDecoder { meaning, positions },
1359        validity.num_positions,
1360    ))
1361}
1362
1363fn num_valid_slots(
1364    meaning: SparseValidityMeaning,
1365    cardinality: u64,
1366    num_slots: u64,
1367    label: &str,
1368) -> Result<u64> {
1369    match meaning {
1370        SparseValidityMeaning::NullPositions => {
1371            num_slots.checked_sub(cardinality).ok_or_else(|| {
1372                Error::invalid_input_source(
1373                    format!(
1374                        "Sparse structural {label} null cardinality {} exceeds slots {}",
1375                        cardinality, num_slots
1376                    )
1377                    .into(),
1378                )
1379            })
1380        }
1381        SparseValidityMeaning::ValidPositions => Ok(cardinality),
1382    }
1383}
1384
1385fn count_decoder(
1386    encoding: &CompressiveEncoding,
1387    cardinality: u64,
1388    label: &str,
1389) -> Result<SparseCountDecoder> {
1390    let sequence = SequenceDecoder::try_new(encoding, 64, cardinality)?;
1391    Ok(match sequence.metadata() {
1392        Some(SequenceMetadata::Empty) => SparseCountDecoder::Ready(SparseCountPlan::Empty),
1393        Some(SequenceMetadata::Constant(value)) => {
1394            if value == 0 {
1395                return Err(Error::invalid_input(format!(
1396                    "Sparse structural {label} constant count is zero"
1397                )));
1398            }
1399            value.checked_mul(cardinality).ok_or_else(|| {
1400                Error::invalid_input(format!(
1401                    "Sparse structural {label} constant count sum overflows"
1402                ))
1403            })?;
1404            SparseCountDecoder::Ready(SparseCountPlan::constant(value, cardinality))
1405        }
1406        _ => SparseCountDecoder::Encoded(sequence),
1407    })
1408}
1409
1410fn layer_decompressors(
1411    layer: &pb23::SparseStructuralLayer,
1412    num_slots: u64,
1413    num_child_slots: u64,
1414) -> Result<SparseLayerDecompressors> {
1415    Ok(match require_layer(layer)? {
1416        pb23::sparse_structural_layer::Layer::Validity(layer) => {
1417            let (validity, _) = validity_set_decoder(layer, num_slots, "validity positions")?;
1418            SparseLayerDecompressors::Validity {
1419                num_slots,
1420                validity,
1421            }
1422        }
1423        pb23::sparse_structural_layer::Layer::List(layer) => {
1424            let non_empty_positions =
1425                require_sequence_encoding(&layer.non_empty_positions, "list non-empty")?;
1426            let num_non_empty = layer.num_non_empty_positions;
1427            let non_empty_positions = position_decoder(
1428                non_empty_positions,
1429                layer.num_non_empty_positions,
1430                num_slots,
1431                "list non-empty positions",
1432            )?;
1433            let counts = count_decoder(
1434                require_sequence_encoding(&layer.counts, "list counts")?,
1435                num_non_empty,
1436                "list counts",
1437            )?;
1438            let (validity, validity_cardinality) = validity_set_decoder(
1439                require_validity_set(&layer.validity, "list")?,
1440                num_slots,
1441                "list validity positions",
1442            )?;
1443            let num_valid_slots = num_valid_slots(
1444                validity.meaning,
1445                validity_cardinality,
1446                num_slots,
1447                "list validity",
1448            )?;
1449            if num_non_empty > num_valid_slots {
1450                return Err(Error::invalid_input(format!(
1451                    "Sparse structural list has {num_non_empty} non-empty slots but only {num_valid_slots} valid slots"
1452                )));
1453            }
1454            if let SparseCountDecoder::Ready(counts) = &counts {
1455                let child_slots = counts.sum()?;
1456                if child_slots != layer.num_child_slots {
1457                    return Err(Error::invalid_input(format!(
1458                        "Sparse structural list count sum {child_slots} does not match child slots {}",
1459                        layer.num_child_slots
1460                    )));
1461                }
1462            }
1463            SparseLayerDecompressors::List {
1464                num_slots,
1465                num_child_slots,
1466                non_empty_positions,
1467                counts,
1468                validity,
1469            }
1470        }
1471        pb23::sparse_structural_layer::Layer::FixedSizeList(layer) => {
1472            let (validity, _) = validity_set_decoder(
1473                require_validity_set(&layer.validity, "fixed-size-list")?,
1474                num_slots,
1475                "fixed-size-list validity positions",
1476            )?;
1477            SparseLayerDecompressors::FixedSizeList {
1478                num_slots,
1479                num_child_slots,
1480                dimension: layer.dimension,
1481                validity,
1482            }
1483        }
1484    })
1485}
1486
1487fn decode_u64_values(
1488    sequence: &SequenceDecoder,
1489    buffers: &mut impl Iterator<Item = Bytes>,
1490    label: &str,
1491) -> Result<Vec<u64>> {
1492    let payload = if sequence.requires_payload() {
1493        Some(LanceBuffer::from_bytes(
1494            next_structural_buffer(buffers, label)?,
1495            1,
1496        ))
1497    } else {
1498        None
1499    };
1500    let decoded = sequence.decode(payload)?;
1501    let fixed = decoded.as_fixed_width().ok_or_else(|| {
1502        Error::invalid_input(format!(
1503            "Sparse structural {label} did not decode to fixed width data"
1504        ))
1505    })?;
1506    Ok(crate::encodings::physical::checked_fixed_values::<u64>(&fixed, label)?.to_vec())
1507}
1508
1509fn decode_position_set(
1510    decoder: &SparsePositionDecoder,
1511    buffers: &mut impl Iterator<Item = Bytes>,
1512    label: &str,
1513) -> Result<SparsePositionPlan> {
1514    match decoder {
1515        SparsePositionDecoder::Ready(positions) => Ok(positions.clone()),
1516        SparsePositionDecoder::Encoded {
1517            sequence,
1518            domain_len,
1519        } => {
1520            let values = decode_u64_values(sequence, buffers, label)?;
1521            SparsePositionPlan::from_positions(values, *domain_len, label)
1522        }
1523    }
1524}
1525
1526fn decode_validity_set(
1527    decoder: &SparseValiditySetDecoder,
1528    buffers: &mut impl Iterator<Item = Bytes>,
1529    label: &str,
1530) -> Result<SparseValiditySet> {
1531    Ok(SparseValiditySet {
1532        meaning: decoder.meaning,
1533        positions: decode_position_set(&decoder.positions, buffers, label)?,
1534    })
1535}
1536
1537fn decode_count_set(
1538    decoder: &SparseCountDecoder,
1539    buffers: &mut impl Iterator<Item = Bytes>,
1540    label: &str,
1541) -> Result<SparseCountPlan> {
1542    match decoder {
1543        SparseCountDecoder::Ready(counts) => Ok(counts.clone()),
1544        SparseCountDecoder::Encoded(sequence) => {
1545            SparseCountPlan::from_counts(decode_u64_values(sequence, buffers, label)?)
1546        }
1547    }
1548}
1549
1550fn next_structural_buffer(buffers: &mut impl Iterator<Item = Bytes>, label: &str) -> Result<Bytes> {
1551    buffers.next().ok_or_else(|| {
1552        Error::invalid_input_source(
1553            format!("Sparse structural {label} is missing its buffer").into(),
1554        )
1555    })
1556}
1557
1558fn decode_layer(
1559    layer: &SparseLayerDecompressors,
1560    buffers: &mut impl Iterator<Item = Bytes>,
1561) -> Result<SparseStructuralLayerPlan> {
1562    Ok(match layer {
1563        SparseLayerDecompressors::Validity {
1564            num_slots,
1565            validity,
1566        } => SparseStructuralLayerPlan::Validity {
1567            num_slots: *num_slots,
1568            validity: decode_validity_set(validity, buffers, "validity positions")?,
1569        },
1570        SparseLayerDecompressors::List {
1571            num_slots,
1572            num_child_slots,
1573            non_empty_positions,
1574            counts,
1575            validity,
1576        } => {
1577            let non_empty_positions =
1578                decode_position_set(non_empty_positions, buffers, "list non-empty positions")?;
1579            let counts = decode_count_set(counts, buffers, "list counts")?;
1580            let validity = decode_validity_set(validity, buffers, "list validity positions")?;
1581            let actual_child_slots = counts.sum()?;
1582            if actual_child_slots != *num_child_slots {
1583                return Err(Error::invalid_input_source(
1584                        format!(
1585                            "Sparse structural list count sum {} does not match declared child slots {}",
1586                            actual_child_slots, num_child_slots
1587                        )
1588                        .into(),
1589                    ));
1590            }
1591            SparseStructuralLayerPlan::List {
1592                num_slots: *num_slots,
1593                num_child_slots: *num_child_slots,
1594                non_empty_positions,
1595                counts,
1596                validity,
1597            }
1598        }
1599        SparseLayerDecompressors::FixedSizeList {
1600            num_slots,
1601            num_child_slots,
1602            dimension,
1603            validity,
1604        } => {
1605            let expected_child_slots = num_slots.checked_mul(*dimension).ok_or_else(|| {
1606                    Error::invalid_input_source(
1607                        format!(
1608                            "Sparse structural fixed-size-list child slot count overflows: slots={}, dimension={}",
1609                            num_slots, dimension
1610                        )
1611                        .into(),
1612                    )
1613                })?;
1614            if expected_child_slots != *num_child_slots {
1615                return Err(Error::invalid_input_source(
1616                        format!(
1617                            "Sparse structural fixed-size-list child slot count {} does not match slots {} * dimension {}",
1618                            num_child_slots, num_slots, dimension
1619                        )
1620                        .into(),
1621                    ));
1622            }
1623            SparseStructuralLayerPlan::FixedSizeList {
1624                num_slots: *num_slots,
1625                dimension: *dimension,
1626                validity: decode_validity_set(
1627                    validity,
1628                    buffers,
1629                    "fixed-size-list validity positions",
1630                )?,
1631            }
1632        }
1633    })
1634}
1635
1636struct SparseStructuralSelection {
1637    plan: SparseStructuralPlan,
1638    leaf_ranges: Vec<Range<u64>>,
1639}
1640
1641struct SparsePositionSelection {
1642    positions: SparsePositionPlan,
1643    ordinal_ranges: Vec<Range<u64>>,
1644}
1645
1646fn validate_slice_ranges(ranges: &[Range<u64>], domain_len: u64, label: &str) -> Result<u64> {
1647    let mut total = 0_u64;
1648    for range in ranges {
1649        if range.start > range.end || range.end > domain_len {
1650            return Err(Error::invalid_input_source(
1651                format!(
1652                    "Sparse structural {label} slice {}..{} is outside domain {}",
1653                    range.start, range.end, domain_len
1654                )
1655                .into(),
1656            ));
1657        }
1658        total = total.checked_add(range.end - range.start).ok_or_else(|| {
1659            Error::invalid_input_source(
1660                format!("Sparse structural {label} slice length overflows").into(),
1661            )
1662        })?;
1663    }
1664    Ok(total)
1665}
1666
1667fn push_coalesced_range(ranges: &mut Vec<Range<u64>>, range: Range<u64>) {
1668    if range.is_empty() {
1669        return;
1670    }
1671    if let Some(last) = ranges.last_mut()
1672        && last.end == range.start
1673    {
1674        last.end = range.end;
1675        return;
1676    }
1677    ranges.push(range);
1678}
1679
1680fn position_segments_to_set(
1681    segments: Vec<Range<u64>>,
1682    output_domain: u64,
1683    label: &str,
1684) -> Result<SparsePositionPlan> {
1685    let segments = coalesce_ranges(segments);
1686    if segments.is_empty() {
1687        return Ok(SparsePositionPlan::empty());
1688    }
1689    if segments.len() == 1 {
1690        let segment = segments.first().ok_or_else(|| {
1691            Error::internal("Sparse structural segment unexpectedly missing".to_string())
1692        })?;
1693        if segment.start == 0 && segment.end == output_domain {
1694            return Ok(SparsePositionPlan::all(output_domain));
1695        }
1696        return Ok(SparsePositionPlan::range(
1697            segment.start,
1698            segment.end - segment.start,
1699        ));
1700    }
1701
1702    let total_len = segments
1703        .iter()
1704        .map(|range| range.end - range.start)
1705        .try_fold(0_u64, |sum, len| {
1706            sum.checked_add(len).ok_or_else(|| {
1707                Error::invalid_input_source(
1708                    format!("Sparse structural {label} segment length overflows").into(),
1709                )
1710            })
1711        })?;
1712    let total_len = usize::try_from(total_len).map_err(|_| {
1713        Error::invalid_input_source(
1714            format!("Sparse structural {label} segment length exceeds usize::MAX").into(),
1715        )
1716    })?;
1717    let mut positions = Vec::with_capacity(total_len);
1718    for segment in segments {
1719        positions.extend(segment);
1720    }
1721    SparsePositionPlan::from_positions(positions, output_domain, label)
1722}
1723
1724fn select_position_set(
1725    positions: &SparsePositionPlan,
1726    ranges: &[Range<u64>],
1727    domain_len: u64,
1728    label: &str,
1729) -> Result<SparsePositionSelection> {
1730    let output_domain = validate_slice_ranges(ranges, domain_len, label)?;
1731    let mut segments = Vec::new();
1732    let mut ordinal_ranges = Vec::new();
1733    let mut output_base = 0_u64;
1734
1735    match positions {
1736        SparsePositionPlan::Empty => {}
1737        SparsePositionPlan::All { len } => {
1738            if *len != domain_len {
1739                return Err(Error::invalid_input_source(
1740                    format!(
1741                        "Sparse structural {label} all set length {} does not match domain {}",
1742                        len, domain_len
1743                    )
1744                    .into(),
1745                ));
1746            }
1747            for range in ranges {
1748                let range_len = range.end - range.start;
1749                let output_end = output_base.checked_add(range_len).ok_or_else(|| {
1750                    Error::invalid_input_source(
1751                        format!("Sparse structural {label} output domain overflows").into(),
1752                    )
1753                })?;
1754                push_coalesced_range(&mut segments, output_base..output_end);
1755                push_coalesced_range(&mut ordinal_ranges, range.clone());
1756                output_base = output_end;
1757            }
1758        }
1759        SparsePositionPlan::Range { start, len } => {
1760            let source_end = start.checked_add(*len).ok_or_else(|| {
1761                Error::invalid_input_source(
1762                    format!("Sparse structural {label} range overflows").into(),
1763                )
1764            })?;
1765            if source_end > domain_len {
1766                return Err(Error::invalid_input_source(
1767                    format!(
1768                        "Sparse structural {label} range {}..{} is outside domain {}",
1769                        start, source_end, domain_len
1770                    )
1771                    .into(),
1772                ));
1773            }
1774            for range in ranges {
1775                let intersect_start = range.start.max(*start);
1776                let intersect_end = range.end.min(source_end);
1777                if intersect_start < intersect_end {
1778                    let out_start = output_base
1779                        .checked_add(intersect_start - range.start)
1780                        .ok_or_else(|| {
1781                            Error::invalid_input_source(
1782                                format!("Sparse structural {label} output position overflows")
1783                                    .into(),
1784                            )
1785                        })?;
1786                    let out_end = output_base
1787                        .checked_add(intersect_end - range.start)
1788                        .ok_or_else(|| {
1789                            Error::invalid_input_source(
1790                                format!("Sparse structural {label} output position overflows")
1791                                    .into(),
1792                            )
1793                        })?;
1794                    push_coalesced_range(&mut segments, out_start..out_end);
1795                    push_coalesced_range(
1796                        &mut ordinal_ranges,
1797                        intersect_start - *start..intersect_end - *start,
1798                    );
1799                }
1800                output_base = output_base
1801                    .checked_add(range.end - range.start)
1802                    .ok_or_else(|| {
1803                        Error::invalid_input_source(
1804                            format!("Sparse structural {label} output domain overflows").into(),
1805                        )
1806                    })?;
1807            }
1808        }
1809        SparsePositionPlan::Arithmetic { start, step, .. } => {
1810            positions.validate_domain(domain_len, label)?;
1811            let mut selected = Vec::new();
1812            let mut single = None;
1813            for range in ranges {
1814                let first = positions.rank(range.start);
1815                let end = positions.rank(range.end);
1816                if first < end {
1817                    push_coalesced_range(&mut ordinal_ranges, first..end);
1818                    let out_start = output_base + (start + step * first - range.start);
1819                    if ranges.len() == 1 {
1820                        single = Some(SparsePositionPlan::Arithmetic {
1821                            start: out_start,
1822                            step: *step,
1823                            len: end - first,
1824                        });
1825                    } else {
1826                        selected.extend((0..end - first).map(|i| out_start + step * i));
1827                    }
1828                }
1829                output_base += range.end - range.start;
1830            }
1831            let positions =
1832                if let Some(SparsePositionPlan::Arithmetic { start, step, len }) = single {
1833                    if len == 1 {
1834                        SparsePositionPlan::range(start, 1)
1835                    } else {
1836                        SparsePositionPlan::Arithmetic { start, step, len }
1837                    }
1838                } else {
1839                    SparsePositionPlan::from_positions(selected, output_domain, label)?
1840                };
1841            return Ok(SparsePositionSelection {
1842                positions,
1843                ordinal_ranges,
1844            });
1845        }
1846        SparsePositionPlan::Explicit(source_positions) => {
1847            let mut out_positions = Vec::new();
1848            for range in ranges {
1849                let idx_start =
1850                    source_positions.partition_point(|position| *position < range.start);
1851                let idx_end = source_positions.partition_point(|position| *position < range.end);
1852                if idx_start < idx_end {
1853                    let selected_positions =
1854                        source_positions.get(idx_start..idx_end).ok_or_else(|| {
1855                            Error::invalid_input_source(
1856                                format!(
1857                                    "Sparse structural {label} explicit position slice is invalid"
1858                                )
1859                                .into(),
1860                            )
1861                        })?;
1862                    for position in selected_positions {
1863                        out_positions.push(
1864                            output_base
1865                                .checked_add(*position - range.start)
1866                                .ok_or_else(|| {
1867                                    Error::invalid_input_source(
1868                                        format!(
1869                                            "Sparse structural {label} output position overflows"
1870                                        )
1871                                        .into(),
1872                                    )
1873                                })?,
1874                        );
1875                    }
1876                    push_coalesced_range(&mut ordinal_ranges, idx_start as u64..idx_end as u64);
1877                }
1878                output_base = output_base
1879                    .checked_add(range.end - range.start)
1880                    .ok_or_else(|| {
1881                        Error::invalid_input_source(
1882                            format!("Sparse structural {label} output domain overflows").into(),
1883                        )
1884                    })?;
1885            }
1886            let positions =
1887                SparsePositionPlan::from_positions(out_positions, output_domain, label)?;
1888            return Ok(SparsePositionSelection {
1889                positions,
1890                ordinal_ranges,
1891            });
1892        }
1893    }
1894
1895    Ok(SparsePositionSelection {
1896        positions: position_segments_to_set(segments, output_domain, label)?,
1897        ordinal_ranges,
1898    })
1899}
1900
1901fn select_validity_set(
1902    validity: &SparseValiditySet,
1903    ranges: &[Range<u64>],
1904    domain_len: u64,
1905    label: &str,
1906) -> Result<SparseValiditySet> {
1907    Ok(SparseValiditySet {
1908        meaning: validity.meaning,
1909        positions: select_position_set(&validity.positions, ranges, domain_len, label)?.positions,
1910    })
1911}
1912
1913fn offsets_from_counts(counts: &[u64]) -> Result<Vec<u64>> {
1914    let mut offsets = Vec::with_capacity(counts.len() + 1);
1915    let mut offset = 0_u64;
1916    offsets.push(offset);
1917    for count in counts {
1918        offset = offset.checked_add(*count).ok_or_else(|| {
1919            Error::invalid_input_source("Sparse structural list count offsets overflow".into())
1920        })?;
1921        offsets.push(offset);
1922    }
1923    Ok(offsets)
1924}
1925
1926fn coalesce_ranges(ranges: Vec<Range<u64>>) -> Vec<Range<u64>> {
1927    let mut coalesced: Vec<Range<u64>> = Vec::with_capacity(ranges.len());
1928    for range in ranges {
1929        if range.is_empty() {
1930            continue;
1931        }
1932        if let Some(last) = coalesced.last_mut()
1933            && last.end == range.start
1934        {
1935            last.end = range.end;
1936            continue;
1937        }
1938        coalesced.push(range);
1939    }
1940    coalesced
1941}
1942
1943fn slice_list_layer(
1944    num_slots: u64,
1945    num_child_slots: u64,
1946    non_empty_positions: &SparsePositionPlan,
1947    counts: &SparseCountPlan,
1948    validity: &SparseValiditySet,
1949    ranges: &[Range<u64>],
1950) -> Result<(SparseStructuralLayerPlan, Vec<Range<u64>>)> {
1951    if non_empty_positions.len() != counts.len() {
1952        return Err(Error::invalid_input_source(
1953            "Sparse structural list has mismatched non-empty positions and counts".into(),
1954        ));
1955    }
1956    let count_sum = counts.sum()?;
1957    if count_sum != num_child_slots {
1958        return Err(Error::invalid_input_source(
1959            format!(
1960                "Sparse structural list count sum {} does not match child slots {}",
1961                count_sum, num_child_slots
1962            )
1963            .into(),
1964        ));
1965    }
1966
1967    let non_empty_selection =
1968        select_position_set(non_empty_positions, ranges, num_slots, "list non-empty")?;
1969    let out_counts = select_count_set(
1970        counts,
1971        &non_empty_selection.ordinal_ranges,
1972        non_empty_selection.positions.len(),
1973    )?;
1974    let child_ranges =
1975        child_ranges_from_counts(counts, num_child_slots, &non_empty_selection.ordinal_ranges)?;
1976    let out_validity = select_validity_set(validity, ranges, num_slots, "list validity")?;
1977    let out_num_slots = validate_slice_ranges(ranges, num_slots, "list")?;
1978    let out_num_child_slots = out_counts.sum()?;
1979    Ok((
1980        SparseStructuralLayerPlan::List {
1981            num_slots: out_num_slots,
1982            num_child_slots: out_num_child_slots,
1983            non_empty_positions: non_empty_selection.positions,
1984            counts: out_counts,
1985            validity: out_validity,
1986        },
1987        child_ranges,
1988    ))
1989}
1990
1991fn select_count_set(
1992    counts: &SparseCountPlan,
1993    ordinal_ranges: &[Range<u64>],
1994    selected_len: u64,
1995) -> Result<SparseCountPlan> {
1996    match counts {
1997        SparseCountPlan::Empty => {
1998            if selected_len != 0 {
1999                return Err(Error::invalid_input_source(
2000                    "Sparse structural selected non-empty positions but counts are empty".into(),
2001                ));
2002            }
2003            Ok(SparseCountPlan::Empty)
2004        }
2005        SparseCountPlan::Constant { value, len } => {
2006            validate_ordinal_ranges(ordinal_ranges, *len, "constant list counts")?;
2007            Ok(SparseCountPlan::constant(*value, selected_len))
2008        }
2009        SparseCountPlan::Explicit {
2010            counts: source_counts,
2011            ..
2012        } => {
2013            validate_ordinal_ranges(
2014                ordinal_ranges,
2015                source_counts.len() as u64,
2016                "explicit list counts",
2017            )?;
2018            let selected_len = usize::try_from(selected_len).map_err(|_| {
2019                Error::invalid_input_source(
2020                    "Sparse structural selected count length exceeds usize::MAX".into(),
2021                )
2022            })?;
2023            let mut out_counts = Vec::with_capacity(selected_len);
2024            for range in ordinal_ranges {
2025                let start = usize::try_from(range.start).map_err(|_| {
2026                    Error::invalid_input_source(
2027                        "Sparse structural count ordinal exceeds usize::MAX".into(),
2028                    )
2029                })?;
2030                let end = usize::try_from(range.end).map_err(|_| {
2031                    Error::invalid_input_source(
2032                        "Sparse structural count ordinal exceeds usize::MAX".into(),
2033                    )
2034                })?;
2035                out_counts.extend_from_slice(source_counts.get(start..end).ok_or_else(|| {
2036                    Error::invalid_input_source(
2037                        "Sparse structural explicit count slice is invalid".into(),
2038                    )
2039                })?);
2040            }
2041            SparseCountPlan::from_counts(out_counts)
2042        }
2043    }
2044}
2045
2046fn validate_ordinal_ranges(ranges: &[Range<u64>], len: u64, label: &str) -> Result<()> {
2047    for range in ranges {
2048        if range.start > range.end || range.end > len {
2049            return Err(Error::invalid_input_source(
2050                format!(
2051                    "Sparse structural {label} ordinal range {}..{} is outside {} values",
2052                    range.start, range.end, len
2053                )
2054                .into(),
2055            ));
2056        }
2057    }
2058    Ok(())
2059}
2060
2061fn child_ranges_from_counts(
2062    counts: &SparseCountPlan,
2063    num_child_slots: u64,
2064    ordinal_ranges: &[Range<u64>],
2065) -> Result<Vec<Range<u64>>> {
2066    if ordinal_ranges.is_empty() {
2067        return Ok(Vec::new());
2068    }
2069    let child_ranges = match counts {
2070        SparseCountPlan::Empty => {
2071            return Err(Error::invalid_input_source(
2072                "Sparse structural selected non-empty positions but counts are empty".into(),
2073            ));
2074        }
2075        SparseCountPlan::Constant { value, len } => {
2076            let expected_child_slots = value.checked_mul(*len).ok_or_else(|| {
2077                Error::invalid_input_source(
2078                    "Sparse structural list constant count sum overflows child slots".into(),
2079                )
2080            })?;
2081            if expected_child_slots != num_child_slots {
2082                return Err(Error::invalid_input_source(
2083                    format!(
2084                        "Sparse structural list constant count sum {} does not match child slots {}",
2085                        expected_child_slots, num_child_slots
2086                    )
2087                    .into(),
2088                ));
2089            }
2090            validate_ordinal_ranges(ordinal_ranges, *len, "constant list counts")?;
2091            ordinal_ranges
2092                .iter()
2093                .map(|range| {
2094                    let start = range.start.checked_mul(*value).ok_or_else(|| {
2095                        Error::invalid_input_source(
2096                            "Sparse structural list child range start overflows".into(),
2097                        )
2098                    })?;
2099                    let end = range.end.checked_mul(*value).ok_or_else(|| {
2100                        Error::invalid_input_source(
2101                            "Sparse structural list child range end overflows".into(),
2102                        )
2103                    })?;
2104                    Ok(start..end)
2105                })
2106                .collect::<Result<Vec<_>>>()?
2107        }
2108        SparseCountPlan::Explicit {
2109            counts,
2110            offsets: value_offsets,
2111        } => {
2112            let last_offset = *value_offsets.last().ok_or_else(|| {
2113                Error::invalid_input_source(
2114                    "Sparse structural list count offsets are unexpectedly empty".into(),
2115                )
2116            })?;
2117            if last_offset != num_child_slots {
2118                return Err(Error::invalid_input_source(
2119                    format!(
2120                        "Sparse structural list count sum {} does not match child slots {}",
2121                        last_offset, num_child_slots
2122                    )
2123                    .into(),
2124                ));
2125            }
2126            validate_ordinal_ranges(ordinal_ranges, counts.len() as u64, "explicit list counts")?;
2127            ordinal_ranges
2128                .iter()
2129                .map(|range| {
2130                    let start = usize::try_from(range.start).map_err(|_| {
2131                        Error::invalid_input_source(
2132                            "Sparse structural count ordinal exceeds usize::MAX".into(),
2133                        )
2134                    })?;
2135                    let end = usize::try_from(range.end).map_err(|_| {
2136                        Error::invalid_input_source(
2137                            "Sparse structural count ordinal exceeds usize::MAX".into(),
2138                        )
2139                    })?;
2140                    let start_offset = *value_offsets.get(start).ok_or_else(|| {
2141                        Error::invalid_input_source(
2142                            "Sparse structural list start offset is missing".into(),
2143                        )
2144                    })?;
2145                    let end_offset = *value_offsets.get(end).ok_or_else(|| {
2146                        Error::invalid_input_source(
2147                            "Sparse structural list end offset is missing".into(),
2148                        )
2149                    })?;
2150                    Ok(start_offset..end_offset)
2151                })
2152                .collect::<Result<Vec<_>>>()?
2153        }
2154    };
2155    Ok(coalesce_ranges(child_ranges))
2156}
2157
2158fn slice_sparse_plan(
2159    plan: &SparseStructuralPlan,
2160    row_ranges: &[Range<u64>],
2161    row_domain: u64,
2162) -> Result<SparseStructuralSelection> {
2163    // The immutable page plan is validated before it enters the page cache.
2164    // Validate each requested range below, without rescanning the whole page.
2165    let mut selected_ranges = row_ranges.to_vec();
2166    let mut selected_domain = row_domain;
2167    let mut sliced_layers = Vec::with_capacity(plan.layers.len());
2168
2169    for layer in &plan.layers {
2170        match layer {
2171            SparseStructuralLayerPlan::Validity {
2172                num_slots,
2173                validity,
2174            } => {
2175                if selected_domain != *num_slots {
2176                    return Err(Error::invalid_input_source(
2177                        format!(
2178                            "Sparse structural validity slice domain {} does not match {} slots",
2179                            selected_domain, num_slots
2180                        )
2181                        .into(),
2182                    ));
2183                }
2184                let out_num_slots =
2185                    validate_slice_ranges(&selected_ranges, *num_slots, "validity")?;
2186                sliced_layers.push(SparseStructuralLayerPlan::Validity {
2187                    num_slots: out_num_slots,
2188                    validity: select_validity_set(
2189                        validity,
2190                        &selected_ranges,
2191                        *num_slots,
2192                        "validity",
2193                    )?,
2194                });
2195            }
2196            SparseStructuralLayerPlan::List {
2197                num_slots,
2198                num_child_slots,
2199                non_empty_positions,
2200                counts,
2201                validity,
2202                ..
2203            } => {
2204                if selected_domain != *num_slots {
2205                    return Err(Error::invalid_input_source(
2206                        format!(
2207                            "Sparse structural list slice domain {} does not match {} slots",
2208                            selected_domain, num_slots
2209                        )
2210                        .into(),
2211                    ));
2212                }
2213                let (sliced_layer, child_ranges) = slice_list_layer(
2214                    *num_slots,
2215                    *num_child_slots,
2216                    non_empty_positions,
2217                    counts,
2218                    validity,
2219                    &selected_ranges,
2220                )?;
2221                sliced_layers.push(sliced_layer);
2222                selected_ranges = child_ranges;
2223                selected_domain = *num_child_slots;
2224            }
2225            SparseStructuralLayerPlan::FixedSizeList {
2226                num_slots,
2227                dimension,
2228                validity,
2229            } => {
2230                if selected_domain != *num_slots {
2231                    return Err(Error::invalid_input_source(
2232                        format!(
2233                            "Sparse structural fixed-size-list slice domain {} does not match {} slots",
2234                            selected_domain, num_slots
2235                        )
2236                        .into(),
2237                    ));
2238                }
2239                let num_child_slots = num_slots.checked_mul(*dimension).ok_or_else(|| {
2240                    Error::invalid_input_source(
2241                        format!(
2242                            "Sparse structural fixed-size-list child slot count overflows: slots={}, dimension={}",
2243                            num_slots, dimension
2244                        )
2245                        .into(),
2246                    )
2247                })?;
2248                let out_num_slots =
2249                    validate_slice_ranges(&selected_ranges, *num_slots, "fixed-size-list")?;
2250                let child_ranges = selected_ranges
2251                    .iter()
2252                    .map(|range| {
2253                        let start = range.start.checked_mul(*dimension).ok_or_else(|| {
2254                            Error::invalid_input_source(
2255                                "Sparse structural fixed-size-list child range start overflows"
2256                                    .into(),
2257                            )
2258                        })?;
2259                        let end = range.end.checked_mul(*dimension).ok_or_else(|| {
2260                            Error::invalid_input_source(
2261                                "Sparse structural fixed-size-list child range end overflows"
2262                                    .into(),
2263                            )
2264                        })?;
2265                        Ok(start..end)
2266                    })
2267                    .collect::<Result<Vec<_>>>()?;
2268                sliced_layers.push(SparseStructuralLayerPlan::FixedSizeList {
2269                    num_slots: out_num_slots,
2270                    dimension: *dimension,
2271                    validity: select_validity_set(
2272                        validity,
2273                        &selected_ranges,
2274                        *num_slots,
2275                        "fixed-size-list validity",
2276                    )?,
2277                });
2278                selected_ranges = child_ranges;
2279                selected_domain = num_child_slots;
2280            }
2281        }
2282    }
2283
2284    if selected_domain != plan.num_visible_items {
2285        return Err(Error::invalid_input_source(
2286            format!(
2287                "Sparse structural selected terminal domain {} does not match {} visible items",
2288                selected_domain, plan.num_visible_items
2289            )
2290            .into(),
2291        ));
2292    }
2293    let num_visible_items =
2294        validate_slice_ranges(&selected_ranges, plan.num_visible_items, "visible value")?;
2295    Ok(SparseStructuralSelection {
2296        plan: SparseStructuralPlan {
2297            layers: sliced_layers,
2298            num_visible_items,
2299        },
2300        leaf_ranges: coalesce_ranges(selected_ranges),
2301    })
2302}
2303
2304#[cfg(test)]
2305mod tests {
2306    use super::*;
2307    fn assert_invalid_input_contains(err: Error, expected: &str) {
2308        let message = err.to_string();
2309        assert!(
2310            matches!(&err, Error::InvalidInput { .. }),
2311            "expected InvalidInput, got {err:?}"
2312        );
2313        assert!(
2314            message.contains(expected),
2315            "expected error to contain {expected:?}, got {message}"
2316        );
2317    }
2318
2319    fn null_set(positions: SparsePositionPlan) -> SparseValiditySet {
2320        SparseValiditySet {
2321            meaning: SparseValidityMeaning::NullPositions,
2322            positions,
2323        }
2324    }
2325
2326    #[test]
2327    fn generic_sparse_positions_remain_symbolic() {
2328        let len = 1_000_000_000_000;
2329        for encoding in [
2330            EncodingUtils::range(64, 3, 7),
2331            EncodingUtils::delta(
2332                64,
2333                3,
2334                EncodingUtils::constant(Some(Bytes::copy_from_slice(&7_u64.to_le_bytes()))),
2335            ),
2336        ] {
2337            let decoder = position_decoder(&encoding, len, 7 * len, "test").unwrap();
2338            let positions = decode_position_set(&decoder, &mut std::iter::empty(), "test").unwrap();
2339            assert_eq!(positions.deep_size(), 0);
2340            assert_eq!(positions.rank(17), 2);
2341            assert!(positions.contains(17));
2342            assert!(!positions.contains(18));
2343            let selected = select_position_set(&positions, &[10..32], 7 * len, "test").unwrap();
2344            assert_eq!(
2345                selected.positions.materialize().unwrap(),
2346                vec![0, 7, 14, 21]
2347            );
2348            assert_eq!(selected.ordinal_ranges, vec![1..5]);
2349            assert_eq!(selected.positions.deep_size(), 0);
2350            let opposite = SparsePositionPlan::Arithmetic {
2351                start: 4,
2352                step: 7,
2353                len,
2354            };
2355            assert!(positions.is_disjoint(&opposite, 7 * len).unwrap());
2356        }
2357    }
2358
2359    #[test]
2360    fn generic_sparse_sequences_reject_invalid_metadata() {
2361        for (encoding, count, domain) in [
2362            (EncodingUtils::range(64, u64::MAX - 1, 2), 2, u64::MAX),
2363            (EncodingUtils::range(64, 0, 0), 2, 3),
2364            (EncodingUtils::range(64, 1, 1), 1, 3),
2365            (EncodingUtils::constant(None), 1, 3),
2366            (EncodingUtils::flat(64, None), 0, 3),
2367            (EncodingUtils::flat(32, None), 2, 3),
2368            (
2369                EncodingUtils::constant(Some(Bytes::copy_from_slice(&1_u64.to_le_bytes()))),
2370                2,
2371                3,
2372            ),
2373        ] {
2374            assert!(position_decoder(&encoding, count, domain, "test").is_err());
2375        }
2376        let zero = EncodingUtils::constant(Some(Bytes::copy_from_slice(&0_u64.to_le_bytes())));
2377        assert!(count_decoder(&zero, 2, "test").is_err());
2378        let huge = EncodingUtils::constant(Some(Bytes::copy_from_slice(&u64::MAX.to_le_bytes())));
2379        assert!(count_decoder(&huge, 2, "test").is_err());
2380    }
2381
2382    #[test]
2383    fn arithmetic_set_operations_match_explicit_positions() {
2384        for a in 0..8 {
2385            for b in 0..8 {
2386                for s in 1..8 {
2387                    for t in 1..8 {
2388                        let first = SparsePositionPlan::Arithmetic {
2389                            start: a,
2390                            step: s,
2391                            len: 5,
2392                        };
2393                        let second = SparsePositionPlan::Arithmetic {
2394                            start: b,
2395                            step: t,
2396                            len: 4,
2397                        };
2398                        let left = first.materialize().unwrap();
2399                        let right = second.materialize().unwrap();
2400                        assert_eq!(
2401                            first.is_disjoint(&second, 64).unwrap(),
2402                            left.iter().all(|v| !right.contains(v))
2403                        );
2404                        assert_eq!(
2405                            first.is_subset_of(&second, 64).unwrap(),
2406                            left.iter().all(|v| right.contains(v))
2407                        );
2408                        assert_eq!(
2409                            second.is_subset_of(&first, 64).unwrap(),
2410                            right.iter().all(|v| left.contains(v))
2411                        );
2412                    }
2413                }
2414            }
2415        }
2416    }
2417
2418    fn valid_set(positions: SparsePositionPlan) -> SparseValiditySet {
2419        SparseValiditySet {
2420            meaning: SparseValidityMeaning::ValidPositions,
2421            positions,
2422        }
2423    }
2424
2425    #[test]
2426    fn semantic_position_and_count_sets_project_without_materializing_ranges() {
2427        let ranges = [1..4];
2428        assert_eq!(
2429            select_position_set(&SparsePositionPlan::Empty, &ranges, 5, "empty")
2430                .unwrap()
2431                .positions,
2432            SparsePositionPlan::Empty
2433        );
2434        assert_eq!(
2435            select_position_set(&SparsePositionPlan::all(5), &ranges, 5, "all")
2436                .unwrap()
2437                .positions,
2438            SparsePositionPlan::all(3)
2439        );
2440        assert_eq!(
2441            select_position_set(&SparsePositionPlan::range(1, 3), &ranges, 5, "range")
2442                .unwrap()
2443                .positions,
2444            SparsePositionPlan::all(3)
2445        );
2446        assert_eq!(
2447            select_position_set(
2448                &SparsePositionPlan::Explicit(vec![0, 2, 4]),
2449                &[0..1, 4..5],
2450                5,
2451                "explicit",
2452            )
2453            .unwrap()
2454            .positions,
2455            SparsePositionPlan::all(2)
2456        );
2457
2458        assert_eq!(
2459            select_count_set(&SparseCountPlan::Empty, &[], 0).unwrap(),
2460            SparseCountPlan::Empty
2461        );
2462        assert_eq!(
2463            select_count_set(&SparseCountPlan::constant(2, 3), &[0..1, 2..3], 2,).unwrap(),
2464            SparseCountPlan::constant(2, 2)
2465        );
2466        assert_eq!(
2467            select_count_set(
2468                &SparseCountPlan::from_counts(vec![1, 2, 3]).unwrap(),
2469                &[0..1, 2..3],
2470                2,
2471            )
2472            .unwrap(),
2473            SparseCountPlan::from_counts(vec![1, 3]).unwrap()
2474        );
2475    }
2476
2477    #[test]
2478    fn validity_polarities_rebuild_the_same_arrow_domain() {
2479        let mut null_builder = BooleanBufferBuilder::new(4);
2480        null_set(SparsePositionPlan::Explicit(vec![1, 3]))
2481            .append_to(&mut null_builder, 4)
2482            .unwrap();
2483        assert_eq!(
2484            null_builder.finish().iter().collect::<Vec<_>>(),
2485            vec![true, false, true, false]
2486        );
2487
2488        let mut valid_builder = BooleanBufferBuilder::new(4);
2489        valid_set(SparsePositionPlan::range(1, 2))
2490            .append_to(&mut valid_builder, 4)
2491            .unwrap();
2492        assert_eq!(
2493            valid_builder.finish().iter().collect::<Vec<_>>(),
2494            vec![false, true, true, false]
2495        );
2496    }
2497
2498    #[test]
2499    fn schema_layer_mismatches_are_invalid_input() {
2500        let mut missing = SparseStructuralUnraveler::new(SparseStructuralPlan {
2501            layers: Vec::new(),
2502            num_visible_items: 1,
2503        });
2504        let mut validity = BooleanBufferBuilder::new(1);
2505        let err = missing.unravel_validity(&mut validity).unwrap_err();
2506        assert_invalid_input_contains(err, "fewer layers than the Arrow schema");
2507
2508        let mut fixed_size_list = SparseStructuralUnraveler::new(SparseStructuralPlan {
2509            layers: vec![SparseStructuralLayerPlan::FixedSizeList {
2510                num_slots: 2,
2511                dimension: 2,
2512                validity: null_set(SparsePositionPlan::empty()),
2513            }],
2514            num_visible_items: 4,
2515        });
2516        let mut validity = BooleanBufferBuilder::new(2);
2517        let err = fixed_size_list.unravel_validity(&mut validity).unwrap_err();
2518        assert_invalid_input_contains(err, "does not match the Arrow schema");
2519
2520        let extra = SparseStructuralUnraveler::new(SparseStructuralPlan {
2521            layers: vec![SparseStructuralLayerPlan::Validity {
2522                num_slots: 1,
2523                validity: null_set(SparsePositionPlan::empty()),
2524            }],
2525            num_visible_items: 1,
2526        });
2527        let err = extra.ensure_exhausted().unwrap_err();
2528        assert_invalid_input_contains(err, "1 unconsumed layer");
2529    }
2530
2531    fn nested_plan() -> SparseStructuralPlan {
2532        SparseStructuralPlan {
2533            layers: vec![
2534                SparseStructuralLayerPlan::Validity {
2535                    num_slots: 6,
2536                    validity: null_set(SparsePositionPlan::Explicit(vec![1, 4])),
2537                },
2538                SparseStructuralLayerPlan::List {
2539                    num_slots: 6,
2540                    num_child_slots: 5,
2541                    non_empty_positions: SparsePositionPlan::Explicit(vec![0, 2, 5]),
2542                    counts: SparseCountPlan::from_counts(vec![2, 1, 2]).unwrap(),
2543                    validity: null_set(SparsePositionPlan::Explicit(vec![1, 4])),
2544                },
2545                SparseStructuralLayerPlan::FixedSizeList {
2546                    num_slots: 5,
2547                    dimension: 2,
2548                    validity: valid_set(SparsePositionPlan::range(1, 3)),
2549                },
2550            ],
2551            num_visible_items: 10,
2552        }
2553    }
2554
2555    #[test]
2556    fn discontiguous_projection_preserves_outer_to_inner_order() {
2557        let selection = slice_sparse_plan(&nested_plan(), &[5..6, 0..1], 6).unwrap();
2558        assert_eq!(selection.leaf_ranges, vec![6..10, 0..4]);
2559        assert_eq!(selection.plan.num_visible_items, 8);
2560        selection.plan.validate(2).unwrap();
2561
2562        let SparseStructuralLayerPlan::List {
2563            non_empty_positions,
2564            counts,
2565            ..
2566        } = &selection.plan.layers[1]
2567        else {
2568            panic!("expected projected list layer");
2569        };
2570        assert_eq!(*non_empty_positions, SparsePositionPlan::all(2));
2571        assert_eq!(*counts, SparseCountPlan::constant(2, 2));
2572    }
2573
2574    #[test]
2575    fn no_value_projection_keeps_empty_and_null_list_structure() {
2576        let selection = slice_sparse_plan(&nested_plan(), &[1..2, 3..4], 6).unwrap();
2577        assert!(selection.leaf_ranges.is_empty());
2578        assert_eq!(selection.plan.num_visible_items, 0);
2579        selection.plan.validate(2).unwrap();
2580
2581        let SparseStructuralLayerPlan::List {
2582            num_slots,
2583            num_child_slots,
2584            non_empty_positions,
2585            counts,
2586            validity,
2587        } = &selection.plan.layers[1]
2588        else {
2589            panic!("expected projected list layer");
2590        };
2591        assert_eq!((*num_slots, *num_child_slots), (2, 0));
2592        assert_eq!(*non_empty_positions, SparsePositionPlan::Empty);
2593        assert_eq!(*counts, SparseCountPlan::Empty);
2594        assert_eq!(*validity, null_set(SparsePositionPlan::range(0, 1)));
2595    }
2596    #[rstest::rstest]
2597    #[case::zero_dimension(0, 4, "dimension is zero")]
2598    #[case::unaligned_rows(3, 4, "not divisible")]
2599    fn rejects_invalid_row_scale(#[case] dimension: u64, #[case] rows: u64, #[case] message: &str) {
2600        let structure = pb23::SparseStructure {
2601            layers: vec![pb23::SparseStructuralLayer {
2602                layer: Some(pb23::sparse_structural_layer::Layer::FixedSizeList(
2603                    pb23::SparseFixedSizeListLayer {
2604                        dimension,
2605                        validity: None,
2606                    },
2607                )),
2608            }],
2609        };
2610        assert_invalid_input_contains(structure.row_count_and_scale(rows).unwrap_err(), message);
2611    }
2612
2613    #[test]
2614    fn rejects_derived_domain_overflow() {
2615        let layer = pb23::SparseStructuralLayer {
2616            layer: Some(pb23::sparse_structural_layer::Layer::FixedSizeList(
2617                pb23::SparseFixedSizeListLayer {
2618                    dimension: 2,
2619                    validity: None,
2620                },
2621            )),
2622        };
2623        assert_invalid_input_contains(child_domain(&layer, u64::MAX).unwrap_err(), "overflows");
2624        let structure = pb23::SparseStructure {
2625            layers: vec![layer; 64],
2626        };
2627        assert_invalid_input_contains(structure.row_count_and_scale(0).unwrap_err(), "overflows");
2628    }
2629
2630    #[rstest::rstest]
2631    #[case::duplicate(vec![1, 1], "strictly increasing")]
2632    #[case::unordered(vec![3, 1], "strictly increasing")]
2633    #[case::out_of_domain(vec![0, 4], "outside")]
2634    fn rejects_invalid_decoded_positions(#[case] positions: Vec<u64>, #[case] message: &str) {
2635        let decoder = position_decoder(&EncodingUtils::flat(64, None), 2, 4, "test").unwrap();
2636        let bytes = Bytes::from(
2637            positions
2638                .into_iter()
2639                .flat_map(u64::to_le_bytes)
2640                .collect::<Vec<_>>(),
2641        );
2642        let error =
2643            decode_position_set(&decoder, &mut vec![bytes].into_iter(), "test").unwrap_err();
2644        assert_invalid_input_contains(error, message);
2645    }
2646
2647    #[test]
2648    fn rejects_malformed_compressed_structural_payload() {
2649        use crate::encodings::physical::block::{CompressionConfig, CompressionScheme};
2650        let encoding = EncodingUtils::wrapped(
2651            CompressionConfig::new(CompressionScheme::Lz4, None),
2652            EncodingUtils::flat(64, None),
2653        )
2654        .unwrap();
2655        let decoder = position_decoder(&encoding, 2, 4, "test").unwrap();
2656        let error = decode_position_set(
2657            &decoder,
2658            &mut vec![Bytes::from_static(&[0xff])].into_iter(),
2659            "test",
2660        )
2661        .unwrap_err();
2662        assert!(matches!(error, Error::InvalidInput { .. }), "{error}");
2663    }
2664}