1use 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#[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
299fn 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 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 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}