1use arrow_array::Array;
2use arrow_array::new_null_array;
3use arrow_schema::DataType;
4use std::sync::Arc;
5
6use arrow_array::builder::{BooleanBuilder, Float64Builder, StringBuilder, UInt8Builder};
7use arrow_array::{ArrayRef, BooleanArray, Float64Array, StringArray, UInt8Array, UInt32Array};
8use once_cell::sync::OnceCell;
9
10use crate::format::FormatId;
11use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
12use rustc_hash::FxHashMap;
13use std::collections::BTreeMap;
14
15#[repr(u8)]
17#[derive(Copy, Clone, Debug, Eq, PartialEq)]
18pub enum TypeTag {
19 Empty = 0,
20 Number = 1,
21 Boolean = 2,
22 Text = 3,
23 Error = 4,
24 DateTime = 5, Duration = 6, Pending = 7,
27}
28
29impl TypeTag {
30 fn from_value(v: &LiteralValue) -> Self {
31 match v {
32 LiteralValue::Empty => TypeTag::Empty,
33 LiteralValue::Int(_) | LiteralValue::Number(_) => TypeTag::Number,
34 LiteralValue::Boolean(_) => TypeTag::Boolean,
35 LiteralValue::Text(_) => TypeTag::Text,
36 LiteralValue::Error(_) => TypeTag::Error,
37 LiteralValue::Date(_) | LiteralValue::DateTime(_) | LiteralValue::Time(_) => {
38 TypeTag::DateTime
39 }
40 LiteralValue::Duration(_) => TypeTag::Duration,
41 LiteralValue::Pending => TypeTag::Pending,
42 LiteralValue::Array(_) => TypeTag::Error, }
44 }
45}
46
47impl TypeTag {
48 #[inline]
49 pub fn from_u8(b: u8) -> Self {
50 match b {
51 x if x == TypeTag::Empty as u8 => TypeTag::Empty,
52 x if x == TypeTag::Number as u8 => TypeTag::Number,
53 x if x == TypeTag::Boolean as u8 => TypeTag::Boolean,
54 x if x == TypeTag::Text as u8 => TypeTag::Text,
55 x if x == TypeTag::Error as u8 => TypeTag::Error,
56 x if x == TypeTag::DateTime as u8 => TypeTag::DateTime,
57 x if x == TypeTag::Duration as u8 => TypeTag::Duration,
58 x if x == TypeTag::Pending as u8 => TypeTag::Pending,
59 _ => TypeTag::Empty,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Copy, Default)]
65pub struct ColumnChunkMeta {
66 pub len: usize,
67 pub non_null_num: usize,
68 pub non_null_bool: usize,
69 pub non_null_text: usize,
70 pub non_null_err: usize,
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct FormatRuns {
76 run_ends: Vec<u32>,
77 format_ids: Vec<u16>,
78}
79
80impl FormatRuns {
81 pub fn from_ids(ids: &[u16]) -> Option<Self> {
82 if ids.iter().all(|id| *id == FormatId::GENERAL.0) {
83 return None;
84 }
85 let mut run_ends = Vec::new();
86 let mut format_ids = Vec::new();
87 for (idx, id) in ids.iter().copied().enumerate() {
88 if format_ids.last().copied() != Some(id) {
89 format_ids.push(id);
90 if idx > 0 {
91 run_ends.push(idx as u32);
92 }
93 }
94 }
95 run_ends.push(ids.len() as u32);
96 Some(Self {
97 run_ends,
98 format_ids,
99 })
100 }
101
102 #[inline]
103 pub fn get(&self, offset: usize) -> FormatId {
104 let run = self
105 .run_ends
106 .partition_point(|end| (*end as usize) <= offset);
107 self.format_ids
108 .get(run)
109 .copied()
110 .map(FormatId)
111 .unwrap_or_default()
112 }
113
114 pub fn to_ids(&self, len: usize) -> Vec<u16> {
115 (0..len).map(|offset| self.get(offset).0).collect()
116 }
117
118 pub(crate) fn all_general_in(&self, offset: usize, len: usize) -> bool {
120 if len == 0 {
121 return true;
122 }
123 let first = self
124 .run_ends
125 .partition_point(|end| (*end as usize) <= offset);
126 let last_off = offset + len - 1;
127 let mut run = first;
128 loop {
129 match self.format_ids.get(run) {
130 None => return true,
132 Some(&id) if id != FormatId::GENERAL.0 => return false,
133 Some(_) => {}
134 }
135 match self.run_ends.get(run) {
136 Some(&end) if (end as usize) <= last_off => run += 1,
137 _ => return true,
138 }
139 }
140 }
141
142 pub fn slice(&self, offset: usize, len: usize) -> Option<Self> {
143 let ids: Vec<_> = (offset..offset.saturating_add(len))
144 .map(|i| self.get(i).0)
145 .collect();
146 Self::from_ids(&ids)
147 }
148}
149
150struct MergedLane<T> {
153 base: Arc<T>,
154 key: (u64, u64),
155 merged: Option<Arc<T>>,
156 pending: usize,
158}
159
160#[derive(Default)]
161struct MergedInner {
162 numbers: Option<MergedLane<Float64Array>>,
163 errors: Option<MergedLane<UInt8Array>>,
164}
165
166#[derive(Default)]
169struct MergedLaneCache {
170 inner: std::sync::Mutex<Option<Box<MergedInner>>>,
172}
173
174impl Clone for MergedLaneCache {
175 fn clone(&self) -> Self {
176 Self::default()
177 }
178}
179
180impl std::fmt::Debug for MergedLaneCache {
181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182 f.write_str("MergedLaneCache")
183 }
184}
185
186impl MergedLaneCache {
187 fn lookup<T>(
188 slot: &mut Option<MergedLane<T>>,
189 base: &Arc<T>,
190 key: (u64, u64),
191 request: usize,
192 chunk_len: usize,
193 build: impl FnOnce() -> Arc<T>,
194 ) -> Option<Arc<T>> {
195 let lane = match slot {
196 Some(lane) if Arc::ptr_eq(&lane.base, base) && lane.key == key => lane,
197 _ => slot.insert(MergedLane {
198 base: base.clone(),
199 key,
200 merged: None,
201 pending: 0,
202 }),
203 };
204 if let Some(merged) = &lane.merged {
205 return Some(merged.clone());
206 }
207 lane.pending = lane.pending.saturating_add(request);
208 if lane.pending <= 2 * chunk_len {
213 return None;
214 }
215 let merged = build();
216 lane.merged = Some(merged.clone());
217 Some(merged)
218 }
219}
220
221#[derive(Debug, Clone)]
222pub struct ColumnChunk {
223 pub numbers: Option<Arc<Float64Array>>,
224 pub booleans: Option<Arc<BooleanArray>>,
225 pub text: Option<ArrayRef>, pub errors: Option<Arc<UInt8Array>>, pub type_tag: Arc<UInt8Array>,
228 pub formula_id: Option<Arc<UInt32Array>>, pub format: Option<FormatRuns>,
231 pub meta: ColumnChunkMeta,
232 lazy_null_numbers: OnceCell<Arc<Float64Array>>,
234 lazy_null_booleans: OnceCell<Arc<BooleanArray>>,
235 lazy_null_text: OnceCell<ArrayRef>,
236 lazy_null_errors: OnceCell<Arc<UInt8Array>>,
237 lowered_text: OnceCell<ArrayRef>,
239 merged: MergedLaneCache,
242 pub overlay: Overlay,
244 pub computed_overlay: Overlay,
246}
247
248impl ColumnChunk {
249 pub(crate) fn criteria_overlay_epochs(&self) -> (u64, u64) {
251 (self.overlay.epoch, self.computed_overlay.epoch)
252 }
253
254 #[inline]
255 pub fn len(&self) -> usize {
256 self.type_tag.len()
257 }
258
259 pub(crate) fn merged_numbers(
267 &self,
268 range: core::ops::Range<usize>,
269 ) -> Option<Arc<Float64Array>> {
270 let base = self.numbers_or_null();
271 let key = (self.overlay.epoch, self.computed_overlay.epoch);
272 let len = self.len();
273 let mut guard = self.merged.inner.lock().ok()?;
274 let inner = guard.get_or_insert_with(Default::default);
275 let entry =
276 MergedLaneCache::lookup(&mut inner.numbers, &base, key, range.len(), len, || {
277 OverlayCascade::new(&self.overlay, &self.computed_overlay)
278 .select_numbers(0..len, &base)
279 })?;
280 Some(Arc::new(entry.slice(range.start, range.len())))
281 }
282
283 pub(crate) fn merged_errors(&self, range: core::ops::Range<usize>) -> Option<Arc<UInt8Array>> {
285 let base = self.errors_or_null();
286 let key = (self.overlay.epoch, self.computed_overlay.epoch);
287 let len = self.len();
288 let mut guard = self.merged.inner.lock().ok()?;
289 let inner = guard.get_or_insert_with(Default::default);
290 let entry =
291 MergedLaneCache::lookup(&mut inner.errors, &base, key, range.len(), len, || {
292 OverlayCascade::new(&self.overlay, &self.computed_overlay)
293 .select_errors(0..len, &base)
294 })?;
295 Some(Arc::new(entry.slice(range.start, range.len())))
296 }
297 #[inline]
298 pub fn is_empty(&self) -> bool {
299 self.len() == 0
300 }
301 #[inline]
302 pub fn numbers_or_null(&self) -> Arc<Float64Array> {
303 #[cfg(test)]
304 crate::engine::range_view::range_work::record(|w| w.provider_requests[0] += 1);
305 if let Some(a) = &self.numbers {
306 return a.clone();
307 }
308 self.lazy_null_numbers
309 .get_or_init(|| {
310 #[cfg(test)]
311 crate::engine::range_view::range_work::record(|w| {
312 w.provider_builds[0] += 1;
313 w.provider_slots[0] += self.len();
314 });
315 let arr = new_null_array(&DataType::Float64, self.len());
316 Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
317 })
318 .clone()
319 }
320 #[inline]
321 pub fn booleans_or_null(&self) -> Arc<BooleanArray> {
322 #[cfg(test)]
323 crate::engine::range_view::range_work::record(|w| w.provider_requests[1] += 1);
324 if let Some(a) = &self.booleans {
325 return a.clone();
326 }
327 self.lazy_null_booleans
328 .get_or_init(|| {
329 #[cfg(test)]
330 crate::engine::range_view::range_work::record(|w| {
331 w.provider_builds[1] += 1;
332 w.provider_slots[1] += self.len();
333 });
334 let arr = new_null_array(&DataType::Boolean, self.len());
335 Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
336 })
337 .clone()
338 }
339 #[inline]
340 pub fn errors_or_null(&self) -> Arc<UInt8Array> {
341 #[cfg(test)]
342 crate::engine::range_view::range_work::record(|w| w.provider_requests[2] += 1);
343 if let Some(a) = &self.errors {
344 return a.clone();
345 }
346 self.lazy_null_errors
347 .get_or_init(|| {
348 #[cfg(test)]
349 crate::engine::range_view::range_work::record(|w| {
350 w.provider_builds[2] += 1;
351 w.provider_slots[2] += self.len();
352 });
353 let arr = new_null_array(&DataType::UInt8, self.len());
354 Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
355 })
356 .clone()
357 }
358 #[inline]
359 pub fn text_or_null(&self) -> ArrayRef {
360 #[cfg(test)]
361 crate::engine::range_view::range_work::record(|w| w.provider_requests[3] += 1);
362 if let Some(a) = &self.text {
363 return a.clone();
364 }
365 self.lazy_null_text
366 .get_or_init(|| {
367 #[cfg(test)]
368 crate::engine::range_view::range_work::record(|w| {
369 w.provider_builds[3] += 1;
370 w.provider_slots[3] += self.len();
371 });
372 new_null_array(&DataType::Utf8, self.len())
373 })
374 .clone()
375 }
376
377 pub fn text_lower_or_null(&self) -> ArrayRef {
379 if let Some(a) = self.lowered_text.get() {
380 return a.clone();
381 }
382 let out: ArrayRef = if let Some(txt) = &self.text {
384 let sa = txt.as_any().downcast_ref::<StringArray>().unwrap();
385 let mut b = arrow_array::builder::StringBuilder::with_capacity(sa.len(), sa.len() * 8);
386 for i in 0..sa.len() {
387 if sa.is_null(i) {
388 b.append_null();
389 } else {
390 b.append_value(sa.value(i).to_lowercase());
391 }
392 }
393 let lowered = b.finish();
394 Arc::new(lowered)
395 } else {
396 new_null_array(&DataType::Utf8, self.len())
397 };
398 self.lowered_text.get_or_init(|| out.clone());
399 out
400 }
401
402 pub fn grow_len_to(&mut self, new_len: usize) {
407 let old_len = self.len();
408 if new_len <= old_len {
409 return;
410 }
411
412 let mut tags: Vec<u8> = self.type_tag.values().to_vec();
414 tags.resize(new_len, TypeTag::Empty as u8);
415 self.type_tag = Arc::new(UInt8Array::from(tags));
416
417 if let Some(a) = &self.numbers {
419 use arrow_array::builder::Float64Builder;
420 let mut b = Float64Builder::with_capacity(new_len);
421 for i in 0..old_len {
422 if a.is_null(i) {
423 b.append_null();
424 } else {
425 b.append_value(a.value(i));
426 }
427 }
428 for _ in old_len..new_len {
429 b.append_null();
430 }
431 self.numbers = Some(Arc::new(b.finish()));
432 }
433 if let Some(a) = &self.booleans {
434 use arrow_array::builder::BooleanBuilder;
435 let mut b = BooleanBuilder::with_capacity(new_len);
436 for i in 0..old_len {
437 if a.is_null(i) {
438 b.append_null();
439 } else {
440 b.append_value(a.value(i));
441 }
442 }
443 for _ in old_len..new_len {
444 b.append_null();
445 }
446 self.booleans = Some(Arc::new(b.finish()));
447 }
448 if let Some(a) = &self.errors {
449 use arrow_array::builder::UInt8Builder;
450 let mut b = UInt8Builder::with_capacity(new_len);
451 for i in 0..old_len {
452 if a.is_null(i) {
453 b.append_null();
454 } else {
455 b.append_value(a.value(i));
456 }
457 }
458 for _ in old_len..new_len {
459 b.append_null();
460 }
461 self.errors = Some(Arc::new(b.finish()));
462 }
463 if let Some(a) = &self.text {
464 use arrow_array::builder::StringBuilder;
465 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
466 let mut b = StringBuilder::with_capacity(new_len, 0);
467 for i in 0..old_len {
468 if sa.is_null(i) {
469 b.append_null();
470 } else {
471 b.append_value(sa.value(i));
472 }
473 }
474 for _ in old_len..new_len {
475 b.append_null();
476 }
477 self.text = Some(Arc::new(b.finish()) as ArrayRef);
478 }
479
480 if let Some(format) = &self.format {
481 let mut ids = format.to_ids(old_len);
482 ids.resize(new_len, FormatId::GENERAL.0);
483 self.format = FormatRuns::from_ids(&ids);
484 }
485
486 self.lazy_null_numbers = OnceCell::new();
488 self.lazy_null_booleans = OnceCell::new();
489 self.lazy_null_text = OnceCell::new();
490 self.lazy_null_errors = OnceCell::new();
491 self.lowered_text = OnceCell::new();
492
493 self.meta.len = new_len;
494 }
495}
496
497#[derive(Debug, Clone)]
498pub struct ArrowColumn {
499 pub chunks: Vec<ColumnChunk>,
500 pub sparse_chunks: FxHashMap<usize, ColumnChunk>,
501 pub index: u32,
502}
503
504impl ArrowColumn {
505 #[inline]
506 pub fn chunk(&self, idx: usize) -> Option<&ColumnChunk> {
507 if idx < self.chunks.len() {
508 Some(&self.chunks[idx])
509 } else {
510 self.sparse_chunks.get(&idx)
511 }
512 }
513
514 #[inline]
515 pub fn chunk_mut(&mut self, idx: usize) -> Option<&mut ColumnChunk> {
516 if idx < self.chunks.len() {
517 Some(&mut self.chunks[idx])
518 } else {
519 self.sparse_chunks.get_mut(&idx)
520 }
521 }
522
523 #[inline]
524 pub fn has_sparse_chunks(&self) -> bool {
525 !self.sparse_chunks.is_empty()
526 }
527
528 #[inline]
529 pub fn total_chunk_count(&self) -> usize {
530 self.chunks.len() + self.sparse_chunks.len()
531 }
532}
533
534#[derive(Debug, Clone)]
535pub struct ArrowSheet {
536 pub name: Arc<str>,
537 pub date_system: crate::engine::DateSystem,
542 pub columns: Vec<ArrowColumn>,
543 pub nrows: u32,
544 pub chunk_starts: Vec<usize>,
545 pub chunk_rows: usize,
550}
551
552#[derive(Debug, Default, Clone)]
553pub struct SheetStore {
554 pub sheets: Vec<ArrowSheet>,
555}
556
557impl SheetStore {
558 pub fn sheet(&self, name: &str) -> Option<&ArrowSheet> {
559 self.sheets.iter().find(|s| s.name.as_ref() == name)
560 }
561 pub fn sheet_mut(&mut self, name: &str) -> Option<&mut ArrowSheet> {
562 self.sheets.iter_mut().find(|s| s.name.as_ref() == name)
563 }
564}
565
566pub struct IngestBuilder {
568 name: Arc<str>,
569 ncols: usize,
570 chunk_rows: usize,
571 date_system: crate::engine::DateSystem,
572
573 num_builders: Vec<Float64Builder>,
575 bool_builders: Vec<BooleanBuilder>,
576 text_builders: Vec<StringBuilder>,
577 err_builders: Vec<UInt8Builder>,
578 tag_builders: Vec<UInt8Builder>,
579 format_builders: Vec<Vec<u16>>,
580
581 lane_counts: Vec<LaneCounts>,
583 next_text_bytes: Vec<usize>,
585 needs_provision: bool,
587
588 chunks: Vec<Vec<ColumnChunk>>, row_in_chunk: usize,
591 total_rows: u32,
592}
593
594#[derive(Debug, Clone, Copy, Default)]
595struct LaneCounts {
596 n_num: usize,
597 n_bool: usize,
598 n_text: usize,
599 n_err: usize,
600}
601
602const FIRST_CHUNK_ROWS: usize = 1024;
604
605impl IngestBuilder {
606 pub fn new(
607 sheet_name: &str,
608 ncols: usize,
609 chunk_rows: usize,
610 date_system: crate::engine::DateSystem,
611 ) -> Self {
612 let mut chunks = Vec::with_capacity(ncols);
613 chunks.resize_with(ncols, Vec::new);
614 let cap = chunk_rows.clamp(1, FIRST_CHUNK_ROWS);
619 Self {
620 name: Arc::from(sheet_name.to_string()),
621 ncols,
622 chunk_rows: chunk_rows.max(1),
623 date_system,
624 num_builders: (0..ncols)
625 .map(|_| Float64Builder::with_capacity(cap))
626 .collect(),
627 bool_builders: (0..ncols)
628 .map(|_| BooleanBuilder::with_capacity(cap))
629 .collect(),
630 text_builders: (0..ncols)
633 .map(|_| StringBuilder::with_capacity(cap, 0))
634 .collect(),
635 err_builders: (0..ncols)
636 .map(|_| UInt8Builder::with_capacity(cap))
637 .collect(),
638 tag_builders: (0..ncols)
639 .map(|_| UInt8Builder::with_capacity(cap))
640 .collect(),
641 format_builders: (0..ncols).map(|_| Vec::with_capacity(cap)).collect(),
642 lane_counts: vec![LaneCounts::default(); ncols],
643 next_text_bytes: vec![0; ncols],
644 needs_provision: false,
645 chunks,
646 row_in_chunk: 0,
647 total_rows: 0,
648 }
649 }
650
651 pub fn append_row_cells<'a>(&mut self, row: &[CellIngest<'a>]) -> Result<(), ExcelError> {
654 assert_eq!(row.len(), self.ncols, "row width mismatch");
655 self.provision_if_needed();
656 for (c, cell) in row.iter().enumerate() {
657 self.format_builders[c].push(match cell {
658 CellIngest::DateSerial(serial) if serial.fract().abs() > f64::EPSILON => {
659 FormatId::DATETIME.0
660 }
661 CellIngest::DateSerial(_) => FormatId::DATE.0,
662 CellIngest::FormattedNumber(_, id) => id.0,
663 CellIngest::DurationSerial(_) => FormatId::DURATION.0,
664 _ => FormatId::GENERAL.0,
665 });
666 match cell {
667 CellIngest::Empty => {
668 self.tag_builders[c].append_value(TypeTag::Empty as u8);
669 self.num_builders[c].append_null();
670 self.bool_builders[c].append_null();
671 self.text_builders[c].append_null();
672 self.err_builders[c].append_null();
673 }
674 CellIngest::Number(n) => {
675 self.tag_builders[c].append_value(TypeTag::Number as u8);
676 self.num_builders[c].append_value(*n);
677 self.lane_counts[c].n_num += 1;
678 self.bool_builders[c].append_null();
679 self.text_builders[c].append_null();
680 self.err_builders[c].append_null();
681 }
682 CellIngest::Boolean(b) => {
683 self.tag_builders[c].append_value(TypeTag::Boolean as u8);
684 self.num_builders[c].append_null();
685 self.bool_builders[c].append_value(*b);
686 self.lane_counts[c].n_bool += 1;
687 self.text_builders[c].append_null();
688 self.err_builders[c].append_null();
689 }
690 CellIngest::Text(s) => {
691 self.tag_builders[c].append_value(TypeTag::Text as u8);
692 self.num_builders[c].append_null();
693 self.bool_builders[c].append_null();
694 self.text_builders[c].append_value(s);
695 self.lane_counts[c].n_text += 1;
696 self.err_builders[c].append_null();
697 }
698 CellIngest::ErrorCode(code) => {
699 self.tag_builders[c].append_value(TypeTag::Error as u8);
700 self.num_builders[c].append_null();
701 self.bool_builders[c].append_null();
702 self.text_builders[c].append_null();
703 self.err_builders[c].append_value(*code);
704 self.lane_counts[c].n_err += 1;
705 }
706 CellIngest::DateSerial(serial) | CellIngest::FormattedNumber(serial, _) => {
707 self.tag_builders[c].append_value(TypeTag::Number as u8);
708 self.num_builders[c].append_value(*serial);
709 self.lane_counts[c].n_num += 1;
710 self.bool_builders[c].append_null();
711 self.text_builders[c].append_null();
712 self.err_builders[c].append_null();
713 }
714 CellIngest::DurationSerial(serial) => {
715 self.tag_builders[c].append_value(TypeTag::Number as u8);
716 self.num_builders[c].append_value(*serial);
717 self.lane_counts[c].n_num += 1;
718 self.bool_builders[c].append_null();
719 self.text_builders[c].append_null();
720 self.err_builders[c].append_null();
721 }
722 CellIngest::Pending => {
723 self.tag_builders[c].append_value(TypeTag::Pending as u8);
724 self.num_builders[c].append_null();
725 self.bool_builders[c].append_null();
726 self.text_builders[c].append_null();
727 self.err_builders[c].append_null();
728 }
729 }
730 }
731 self.row_in_chunk += 1;
732 self.total_rows += 1;
733 if self.row_in_chunk >= self.chunk_rows {
734 self.finish_chunk();
735 }
736 Ok(())
737 }
738
739 pub fn append_row_cells_iter<'a, I>(&mut self, iter: I) -> Result<(), ExcelError>
742 where
743 I: ExactSizeIterator<Item = CellIngest<'a>>,
744 {
745 assert_eq!(iter.len(), self.ncols, "row width mismatch");
746 self.provision_if_needed();
747 for (c, cell) in iter.enumerate() {
748 self.format_builders[c].push(match cell {
749 CellIngest::DateSerial(serial) if serial.fract().abs() > f64::EPSILON => {
750 FormatId::DATETIME.0
751 }
752 CellIngest::DateSerial(_) => FormatId::DATE.0,
753 CellIngest::FormattedNumber(_, id) => id.0,
754 CellIngest::DurationSerial(_) => FormatId::DURATION.0,
755 _ => FormatId::GENERAL.0,
756 });
757 match cell {
758 CellIngest::Empty => {
759 self.tag_builders[c].append_value(TypeTag::Empty as u8);
760 self.num_builders[c].append_null();
761 self.bool_builders[c].append_null();
762 self.text_builders[c].append_null();
763 self.err_builders[c].append_null();
764 }
765 CellIngest::Number(n) => {
766 self.tag_builders[c].append_value(TypeTag::Number as u8);
767 self.num_builders[c].append_value(n);
768 self.lane_counts[c].n_num += 1;
769 self.bool_builders[c].append_null();
770 self.text_builders[c].append_null();
771 self.err_builders[c].append_null();
772 }
773 CellIngest::Boolean(b) => {
774 self.tag_builders[c].append_value(TypeTag::Boolean as u8);
775 self.num_builders[c].append_null();
776 self.bool_builders[c].append_value(b);
777 self.lane_counts[c].n_bool += 1;
778 self.text_builders[c].append_null();
779 self.err_builders[c].append_null();
780 }
781 CellIngest::Text(s) => {
782 self.tag_builders[c].append_value(TypeTag::Text as u8);
783 self.num_builders[c].append_null();
784 self.bool_builders[c].append_null();
785 self.text_builders[c].append_value(s);
786 self.lane_counts[c].n_text += 1;
787 self.err_builders[c].append_null();
788 }
789 CellIngest::ErrorCode(code) => {
790 self.tag_builders[c].append_value(TypeTag::Error as u8);
791 self.num_builders[c].append_null();
792 self.bool_builders[c].append_null();
793 self.text_builders[c].append_null();
794 self.err_builders[c].append_value(code);
795 self.lane_counts[c].n_err += 1;
796 }
797 CellIngest::DateSerial(serial) | CellIngest::FormattedNumber(serial, _) => {
798 self.tag_builders[c].append_value(TypeTag::Number as u8);
799 self.num_builders[c].append_value(serial);
800 self.lane_counts[c].n_num += 1;
801 self.bool_builders[c].append_null();
802 self.text_builders[c].append_null();
803 self.err_builders[c].append_null();
804 }
805 CellIngest::DurationSerial(serial) => {
806 self.tag_builders[c].append_value(TypeTag::Number as u8);
807 self.num_builders[c].append_value(serial);
808 self.lane_counts[c].n_num += 1;
809 self.bool_builders[c].append_null();
810 self.text_builders[c].append_null();
811 self.err_builders[c].append_null();
812 }
813 CellIngest::Pending => {
814 self.tag_builders[c].append_value(TypeTag::Pending as u8);
815 self.num_builders[c].append_null();
816 self.bool_builders[c].append_null();
817 self.text_builders[c].append_null();
818 self.err_builders[c].append_null();
819 }
820 }
821 }
822 self.row_in_chunk += 1;
823 self.total_rows += 1;
824 if self.row_in_chunk >= self.chunk_rows {
825 self.finish_chunk();
826 }
827 Ok(())
828 }
829
830 pub fn append_row(&mut self, row: &[LiteralValue]) -> Result<(), ExcelError> {
832 assert_eq!(row.len(), self.ncols, "row width mismatch");
833 self.provision_if_needed();
834
835 for (c, v) in row.iter().enumerate() {
836 self.format_builders[c].push(match v {
837 LiteralValue::Date(_) => FormatId::DATE.0,
838 LiteralValue::DateTime(_) => FormatId::DATETIME.0,
839 LiteralValue::Time(_) => FormatId::TIME.0,
840 LiteralValue::Duration(_) => FormatId::DURATION.0,
841 _ => FormatId::GENERAL.0,
842 });
843 let tag = match v {
844 LiteralValue::Date(_)
845 | LiteralValue::DateTime(_)
846 | LiteralValue::Time(_)
847 | LiteralValue::Duration(_) => TypeTag::Number,
848 _ => TypeTag::from_value(v),
849 } as u8;
850 self.tag_builders[c].append_value(tag);
851
852 match v {
853 LiteralValue::Empty => {
854 self.num_builders[c].append_null();
855 self.bool_builders[c].append_null();
856 self.text_builders[c].append_null();
857 self.err_builders[c].append_null();
858 }
859 LiteralValue::Int(i) => {
860 self.num_builders[c].append_value(*i as f64);
861 self.lane_counts[c].n_num += 1;
862 self.bool_builders[c].append_null();
863 self.text_builders[c].append_null();
864 self.err_builders[c].append_null();
865 }
866 LiteralValue::Number(n) => {
867 self.num_builders[c].append_value(*n);
868 self.lane_counts[c].n_num += 1;
869 self.bool_builders[c].append_null();
870 self.text_builders[c].append_null();
871 self.err_builders[c].append_null();
872 }
873 LiteralValue::Boolean(b) => {
874 self.num_builders[c].append_null();
875 self.bool_builders[c].append_value(*b);
876 self.lane_counts[c].n_bool += 1;
877 self.text_builders[c].append_null();
878 self.err_builders[c].append_null();
879 }
880 LiteralValue::Text(s) => {
881 self.num_builders[c].append_null();
882 self.bool_builders[c].append_null();
883 self.text_builders[c].append_value(s);
884 self.lane_counts[c].n_text += 1;
885 self.err_builders[c].append_null();
886 }
887 LiteralValue::Error(e) => {
888 self.num_builders[c].append_null();
889 self.bool_builders[c].append_null();
890 self.text_builders[c].append_null();
891 self.err_builders[c].append_value(map_error_code(e.kind));
892 self.lane_counts[c].n_err += 1;
893 }
894 LiteralValue::Date(d) => {
896 let dt = d.and_hms_opt(0, 0, 0).unwrap();
897 let serial = formualizer_common::datetime_to_serial_for(self.date_system, &dt);
898 self.num_builders[c].append_value(serial);
899 self.lane_counts[c].n_num += 1;
900 self.bool_builders[c].append_null();
901 self.text_builders[c].append_null();
902 self.err_builders[c].append_null();
903 }
904 LiteralValue::DateTime(dt) => {
905 let serial = formualizer_common::datetime_to_serial_for(self.date_system, dt);
906 self.num_builders[c].append_value(serial);
907 self.lane_counts[c].n_num += 1;
908 self.bool_builders[c].append_null();
909 self.text_builders[c].append_null();
910 self.err_builders[c].append_null();
911 }
912 LiteralValue::Time(t) => {
913 let serial = formualizer_common::time_to_fraction(t);
914 self.num_builders[c].append_value(serial);
915 self.lane_counts[c].n_num += 1;
916 self.bool_builders[c].append_null();
917 self.text_builders[c].append_null();
918 self.err_builders[c].append_null();
919 }
920 LiteralValue::Duration(dur) => {
921 let serial = dur.num_seconds() as f64 / 86_400.0;
922 self.num_builders[c].append_value(serial);
923 self.lane_counts[c].n_num += 1;
924 self.bool_builders[c].append_null();
925 self.text_builders[c].append_null();
926 self.err_builders[c].append_null();
927 }
928 LiteralValue::Array(_) => {
929 self.num_builders[c].append_null();
931 self.bool_builders[c].append_null();
932 self.text_builders[c].append_null();
933 self.err_builders[c].append_value(map_error_code(ExcelErrorKind::Value));
934 self.lane_counts[c].n_err += 1;
935 }
936 LiteralValue::Pending => {
937 self.num_builders[c].append_null();
939 self.bool_builders[c].append_null();
940 self.text_builders[c].append_null();
941 self.err_builders[c].append_null();
942 }
943 }
944 }
945
946 self.row_in_chunk += 1;
947 self.total_rows += 1;
948
949 if self.row_in_chunk >= self.chunk_rows {
950 self.finish_chunk();
951 }
952
953 Ok(())
954 }
955
956 #[inline]
960 fn provision_if_needed(&mut self) {
961 if !self.needs_provision {
962 return;
963 }
964 self.needs_provision = false;
965 for c in 0..self.ncols {
966 self.num_builders[c] = Float64Builder::with_capacity(self.chunk_rows);
967 self.bool_builders[c] = BooleanBuilder::with_capacity(self.chunk_rows);
968 self.text_builders[c] =
970 StringBuilder::with_capacity(self.chunk_rows, self.next_text_bytes[c]);
971 self.err_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
972 self.tag_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
973 self.format_builders[c] = Vec::with_capacity(self.chunk_rows);
974 }
975 }
976
977 fn finish_chunk(&mut self) {
978 if self.row_in_chunk == 0 {
979 return;
980 }
981 let exact = self.row_in_chunk.saturating_mul(2) <= self.chunk_rows;
986 for c in 0..self.ncols {
987 let len = self.row_in_chunk;
988 let numbers_arc: Option<Arc<Float64Array>> = if self.lane_counts[c].n_num == 0 {
989 None
990 } else if exact {
991 Some(Arc::new(self.num_builders[c].finish_cloned()))
992 } else {
993 Some(Arc::new(self.num_builders[c].finish()))
994 };
995 let booleans_arc: Option<Arc<BooleanArray>> = if self.lane_counts[c].n_bool == 0 {
996 None
997 } else if exact {
998 Some(Arc::new(self.bool_builders[c].finish_cloned()))
999 } else {
1000 Some(Arc::new(self.bool_builders[c].finish()))
1001 };
1002 let mut text_bytes = 0usize;
1003 let text_ref: Option<ArrayRef> = if self.lane_counts[c].n_text == 0 {
1004 None
1005 } else {
1006 let text = if exact {
1007 self.text_builders[c].finish_cloned()
1008 } else {
1009 self.text_builders[c].finish()
1010 };
1011 text_bytes = text.values().len();
1012 Some(Arc::new(text))
1013 };
1014 let errors_arc: Option<Arc<UInt8Array>> = if self.lane_counts[c].n_err == 0 {
1015 None
1016 } else if exact {
1017 Some(Arc::new(self.err_builders[c].finish_cloned()))
1018 } else {
1019 Some(Arc::new(self.err_builders[c].finish()))
1020 };
1021 let tags: UInt8Array = if exact {
1022 self.tag_builders[c].finish_cloned()
1023 } else {
1024 self.tag_builders[c].finish()
1025 };
1026
1027 let chunk = ColumnChunk {
1028 numbers: numbers_arc,
1029 booleans: booleans_arc,
1030 text: text_ref,
1031 errors: errors_arc,
1032 type_tag: Arc::new(tags),
1033 formula_id: None,
1034 format: FormatRuns::from_ids(&self.format_builders[c]),
1035 meta: ColumnChunkMeta {
1036 len,
1037 non_null_num: self.lane_counts[c].n_num,
1038 non_null_bool: self.lane_counts[c].n_bool,
1039 non_null_text: self.lane_counts[c].n_text,
1040 non_null_err: self.lane_counts[c].n_err,
1041 },
1042 lazy_null_numbers: OnceCell::new(),
1043 lazy_null_booleans: OnceCell::new(),
1044 lazy_null_text: OnceCell::new(),
1045 lazy_null_errors: OnceCell::new(),
1046 lowered_text: OnceCell::new(),
1047 merged: MergedLaneCache::default(),
1048 overlay: Overlay::new(),
1049 computed_overlay: Overlay::new(),
1050 };
1051 self.chunks[c].push(chunk);
1052
1053 self.lane_counts[c] = LaneCounts::default();
1054 self.next_text_bytes[c] = text_bytes;
1055 }
1056 self.row_in_chunk = 0;
1057 self.needs_provision = true;
1058 }
1059
1060 pub fn finish(mut self) -> ArrowSheet {
1061 self.finish_chunk();
1063
1064 let mut columns = Vec::with_capacity(self.ncols);
1065 for (idx, chunks) in self.chunks.into_iter().enumerate() {
1066 columns.push(ArrowColumn {
1067 chunks,
1068 sparse_chunks: FxHashMap::default(),
1069 index: idx as u32,
1070 });
1071 }
1072 let mut chunk_starts: Vec<usize> = Vec::new();
1074 if let Some(col0) = columns.first() {
1075 let chunks_len0 = col0.chunks.len();
1076 for (ci, col) in columns.iter().enumerate() {
1077 if col.chunks.len() != chunks_len0 {
1078 panic!(
1079 "ArrowSheet chunk misalignment: column {} chunks={} != {}",
1080 ci,
1081 col.chunks.len(),
1082 chunks_len0
1083 );
1084 }
1085 }
1086 let mut cur = 0usize;
1087 for i in 0..chunks_len0 {
1088 let len_i = col0.chunks[i].type_tag.len();
1089 for (ci, col) in columns.iter().enumerate() {
1090 let got = col.chunks[i].type_tag.len();
1091 if got != len_i {
1092 panic!(
1093 "ArrowSheet chunk row-length misalignment at chunk {i}: col {ci} len={got} != {len_i}"
1094 );
1095 }
1096 }
1097 chunk_starts.push(cur);
1098 cur += len_i;
1099 }
1100 }
1101 ArrowSheet {
1102 name: self.name,
1103 date_system: self.date_system,
1104 columns,
1105 nrows: self.total_rows,
1106 chunk_starts,
1107 chunk_rows: self.chunk_rows,
1108 }
1109 }
1110}
1111
1112pub fn map_error_code(kind: ExcelErrorKind) -> u8 {
1113 match kind {
1114 ExcelErrorKind::Null => 1,
1115 ExcelErrorKind::Ref => 2,
1116 ExcelErrorKind::Name => 3,
1117 ExcelErrorKind::Value => 4,
1118 ExcelErrorKind::Div => 5,
1119 ExcelErrorKind::Na => 6,
1120 ExcelErrorKind::Num => 7,
1121 ExcelErrorKind::Error => 8,
1122 ExcelErrorKind::NImpl => 9,
1123 ExcelErrorKind::Spill => 10,
1124 ExcelErrorKind::Calc => 11,
1125 ExcelErrorKind::Circ => 12,
1126 ExcelErrorKind::Cancelled => 13,
1127 _ => 8,
1128 }
1129}
1130
1131pub fn unmap_error_code(code: u8) -> ExcelErrorKind {
1132 match code {
1133 1 => ExcelErrorKind::Null,
1134 2 => ExcelErrorKind::Ref,
1135 3 => ExcelErrorKind::Name,
1136 4 => ExcelErrorKind::Value,
1137 5 => ExcelErrorKind::Div,
1138 6 => ExcelErrorKind::Na,
1139 7 => ExcelErrorKind::Num,
1140 8 => ExcelErrorKind::Error,
1141 9 => ExcelErrorKind::NImpl,
1142 10 => ExcelErrorKind::Spill,
1143 11 => ExcelErrorKind::Calc,
1144 12 => ExcelErrorKind::Circ,
1145 13 => ExcelErrorKind::Cancelled,
1146 _ => ExcelErrorKind::Error,
1147 }
1148}
1149
1150pub enum CellIngest<'a> {
1154 Empty,
1155 Number(f64),
1156 Boolean(bool),
1157 Text(&'a str),
1158 ErrorCode(u8),
1159 DateSerial(f64),
1160 DurationSerial(f64),
1161 FormattedNumber(f64, FormatId),
1162 Pending,
1163}
1164
1165#[derive(Debug, Clone, PartialEq)]
1166pub enum OverlayValue {
1167 Empty,
1168 Number(f64),
1169 DateTime(f64),
1171 Duration(f64),
1173 Boolean(bool),
1174 Text(Arc<str>),
1175 Error(u8),
1176 Pending,
1177}
1178
1179impl OverlayValue {
1180 pub fn from_literal_value(
1181 value: &LiteralValue,
1182 date_system: crate::engine::DateSystem,
1183 ) -> Self {
1184 match value {
1185 LiteralValue::Empty => OverlayValue::Empty,
1186 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
1187 LiteralValue::Number(n) => OverlayValue::Number(*n),
1188 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
1189 LiteralValue::Text(s) => OverlayValue::Text(Arc::from(s.clone())),
1190 LiteralValue::Error(e) => OverlayValue::Error(map_error_code(e.kind)),
1191 LiteralValue::Date(d) => {
1192 let dt = d.and_hms_opt(0, 0, 0).unwrap();
1193 OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, &dt))
1194 }
1195 LiteralValue::DateTime(dt) => {
1196 OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, dt))
1197 }
1198 LiteralValue::Time(t) => {
1199 OverlayValue::DateTime(formualizer_common::time_to_fraction(t))
1200 }
1201 LiteralValue::Duration(d) => OverlayValue::Duration(d.num_seconds() as f64 / 86_400.0),
1202 LiteralValue::Pending => OverlayValue::Pending,
1203 LiteralValue::Array(_) => OverlayValue::Error(map_error_code(ExcelErrorKind::Value)),
1204 }
1205 }
1206
1207 #[inline]
1208 pub(crate) fn estimated_payload_bytes(&self) -> usize {
1209 match self {
1210 OverlayValue::Empty | OverlayValue::Pending => 0,
1211 OverlayValue::Number(_) | OverlayValue::DateTime(_) | OverlayValue::Duration(_) => {
1212 core::mem::size_of::<f64>()
1213 }
1214 OverlayValue::Boolean(_) => core::mem::size_of::<bool>(),
1215 OverlayValue::Error(_) => core::mem::size_of::<u8>(),
1216 OverlayValue::Text(s) => s.len(),
1218 }
1219 }
1220
1221 #[inline]
1222 pub(crate) fn type_tag(&self) -> TypeTag {
1223 match self {
1224 OverlayValue::Empty => TypeTag::Empty,
1225 OverlayValue::Number(_) => TypeTag::Number,
1226 OverlayValue::DateTime(_) => TypeTag::DateTime,
1227 OverlayValue::Duration(_) => TypeTag::Duration,
1228 OverlayValue::Boolean(_) => TypeTag::Boolean,
1229 OverlayValue::Text(_) => TypeTag::Text,
1230 OverlayValue::Error(_) => TypeTag::Error,
1231 OverlayValue::Pending => TypeTag::Pending,
1232 }
1233 }
1234
1235 #[inline]
1236 pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
1237 match self {
1238 OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
1239 Some(*n)
1240 }
1241 _ => None,
1242 }
1243 }
1244
1245 #[inline]
1246 pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
1247 match self {
1248 OverlayValue::Boolean(b) => Some(*b),
1249 _ => None,
1250 }
1251 }
1252
1253 #[inline]
1254 pub(crate) fn text_lane_value(&self) -> Option<&str> {
1255 match self {
1256 OverlayValue::Text(s) => Some(s.as_ref()),
1257 _ => None,
1258 }
1259 }
1260
1261 #[inline]
1262 pub(crate) fn error_lane_value(&self) -> Option<u8> {
1263 match self {
1264 OverlayValue::Error(code) => Some(*code),
1265 _ => None,
1266 }
1267 }
1268
1269 pub(crate) fn lowered_text_value(&self) -> Option<String> {
1270 match self {
1271 OverlayValue::Text(s) => Some(s.to_lowercase()),
1272 OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
1273 Some(n.to_string())
1274 }
1275 OverlayValue::Boolean(b) => Some(if *b { "true" } else { "false" }.to_string()),
1276 OverlayValue::Empty | OverlayValue::Error(_) | OverlayValue::Pending => None,
1277 }
1278 }
1279
1280 pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
1281 match self {
1282 OverlayValue::Empty => LiteralValue::Empty,
1283 OverlayValue::Number(n) => LiteralValue::Number(*n),
1284 OverlayValue::DateTime(serial) | OverlayValue::Duration(serial) => {
1285 let _ = date_system;
1286 LiteralValue::Number(*serial)
1287 }
1288 OverlayValue::Boolean(b) => LiteralValue::Boolean(*b),
1289 OverlayValue::Text(s) => LiteralValue::Text((**s).to_string()),
1290 OverlayValue::Error(code) => {
1291 LiteralValue::Error(ExcelError::new(unmap_error_code(*code)))
1292 }
1293 OverlayValue::Pending => LiteralValue::Pending,
1294 }
1295 }
1296
1297 #[cfg(test)]
1298 pub(crate) fn to_literal(&self) -> LiteralValue {
1299 self.to_literal_for(crate::engine::DateSystem::Excel1900)
1300 }
1301}
1302
1303#[derive(Debug, Clone)]
1304pub(crate) enum OverlayScalar<'a> {
1305 Borrowed(&'a OverlayValue),
1306 Owned(OverlayValue),
1307}
1308
1309impl<'a> OverlayScalar<'a> {
1310 #[inline]
1311 fn as_value(&self) -> &OverlayValue {
1312 match self {
1313 OverlayScalar::Borrowed(value) => value,
1314 OverlayScalar::Owned(value) => value,
1315 }
1316 }
1317
1318 #[inline]
1319 pub(crate) fn to_overlay_value(&self) -> OverlayValue {
1320 self.as_value().clone()
1321 }
1322
1323 #[inline]
1324 pub(crate) fn type_tag(&self) -> TypeTag {
1325 self.as_value().type_tag()
1326 }
1327
1328 #[inline]
1329 pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
1330 self.as_value().numeric_lane_value()
1331 }
1332
1333 #[inline]
1334 pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
1335 self.as_value().boolean_lane_value()
1336 }
1337
1338 #[inline]
1339 pub(crate) fn text_lane_value(&self) -> Option<&str> {
1340 self.as_value().text_lane_value()
1341 }
1342
1343 #[inline]
1344 pub(crate) fn error_lane_value(&self) -> Option<u8> {
1345 self.as_value().error_lane_value()
1346 }
1347
1348 pub(crate) fn lowered_text_value(&self) -> Option<String> {
1349 self.as_value().lowered_text_value()
1350 }
1351
1352 pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
1353 self.as_value().to_literal_for(date_system)
1354 }
1355
1356 #[cfg(test)]
1357 pub(crate) fn to_literal(&self) -> LiteralValue {
1358 self.to_literal_for(crate::engine::DateSystem::Excel1900)
1359 }
1360}
1361
1362const OVERLAY_ENTRY_BASE_BYTES: usize = 32;
1363const OVERLAY_FRAGMENT_BASE_BYTES: usize = 48;
1364
1365#[allow(dead_code)]
1366#[derive(Debug, Clone)]
1367pub(crate) struct OverlayFragmentPayload {
1368 type_tags: Arc<UInt8Array>,
1369 numbers: Option<Arc<Float64Array>>,
1370 booleans: Option<Arc<BooleanArray>>,
1371 text: Option<ArrayRef>,
1372 errors: Option<Arc<UInt8Array>>,
1373 estimated_bytes: usize,
1374 root_len: usize,
1377}
1378
1379impl OverlayFragmentPayload {
1380 const SHARE_MIN_FRACTION: usize = 4;
1386 const SHARE_ALWAYS_LEN: usize = 64;
1387
1388 #[inline]
1389 fn shares(root_len: usize, len: usize) -> bool {
1390 root_len <= Self::SHARE_ALWAYS_LEN
1391 || len.saturating_mul(Self::SHARE_MIN_FRACTION) >= root_len
1392 }
1393
1394 #[inline]
1395 fn len(&self) -> usize {
1396 self.type_tags.len()
1397 }
1398
1399 fn slice(&self, offset: usize, len: usize) -> Self {
1400 debug_assert!(offset.saturating_add(len) <= self.len());
1401 if len == self.len() {
1402 return self.clone();
1403 }
1404 if !Self::shares(self.root_len, len) {
1405 return Self::from_values(self.values_slice(offset, len));
1406 }
1407 fn cut<A: Array + Clone + 'static>(a: &Arc<A>, offset: usize, len: usize) -> Arc<A> {
1408 let sliced = a.slice(offset, len);
1409 Arc::new(
1410 sliced
1411 .as_any()
1412 .downcast_ref::<A>()
1413 .expect("slice keeps the array type")
1414 .clone(),
1415 )
1416 }
1417 let own = self.len().max(1);
1418 Self {
1419 type_tags: cut(&self.type_tags, offset, len),
1420 numbers: self.numbers.as_ref().map(|a| cut(a, offset, len)),
1421 booleans: self.booleans.as_ref().map(|a| cut(a, offset, len)),
1422 text: self.text.as_ref().map(|a| a.slice(offset, len)),
1423 errors: self.errors.as_ref().map(|a| cut(a, offset, len)),
1424 estimated_bytes: self.estimated_bytes.saturating_mul(len) / own,
1427 root_len: self.root_len,
1428 }
1429 }
1430
1431 fn from_values(values: Vec<OverlayValue>) -> Self {
1432 let len = values.len();
1433 let mut tag_b = UInt8Builder::with_capacity(len);
1434 let mut nb = Float64Builder::with_capacity(len);
1435 let mut bb = BooleanBuilder::with_capacity(len);
1436 let mut sb = StringBuilder::with_capacity(len, len.saturating_mul(8));
1437 let mut eb = UInt8Builder::with_capacity(len);
1438 let mut non_num = 0usize;
1439 let mut non_bool = 0usize;
1440 let mut non_text = 0usize;
1441 let mut non_err = 0usize;
1442
1443 for value in &values {
1444 append_overlay_value_to_lane_builders(
1445 value,
1446 &mut tag_b,
1447 &mut nb,
1448 &mut bb,
1449 &mut sb,
1450 &mut eb,
1451 &mut non_num,
1452 &mut non_bool,
1453 &mut non_text,
1454 &mut non_err,
1455 );
1456 }
1457
1458 let type_tags = Arc::new(tag_b.finish());
1459 let numbers = {
1460 let a = nb.finish();
1461 (non_num > 0).then(|| Arc::new(a))
1462 };
1463 let booleans = {
1464 let a = bb.finish();
1465 (non_bool > 0).then(|| Arc::new(a))
1466 };
1467 let text = {
1468 let a = sb.finish();
1469 (non_text > 0).then(|| Arc::new(a) as ArrayRef)
1470 };
1471 let errors = {
1472 let a = eb.finish();
1473 (non_err > 0).then(|| Arc::new(a))
1474 };
1475
1476 let estimated_bytes = type_tags
1477 .get_array_memory_size()
1478 .saturating_add(
1479 numbers
1480 .as_ref()
1481 .map(|a| a.get_array_memory_size())
1482 .unwrap_or(0),
1483 )
1484 .saturating_add(
1485 booleans
1486 .as_ref()
1487 .map(|a| a.get_array_memory_size())
1488 .unwrap_or(0),
1489 )
1490 .saturating_add(
1491 text.as_ref()
1492 .map(|a| a.get_array_memory_size())
1493 .unwrap_or(0),
1494 )
1495 .saturating_add(
1496 errors
1497 .as_ref()
1498 .map(|a| a.get_array_memory_size())
1499 .unwrap_or(0),
1500 );
1501
1502 Self {
1503 type_tags,
1504 numbers,
1505 booleans,
1506 text,
1507 errors,
1508 estimated_bytes,
1509 root_len: len,
1510 }
1511 }
1512
1513 fn overlay_value(&self, idx: usize) -> Option<OverlayValue> {
1514 if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
1515 return None;
1516 }
1517 match TypeTag::from_u8(self.type_tags.value(idx)) {
1518 TypeTag::Empty => Some(OverlayValue::Empty),
1519 TypeTag::Number => Some(OverlayValue::Number(self.number_at(idx)?)),
1520 TypeTag::DateTime => Some(OverlayValue::DateTime(self.number_at(idx)?)),
1521 TypeTag::Duration => Some(OverlayValue::Duration(self.number_at(idx)?)),
1522 TypeTag::Boolean => Some(OverlayValue::Boolean(self.boolean_at(idx)?)),
1523 TypeTag::Text => Some(OverlayValue::Text(Arc::from(self.text_at(idx)?))),
1524 TypeTag::Error => Some(OverlayValue::Error(self.error_at(idx)?)),
1525 TypeTag::Pending => Some(OverlayValue::Pending),
1526 }
1527 }
1528
1529 #[inline]
1530 fn get_scalar(&self, idx: usize) -> Option<OverlayScalar<'_>> {
1531 self.overlay_value(idx).map(OverlayScalar::Owned)
1532 }
1533
1534 #[inline]
1535 fn number_at(&self, idx: usize) -> Option<f64> {
1536 let arr = self.numbers.as_ref()?;
1537 (!arr.is_null(idx)).then(|| arr.value(idx))
1538 }
1539
1540 #[inline]
1541 fn boolean_at(&self, idx: usize) -> Option<bool> {
1542 let arr = self.booleans.as_ref()?;
1543 (!arr.is_null(idx)).then(|| arr.value(idx))
1544 }
1545
1546 #[inline]
1547 fn text_at(&self, idx: usize) -> Option<&str> {
1548 let arr = self.text.as_ref()?;
1549 let arr = arr.as_any().downcast_ref::<StringArray>()?;
1550 (!arr.is_null(idx)).then(|| arr.value(idx))
1551 }
1552
1553 #[inline]
1554 fn error_at(&self, idx: usize) -> Option<u8> {
1555 let arr = self.errors.as_ref()?;
1556 (!arr.is_null(idx)).then(|| arr.value(idx))
1557 }
1558
1559 #[inline]
1560 fn values_slice(&self, start: usize, len: usize) -> Vec<OverlayValue> {
1561 (start..start.saturating_add(len))
1562 .filter_map(|idx| self.overlay_value(idx))
1563 .collect()
1564 }
1565
1566 #[inline]
1567 fn estimated_bytes(&self) -> usize {
1568 self.estimated_bytes
1569 }
1570}
1571#[derive(Debug, Clone)]
1572pub(crate) enum OverlayFragment {
1573 SparseOffsets {
1574 offsets: Vec<u32>,
1575 payload: OverlayFragmentPayload,
1576 },
1577 DenseRange {
1578 start: u32,
1579 len: u32,
1580 payload: OverlayFragmentPayload,
1581 },
1582 RunRange {
1583 start: u32,
1584 len: u32,
1585 run_ends: RunEnds,
1586 payload: OverlayFragmentPayload,
1587 },
1588}
1589
1590#[derive(Debug, Clone)]
1594pub(crate) struct RunEnds {
1595 ends: arrow_buffer::ScalarBuffer<u32>,
1596 base: u32,
1597 len: u32,
1598}
1599
1600impl RunEnds {
1601 fn new(ends: Vec<u32>) -> Self {
1602 let len = ends.last().copied().unwrap_or(0);
1603 Self {
1604 ends: ends.into(),
1605 base: 0,
1606 len,
1607 }
1608 }
1609
1610 #[inline]
1611 fn count(&self) -> usize {
1612 self.ends.len()
1613 }
1614
1615 #[inline]
1617 fn end(&self, idx: usize) -> usize {
1618 (self.ends[idx] - self.base).min(self.len) as usize
1619 }
1620
1621 #[inline]
1623 fn run_at(&self, rel: usize) -> usize {
1624 let key = rel as u64 + self.base as u64;
1625 self.ends.partition_point(|end| (*end as u64) <= key)
1626 }
1627
1628 fn slice(&self, rel_start: usize, rel_end: usize, share: bool) -> (Self, usize, usize) {
1630 let lo = self.run_at(rel_start);
1631 let hi = self.run_at(rel_end - 1) + 1;
1632 let len = (rel_end - rel_start) as u32;
1633 let ends = if share {
1634 Self {
1635 ends: self.ends.slice(lo, hi - lo),
1636 base: self.base + rel_start as u32,
1637 len,
1638 }
1639 } else {
1640 let mut ends: Vec<u32> = (lo..hi)
1641 .map(|idx| (self.end(idx) - rel_start) as u32)
1642 .collect();
1643 if let Some(last) = ends.last_mut() {
1644 *last = (*last).min(len);
1645 }
1646 Self::new(ends)
1647 };
1648 (ends, lo, hi - lo)
1649 }
1650}
1651
1652impl OverlayFragment {
1653 const MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK: usize = 128;
1654
1655 pub(crate) fn sparse_offsets(items: Vec<(usize, OverlayValue)>) -> Option<Self> {
1656 let mut by_offset: BTreeMap<usize, OverlayValue> = BTreeMap::new();
1657 for (offset, value) in items {
1658 by_offset.insert(offset, value);
1659 }
1660 if by_offset.is_empty() {
1661 return None;
1662 }
1663
1664 let mut offsets = Vec::with_capacity(by_offset.len());
1665 let mut values = Vec::with_capacity(by_offset.len());
1666 for (offset, value) in by_offset {
1667 offsets.push(u32::try_from(offset).expect("overlay offset fits in u32"));
1668 values.push(value);
1669 }
1670
1671 Some(Self::SparseOffsets {
1672 offsets,
1673 payload: OverlayFragmentPayload::from_values(values),
1674 })
1675 }
1676
1677 pub(crate) fn sparse_offsets_if_estimated_smaller_than_points(
1678 items: Vec<(usize, OverlayValue)>,
1679 point_estimate: usize,
1680 ) -> Option<Result<Self, Vec<(usize, OverlayValue)>>> {
1681 let fragment = Self::sparse_offsets(items)?;
1682 if fragment.estimated_bytes() < point_estimate {
1683 Some(Ok(fragment))
1684 } else {
1685 Some(Err(fragment.cells()))
1686 }
1687 }
1688
1689 pub(crate) fn dense_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1690 let len = values.len();
1691 if len == 0 {
1692 return None;
1693 }
1694 Some(Self::DenseRange {
1695 start: u32::try_from(start).expect("overlay start fits in u32"),
1696 len: u32::try_from(len).expect("overlay length fits in u32"),
1697 payload: OverlayFragmentPayload::from_values(values),
1698 })
1699 }
1700
1701 pub(crate) fn run_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1702 if values.is_empty() {
1703 return None;
1704 }
1705
1706 let mut run_ends = Vec::new();
1707 let mut run_values = Vec::new();
1708 let mut current = values[0].clone();
1709 for (idx, value) in values.iter().enumerate().skip(1) {
1710 if *value != current {
1711 run_ends.push(idx);
1712 run_values.push(current);
1713 current = value.clone();
1714 }
1715 }
1716 run_ends.push(values.len());
1717 run_values.push(current);
1718
1719 Self::run_range_from_parts(start, values.len(), run_ends, run_values)
1720 }
1721
1722 fn run_range_from_parts(
1723 start: usize,
1724 len: usize,
1725 run_ends: Vec<usize>,
1726 values: Vec<OverlayValue>,
1727 ) -> Option<Self> {
1728 if len == 0 || run_ends.is_empty() || run_ends.len() != values.len() {
1729 return None;
1730 }
1731
1732 let mut merged_ends: Vec<u32> = Vec::with_capacity(run_ends.len());
1733 let mut merged_values: Vec<OverlayValue> = Vec::with_capacity(values.len());
1734 let mut prev_end = 0usize;
1735 for (end, value) in run_ends.into_iter().zip(values) {
1736 if end <= prev_end || end > len {
1737 return None;
1738 }
1739 if merged_values.last().is_some_and(|last| *last == value) {
1740 if let Some(last_end) = merged_ends.last_mut() {
1741 *last_end = u32::try_from(end).expect("run end fits in u32");
1742 }
1743 } else {
1744 merged_ends.push(u32::try_from(end).expect("run end fits in u32"));
1745 merged_values.push(value);
1746 }
1747 prev_end = end;
1748 }
1749
1750 if prev_end != len || merged_ends.last().copied() != Some(len as u32) {
1751 return None;
1752 }
1753
1754 Some(Self::RunRange {
1755 start: u32::try_from(start).expect("overlay start fits in u32"),
1756 len: u32::try_from(len).expect("overlay length fits in u32"),
1757 run_ends: RunEnds::new(merged_ends),
1758 payload: OverlayFragmentPayload::from_values(merged_values),
1759 })
1760 }
1761
1762 #[inline]
1763 fn estimated_bytes(&self) -> usize {
1764 match self {
1765 OverlayFragment::SparseOffsets { offsets, payload } => OVERLAY_FRAGMENT_BASE_BYTES
1766 .saturating_add(offsets.len().saturating_mul(core::mem::size_of::<u32>()))
1767 .saturating_add(payload.estimated_bytes()),
1768 OverlayFragment::DenseRange { payload, .. } => {
1769 OVERLAY_FRAGMENT_BASE_BYTES.saturating_add(payload.estimated_bytes())
1770 }
1771 OverlayFragment::RunRange {
1772 run_ends, payload, ..
1773 } => OVERLAY_FRAGMENT_BASE_BYTES
1774 .saturating_add(run_ends.count().saturating_mul(core::mem::size_of::<u32>()))
1775 .saturating_add(payload.estimated_bytes()),
1776 }
1777 }
1778
1779 #[inline]
1780 fn coverage_len(&self) -> usize {
1781 match self {
1782 OverlayFragment::SparseOffsets { offsets, .. } => offsets.len(),
1783 OverlayFragment::DenseRange { len, .. } | OverlayFragment::RunRange { len, .. } => {
1784 *len as usize
1785 }
1786 }
1787 }
1788
1789 pub(crate) fn max_covered_offset(&self) -> usize {
1790 match self {
1791 OverlayFragment::SparseOffsets { offsets, .. } => {
1792 offsets.iter().copied().max().unwrap_or(0) as usize
1793 }
1794 OverlayFragment::DenseRange { start, len, .. }
1795 | OverlayFragment::RunRange { start, len, .. } => (*start as usize)
1796 .saturating_add(*len as usize)
1797 .saturating_sub(1),
1798 }
1799 }
1800
1801 fn interval_coverage(&self) -> Option<core::ops::Range<usize>> {
1802 match self {
1803 OverlayFragment::DenseRange { start, len, .. }
1804 | OverlayFragment::RunRange { start, len, .. } => {
1805 let start = *start as usize;
1806 Some(start..start.saturating_add(*len as usize))
1807 }
1808 OverlayFragment::SparseOffsets { .. } => None,
1809 }
1810 }
1811
1812 fn sparse_offsets_slice(&self) -> Option<&[u32]> {
1813 match self {
1814 OverlayFragment::SparseOffsets { offsets, .. } => Some(offsets.as_slice()),
1815 _ => None,
1816 }
1817 }
1818
1819 fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
1820 if range.is_empty() {
1821 return false;
1822 }
1823 match self {
1824 OverlayFragment::SparseOffsets { offsets, .. } => {
1825 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1826 let idx = offsets.partition_point(|off| *off < start);
1827 offsets
1828 .get(idx)
1829 .is_some_and(|off| (*off as usize) < range.end)
1830 }
1831 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1832 .interval_coverage()
1833 .is_some_and(|r| r.start < range.end && range.start < r.end),
1834 }
1835 }
1836
1837 fn intersects_fragment_exact(&self, replacement: &OverlayFragment) -> bool {
1838 if let Some(offsets) = replacement.sparse_offsets_slice() {
1839 self.intersects_sparse_offsets(offsets)
1840 } else if let Some(range) = replacement.interval_coverage() {
1841 self.intersects_interval(range)
1842 } else {
1843 false
1844 }
1845 }
1846
1847 fn intersects_interval(&self, range: core::ops::Range<usize>) -> bool {
1848 if range.is_empty() {
1849 return false;
1850 }
1851 match self {
1852 OverlayFragment::SparseOffsets { offsets, .. } => {
1853 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1854 let idx = offsets.partition_point(|off| *off < start);
1855 offsets
1856 .get(idx)
1857 .is_some_and(|off| (*off as usize) < range.end)
1858 }
1859 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1860 .interval_coverage()
1861 .is_some_and(|own| own.start < range.end && range.start < own.end),
1862 }
1863 }
1864
1865 fn intersects_sparse_offsets(&self, replacement_offsets: &[u32]) -> bool {
1866 if replacement_offsets.is_empty() {
1867 return false;
1868 }
1869 match self {
1870 OverlayFragment::SparseOffsets { offsets, .. } => {
1871 Self::sorted_offsets_intersect(offsets, replacement_offsets)
1872 }
1873 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
1874 self.interval_coverage().is_some_and(|range| {
1875 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1876 let idx = replacement_offsets.partition_point(|off| *off < start);
1877 replacement_offsets
1878 .get(idx)
1879 .is_some_and(|off| (*off as usize) < range.end)
1880 })
1881 }
1882 }
1883 }
1884
1885 fn sorted_offsets_intersect(a: &[u32], b: &[u32]) -> bool {
1886 let mut ai = 0usize;
1887 let mut bi = 0usize;
1888 while ai < a.len() && bi < b.len() {
1889 match a[ai].cmp(&b[bi]) {
1890 core::cmp::Ordering::Equal => return true,
1891 core::cmp::Ordering::Less => ai += 1,
1892 core::cmp::Ordering::Greater => bi += 1,
1893 }
1894 }
1895 false
1896 }
1897
1898 fn covers_offset(&self, off: usize) -> bool {
1899 self.get_scalar(off).is_some()
1900 }
1901
1902 #[inline]
1905 fn covers_offset_fast(&self, off: usize) -> bool {
1906 match self {
1907 OverlayFragment::SparseOffsets { offsets, .. } => {
1908 u32::try_from(off).is_ok_and(|off| offsets.binary_search(&off).is_ok())
1909 }
1910 OverlayFragment::DenseRange { start, len, .. }
1911 | OverlayFragment::RunRange { start, len, .. } => {
1912 let start = *start as usize;
1913 off >= start && off - start < *len as usize
1914 }
1915 }
1916 }
1917
1918 fn fold_points(
1922 &self,
1923 points: &mut FxHashMap<usize, OverlayValue>,
1924 ) -> Option<(usize, OverlayFragment)> {
1925 let mut removed = 0usize;
1926 let mut take = |off: usize, points: &mut FxHashMap<usize, OverlayValue>| {
1927 points.remove(&off).inspect(|v| {
1928 removed = removed.saturating_add(Overlay::point_estimate(v));
1929 })
1930 };
1931 match self {
1932 OverlayFragment::SparseOffsets { offsets, payload } => {
1933 if !offsets
1934 .iter()
1935 .any(|off| points.contains_key(&(*off as usize)))
1936 {
1937 return None;
1938 }
1939 let cells: Vec<_> = offsets
1940 .iter()
1941 .enumerate()
1942 .filter_map(|(idx, off)| {
1943 let off = *off as usize;
1944 take(off, points)
1945 .or_else(|| payload.overlay_value(idx))
1946 .map(|v| (off, v))
1947 })
1948 .collect();
1949 let rebuilt = OverlayFragment::sparse_offsets(cells)?;
1950 Some((removed, rebuilt))
1951 }
1952 OverlayFragment::DenseRange { start, len, .. }
1953 | OverlayFragment::RunRange { start, len, .. } => {
1954 let start = *start as usize;
1955 let len = *len as usize;
1956 if !(start..start + len).any(|off| points.contains_key(&off)) {
1957 return None;
1958 }
1959 let values: Vec<OverlayValue> = (start..start + len)
1960 .map(|off| {
1961 take(off, points).unwrap_or_else(|| {
1962 self.get_scalar(off)
1963 .expect("fragment covers its interval")
1964 .to_overlay_value()
1965 })
1966 })
1967 .collect();
1968 let rebuilt = if matches!(self, OverlayFragment::RunRange { .. }) {
1969 OverlayFragment::run_range(start, values)?
1970 } else {
1971 OverlayFragment::dense_range(start, values)?
1972 };
1973 Some((removed, rebuilt))
1974 }
1975 }
1976 }
1977
1978 fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
1979 match self {
1980 OverlayFragment::SparseOffsets { offsets, payload } => {
1981 let off = u32::try_from(off).ok()?;
1982 let idx = offsets.binary_search(&off).ok()?;
1983 payload.get_scalar(idx)
1984 }
1985 OverlayFragment::DenseRange {
1986 start,
1987 len,
1988 payload,
1989 } => {
1990 let start = *start as usize;
1991 let rel = off.checked_sub(start)?;
1992 if rel >= *len as usize {
1993 return None;
1994 }
1995 payload.get_scalar(rel)
1996 }
1997 OverlayFragment::RunRange {
1998 start,
1999 len,
2000 run_ends,
2001 payload,
2002 } => {
2003 let start = *start as usize;
2004 let rel = off.checked_sub(start)?;
2005 if rel >= *len as usize {
2006 return None;
2007 }
2008 payload.get_scalar(run_ends.run_at(rel))
2009 }
2010 }
2011 }
2012
2013 fn subtract_fragment(&self, replacement: &OverlayFragment) -> Vec<OverlayFragment> {
2014 if let Some(offsets) = replacement.sparse_offsets_slice() {
2015 self.subtract_sparse_offsets(offsets)
2016 } else if let Some(range) = replacement.interval_coverage() {
2017 self.subtract_interval(range)
2018 } else {
2019 vec![self.clone()]
2020 }
2021 }
2022
2023 fn subtract_offset(&self, off: usize) -> Vec<OverlayFragment> {
2024 match self {
2025 OverlayFragment::SparseOffsets { .. } => {
2026 let Ok(off) = u32::try_from(off) else {
2027 return vec![self.clone()];
2028 };
2029 self.subtract_sparse_offsets(core::slice::from_ref(&off))
2030 }
2031 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
2032 self.subtract_interval(off..off.saturating_add(1))
2033 }
2034 }
2035 }
2036
2037 fn subtract_interval(&self, replacement: core::ops::Range<usize>) -> Vec<OverlayFragment> {
2038 if replacement.is_empty() {
2039 return vec![self.clone()];
2040 }
2041
2042 match self {
2043 OverlayFragment::SparseOffsets { offsets, payload } => {
2044 let cells: Vec<_> = offsets
2045 .iter()
2046 .enumerate()
2047 .filter_map(|(idx, off)| {
2048 let off_usize = *off as usize;
2049 (!replacement.contains(&off_usize))
2050 .then(|| payload.overlay_value(idx).map(|value| (off_usize, value)))?
2051 })
2052 .collect();
2053 OverlayFragment::sparse_offsets(cells).into_iter().collect()
2054 }
2055 OverlayFragment::DenseRange { .. } => {
2056 let Some(own) = self.interval_coverage() else {
2057 return vec![self.clone()];
2058 };
2059 if own.end <= replacement.start || replacement.end <= own.start {
2060 return vec![self.clone()];
2061 }
2062 let cut_start = replacement.start.max(own.start);
2063 let cut_end = replacement.end.min(own.end);
2064 let mut out = Vec::with_capacity(2);
2065 if own.start < cut_start
2066 && let Some(left) =
2067 self.dense_segment_with_start(own.start, own.start, cut_start)
2068 {
2069 out.push(left);
2070 }
2071 if cut_end < own.end
2072 && let Some(right) = self.dense_segment_with_start(cut_end, cut_end, own.end)
2073 {
2074 out.push(right);
2075 }
2076 out
2077 }
2078 OverlayFragment::RunRange { .. } => {
2079 let Some(own) = self.interval_coverage() else {
2080 return vec![self.clone()];
2081 };
2082 if own.end <= replacement.start || replacement.end <= own.start {
2083 return vec![self.clone()];
2084 }
2085 let cut_start = replacement.start.max(own.start);
2086 let cut_end = replacement.end.min(own.end);
2087 let mut out = Vec::with_capacity(2);
2088 if own.start < cut_start
2089 && let Some(left) = self.run_segment_with_start(own.start, own.start, cut_start)
2090 {
2091 out.push(left);
2092 }
2093 if cut_end < own.end
2094 && let Some(right) = self.run_segment_with_start(cut_end, cut_end, own.end)
2095 {
2096 out.push(right);
2097 }
2098 out
2099 }
2100 }
2101 }
2102
2103 fn subtract_sparse_offsets(&self, replacement_offsets: &[u32]) -> Vec<OverlayFragment> {
2104 if replacement_offsets.is_empty() {
2105 return vec![self.clone()];
2106 }
2107
2108 match self {
2109 OverlayFragment::SparseOffsets { offsets, payload } => {
2110 let cells: Vec<_> = offsets
2111 .iter()
2112 .enumerate()
2113 .filter_map(|(idx, off)| {
2114 replacement_offsets.binary_search(off).is_err().then(|| {
2115 payload
2116 .overlay_value(idx)
2117 .map(|value| (*off as usize, value))
2118 })?
2119 })
2120 .collect();
2121 OverlayFragment::sparse_offsets(cells).into_iter().collect()
2122 }
2123 OverlayFragment::DenseRange { .. } => {
2124 self.subtract_sparse_offsets_from_dense(replacement_offsets)
2125 }
2126 OverlayFragment::RunRange { .. } => {
2127 self.subtract_sparse_offsets_from_run(replacement_offsets)
2128 }
2129 }
2130 }
2131
2132 fn sparse_holes_in_interval(offsets: &[u32], range: core::ops::Range<usize>) -> Vec<usize> {
2133 if range.is_empty() {
2134 return Vec::new();
2135 }
2136 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
2137 let mut idx = offsets.partition_point(|off| *off < start);
2138 let mut holes = Vec::new();
2139 let mut last = None;
2140 while let Some(off) = offsets.get(idx).copied() {
2141 let off_usize = off as usize;
2142 if off_usize >= range.end {
2143 break;
2144 }
2145 if last != Some(off_usize) {
2146 holes.push(off_usize);
2147 last = Some(off_usize);
2148 }
2149 idx += 1;
2150 }
2151 holes
2152 }
2153
2154 fn subtract_sparse_offsets_from_dense(
2155 &self,
2156 replacement_offsets: &[u32],
2157 ) -> Vec<OverlayFragment> {
2158 let Some(own) = self.interval_coverage() else {
2159 return vec![self.clone()];
2160 };
2161 let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
2162 if holes.is_empty() {
2163 return vec![self.clone()];
2164 }
2165 if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
2166 return self.sparse_remainder_excluding_offsets(&holes);
2167 }
2168
2169 let mut out = Vec::with_capacity(holes.len().saturating_add(1));
2170 let mut seg_start = own.start;
2171 for hole in holes {
2172 if seg_start < hole
2173 && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, hole)
2174 {
2175 out.push(segment);
2176 }
2177 seg_start = hole.saturating_add(1);
2178 }
2179 if seg_start < own.end
2180 && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, own.end)
2181 {
2182 out.push(segment);
2183 }
2184 out
2185 }
2186
2187 fn subtract_sparse_offsets_from_run(
2188 &self,
2189 replacement_offsets: &[u32],
2190 ) -> Vec<OverlayFragment> {
2191 let Some(own) = self.interval_coverage() else {
2192 return vec![self.clone()];
2193 };
2194 let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
2195 if holes.is_empty() {
2196 return vec![self.clone()];
2197 }
2198 if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
2199 return self.sparse_remainder_excluding_offsets(&holes);
2200 }
2201
2202 let mut out = Vec::with_capacity(holes.len().saturating_add(1));
2203 let mut seg_start = own.start;
2204 for hole in holes {
2205 if seg_start < hole
2206 && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, hole)
2207 {
2208 out.push(segment);
2209 }
2210 seg_start = hole.saturating_add(1);
2211 }
2212 if seg_start < own.end
2213 && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, own.end)
2214 {
2215 out.push(segment);
2216 }
2217 out
2218 }
2219
2220 fn sparse_remainder_excluding_offsets(&self, sorted_holes: &[usize]) -> Vec<OverlayFragment> {
2221 let cells: Vec<_> = self
2222 .cells()
2223 .into_iter()
2224 .filter(|(off, _)| sorted_holes.binary_search(off).is_err())
2225 .collect();
2226 OverlayFragment::sparse_offsets(cells).into_iter().collect()
2227 }
2228
2229 fn dense_segment_with_start(
2230 &self,
2231 new_start: usize,
2232 abs_start: usize,
2233 abs_end: usize,
2234 ) -> Option<OverlayFragment> {
2235 match self {
2236 OverlayFragment::DenseRange { start, payload, .. } => {
2237 if abs_start >= abs_end {
2238 return None;
2239 }
2240 let base = *start as usize;
2241 let rel_start = abs_start.checked_sub(base)?;
2242 let len = abs_end
2243 .saturating_sub(abs_start)
2244 .min(payload.len().saturating_sub(rel_start));
2245 if len == 0 {
2246 return None;
2247 }
2248 Some(OverlayFragment::DenseRange {
2249 start: u32::try_from(new_start).expect("overlay start fits in u32"),
2250 len: len as u32,
2251 payload: payload.slice(rel_start, len),
2252 })
2253 }
2254 _ => None,
2255 }
2256 }
2257
2258 fn run_segment_with_start(
2259 &self,
2260 new_start: usize,
2261 abs_start: usize,
2262 abs_end: usize,
2263 ) -> Option<OverlayFragment> {
2264 let OverlayFragment::RunRange {
2265 start,
2266 len,
2267 run_ends,
2268 payload,
2269 } = self
2270 else {
2271 return None;
2272 };
2273 if abs_start >= abs_end {
2274 return None;
2275 }
2276 let base = *start as usize;
2277 let frag_end = base.saturating_add(*len as usize);
2278 if abs_start < base || abs_end > frag_end {
2279 return None;
2280 }
2281
2282 let rel_start = abs_start - base;
2283 let rel_end = abs_end - base;
2284 let lo = run_ends.run_at(rel_start);
2287 let hi = run_ends.run_at(rel_end - 1) + 1;
2288 let share = OverlayFragmentPayload::shares(payload.root_len, hi - lo);
2289 let (new_ends, lo, count) = run_ends.slice(rel_start, rel_end, share);
2290 let payload = if share {
2291 payload.slice(lo, count)
2292 } else {
2293 OverlayFragmentPayload::from_values(payload.values_slice(lo, count))
2294 };
2295 Some(OverlayFragment::RunRange {
2296 start: u32::try_from(new_start).expect("overlay start fits in u32"),
2297 len: (rel_end - rel_start) as u32,
2298 run_ends: new_ends,
2299 payload,
2300 })
2301 }
2302
2303 fn cells(&self) -> Vec<(usize, OverlayValue)> {
2304 match self {
2305 OverlayFragment::SparseOffsets { offsets, payload } => offsets
2306 .iter()
2307 .enumerate()
2308 .filter_map(|(idx, off)| {
2309 payload
2310 .overlay_value(idx)
2311 .map(|value| (*off as usize, value))
2312 })
2313 .collect(),
2314 OverlayFragment::DenseRange {
2315 start,
2316 len,
2317 payload,
2318 } => {
2319 let start = *start as usize;
2320 (0..*len as usize)
2321 .filter_map(|idx| {
2322 payload
2323 .overlay_value(idx)
2324 .map(|value| (start.saturating_add(idx), value))
2325 })
2326 .collect()
2327 }
2328 OverlayFragment::RunRange { start, len, .. } => {
2329 let start = *start as usize;
2330 (0..*len as usize)
2331 .filter_map(|idx| {
2332 self.get_scalar(start.saturating_add(idx))
2333 .map(|value| (start.saturating_add(idx), value.to_overlay_value()))
2334 })
2335 .collect()
2336 }
2337 }
2338 }
2339
2340 fn slice(&self, off: usize, len: usize) -> Option<OverlayFragment> {
2341 let end = off.saturating_add(len);
2342 if len == 0 {
2343 return None;
2344 }
2345
2346 match self {
2347 OverlayFragment::SparseOffsets { offsets, payload } => {
2348 let start = u32::try_from(off).unwrap_or(u32::MAX);
2349 let lo = offsets.partition_point(|candidate| *candidate < start);
2350 let hi = offsets.partition_point(|candidate| (*candidate as usize) < end);
2351 let cells: Vec<_> = (lo..hi)
2352 .filter_map(|idx| {
2353 let rebased = (offsets[idx] as usize).saturating_sub(off);
2354 payload.overlay_value(idx).map(|value| (rebased, value))
2355 })
2356 .collect();
2357 OverlayFragment::sparse_offsets(cells)
2358 }
2359 OverlayFragment::DenseRange { .. } => {
2360 let own = self.interval_coverage()?;
2361 let seg_start = own.start.max(off);
2362 let seg_end = own.end.min(end);
2363 if seg_start >= seg_end {
2364 return None;
2365 }
2366 self.dense_segment_with_start(seg_start - off, seg_start, seg_end)
2367 }
2368 OverlayFragment::RunRange { .. } => {
2369 let own = self.interval_coverage()?;
2370 let seg_start = own.start.max(off);
2371 let seg_end = own.end.min(end);
2372 if seg_start >= seg_end {
2373 return None;
2374 }
2375 self.run_segment_with_start(seg_start - off, seg_start, seg_end)
2376 }
2377 }
2378 }
2379}
2380static OVERLAY_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
2381
2382#[inline]
2383fn next_overlay_epoch() -> u64 {
2384 OVERLAY_EPOCH.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
2385}
2386
2387impl Default for Overlay {
2388 fn default() -> Self {
2389 Self::new()
2390 }
2391}
2392
2393#[derive(Debug, Clone)]
2394pub struct Overlay {
2395 epoch: u64,
2398 points: FxHashMap<usize, OverlayValue>,
2399 format_points: Option<Box<FxHashMap<usize, FormatId>>>,
2401 fragments: Vec<OverlayFragment>,
2402 shadowed: u32,
2406 fragment_coverage: u32,
2408 estimated_bytes: usize,
2412}
2413
2414impl Overlay {
2415 const ENTRY_BASE_BYTES: usize = OVERLAY_ENTRY_BASE_BYTES;
2418
2419 pub fn new() -> Self {
2420 Self {
2421 epoch: next_overlay_epoch(),
2422 points: FxHashMap::default(),
2423 format_points: None,
2424 fragments: Vec::new(),
2425 shadowed: 0,
2426 fragment_coverage: 0,
2427 estimated_bytes: 0,
2428 }
2429 }
2430
2431 #[inline]
2432 fn point_estimate(v: &OverlayValue) -> usize {
2433 Self::ENTRY_BASE_BYTES + v.estimated_payload_bytes()
2434 }
2435
2436 #[inline]
2437 fn adjust_estimated_bytes(&mut self, delta: isize) {
2438 if delta >= 0 {
2439 self.estimated_bytes = self.estimated_bytes.saturating_add(delta as usize);
2440 } else {
2441 self.estimated_bytes = self.estimated_bytes.saturating_sub((-delta) as usize);
2442 }
2443 }
2444
2445 #[inline]
2446 pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
2447 self.points
2448 .get(&off)
2449 .map(OverlayScalar::Borrowed)
2450 .or_else(|| self.fragments.iter().rev().find_map(|f| f.get_scalar(off)))
2451 }
2452
2453 #[inline]
2454 pub fn get(&self, off: usize) -> Option<OverlayValue> {
2455 self.get_scalar(off).map(|value| value.to_overlay_value())
2456 }
2457
2458 #[inline]
2459 pub fn get_format(&self, off: usize) -> Option<FormatId> {
2460 self.format_points.as_ref()?.get(&off).copied()
2461 }
2462
2463 #[inline]
2464 pub(crate) fn has_formats(&self) -> bool {
2465 self.format_points.as_ref().is_some_and(|m| !m.is_empty())
2466 }
2467
2468 #[inline]
2469 pub fn set_format(&mut self, off: usize, format: Option<FormatId>) {
2470 match format.filter(|id| *id != FormatId::GENERAL) {
2471 Some(id) => {
2472 self.format_points
2473 .get_or_insert_with(Default::default)
2474 .insert(off, id);
2475 }
2476 None => {
2477 if let Some(map) = self.format_points.as_mut() {
2478 map.remove(&off);
2479 }
2480 }
2481 }
2482 }
2483
2484 pub(crate) fn clear_format_range(&mut self, start: usize, end: usize) {
2487 let Some(map) = self.format_points.as_mut() else {
2488 return;
2489 };
2490 if map.is_empty() || start >= end {
2491 return;
2492 }
2493 map.retain(|off, _| *off < start || *off >= end);
2494 }
2495
2496 pub(crate) fn clear_format_offsets(&mut self, offsets: &[usize]) {
2498 let Some(map) = self.format_points.as_mut() else {
2499 return;
2500 };
2501 for off in offsets {
2502 map.remove(off);
2503 }
2504 }
2505
2506 #[inline]
2512 pub(crate) fn set_scalar(&mut self, off: usize, v: OverlayValue) -> isize {
2513 self.epoch = next_overlay_epoch();
2514 let new_est = Self::point_estimate(&v);
2515 let old_est = match self.points.insert(off, v) {
2516 Some(old) => Self::point_estimate(&old),
2517 None => {
2518 if self.fragment_covers(off) {
2519 self.shadowed += 1;
2520 }
2521 0
2522 }
2523 };
2524 let delta = new_est as isize - old_est as isize;
2525 self.adjust_estimated_bytes(delta);
2526 if self.shadowed as usize
2527 >= Self::SHADOW_FOLD_MIN.max(self.fragment_coverage as usize / Self::SHADOW_FOLD_DEN)
2528 {
2529 delta.saturating_add(self.fold_shadowed_points())
2530 } else {
2531 delta
2532 }
2533 }
2534
2535 const SHADOW_FOLD_MIN: usize = 32;
2539 const SHADOW_FOLD_DEN: usize = 8;
2540
2541 #[inline]
2542 fn fragment_covers(&self, off: usize) -> bool {
2543 self.fragments.iter().any(|f| f.covers_offset_fast(off))
2544 }
2545
2546 fn fold_shadowed_points(&mut self) -> isize {
2549 if self.shadowed == 0 {
2550 return 0;
2551 }
2552 let mut delta = 0isize;
2553 let mut fragments = core::mem::take(&mut self.fragments);
2554 for fragment in fragments.iter_mut() {
2555 let Some(folded) = fragment.fold_points(&mut self.points) else {
2556 continue;
2557 };
2558 let (points_removed, replacement) = folded;
2559 delta = delta.saturating_sub(points_removed as isize);
2560 delta = delta.saturating_add(
2561 replacement.estimated_bytes() as isize - fragment.estimated_bytes() as isize,
2562 );
2563 *fragment = replacement;
2564 }
2565 self.fragments = fragments;
2566 self.shadowed = 0;
2567 self.adjust_estimated_bytes(delta);
2568 delta
2569 }
2570
2571 fn shadowed_among(&self, offsets: &[usize]) -> usize {
2573 if self.shadowed == 0 {
2574 return 0;
2575 }
2576 offsets
2577 .iter()
2578 .filter(|off| self.fragment_covers(**off))
2579 .count()
2580 }
2581
2582 #[inline]
2583 pub fn set(&mut self, off: usize, v: OverlayValue) -> isize {
2584 self.set_scalar(off, v)
2585 }
2586
2587 pub(crate) fn apply_fragment(&mut self, fragment: OverlayFragment) -> isize {
2588 self.epoch = next_overlay_epoch();
2589 if fragment
2594 .coverage_len()
2595 .saturating_mul(Self::SHADOW_FOLD_DEN)
2596 < self.fragment_coverage as usize
2597 {
2598 let mut delta = 0isize;
2599 for (off, value) in fragment.cells() {
2600 delta = delta.saturating_add(self.set_scalar(off, value));
2601 }
2602 return delta;
2603 }
2604 let mut delta = self.remove_points_covered_by_fragment(&fragment);
2605 delta = delta.saturating_add(self.remove_fragments_covered_by_fragment(&fragment));
2606
2607 let fragment_est = fragment.estimated_bytes();
2608 self.fragment_coverage = self
2609 .fragment_coverage
2610 .saturating_add(fragment.coverage_len() as u32);
2611 self.fragments.push(fragment);
2612 self.adjust_estimated_bytes(fragment_est as isize);
2613 delta.saturating_add(fragment_est as isize)
2614 }
2615
2616 fn point_offsets_in_range(&self, range: core::ops::Range<usize>) -> Vec<usize> {
2618 if range.len() < self.points.len() {
2619 range.filter(|off| self.points.contains_key(off)).collect()
2620 } else {
2621 self.points
2622 .keys()
2623 .copied()
2624 .filter(|off| range.contains(off))
2625 .collect()
2626 }
2627 }
2628
2629 fn drop_points(&mut self, offsets: &[usize]) -> usize {
2632 let shadowed = self.shadowed_among(offsets);
2633 let mut removed = 0usize;
2634 let mut dropped = 0usize;
2635 for off in offsets {
2636 if let Some(old) = self.points.remove(off) {
2637 removed = removed.saturating_add(Self::point_estimate(&old));
2638 dropped += 1;
2639 }
2640 }
2641 debug_assert!(shadowed <= dropped);
2642 self.shadowed = self.shadowed.saturating_sub(shadowed as u32);
2643 self.estimated_bytes = self.estimated_bytes.saturating_sub(removed);
2644 removed
2645 }
2646
2647 fn remove_points_covered_by_fragment(&mut self, fragment: &OverlayFragment) -> isize {
2648 let offsets: Vec<usize> = match fragment {
2649 OverlayFragment::SparseOffsets { offsets, .. } => offsets
2650 .iter()
2651 .map(|off| *off as usize)
2652 .filter(|off| self.points.contains_key(off))
2653 .collect(),
2654 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => fragment
2655 .interval_coverage()
2656 .map(|range| self.point_offsets_in_range(range))
2657 .unwrap_or_default(),
2658 };
2659 if offsets.is_empty() {
2660 return 0;
2661 }
2662 -(self.drop_points(&offsets) as isize)
2663 }
2664
2665 fn remove_fragments_covered_by_fragment(&mut self, replacement: &OverlayFragment) -> isize {
2666 if self.fragments.is_empty() {
2667 return 0;
2668 }
2669
2670 let mut delta: isize = 0;
2671 let mut fragments = Vec::with_capacity(self.fragments.len());
2672 for fragment in self.fragments.drain(..) {
2673 if !fragment.intersects_fragment_exact(replacement) {
2674 fragments.push(fragment);
2675 continue;
2676 }
2677
2678 let old_est = fragment.estimated_bytes();
2679 let replacements = fragment.subtract_fragment(replacement);
2680 let new_est = replacements
2681 .iter()
2682 .map(OverlayFragment::estimated_bytes)
2683 .fold(0usize, usize::saturating_add);
2684 fragments.extend(replacements);
2685 delta = delta.saturating_add(new_est as isize - old_est as isize);
2686 }
2687 self.fragments = fragments;
2688 self.refresh_fragment_coverage();
2689 self.adjust_estimated_bytes(delta);
2690 delta
2691 }
2692
2693 #[inline]
2694 fn refresh_fragment_coverage(&mut self) {
2695 self.fragment_coverage = self
2696 .fragments
2697 .iter()
2698 .map(OverlayFragment::coverage_len)
2699 .fold(0usize, usize::saturating_add) as u32;
2700 }
2701
2702 #[inline]
2703 pub(crate) fn remove_scalar(&mut self, off: usize) -> isize {
2704 self.epoch = next_overlay_epoch();
2705 let mut delta = 0isize;
2706 if self.points.contains_key(&off) {
2707 delta = delta.saturating_sub(self.drop_points(&[off]) as isize);
2708 }
2709
2710 if !self.fragments.is_empty() && self.fragment_covers(off) {
2711 let mut fragment_delta = 0isize;
2712 let mut fragments = Vec::with_capacity(self.fragments.len() + 1);
2713 for fragment in self.fragments.drain(..) {
2714 if !fragment.covers_offset_fast(off) {
2715 fragments.push(fragment);
2716 continue;
2717 }
2718
2719 let old_est = fragment.estimated_bytes();
2720 let replacements = fragment.subtract_offset(off);
2721 let new_est = replacements
2722 .iter()
2723 .map(OverlayFragment::estimated_bytes)
2724 .fold(0usize, usize::saturating_add);
2725 fragments.extend(replacements);
2726 fragment_delta = fragment_delta.saturating_add(new_est as isize - old_est as isize);
2727 }
2728 self.fragments = fragments;
2729 self.refresh_fragment_coverage();
2730 self.adjust_estimated_bytes(fragment_delta);
2731 delta = delta.saturating_add(fragment_delta);
2732 }
2733
2734 delta
2735 }
2736
2737 #[inline]
2738 pub fn remove(&mut self, off: usize) -> isize {
2739 self.remove_scalar(off)
2740 }
2741
2742 pub(crate) fn remove_range(&mut self, range: core::ops::Range<usize>) -> isize {
2743 self.epoch = next_overlay_epoch();
2744 if range.is_empty() {
2745 return 0;
2746 }
2747
2748 let mut delta = 0isize;
2749 let offsets = self.point_offsets_in_range(range.clone());
2750 if !offsets.is_empty() {
2751 delta = delta.saturating_sub(self.drop_points(&offsets) as isize);
2752 }
2753
2754 if !self.fragments.is_empty() {
2755 let mut fragment_delta = 0isize;
2756 let mut fragments = Vec::with_capacity(self.fragments.len());
2757 for fragment in self.fragments.drain(..) {
2758 if !fragment.has_any_in_range(range.clone()) {
2759 fragments.push(fragment);
2760 continue;
2761 }
2762 let old_est = fragment.estimated_bytes();
2763 let replacements = fragment.subtract_interval(range.clone());
2764 let new_est = replacements
2765 .iter()
2766 .map(OverlayFragment::estimated_bytes)
2767 .fold(0usize, usize::saturating_add);
2768 fragments.extend(replacements);
2769 fragment_delta = fragment_delta.saturating_add(new_est as isize - old_est as isize);
2770 }
2771 self.fragments = fragments;
2772 self.refresh_fragment_coverage();
2773 self.adjust_estimated_bytes(fragment_delta);
2774 delta = delta.saturating_add(fragment_delta);
2775 }
2776
2777 delta
2778 }
2779
2780 #[inline]
2781 pub(crate) fn clear_all(&mut self) -> usize {
2782 self.epoch = next_overlay_epoch();
2783 let freed = self.estimated_bytes;
2784 self.points.clear();
2785 self.fragments.clear();
2786 self.shadowed = 0;
2787 self.fragment_coverage = 0;
2788 self.estimated_bytes = 0;
2789 freed
2790 }
2791
2792 #[inline]
2793 pub fn clear(&mut self) -> usize {
2794 self.clear_all()
2795 }
2796
2797 #[inline]
2799 pub fn len(&self) -> usize {
2800 (self.points.len() - self.shadowed as usize).saturating_add(self.fragment_coverage as usize)
2801 }
2802
2803 #[inline]
2804 pub fn estimated_bytes(&self) -> usize {
2805 self.estimated_bytes
2806 }
2807
2808 #[inline]
2809 pub fn is_empty(&self) -> bool {
2810 self.points.is_empty() && self.fragments.is_empty()
2811 }
2812
2813 #[inline]
2814 pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2815 let points = if range.len() < self.points.len() {
2816 range.clone().any(|off| self.points.contains_key(&off))
2817 } else {
2818 self.points.keys().any(|k| range.contains(k))
2819 };
2820 points
2821 || self
2822 .fragments
2823 .iter()
2824 .any(|fragment| fragment.has_any_in_range(range.clone()))
2825 }
2826
2827 #[inline]
2828 pub fn any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2829 self.has_any_in_range(range)
2830 }
2831
2832 pub(crate) fn slice(&self, off: usize, len: usize) -> Overlay {
2833 let mut out = Overlay::new();
2834 let end = off.saturating_add(len);
2835 for fragment in &self.fragments {
2836 if let Some(sliced) = fragment.slice(off, len) {
2837 let _ = out.apply_fragment(sliced);
2838 }
2839 }
2840 for (k, v) in self.points.iter() {
2841 if *k >= off && *k < end {
2842 let _ = out.set_scalar(*k - off, v.clone());
2843 }
2844 }
2845 for (k, format) in self.format_points.iter().flat_map(|m| m.iter()) {
2846 if *k >= off && *k < end {
2847 out.set_format(*k - off, Some(*format));
2848 }
2849 }
2850 out
2851 }
2852
2853 pub fn iter(&self) -> impl Iterator<Item = (usize, OverlayValue)> {
2855 let mut cells = BTreeMap::new();
2856 for fragment in &self.fragments {
2857 for (off, value) in fragment.cells() {
2858 cells.insert(off, value);
2859 }
2860 }
2861 for (off, value) in &self.points {
2862 cells.insert(*off, value.clone());
2863 }
2864 cells.into_iter()
2865 }
2866
2867 #[inline]
2873 pub(crate) fn for_each_point_in_range(
2874 &self,
2875 range: core::ops::Range<usize>,
2876 mut visit: impl FnMut(usize, &OverlayValue),
2877 ) {
2878 if self.points.is_empty() {
2879 return;
2880 }
2881 if range.len() < self.points.len() {
2882 for off in range {
2883 if let Some(value) = self.points.get(&off) {
2884 visit(off, value);
2885 }
2886 }
2887 } else {
2888 for (off, value) in &self.points {
2889 if range.contains(off) {
2890 visit(*off, value);
2891 }
2892 }
2893 }
2894 }
2895
2896 pub(crate) fn iter_points(&self) -> impl Iterator<Item = (&usize, &OverlayValue)> {
2897 self.points.iter()
2898 }
2899}
2900
2901#[cfg(test)]
2902#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
2903pub(crate) struct OverlayDebugStats {
2904 pub(crate) points: usize,
2905 pub(crate) sparse_fragments: usize,
2906 pub(crate) dense_fragments: usize,
2907 pub(crate) run_fragments: usize,
2908 pub(crate) covered_len: usize,
2909}
2910
2911#[cfg(test)]
2912impl Overlay {
2913 pub(crate) fn debug_stats(&self) -> OverlayDebugStats {
2914 let mut stats = OverlayDebugStats {
2915 points: self.points.len(),
2916 covered_len: self.len(),
2917 ..OverlayDebugStats::default()
2918 };
2919 for fragment in &self.fragments {
2920 match fragment {
2921 OverlayFragment::SparseOffsets { .. } => stats.sparse_fragments += 1,
2922 OverlayFragment::DenseRange { .. } => stats.dense_fragments += 1,
2923 OverlayFragment::RunRange { .. } => stats.run_fragments += 1,
2924 }
2925 }
2926 stats
2927 }
2928
2929 pub(crate) fn debug_is_normalized(&self) -> bool {
2932 let mut covered = std::collections::HashSet::new();
2933 for fragment in &self.fragments {
2934 for (off, _) in fragment.cells() {
2935 if !covered.insert(off) {
2936 return false;
2937 }
2938 }
2939 }
2940 let fragment_coverage = covered.len();
2941 let mut shadowed = 0usize;
2942 for off in self.points.keys().copied() {
2943 if !covered.insert(off) {
2944 shadowed += 1;
2945 }
2946 }
2947 shadowed == self.shadowed as usize
2948 && fragment_coverage == self.fragment_coverage as usize
2949 && covered.len() == self.len()
2950 }
2951
2952 pub(crate) fn debug_recomputed_estimated_bytes(&self) -> usize {
2953 let point_bytes = self
2954 .points
2955 .values()
2956 .map(Self::point_estimate)
2957 .fold(0usize, usize::saturating_add);
2958 let fragment_bytes = self
2959 .fragments
2960 .iter()
2961 .map(OverlayFragment::estimated_bytes)
2962 .fold(0usize, usize::saturating_add);
2963 point_bytes.saturating_add(fragment_bytes)
2964 }
2965}
2966
2967#[derive(Debug, Clone, Copy, Default)]
2968#[cfg_attr(test, derive(serde::Serialize))]
2969pub(crate) struct OverlaySelectStats {
2970 pub(crate) zip_select_calls: usize,
2971 pub(crate) direct_dense_slices: usize,
2972 pub(crate) direct_run_materializations: usize,
2973 pub(crate) partial_sparse_intersections: usize,
2974 pub(crate) partial_dense_intersections: usize,
2975 pub(crate) partial_run_intersections: usize,
2976 pub(crate) partial_overlay_builds: usize,
2977 pub(crate) row_scalar_fallbacks: usize,
2978 pub(crate) point_entries_applied: usize,
2979 pub(crate) fragment_intersections: usize,
2980 pub(crate) small_point_selects: usize,
2982 pub(crate) point_patch_selects: usize,
2984}
2985
2986#[cfg(test)]
2987thread_local! {
2988 static OVERLAY_SELECT_STATS: std::cell::RefCell<OverlaySelectStats> =
2989 std::cell::RefCell::new(OverlaySelectStats::default());
2990}
2991
2992#[cfg(test)]
2993pub(crate) fn reset_overlay_select_stats() {
2994 OVERLAY_SELECT_STATS.with(|stats| *stats.borrow_mut() = OverlaySelectStats::default());
2995}
2996
2997#[cfg(test)]
2998pub(crate) fn snapshot_overlay_select_stats() -> OverlaySelectStats {
2999 OVERLAY_SELECT_STATS.with(|stats| *stats.borrow())
3000}
3001
3002#[cfg(test)]
3003fn record_overlay_select_stats(f: impl FnOnce(&mut OverlaySelectStats)) {
3004 OVERLAY_SELECT_STATS.with(|stats| f(&mut stats.borrow_mut()));
3005}
3006
3007#[cfg(not(test))]
3008#[inline]
3009fn record_overlay_select_stats(_f: impl FnOnce(&mut OverlaySelectStats)) {}
3010
3011#[derive(Debug, Clone, Copy, Eq, PartialEq)]
3012enum OverlayFragmentShape {
3013 Sparse,
3014 Dense,
3015 Run,
3016}
3017
3018struct OverlaySlots<T> {
3019 present: Vec<bool>,
3020 values: Vec<Option<T>>,
3021 any_present: bool,
3022}
3023
3024impl<T> OverlaySlots<T> {
3025 fn new(len: usize) -> Self {
3026 Self {
3027 present: vec![false; len],
3028 values: (0..len).map(|_| None).collect(),
3029 any_present: false,
3030 }
3031 }
3032
3033 #[inline]
3034 fn set(&mut self, idx: usize, value: Option<T>) {
3035 if idx >= self.present.len() {
3036 return;
3037 }
3038 self.present[idx] = true;
3039 self.values[idx] = value;
3040 self.any_present = true;
3041 }
3042
3043 #[inline]
3044 fn any_present(&self) -> bool {
3045 self.any_present
3046 }
3047}
3048
3049pub(crate) struct OverlayCascade<'a> {
3050 user: &'a Overlay,
3051 computed: &'a Overlay,
3052}
3053
3054impl<'a> OverlayCascade<'a> {
3055 #[inline]
3056 pub(crate) fn new(user: &'a Overlay, computed: &'a Overlay) -> Self {
3057 Self { user, computed }
3058 }
3059
3060 #[inline]
3061 pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'a>> {
3062 self.user
3063 .get_scalar(off)
3064 .or_else(|| self.computed.get_scalar(off))
3065 }
3066
3067 #[inline]
3068 pub(crate) fn get_format(&self, off: usize) -> Option<FormatId> {
3069 self.user
3070 .get_format(off)
3071 .or_else(|| self.computed.get_format(off))
3072 }
3073
3074 #[inline]
3075 pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
3076 self.user.has_any_in_range(range.clone()) || self.computed.has_any_in_range(range)
3077 }
3078
3079 #[allow(clippy::type_complexity)]
3082 fn small_points_only(
3083 &self,
3084 range: core::ops::Range<usize>,
3085 ) -> Option<
3086 impl Iterator<Item = (usize, Option<&'a OverlayValue>, Option<&'a OverlayValue>)> + 'a,
3087 > {
3088 const SMALL: usize = 16;
3089 if range.len() > SMALL
3090 || self
3091 .user
3092 .fragments
3093 .iter()
3094 .chain(self.computed.fragments.iter())
3095 .any(|f| f.has_any_in_range(range.clone()))
3096 {
3097 return None;
3098 }
3099 let (user, computed) = (self.user, self.computed);
3100 let start = range.start;
3101 Some(range.map(move |off| {
3102 (
3103 off - start,
3104 user.points.get(&off),
3105 computed.points.get(&off),
3106 )
3107 }))
3108 }
3109
3110 fn points_only_patch<T: arrow_array::ArrowPrimitiveType>(
3117 &self,
3118 range: core::ops::Range<usize>,
3119 base: &arrow_array::PrimitiveArray<T>,
3120 lane: impl Fn(&OverlayValue) -> Option<T::Native>,
3121 ) -> Option<Arc<arrow_array::PrimitiveArray<T>>> {
3122 if self
3123 .user
3124 .fragments
3125 .iter()
3126 .chain(self.computed.fragments.iter())
3127 .any(|f| f.has_any_in_range(range.clone()))
3128 {
3129 return None;
3130 }
3131 let len = range.len();
3132 debug_assert_eq!(base.len(), len);
3133 let mut values: Option<Vec<T::Native>> = None;
3134 let mut validity: Option<arrow_buffer::BooleanBufferBuilder> = None;
3135 for layer in [self.computed, self.user] {
3136 layer.for_each_point_in_range(range.clone(), |off, value| {
3137 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3138 let i = off - range.start;
3139 let values = values.get_or_insert_with(|| {
3140 if let Some(nulls) = base.nulls() {
3144 let mut bits = arrow_buffer::BooleanBufferBuilder::new(len);
3145 bits.append_buffer(nulls.inner());
3146 validity = Some(bits);
3147 }
3148 base.values().to_vec()
3149 });
3150 match lane(value) {
3151 Some(v) => {
3152 values[i] = v;
3153 if let Some(bits) = validity.as_mut() {
3154 bits.set_bit(i, true);
3155 }
3156 }
3157 None => {
3158 values[i] = T::Native::default();
3159 validity
3160 .get_or_insert_with(|| {
3161 let mut bits = arrow_buffer::BooleanBufferBuilder::new(len);
3162 bits.append_n(len, true);
3163 bits
3164 })
3165 .set_bit(i, false);
3166 }
3167 }
3168 });
3169 }
3170 let Some(values) = values else {
3171 return Some(Arc::new(base.clone()));
3172 };
3173 record_overlay_select_stats(|stats| stats.point_patch_selects += 1);
3174 let nulls = match validity {
3175 Some(mut bits) => Some(arrow_buffer::NullBuffer::new(bits.finish())),
3176 None => base.nulls().cloned(),
3177 }
3178 .filter(|nulls| nulls.null_count() > 0);
3179 Some(Arc::new(arrow_array::PrimitiveArray::<T>::new(
3180 values.into(),
3181 nulls,
3182 )))
3183 }
3184
3185 pub(crate) fn select_numbers(
3186 &self,
3187 range: core::ops::Range<usize>,
3188 base: &Float64Array,
3189 ) -> Arc<Float64Array> {
3190 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3191 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3192 return Self::dense_numbers(fragment, range);
3193 }
3194 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3195 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3196 return Self::run_numbers(fragment, range);
3197 }
3198 if !self.user.has_any_in_range(range.clone()) {
3199 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3200 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3201 return Self::dense_numbers(fragment, range);
3202 }
3203 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3204 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3205 return Self::run_numbers(fragment, range);
3206 }
3207 }
3208
3209 if !self.has_any_in_range(range.clone()) {
3210 return Arc::new(base.clone());
3211 }
3212 if let Some(small) = self.small_points_only(range.clone()) {
3213 record_overlay_select_stats(|stats| stats.small_point_selects += 1);
3214 let values: Vec<Option<f64>> = small
3217 .map(|(i, user, computed)| match user.or(computed) {
3218 Some(v) => {
3219 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3220 v.numeric_lane_value()
3221 }
3222 None => base.is_valid(i).then(|| base.value(i)),
3223 })
3224 .collect();
3225 return Arc::new(Float64Array::from(values));
3226 }
3227 if let Some(patched) =
3228 self.points_only_patch(range.clone(), base, OverlayValue::numeric_lane_value)
3229 {
3230 return patched;
3231 }
3232
3233 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3234 let len = range.end.saturating_sub(range.start);
3235 let mut slots = OverlaySlots::<f64>::new(len);
3236 Self::apply_number_layer(self.computed, range.clone(), &mut slots);
3237 Self::apply_number_layer(self.user, range.clone(), &mut slots);
3238 if !slots.any_present() {
3239 return Arc::new(base.clone());
3240 }
3241
3242 let mut mask_b = BooleanBuilder::with_capacity(len);
3243 let mut values_b = Float64Builder::with_capacity(len);
3244 for idx in 0..len {
3245 mask_b.append_value(slots.present[idx]);
3246 match slots.values[idx] {
3247 Some(value) => values_b.append_value(value),
3248 None => values_b.append_null(),
3249 }
3250 }
3251 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3252 let mask = mask_b.finish();
3253 let values = values_b.finish();
3254 let zipped =
3255 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip numeric overlay");
3256 Arc::new(
3257 zipped
3258 .as_any()
3259 .downcast_ref::<Float64Array>()
3260 .expect("numeric overlay zip type")
3261 .clone(),
3262 )
3263 }
3264
3265 pub(crate) fn select_booleans(
3266 &self,
3267 range: core::ops::Range<usize>,
3268 base: &BooleanArray,
3269 ) -> Arc<BooleanArray> {
3270 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3271 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3272 return Self::dense_booleans(fragment, range);
3273 }
3274 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3275 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3276 return Self::run_booleans(fragment, range);
3277 }
3278 if !self.user.has_any_in_range(range.clone()) {
3279 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3280 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3281 return Self::dense_booleans(fragment, range);
3282 }
3283 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3284 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3285 return Self::run_booleans(fragment, range);
3286 }
3287 }
3288
3289 if !self.has_any_in_range(range.clone()) {
3290 return Arc::new(base.clone());
3291 }
3292
3293 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3294 let len = range.end.saturating_sub(range.start);
3295 let mut slots = OverlaySlots::<bool>::new(len);
3296 Self::apply_boolean_layer(self.computed, range.clone(), &mut slots);
3297 Self::apply_boolean_layer(self.user, range.clone(), &mut slots);
3298 if !slots.any_present() {
3299 return Arc::new(base.clone());
3300 }
3301
3302 let mut mask_b = BooleanBuilder::with_capacity(len);
3303 let mut values_b = BooleanBuilder::with_capacity(len);
3304 for idx in 0..len {
3305 mask_b.append_value(slots.present[idx]);
3306 match slots.values[idx] {
3307 Some(value) => values_b.append_value(value),
3308 None => values_b.append_null(),
3309 }
3310 }
3311 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3312 let mask = mask_b.finish();
3313 let values = values_b.finish();
3314 let zipped =
3315 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip boolean overlay");
3316 Arc::new(
3317 zipped
3318 .as_any()
3319 .downcast_ref::<BooleanArray>()
3320 .expect("boolean overlay zip type")
3321 .clone(),
3322 )
3323 }
3324
3325 pub(crate) fn select_text(
3326 &self,
3327 range: core::ops::Range<usize>,
3328 base: &StringArray,
3329 ) -> ArrayRef {
3330 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3331 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3332 return Self::dense_text(fragment, range);
3333 }
3334 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3335 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3336 return Self::run_text(fragment, range);
3337 }
3338 if !self.user.has_any_in_range(range.clone()) {
3339 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3340 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3341 return Self::dense_text(fragment, range);
3342 }
3343 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3344 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3345 return Self::run_text(fragment, range);
3346 }
3347 }
3348
3349 if !self.has_any_in_range(range.clone()) {
3350 return Arc::new(base.clone()) as ArrayRef;
3351 }
3352
3353 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3354 let len = range.end.saturating_sub(range.start);
3355 let mut slots = OverlaySlots::<String>::new(len);
3356 Self::apply_text_layer(self.computed, range.clone(), &mut slots);
3357 Self::apply_text_layer(self.user, range.clone(), &mut slots);
3358 if !slots.any_present() {
3359 return Arc::new(base.clone()) as ArrayRef;
3360 }
3361
3362 let mut mask_b = BooleanBuilder::with_capacity(len);
3363 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3364 for idx in 0..len {
3365 mask_b.append_value(slots.present[idx]);
3366 match &slots.values[idx] {
3367 Some(value) => values_b.append_value(value),
3368 None => values_b.append_null(),
3369 }
3370 }
3371 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3372 let mask = mask_b.finish();
3373 let values = values_b.finish();
3374 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip text overlay")
3375 }
3376
3377 pub(crate) fn select_errors(
3378 &self,
3379 range: core::ops::Range<usize>,
3380 base: &UInt8Array,
3381 ) -> Arc<UInt8Array> {
3382 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3383 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3384 return Self::dense_errors(fragment, range);
3385 }
3386 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3387 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3388 return Self::run_errors(fragment, range);
3389 }
3390 if !self.user.has_any_in_range(range.clone()) {
3391 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3392 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3393 return Self::dense_errors(fragment, range);
3394 }
3395 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3396 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3397 return Self::run_errors(fragment, range);
3398 }
3399 }
3400
3401 if !self.has_any_in_range(range.clone()) {
3402 return Arc::new(base.clone());
3403 }
3404 if let Some(small) = self.small_points_only(range.clone()) {
3405 record_overlay_select_stats(|stats| stats.small_point_selects += 1);
3406 let values: Vec<Option<u8>> = small
3407 .map(|(i, user, computed)| match user.or(computed) {
3408 Some(v) => {
3409 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3410 v.error_lane_value()
3411 }
3412 None => base.is_valid(i).then(|| base.value(i)),
3413 })
3414 .collect();
3415 return Arc::new(UInt8Array::from(values));
3416 }
3417 if let Some(patched) =
3418 self.points_only_patch(range.clone(), base, OverlayValue::error_lane_value)
3419 {
3420 return patched;
3421 }
3422
3423 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3424 let len = range.end.saturating_sub(range.start);
3425 let mut slots = OverlaySlots::<u8>::new(len);
3426 Self::apply_error_layer(self.computed, range.clone(), &mut slots);
3427 Self::apply_error_layer(self.user, range.clone(), &mut slots);
3428 if !slots.any_present() {
3429 return Arc::new(base.clone());
3430 }
3431
3432 let mut mask_b = BooleanBuilder::with_capacity(len);
3433 let mut values_b = UInt8Builder::with_capacity(len);
3434 for idx in 0..len {
3435 mask_b.append_value(slots.present[idx]);
3436 match slots.values[idx] {
3437 Some(value) => values_b.append_value(value),
3438 None => values_b.append_null(),
3439 }
3440 }
3441 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3442 let mask = mask_b.finish();
3443 let values = values_b.finish();
3444 let zipped =
3445 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip error overlay");
3446 Arc::new(
3447 zipped
3448 .as_any()
3449 .downcast_ref::<UInt8Array>()
3450 .expect("error overlay zip type")
3451 .clone(),
3452 )
3453 }
3454
3455 pub(crate) fn select_type_tags(
3456 &self,
3457 range: core::ops::Range<usize>,
3458 base: &UInt8Array,
3459 ) -> Arc<UInt8Array> {
3460 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3461 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3462 return Self::dense_type_tags(fragment, range);
3463 }
3464 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3465 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3466 return Self::run_type_tags(fragment, range);
3467 }
3468 if !self.user.has_any_in_range(range.clone()) {
3469 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3470 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3471 return Self::dense_type_tags(fragment, range);
3472 }
3473 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3474 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3475 return Self::run_type_tags(fragment, range);
3476 }
3477 }
3478
3479 if !self.has_any_in_range(range.clone()) {
3480 return Arc::new(base.clone());
3481 }
3482
3483 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3484 let len = range.end.saturating_sub(range.start);
3485 let mut slots = OverlaySlots::<u8>::new(len);
3486 Self::apply_type_tag_layer(self.computed, range.clone(), &mut slots);
3487 Self::apply_type_tag_layer(self.user, range.clone(), &mut slots);
3488 if !slots.any_present() {
3489 return Arc::new(base.clone());
3490 }
3491
3492 let mut mask_b = BooleanBuilder::with_capacity(len);
3493 let mut values_b = UInt8Builder::with_capacity(len);
3494 for idx in 0..len {
3495 mask_b.append_value(slots.present[idx]);
3496 match slots.values[idx] {
3497 Some(value) => values_b.append_value(value),
3498 None => values_b.append_null(),
3499 }
3500 }
3501 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3502 let mask = mask_b.finish();
3503 let values = values_b.finish();
3504 let zipped =
3505 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip type-tag overlay");
3506 Arc::new(
3507 zipped
3508 .as_any()
3509 .downcast_ref::<UInt8Array>()
3510 .expect("type-tag overlay zip type")
3511 .clone(),
3512 )
3513 }
3514
3515 pub(crate) fn select_lowered_text(
3516 &self,
3517 range: core::ops::Range<usize>,
3518 base: &StringArray,
3519 ) -> Arc<StringArray> {
3520 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3521 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3522 return Self::dense_lowered_text(fragment, range);
3523 }
3524 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3525 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3526 return Self::run_lowered_text(fragment, range);
3527 }
3528 if !self.user.has_any_in_range(range.clone()) {
3529 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3530 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3531 return Self::dense_lowered_text(fragment, range);
3532 }
3533 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3534 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3535 return Self::run_lowered_text(fragment, range);
3536 }
3537 }
3538
3539 if !self.has_any_in_range(range.clone()) {
3540 return Arc::new(base.clone());
3541 }
3542 if self.user.fragments.is_empty() && self.computed.fragments.is_empty() {
3543 return self.select_lowered_text_point_scalar(range, base);
3544 }
3545
3546 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3547 let len = range.end.saturating_sub(range.start);
3548 let mut slots = OverlaySlots::<String>::new(len);
3549 Self::apply_lowered_text_layer(self.computed, range.clone(), &mut slots);
3550 Self::apply_lowered_text_layer(self.user, range.clone(), &mut slots);
3551 if !slots.any_present() {
3552 return Arc::new(base.clone());
3553 }
3554
3555 let mut mask_b = BooleanBuilder::with_capacity(len);
3556 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3557 for idx in 0..len {
3558 mask_b.append_value(slots.present[idx]);
3559 match &slots.values[idx] {
3560 Some(value) => values_b.append_value(value),
3561 None => values_b.append_null(),
3562 }
3563 }
3564 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3565 let mask = mask_b.finish();
3566 let values = values_b.finish();
3567 let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
3568 .expect("zip lowered text overlay");
3569 Arc::new(
3570 zipped
3571 .as_any()
3572 .downcast_ref::<StringArray>()
3573 .expect("lowered text overlay zip type")
3574 .clone(),
3575 )
3576 }
3577
3578 fn select_lowered_text_point_scalar(
3579 &self,
3580 range: core::ops::Range<usize>,
3581 base: &StringArray,
3582 ) -> Arc<StringArray> {
3583 let len = range.end.saturating_sub(range.start);
3584 let mut mask_b = BooleanBuilder::with_capacity(len);
3585 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3586 record_overlay_select_stats(|stats| stats.row_scalar_fallbacks += len);
3587 for off in range {
3588 if let Some(value) = self.get_scalar(off) {
3589 mask_b.append_value(true);
3590 if let Some(s) = value.lowered_text_value() {
3591 values_b.append_value(&s);
3592 } else {
3593 values_b.append_null();
3594 }
3595 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3596 } else {
3597 mask_b.append_value(false);
3598 values_b.append_null();
3599 }
3600 }
3601 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3602 let mask = mask_b.finish();
3603 let values = values_b.finish();
3604 let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
3605 .expect("zip lowered text overlay");
3606 Arc::new(
3607 zipped
3608 .as_any()
3609 .downcast_ref::<StringArray>()
3610 .expect("lowered text overlay zip type")
3611 .clone(),
3612 )
3613 }
3614
3615 fn dense_numbers(
3616 fragment: &OverlayFragment,
3617 range: core::ops::Range<usize>,
3618 ) -> Arc<Float64Array> {
3619 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3620 Self::payload_numbers_slice(payload, rel_start, len)
3621 }
3622
3623 fn dense_booleans(
3624 fragment: &OverlayFragment,
3625 range: core::ops::Range<usize>,
3626 ) -> Arc<BooleanArray> {
3627 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3628 Self::payload_booleans_slice(payload, rel_start, len)
3629 }
3630
3631 fn dense_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
3632 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3633 Self::payload_text_slice(payload, rel_start, len)
3634 }
3635
3636 fn dense_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
3637 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3638 Self::payload_errors_slice(payload, rel_start, len)
3639 }
3640
3641 fn dense_type_tags(
3642 fragment: &OverlayFragment,
3643 range: core::ops::Range<usize>,
3644 ) -> Arc<UInt8Array> {
3645 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3646 Self::payload_type_tags_slice(payload, rel_start, len)
3647 }
3648
3649 fn dense_lowered_text(
3650 fragment: &OverlayFragment,
3651 range: core::ops::Range<usize>,
3652 ) -> Arc<StringArray> {
3653 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3654 Self::payload_lowered_text_materialize(payload, rel_start, len)
3655 }
3656
3657 fn dense_payload_window(
3658 fragment: &OverlayFragment,
3659 range: core::ops::Range<usize>,
3660 ) -> (usize, usize, &OverlayFragmentPayload) {
3661 let OverlayFragment::DenseRange { start, payload, .. } = fragment else {
3662 unreachable!("dense payload window requires DenseRange")
3663 };
3664 let rel_start = range.start.saturating_sub(*start as usize);
3665 (rel_start, range.end.saturating_sub(range.start), payload)
3666 }
3667
3668 fn run_numbers(
3669 fragment: &OverlayFragment,
3670 range: core::ops::Range<usize>,
3671 ) -> Arc<Float64Array> {
3672 let mut b = Float64Builder::with_capacity(range.end.saturating_sub(range.start));
3673 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3674 if let Some(value) = payload.number_at(run_idx) {
3675 for _ in 0..repeat {
3676 b.append_value(value);
3677 }
3678 } else {
3679 for _ in 0..repeat {
3680 b.append_null();
3681 }
3682 }
3683 });
3684 Arc::new(b.finish())
3685 }
3686
3687 fn run_booleans(
3688 fragment: &OverlayFragment,
3689 range: core::ops::Range<usize>,
3690 ) -> Arc<BooleanArray> {
3691 let mut b = BooleanBuilder::with_capacity(range.end.saturating_sub(range.start));
3692 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3693 if let Some(value) = payload.boolean_at(run_idx) {
3694 for _ in 0..repeat {
3695 b.append_value(value);
3696 }
3697 } else {
3698 for _ in 0..repeat {
3699 b.append_null();
3700 }
3701 }
3702 });
3703 Arc::new(b.finish())
3704 }
3705
3706 fn run_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
3707 let mut b = StringBuilder::with_capacity(
3708 range.end.saturating_sub(range.start),
3709 range.end.saturating_sub(range.start).saturating_mul(8),
3710 );
3711 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3712 if let Some(value) = payload.text_at(run_idx) {
3713 for _ in 0..repeat {
3714 b.append_value(value);
3715 }
3716 } else {
3717 for _ in 0..repeat {
3718 b.append_null();
3719 }
3720 }
3721 });
3722 Arc::new(b.finish()) as ArrayRef
3723 }
3724
3725 fn run_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
3726 let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
3727 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3728 if let Some(value) = payload.error_at(run_idx) {
3729 for _ in 0..repeat {
3730 b.append_value(value);
3731 }
3732 } else {
3733 for _ in 0..repeat {
3734 b.append_null();
3735 }
3736 }
3737 });
3738 Arc::new(b.finish())
3739 }
3740
3741 fn run_type_tags(
3742 fragment: &OverlayFragment,
3743 range: core::ops::Range<usize>,
3744 ) -> Arc<UInt8Array> {
3745 let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
3746 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3747 let tag = payload.type_tag_at(run_idx).unwrap_or(TypeTag::Empty) as u8;
3748 for _ in 0..repeat {
3749 b.append_value(tag);
3750 }
3751 });
3752 Arc::new(b.finish())
3753 }
3754
3755 fn run_lowered_text(
3756 fragment: &OverlayFragment,
3757 range: core::ops::Range<usize>,
3758 ) -> Arc<StringArray> {
3759 let mut b = StringBuilder::with_capacity(
3760 range.end.saturating_sub(range.start),
3761 range.end.saturating_sub(range.start).saturating_mul(8),
3762 );
3763 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3764 let value = Self::payload_lowered_text_at(payload, run_idx);
3765 if let Some(value) = value {
3766 for _ in 0..repeat {
3767 b.append_value(&value);
3768 }
3769 } else {
3770 for _ in 0..repeat {
3771 b.append_null();
3772 }
3773 }
3774 });
3775 Arc::new(b.finish())
3776 }
3777
3778 fn payload_numbers_slice(
3779 payload: &OverlayFragmentPayload,
3780 start: usize,
3781 len: usize,
3782 ) -> Arc<Float64Array> {
3783 if let Some(array) = &payload.numbers {
3784 let sliced = array.slice(start, len);
3785 Arc::new(
3786 sliced
3787 .as_any()
3788 .downcast_ref::<Float64Array>()
3789 .unwrap()
3790 .clone(),
3791 )
3792 } else {
3793 Self::null_numbers(len)
3794 }
3795 }
3796
3797 fn payload_booleans_slice(
3798 payload: &OverlayFragmentPayload,
3799 start: usize,
3800 len: usize,
3801 ) -> Arc<BooleanArray> {
3802 if let Some(array) = &payload.booleans {
3803 let sliced = array.slice(start, len);
3804 Arc::new(
3805 sliced
3806 .as_any()
3807 .downcast_ref::<BooleanArray>()
3808 .unwrap()
3809 .clone(),
3810 )
3811 } else {
3812 Self::null_booleans(len)
3813 }
3814 }
3815
3816 fn payload_text_slice(payload: &OverlayFragmentPayload, start: usize, len: usize) -> ArrayRef {
3817 if let Some(array) = &payload.text {
3818 array.slice(start, len)
3819 } else {
3820 new_null_array(&DataType::Utf8, len)
3821 }
3822 }
3823
3824 fn payload_errors_slice(
3825 payload: &OverlayFragmentPayload,
3826 start: usize,
3827 len: usize,
3828 ) -> Arc<UInt8Array> {
3829 if let Some(array) = &payload.errors {
3830 let sliced = array.slice(start, len);
3831 Arc::new(
3832 sliced
3833 .as_any()
3834 .downcast_ref::<UInt8Array>()
3835 .unwrap()
3836 .clone(),
3837 )
3838 } else {
3839 Self::null_errors(len)
3840 }
3841 }
3842
3843 fn payload_type_tags_slice(
3844 payload: &OverlayFragmentPayload,
3845 start: usize,
3846 len: usize,
3847 ) -> Arc<UInt8Array> {
3848 let sliced = payload.type_tags.slice(start, len);
3849 Arc::new(
3850 sliced
3851 .as_any()
3852 .downcast_ref::<UInt8Array>()
3853 .unwrap()
3854 .clone(),
3855 )
3856 }
3857
3858 fn payload_lowered_text_materialize(
3859 payload: &OverlayFragmentPayload,
3860 start: usize,
3861 len: usize,
3862 ) -> Arc<StringArray> {
3863 let mut b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3864 for idx in start..start.saturating_add(len) {
3865 if let Some(value) = Self::payload_lowered_text_at(payload, idx) {
3866 b.append_value(&value);
3867 } else {
3868 b.append_null();
3869 }
3870 }
3871 Arc::new(b.finish())
3872 }
3873
3874 fn payload_lowered_text_at(payload: &OverlayFragmentPayload, idx: usize) -> Option<String> {
3875 match payload.type_tag_at(idx)? {
3876 TypeTag::Text => payload.text_at(idx).map(|value| value.to_lowercase()),
3877 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
3878 payload.number_at(idx).map(|value| value.to_string())
3879 }
3880 TypeTag::Boolean => payload
3881 .boolean_at(idx)
3882 .map(|value| if value { "true" } else { "false" }.to_string()),
3883 TypeTag::Empty | TypeTag::Error | TypeTag::Pending => None,
3884 }
3885 }
3886
3887 fn null_numbers(len: usize) -> Arc<Float64Array> {
3888 let arr = new_null_array(&DataType::Float64, len);
3889 Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
3890 }
3891
3892 fn null_booleans(len: usize) -> Arc<BooleanArray> {
3893 let arr = new_null_array(&DataType::Boolean, len);
3894 Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
3895 }
3896
3897 fn null_errors(len: usize) -> Arc<UInt8Array> {
3898 let arr = new_null_array(&DataType::UInt8, len);
3899 Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
3900 }
3901
3902 fn apply_number_layer(
3903 layer: &Overlay,
3904 range: core::ops::Range<usize>,
3905 slots: &mut OverlaySlots<f64>,
3906 ) {
3907 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3908 payload.number_at(idx)
3909 });
3910 layer.for_each_point_in_range(range.clone(), |off, value| {
3911 slots.set(off - range.start, value.numeric_lane_value());
3912 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3913 });
3914 }
3915
3916 fn apply_boolean_layer(
3917 layer: &Overlay,
3918 range: core::ops::Range<usize>,
3919 slots: &mut OverlaySlots<bool>,
3920 ) {
3921 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3922 payload.boolean_at(idx)
3923 });
3924 layer.for_each_point_in_range(range.clone(), |off, value| {
3925 slots.set(off - range.start, value.boolean_lane_value());
3926 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3927 });
3928 }
3929
3930 fn apply_text_layer(
3931 layer: &Overlay,
3932 range: core::ops::Range<usize>,
3933 slots: &mut OverlaySlots<String>,
3934 ) {
3935 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3936 payload.text_at(idx).map(ToString::to_string)
3937 });
3938 layer.for_each_point_in_range(range.clone(), |off, value| {
3939 slots.set(
3940 off - range.start,
3941 value.text_lane_value().map(ToString::to_string),
3942 );
3943 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3944 });
3945 }
3946
3947 fn apply_error_layer(
3948 layer: &Overlay,
3949 range: core::ops::Range<usize>,
3950 slots: &mut OverlaySlots<u8>,
3951 ) {
3952 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3953 payload.error_at(idx)
3954 });
3955 layer.for_each_point_in_range(range.clone(), |off, value| {
3956 slots.set(off - range.start, value.error_lane_value());
3957 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3958 });
3959 }
3960
3961 fn apply_type_tag_layer(
3962 layer: &Overlay,
3963 range: core::ops::Range<usize>,
3964 slots: &mut OverlaySlots<u8>,
3965 ) {
3966 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3967 payload.type_tag_at(idx).map(|tag| tag as u8)
3968 });
3969 layer.for_each_point_in_range(range.clone(), |off, value| {
3970 slots.set(off - range.start, Some(value.type_tag() as u8));
3971 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3972 });
3973 }
3974
3975 fn apply_lowered_text_layer(
3976 layer: &Overlay,
3977 range: core::ops::Range<usize>,
3978 slots: &mut OverlaySlots<String>,
3979 ) {
3980 Self::apply_fragment_layer(layer, range.clone(), slots, Self::payload_lowered_text_at);
3981 layer.for_each_point_in_range(range.clone(), |off, value| {
3982 slots.set(off - range.start, value.lowered_text_value());
3983 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3984 });
3985 }
3986
3987 fn apply_fragment_layer<T>(
3988 layer: &Overlay,
3989 range: core::ops::Range<usize>,
3990 slots: &mut OverlaySlots<T>,
3991 mut value_at: impl FnMut(&OverlayFragmentPayload, usize) -> Option<T>,
3992 ) {
3993 for fragment in &layer.fragments {
3994 if !fragment.has_any_in_range(range.clone()) {
3995 continue;
3996 }
3997 Self::record_fragment_intersection(fragment);
3998 Self::for_each_fragment_payload_index(
3999 fragment,
4000 range.clone(),
4001 |out_idx, payload, payload_idx| {
4002 slots.set(out_idx, value_at(payload, payload_idx));
4003 },
4004 );
4005 }
4006 }
4007
4008 fn record_fragment_intersection(fragment: &OverlayFragment) {
4009 let shape = match fragment {
4010 OverlayFragment::SparseOffsets { .. } => OverlayFragmentShape::Sparse,
4011 OverlayFragment::DenseRange { .. } => OverlayFragmentShape::Dense,
4012 OverlayFragment::RunRange { .. } => OverlayFragmentShape::Run,
4013 };
4014 record_overlay_select_stats(|stats| {
4015 stats.fragment_intersections += 1;
4016 match shape {
4017 OverlayFragmentShape::Sparse => stats.partial_sparse_intersections += 1,
4018 OverlayFragmentShape::Dense => stats.partial_dense_intersections += 1,
4019 OverlayFragmentShape::Run => stats.partial_run_intersections += 1,
4020 }
4021 });
4022 }
4023
4024 fn for_each_fragment_payload_index(
4025 fragment: &OverlayFragment,
4026 range: core::ops::Range<usize>,
4027 mut f: impl FnMut(usize, &OverlayFragmentPayload, usize),
4028 ) {
4029 if range.is_empty() {
4030 return;
4031 }
4032 match fragment {
4033 OverlayFragment::SparseOffsets { offsets, payload } => {
4034 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
4035 let lo = offsets.partition_point(|off| *off < start);
4036 let hi = offsets.partition_point(|off| (*off as usize) < range.end);
4037 for (idx, off) in offsets.iter().enumerate().take(hi).skip(lo) {
4038 let out_idx = (*off as usize).saturating_sub(range.start);
4039 f(out_idx, payload, idx);
4040 }
4041 }
4042 OverlayFragment::DenseRange {
4043 start,
4044 len,
4045 payload,
4046 } => {
4047 let frag_start = *start as usize;
4048 let frag_end = frag_start.saturating_add(*len as usize);
4049 let inter_start = frag_start.max(range.start);
4050 let inter_end = frag_end.min(range.end);
4051 if inter_start >= inter_end {
4052 return;
4053 }
4054 for abs in inter_start..inter_end {
4055 f(abs - range.start, payload, abs - frag_start);
4056 }
4057 }
4058 OverlayFragment::RunRange {
4059 start,
4060 len,
4061 run_ends,
4062 payload,
4063 } => {
4064 let frag_start = *start as usize;
4065 let frag_end = frag_start.saturating_add(*len as usize);
4066 let inter_start = frag_start.max(range.start);
4067 let inter_end = frag_end.min(range.end);
4068 if inter_start >= inter_end {
4069 return;
4070 }
4071 let first = run_ends.run_at(inter_start - frag_start);
4072 let mut prev_end = if first == 0 {
4073 0
4074 } else {
4075 run_ends.end(first - 1)
4076 };
4077 for run_idx in first..run_ends.count() {
4078 let run_end = run_ends.end(run_idx);
4079 let run_start_abs = frag_start.saturating_add(prev_end);
4080 let run_end_abs = frag_start.saturating_add(run_end);
4081 let start_abs = run_start_abs.max(inter_start);
4082 let end_abs = run_end_abs.min(inter_end);
4083 if start_abs < end_abs {
4084 for abs in start_abs..end_abs {
4085 f(abs - range.start, payload, run_idx);
4086 }
4087 }
4088 prev_end = run_end;
4089 if run_end_abs >= inter_end {
4090 break;
4091 }
4092 }
4093 }
4094 }
4095 }
4096
4097 fn for_each_run_payload_index(
4098 fragment: &OverlayFragment,
4099 range: core::ops::Range<usize>,
4100 mut f: impl FnMut(&OverlayFragmentPayload, usize, usize),
4101 ) {
4102 let OverlayFragment::RunRange {
4103 start,
4104 len,
4105 run_ends,
4106 payload,
4107 } = fragment
4108 else {
4109 unreachable!("run payload iteration requires RunRange")
4110 };
4111 let frag_start = *start as usize;
4112 let frag_end = frag_start.saturating_add(*len as usize);
4113 let inter_start = frag_start.max(range.start);
4114 let inter_end = frag_end.min(range.end);
4115 if inter_start >= inter_end {
4116 return;
4117 }
4118 let first = run_ends.run_at(inter_start - frag_start);
4119 let mut prev_end = if first == 0 {
4120 0
4121 } else {
4122 run_ends.end(first - 1)
4123 };
4124 for run_idx in first..run_ends.count() {
4125 let run_end = run_ends.end(run_idx);
4126 let run_start_abs = frag_start.saturating_add(prev_end);
4127 let run_end_abs = frag_start.saturating_add(run_end);
4128 let start_abs = run_start_abs.max(inter_start);
4129 let end_abs = run_end_abs.min(inter_end);
4130 if start_abs < end_abs {
4131 f(payload, run_idx, end_abs - start_abs);
4132 }
4133 prev_end = run_end;
4134 if run_end_abs >= inter_end {
4135 break;
4136 }
4137 }
4138 }
4139}
4140
4141impl OverlayFragmentPayload {
4142 #[inline]
4143 fn type_tag_at(&self, idx: usize) -> Option<TypeTag> {
4144 if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
4145 return None;
4146 }
4147 Some(TypeTag::from_u8(self.type_tags.value(idx)))
4148 }
4149}
4150
4151impl Overlay {
4152 fn full_cover_dense_fragment(
4153 &self,
4154 range: core::ops::Range<usize>,
4155 ) -> Option<&OverlayFragment> {
4156 self.full_cover_single_fragment(range, OverlayFragmentShape::Dense)
4157 }
4158
4159 fn full_cover_run_fragment(&self, range: core::ops::Range<usize>) -> Option<&OverlayFragment> {
4160 self.full_cover_single_fragment(range, OverlayFragmentShape::Run)
4161 }
4162
4163 fn full_cover_single_fragment(
4164 &self,
4165 range: core::ops::Range<usize>,
4166 shape: OverlayFragmentShape,
4167 ) -> Option<&OverlayFragment> {
4168 if range.is_empty() || self.points.keys().any(|off| range.contains(off)) {
4169 return None;
4170 }
4171 let mut found = None;
4172 for fragment in &self.fragments {
4173 if !fragment.has_any_in_range(range.clone()) {
4174 continue;
4175 }
4176 let shape_matches = matches!(
4177 (shape, fragment),
4178 (
4179 OverlayFragmentShape::Dense,
4180 OverlayFragment::DenseRange { .. }
4181 ) | (OverlayFragmentShape::Run, OverlayFragment::RunRange { .. })
4182 );
4183 let covers = fragment
4184 .interval_coverage()
4185 .is_some_and(|own| own.start <= range.start && range.end <= own.end);
4186 if shape_matches && covers && found.is_none() {
4187 found = Some(fragment);
4188 } else {
4189 return None;
4190 }
4191 }
4192 found
4193 }
4194}
4195fn append_overlay_value_to_lane_builders(
4196 ov: &OverlayValue,
4197 tag_b: &mut UInt8Builder,
4198 nb: &mut Float64Builder,
4199 bb: &mut BooleanBuilder,
4200 sb: &mut StringBuilder,
4201 eb: &mut UInt8Builder,
4202 non_num: &mut usize,
4203 non_bool: &mut usize,
4204 non_text: &mut usize,
4205 non_err: &mut usize,
4206) {
4207 match ov {
4208 OverlayValue::Empty => {
4209 tag_b.append_value(TypeTag::Empty as u8);
4210 nb.append_null();
4211 bb.append_null();
4212 sb.append_null();
4213 eb.append_null();
4214 }
4215 OverlayValue::Number(n) => {
4216 tag_b.append_value(TypeTag::Number as u8);
4217 nb.append_value(*n);
4218 *non_num += 1;
4219 bb.append_null();
4220 sb.append_null();
4221 eb.append_null();
4222 }
4223 OverlayValue::DateTime(serial) => {
4224 tag_b.append_value(TypeTag::DateTime as u8);
4225 nb.append_value(*serial);
4226 *non_num += 1;
4227 bb.append_null();
4228 sb.append_null();
4229 eb.append_null();
4230 }
4231 OverlayValue::Duration(serial) => {
4232 tag_b.append_value(TypeTag::Duration as u8);
4233 nb.append_value(*serial);
4234 *non_num += 1;
4235 bb.append_null();
4236 sb.append_null();
4237 eb.append_null();
4238 }
4239 OverlayValue::Boolean(b) => {
4240 tag_b.append_value(TypeTag::Boolean as u8);
4241 nb.append_null();
4242 bb.append_value(*b);
4243 *non_bool += 1;
4244 sb.append_null();
4245 eb.append_null();
4246 }
4247 OverlayValue::Text(s) => {
4248 tag_b.append_value(TypeTag::Text as u8);
4249 nb.append_null();
4250 bb.append_null();
4251 sb.append_value(s);
4252 *non_text += 1;
4253 eb.append_null();
4254 }
4255 OverlayValue::Error(code) => {
4256 tag_b.append_value(TypeTag::Error as u8);
4257 nb.append_null();
4258 bb.append_null();
4259 sb.append_null();
4260 eb.append_value(*code);
4261 *non_err += 1;
4262 }
4263 OverlayValue::Pending => {
4264 tag_b.append_value(TypeTag::Pending as u8);
4265 nb.append_null();
4266 bb.append_null();
4267 sb.append_null();
4268 eb.append_null();
4269 }
4270 }
4271}
4272
4273impl ArrowSheet {
4274 pub fn new_sparse(sheet_name: &str, ncols: usize, nrows: usize, chunk_rows: usize) -> Self {
4280 Self::new_sparse_with_date_system(
4281 sheet_name,
4282 ncols,
4283 nrows,
4284 chunk_rows,
4285 crate::engine::DateSystem::Excel1900,
4286 )
4287 }
4288
4289 pub fn new_sparse_with_date_system(
4291 sheet_name: &str,
4292 ncols: usize,
4293 nrows: usize,
4294 chunk_rows: usize,
4295 date_system: crate::engine::DateSystem,
4296 ) -> Self {
4297 let chunk_rows = chunk_rows.max(1);
4298 let columns = (0..ncols)
4299 .map(|idx| ArrowColumn {
4300 chunks: Vec::new(),
4301 sparse_chunks: FxHashMap::default(),
4302 index: idx as u32,
4303 })
4304 .collect();
4305 let mut sheet = Self {
4306 name: Arc::from(sheet_name.to_string()),
4307 date_system,
4308 columns,
4309 nrows: 0,
4310 chunk_starts: Vec::new(),
4311 chunk_rows,
4312 };
4313 sheet.ensure_row_capacity(nrows);
4314 sheet
4315 }
4316
4317 pub fn set_sparse_overlay_value(
4321 &mut self,
4322 abs_row: usize,
4323 abs_col: usize,
4324 value: OverlayValue,
4325 ) -> isize {
4326 if abs_col >= self.columns.len() {
4327 let start = self.columns.len();
4328 self.columns
4329 .extend((start..=abs_col).map(|idx| ArrowColumn {
4330 chunks: Vec::new(),
4331 sparse_chunks: FxHashMap::default(),
4332 index: idx as u32,
4333 }));
4334 }
4335 if abs_row >= self.nrows as usize {
4336 self.ensure_row_capacity(abs_row + 1);
4337 }
4338 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4339 return 0;
4340 };
4341 let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) else {
4342 return 0;
4343 };
4344 ch.overlay.set(in_off, value)
4345 }
4346
4347 pub fn set_sparse_overlay_format(
4348 &mut self,
4349 abs_row: usize,
4350 abs_col: usize,
4351 format: Option<FormatId>,
4352 ) {
4353 if abs_row >= self.nrows as usize || abs_col >= self.columns.len() {
4354 return;
4355 }
4356 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4357 return;
4358 };
4359 if let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) {
4360 ch.overlay.set_format(in_off, format);
4361 }
4362 }
4363
4364 pub(crate) fn clear_format(&mut self, abs_row: usize, abs_col: usize) {
4366 if abs_row >= self.nrows as usize || abs_col >= self.columns.len() {
4367 return;
4368 }
4369 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4370 return;
4371 };
4372 let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) else {
4373 return;
4374 };
4375 ch.overlay.set_format(in_off, None);
4376 ch.computed_overlay.set_format(in_off, None);
4377 if let Some(runs) = &ch.format {
4378 let mut ids = runs.to_ids(ch.len());
4379 ids[in_off] = FormatId::GENERAL.0;
4380 ch.format = FormatRuns::from_ids(&ids);
4381 }
4382 }
4383
4384 pub fn shape(&self) -> Vec<ColumnShape> {
4386 self.columns
4387 .iter()
4388 .map(|c| {
4389 let chunks = c.chunks.len();
4390 let rows = self.nrows as usize;
4391 let has_num = c.chunks.iter().any(|ch| ch.meta.non_null_num > 0);
4392 let has_bool = c.chunks.iter().any(|ch| ch.meta.non_null_bool > 0);
4393 let has_text = c.chunks.iter().any(|ch| ch.meta.non_null_text > 0);
4394 let has_err = c.chunks.iter().any(|ch| ch.meta.non_null_err > 0);
4395 ColumnShape {
4396 index: c.index,
4397 chunks,
4398 rows,
4399 has_num,
4400 has_bool,
4401 has_text,
4402 has_err,
4403 }
4404 })
4405 .collect()
4406 }
4407
4408 pub fn range_view(
4409 &self,
4410 sr: usize,
4411 sc: usize,
4412 er: usize,
4413 ec: usize,
4414 ) -> crate::engine::range_view::RangeView<'_> {
4415 let r0 = er.checked_sub(sr).map(|d| d + 1).unwrap_or(0);
4416 let c0 = ec.checked_sub(sc).map(|d| d + 1).unwrap_or(0);
4417 let (rows, cols) = if r0 == 0 || c0 == 0 { (0, 0) } else { (r0, c0) };
4418 crate::engine::range_view::RangeView::new(
4419 crate::engine::range_view::RangeBacking::Borrowed(self),
4420 sr,
4421 sc,
4422 er,
4423 ec,
4424 rows,
4425 cols,
4426 )
4427 }
4428
4429 pub(crate) fn has_formats(&self) -> bool {
4430 self.columns.iter().any(|column| {
4431 column
4432 .chunks
4433 .iter()
4434 .chain(column.sparse_chunks.values())
4435 .any(|chunk| {
4436 chunk.format.is_some()
4437 || chunk.overlay.has_formats()
4438 || chunk.computed_overlay.has_formats()
4439 })
4440 })
4441 }
4442
4443 pub fn format_id(&self, abs_row: usize, abs_col: usize) -> Option<FormatId> {
4445 let (ch_idx, in_off) = self.chunk_of_row(abs_row)?;
4446 let ch = self.columns.get(abs_col)?.chunk(ch_idx)?;
4447 ch.overlay
4448 .get_format(in_off)
4449 .or_else(|| {
4450 ch.format
4451 .as_ref()
4452 .map(|runs| runs.get(in_off))
4453 .filter(|id| *id != FormatId::GENERAL)
4454 })
4455 .or_else(|| ch.computed_overlay.get_format(in_off))
4456 .filter(|id| *id != FormatId::GENERAL)
4457 }
4458
4459 #[inline]
4463 pub fn get_cell_value(&self, abs_row: usize, abs_col: usize) -> LiteralValue {
4464 let sheet_rows = self.nrows as usize;
4465 if abs_row >= sheet_rows {
4466 return LiteralValue::Empty;
4467 }
4468 if abs_col >= self.columns.len() {
4469 return LiteralValue::Empty;
4470 }
4471 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4472 return LiteralValue::Empty;
4473 };
4474 let col_ref = &self.columns[abs_col];
4475 let Some(ch) = col_ref.chunk(ch_idx) else {
4476 return LiteralValue::Empty;
4477 };
4478
4479 let cascade = OverlayCascade::new(&ch.overlay, &ch.computed_overlay);
4481 if let Some(ov) = cascade.get_scalar(in_off) {
4482 return ov.to_literal_for(self.date_system);
4483 }
4484
4485 let tag_u8 = ch.type_tag.value(in_off);
4487 match TypeTag::from_u8(tag_u8) {
4488 TypeTag::Empty => LiteralValue::Empty,
4489 TypeTag::Number => {
4490 if let Some(arr) = &ch.numbers {
4491 if arr.is_null(in_off) {
4492 return LiteralValue::Empty;
4493 }
4494 LiteralValue::Number(arr.value(in_off))
4495 } else {
4496 LiteralValue::Empty
4497 }
4498 }
4499 TypeTag::DateTime | TypeTag::Duration => {
4500 if let Some(arr) = &ch.numbers {
4501 if arr.is_null(in_off) {
4502 LiteralValue::Empty
4503 } else {
4504 LiteralValue::Number(arr.value(in_off))
4505 }
4506 } else {
4507 LiteralValue::Empty
4508 }
4509 }
4510 TypeTag::Boolean => {
4511 if let Some(arr) = &ch.booleans {
4512 if arr.is_null(in_off) {
4513 return LiteralValue::Empty;
4514 }
4515 LiteralValue::Boolean(arr.value(in_off))
4516 } else {
4517 LiteralValue::Empty
4518 }
4519 }
4520 TypeTag::Text => {
4521 if let Some(arr) = &ch.text {
4522 if arr.is_null(in_off) {
4523 return LiteralValue::Empty;
4524 }
4525 let sa = arr
4526 .as_any()
4527 .downcast_ref::<arrow_array::StringArray>()
4528 .unwrap();
4529 LiteralValue::Text(sa.value(in_off).to_string())
4530 } else {
4531 LiteralValue::Empty
4532 }
4533 }
4534 TypeTag::Error => {
4535 if let Some(arr) = &ch.errors {
4536 if arr.is_null(in_off) {
4537 return LiteralValue::Empty;
4538 }
4539 let kind = unmap_error_code(arr.value(in_off));
4540 LiteralValue::Error(ExcelError::new(kind))
4541 } else {
4542 LiteralValue::Empty
4543 }
4544 }
4545 TypeTag::Pending => LiteralValue::Pending,
4546 }
4547 }
4548
4549 pub fn ensure_row_capacity(&mut self, target_rows: usize) {
4554 if target_rows as u32 <= self.nrows {
4555 return;
4556 }
4557
4558 let chunk_size = self.chunk_rows.max(1);
4559
4560 if self.chunk_starts.is_empty() {
4564 self.chunk_starts.push(0);
4565 }
4566
4567 let mut next_start = self
4570 .chunk_starts
4571 .last()
4572 .copied()
4573 .unwrap_or(0)
4574 .saturating_add(chunk_size);
4575 while next_start < target_rows {
4576 self.chunk_starts.push(next_start);
4577 next_start = next_start.saturating_add(chunk_size);
4578 }
4579
4580 self.nrows = target_rows as u32;
4581
4582 let starts = self.chunk_starts.clone();
4587 let nrows = self.nrows as usize;
4588 let required_len_for = |ch_idx: usize| -> Option<usize> {
4589 let start = *starts.get(ch_idx)?;
4590 let end = starts.get(ch_idx + 1).copied().unwrap_or(nrows);
4591 Some(end.saturating_sub(start))
4592 };
4593
4594 for col in &mut self.columns {
4595 for (idx, ch) in col.chunks.iter_mut().enumerate() {
4596 if let Some(req) = required_len_for(idx) {
4597 ch.grow_len_to(req);
4598 }
4599 }
4600 if !col.sparse_chunks.is_empty() {
4601 let keys: Vec<usize> = col.sparse_chunks.keys().copied().collect();
4602 for idx in keys {
4603 if let (Some(req), Some(ch)) =
4604 (required_len_for(idx), col.sparse_chunks.get_mut(&idx))
4605 {
4606 ch.grow_len_to(req);
4607 }
4608 }
4609 }
4610 }
4611 }
4612
4613 pub fn ensure_column_chunk_mut(
4617 &mut self,
4618 col_idx: usize,
4619 ch_idx: usize,
4620 ) -> Option<&mut ColumnChunk> {
4621 let start = *self.chunk_starts.get(ch_idx)?;
4622 let end = self
4623 .chunk_starts
4624 .get(ch_idx + 1)
4625 .copied()
4626 .unwrap_or(self.nrows as usize);
4627 let len = end.saturating_sub(start);
4628
4629 let col = self.columns.get_mut(col_idx)?;
4630 if ch_idx < col.chunks.len() {
4631 return Some(&mut col.chunks[ch_idx]);
4632 }
4633 Some(
4634 col.sparse_chunks
4635 .entry(ch_idx)
4636 .or_insert_with(|| Self::make_empty_chunk(len)),
4637 )
4638 }
4639
4640 pub fn chunk_of_row(&self, abs_row: usize) -> Option<(usize, usize)> {
4642 if abs_row >= self.nrows as usize {
4643 return None;
4644 }
4645 let ch_idx = match self.chunk_starts.binary_search(&abs_row) {
4646 Ok(i) => i,
4647 Err(0) => 0,
4648 Err(i) => i - 1,
4649 };
4650 let start = self.chunk_starts[ch_idx];
4651 Some((ch_idx, abs_row - start))
4652 }
4653
4654 fn recompute_chunk_starts(&mut self) {
4655 self.chunk_starts.clear();
4656 if let Some(col0) = self.columns.first() {
4657 let mut cur = 0usize;
4658 for ch in &col0.chunks {
4659 self.chunk_starts.push(cur);
4660 cur += ch.type_tag.len();
4661 }
4662 }
4663 }
4664
4665 fn make_empty_chunk(len: usize) -> ColumnChunk {
4666 ColumnChunk {
4667 numbers: None,
4668 booleans: None,
4669 text: None,
4670 errors: None,
4671 type_tag: Arc::new(UInt8Array::from(vec![TypeTag::Empty as u8; len])),
4672 formula_id: None,
4673 format: None,
4674 meta: ColumnChunkMeta {
4675 len,
4676 non_null_num: 0,
4677 non_null_bool: 0,
4678 non_null_text: 0,
4679 non_null_err: 0,
4680 },
4681 lazy_null_numbers: OnceCell::new(),
4682 lazy_null_booleans: OnceCell::new(),
4683 lazy_null_text: OnceCell::new(),
4684 lazy_null_errors: OnceCell::new(),
4685 lowered_text: OnceCell::new(),
4686 merged: MergedLaneCache::default(),
4687 overlay: Overlay::new(),
4688 computed_overlay: Overlay::new(),
4689 }
4690 }
4691
4692 fn slice_chunk(ch: &ColumnChunk, off: usize, len: usize) -> ColumnChunk {
4693 use arrow_array::Array;
4695 let type_tag: Arc<UInt8Array> = Arc::new(
4696 Array::slice(ch.type_tag.as_ref(), off, len)
4697 .as_any()
4698 .downcast_ref::<UInt8Array>()
4699 .unwrap()
4700 .clone(),
4701 );
4702 let numbers: Option<Arc<Float64Array>> = ch.numbers.as_ref().and_then(|a| {
4704 let sl = Array::slice(a.as_ref(), off, len);
4705 let fa = sl.as_any().downcast_ref::<Float64Array>().unwrap().clone();
4706 let nn = len.saturating_sub(fa.null_count());
4707 if nn == 0 { None } else { Some(Arc::new(fa)) }
4708 });
4709 let booleans: Option<Arc<BooleanArray>> = ch.booleans.as_ref().and_then(|a| {
4710 let sl = Array::slice(a.as_ref(), off, len);
4711 let ba = sl.as_any().downcast_ref::<BooleanArray>().unwrap().clone();
4712 let nn = len.saturating_sub(ba.null_count());
4713 if nn == 0 { None } else { Some(Arc::new(ba)) }
4714 });
4715 let text: Option<ArrayRef> = ch.text.as_ref().and_then(|a| {
4716 let sl = Array::slice(a.as_ref(), off, len);
4717 let sa = sl.as_any().downcast_ref::<StringArray>().unwrap().clone();
4718 let nn = len.saturating_sub(sa.null_count());
4719 if nn == 0 {
4720 None
4721 } else {
4722 Some(Arc::new(sa) as ArrayRef)
4723 }
4724 });
4725 let errors: Option<Arc<UInt8Array>> = ch.errors.as_ref().and_then(|a| {
4726 let sl = Array::slice(a.as_ref(), off, len);
4727 let ea = sl.as_any().downcast_ref::<UInt8Array>().unwrap().clone();
4728 let nn = len.saturating_sub(ea.null_count());
4729 if nn == 0 { None } else { Some(Arc::new(ea)) }
4730 });
4731 let overlay = ch.overlay.slice(off, len);
4733 let computed_overlay = ch.computed_overlay.slice(off, len);
4734 let non_null_num = numbers.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4735 let non_null_bool = booleans.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4736 let non_null_text = text.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4737 let non_null_err = errors.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4738 ColumnChunk {
4739 numbers: numbers.clone(),
4740 booleans: booleans.clone(),
4741 text: text.clone(),
4742 errors: errors.clone(),
4743 type_tag,
4744 formula_id: None,
4745 format: ch.format.as_ref().and_then(|runs| runs.slice(off, len)),
4746 meta: ColumnChunkMeta {
4747 len,
4748 non_null_num,
4749 non_null_bool,
4750 non_null_text,
4751 non_null_err,
4752 },
4753 lazy_null_numbers: OnceCell::new(),
4754 lazy_null_booleans: OnceCell::new(),
4755 lazy_null_text: OnceCell::new(),
4756 lazy_null_errors: OnceCell::new(),
4757 lowered_text: OnceCell::new(),
4758 merged: MergedLaneCache::default(),
4759 overlay,
4760 computed_overlay,
4761 }
4762 }
4763
4764 pub fn maybe_compact_chunk(
4767 &mut self,
4768 col_idx: usize,
4769 ch_idx: usize,
4770 abs_threshold: usize,
4771 frac_den: usize,
4772 ) -> usize {
4773 if col_idx >= self.columns.len() {
4774 return 0;
4775 }
4776
4777 let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
4778 let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
4779 return 0;
4780 };
4781 let len = ch_ref.type_tag.len();
4782 if len == 0 {
4783 return 0;
4784 }
4785
4786 let ov_len = ch_ref.overlay.len();
4787 let den = frac_den.max(1);
4788 let trig = ov_len > (len / den) || ov_len > abs_threshold;
4789 if !trig {
4790 return 0;
4791 }
4792
4793 let mut tag_b = UInt8Builder::with_capacity(len);
4795 let mut nb = Float64Builder::with_capacity(len);
4796 let mut bb = BooleanBuilder::with_capacity(len);
4797 let mut sb = StringBuilder::with_capacity(len, len * 8);
4798 let mut eb = UInt8Builder::with_capacity(len);
4799 let mut non_num = 0usize;
4800 let mut non_bool = 0usize;
4801 let mut non_text = 0usize;
4802 let mut non_err = 0usize;
4803
4804 for i in 0..len {
4805 if let Some(ov) = ch_ref.overlay.get_scalar(i) {
4807 let ov = ov.to_overlay_value();
4808 append_overlay_value_to_lane_builders(
4809 &ov,
4810 &mut tag_b,
4811 &mut nb,
4812 &mut bb,
4813 &mut sb,
4814 &mut eb,
4815 &mut non_num,
4816 &mut non_bool,
4817 &mut non_text,
4818 &mut non_err,
4819 );
4820 } else {
4821 let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
4822 match tag {
4823 TypeTag::Empty => {
4824 tag_b.append_value(TypeTag::Empty as u8);
4825 nb.append_null();
4826 bb.append_null();
4827 sb.append_null();
4828 eb.append_null();
4829 }
4830 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
4831 tag_b.append_value(tag as u8);
4832 if let Some(a) = &ch_ref.numbers {
4833 let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
4834 if fa.is_null(i) {
4835 nb.append_null();
4836 } else {
4837 nb.append_value(fa.value(i));
4838 non_num += 1;
4839 }
4840 } else {
4841 nb.append_null();
4842 }
4843 bb.append_null();
4844 sb.append_null();
4845 eb.append_null();
4846 }
4847 TypeTag::Boolean => {
4848 tag_b.append_value(TypeTag::Boolean as u8);
4849 nb.append_null();
4850 if let Some(a) = &ch_ref.booleans {
4851 let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
4852 if ba.is_null(i) {
4853 bb.append_null();
4854 } else {
4855 bb.append_value(ba.value(i));
4856 non_bool += 1;
4857 }
4858 } else {
4859 bb.append_null();
4860 }
4861 sb.append_null();
4862 eb.append_null();
4863 }
4864 TypeTag::Text => {
4865 tag_b.append_value(TypeTag::Text as u8);
4866 nb.append_null();
4867 bb.append_null();
4868 if let Some(a) = &ch_ref.text {
4869 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
4870 if sa.is_null(i) {
4871 sb.append_null();
4872 } else {
4873 sb.append_value(sa.value(i));
4874 non_text += 1;
4875 }
4876 } else {
4877 sb.append_null();
4878 }
4879 eb.append_null();
4880 }
4881 TypeTag::Error => {
4882 tag_b.append_value(TypeTag::Error as u8);
4883 nb.append_null();
4884 bb.append_null();
4885 sb.append_null();
4886 if let Some(a) = &ch_ref.errors {
4887 let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
4888 if ea.is_null(i) {
4889 eb.append_null();
4890 } else {
4891 eb.append_value(ea.value(i));
4892 non_err += 1;
4893 }
4894 } else {
4895 eb.append_null();
4896 }
4897 }
4898 TypeTag::Pending => {
4899 tag_b.append_value(TypeTag::Pending as u8);
4900 nb.append_null();
4901 bb.append_null();
4902 sb.append_null();
4903 eb.append_null();
4904 }
4905 }
4906 }
4907 }
4908
4909 let tags = Arc::new(tag_b.finish());
4910 let numbers = {
4911 let a = nb.finish();
4912 if non_num == 0 {
4913 None
4914 } else {
4915 Some(Arc::new(a))
4916 }
4917 };
4918 let booleans = {
4919 let a = bb.finish();
4920 if non_bool == 0 {
4921 None
4922 } else {
4923 Some(Arc::new(a))
4924 }
4925 };
4926 let text = {
4927 let a = sb.finish();
4928 if non_text == 0 {
4929 None
4930 } else {
4931 Some(Arc::new(a) as ArrayRef)
4932 }
4933 };
4934 let errors = {
4935 let a = eb.finish();
4936 if non_err == 0 {
4937 None
4938 } else {
4939 Some(Arc::new(a))
4940 }
4941 };
4942
4943 (
4944 len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
4945 )
4946 };
4947
4948 let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
4949 return 0;
4950 };
4951
4952 ch_mut.type_tag = tags;
4953 ch_mut.numbers = numbers;
4954 ch_mut.booleans = booleans;
4955 ch_mut.text = text;
4956 ch_mut.errors = errors;
4957 let freed = ch_mut.overlay.clear();
4958 ch_mut.lowered_text = OnceCell::new();
4959 ch_mut.meta.len = len;
4960 ch_mut.meta.non_null_num = non_num;
4961 ch_mut.meta.non_null_bool = non_bool;
4962 ch_mut.meta.non_null_text = non_text;
4963 ch_mut.meta.non_null_err = non_err;
4964 freed
4965 }
4966
4967 pub fn compact_computed_overlay_chunk(&mut self, col_idx: usize, ch_idx: usize) -> usize {
4975 if col_idx >= self.columns.len() {
4976 return 0;
4977 }
4978
4979 let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
4980 let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
4981 return 0;
4982 };
4983 let len = ch_ref.type_tag.len();
4984 if len == 0 || ch_ref.computed_overlay.is_empty() {
4985 return 0;
4986 }
4987
4988 let mut tag_b = UInt8Builder::with_capacity(len);
4989 let mut nb = Float64Builder::with_capacity(len);
4990 let mut bb = BooleanBuilder::with_capacity(len);
4991 let mut sb = StringBuilder::with_capacity(len, len * 8);
4992 let mut eb = UInt8Builder::with_capacity(len);
4993 let mut non_num = 0usize;
4994 let mut non_bool = 0usize;
4995 let mut non_text = 0usize;
4996 let mut non_err = 0usize;
4997
4998 for i in 0..len {
4999 if let Some(ov) = ch_ref.computed_overlay.get_scalar(i) {
5000 let ov = ov.to_overlay_value();
5001 append_overlay_value_to_lane_builders(
5002 &ov,
5003 &mut tag_b,
5004 &mut nb,
5005 &mut bb,
5006 &mut sb,
5007 &mut eb,
5008 &mut non_num,
5009 &mut non_bool,
5010 &mut non_text,
5011 &mut non_err,
5012 );
5013 } else {
5014 let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
5015 match tag {
5016 TypeTag::Empty => {
5017 tag_b.append_value(TypeTag::Empty as u8);
5018 nb.append_null();
5019 bb.append_null();
5020 sb.append_null();
5021 eb.append_null();
5022 }
5023 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
5024 tag_b.append_value(tag as u8);
5025 if let Some(a) = &ch_ref.numbers {
5026 let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
5027 if fa.is_null(i) {
5028 nb.append_null();
5029 } else {
5030 nb.append_value(fa.value(i));
5031 non_num += 1;
5032 }
5033 } else {
5034 nb.append_null();
5035 }
5036 bb.append_null();
5037 sb.append_null();
5038 eb.append_null();
5039 }
5040 TypeTag::Boolean => {
5041 tag_b.append_value(TypeTag::Boolean as u8);
5042 nb.append_null();
5043 if let Some(a) = &ch_ref.booleans {
5044 let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
5045 if ba.is_null(i) {
5046 bb.append_null();
5047 } else {
5048 bb.append_value(ba.value(i));
5049 non_bool += 1;
5050 }
5051 } else {
5052 bb.append_null();
5053 }
5054 sb.append_null();
5055 eb.append_null();
5056 }
5057 TypeTag::Text => {
5058 tag_b.append_value(TypeTag::Text as u8);
5059 nb.append_null();
5060 bb.append_null();
5061 if let Some(a) = &ch_ref.text {
5062 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
5063 if sa.is_null(i) {
5064 sb.append_null();
5065 } else {
5066 sb.append_value(sa.value(i));
5067 non_text += 1;
5068 }
5069 } else {
5070 sb.append_null();
5071 }
5072 eb.append_null();
5073 }
5074 TypeTag::Error => {
5075 tag_b.append_value(TypeTag::Error as u8);
5076 nb.append_null();
5077 bb.append_null();
5078 sb.append_null();
5079 if let Some(a) = &ch_ref.errors {
5080 let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
5081 if ea.is_null(i) {
5082 eb.append_null();
5083 } else {
5084 eb.append_value(ea.value(i));
5085 non_err += 1;
5086 }
5087 } else {
5088 eb.append_null();
5089 }
5090 }
5091 TypeTag::Pending => {
5092 tag_b.append_value(TypeTag::Pending as u8);
5093 nb.append_null();
5094 bb.append_null();
5095 sb.append_null();
5096 eb.append_null();
5097 }
5098 }
5099 }
5100 }
5101
5102 let tags = Arc::new(tag_b.finish());
5103 let numbers = {
5104 let a = nb.finish();
5105 if non_num == 0 {
5106 None
5107 } else {
5108 Some(Arc::new(a))
5109 }
5110 };
5111 let booleans = {
5112 let a = bb.finish();
5113 if non_bool == 0 {
5114 None
5115 } else {
5116 Some(Arc::new(a))
5117 }
5118 };
5119 let text = {
5120 let a = sb.finish();
5121 if non_text == 0 {
5122 None
5123 } else {
5124 Some(Arc::new(a) as ArrayRef)
5125 }
5126 };
5127 let errors = {
5128 let a = eb.finish();
5129 if non_err == 0 {
5130 None
5131 } else {
5132 Some(Arc::new(a))
5133 }
5134 };
5135
5136 (
5137 len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
5138 )
5139 };
5140
5141 let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
5142 return 0;
5143 };
5144
5145 ch_mut.type_tag = tags;
5146 ch_mut.numbers = numbers;
5147 ch_mut.booleans = booleans;
5148 ch_mut.text = text;
5149 ch_mut.errors = errors;
5150 let freed = ch_mut.computed_overlay.clear();
5151 ch_mut.lowered_text = OnceCell::new();
5152 ch_mut.meta.len = len;
5153 ch_mut.meta.non_null_num = non_num;
5154 ch_mut.meta.non_null_bool = non_bool;
5155 ch_mut.meta.non_null_text = non_text;
5156 ch_mut.meta.non_null_err = non_err;
5157 freed
5158 }
5159
5160 pub fn compact_computed_overlay_sparse_chunk(
5163 &mut self,
5164 col_idx: usize,
5165 ch_idx: usize,
5166 ) -> usize {
5167 self.compact_computed_overlay_chunk(col_idx, ch_idx)
5170 }
5171
5172 pub fn insert_rows(&mut self, before: usize, count: usize) {
5174 if count == 0 {
5175 return;
5176 }
5177
5178 let total_rows = self.nrows as usize;
5179 if total_rows == 0 {
5180 self.nrows = count as u32;
5181 if self.nrows > 0 && self.chunk_starts.is_empty() {
5182 self.chunk_starts.push(0);
5183 }
5184 return;
5185 }
5186
5187 if self.chunk_starts.is_empty() {
5189 self.chunk_starts.push(0);
5190 }
5191
5192 let dense_aligned = self
5194 .columns
5195 .iter()
5196 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5197
5198 let insert_at = before.min(total_rows);
5199 let (split_idx, split_off) = if insert_at == total_rows {
5200 let last_idx = self.chunk_starts.len() - 1;
5202 let last_start = self.chunk_starts[last_idx];
5203 let last_len = total_rows.saturating_sub(last_start);
5204 (last_idx, last_len)
5205 } else {
5206 self.chunk_of_row(insert_at).unwrap_or((0, 0))
5207 };
5208
5209 if dense_aligned {
5210 for col in &mut self.columns {
5212 let mut new_chunks: Vec<ColumnChunk> = Vec::with_capacity(col.chunks.len() + 2);
5213 for i in 0..col.chunks.len() {
5214 if i != split_idx {
5215 new_chunks.push(col.chunks[i].clone());
5216 } else {
5217 let orig = &col.chunks[i];
5218 let len = orig.type_tag.len();
5219 if split_off > 0 {
5220 new_chunks.push(Self::slice_chunk(orig, 0, split_off));
5221 }
5222 new_chunks.push(Self::make_empty_chunk(count));
5223 if split_off < len {
5224 new_chunks.push(Self::slice_chunk(orig, split_off, len - split_off));
5225 }
5226 }
5227 }
5228 col.chunks = new_chunks;
5229 col.sparse_chunks.clear();
5230 }
5231 self.nrows = (total_rows + count) as u32;
5232 self.recompute_chunk_starts();
5233 return;
5234 }
5235
5236 #[derive(Clone, Copy)]
5238 enum PlanItem {
5239 Slice {
5240 old_idx: usize,
5241 off: usize,
5242 len: usize,
5243 },
5244 Empty {
5245 len: usize,
5246 },
5247 }
5248
5249 let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len() + 2);
5250 for old_idx in 0..self.chunk_starts.len() {
5251 let ch_start = self.chunk_starts[old_idx];
5252 let ch_end = self
5253 .chunk_starts
5254 .get(old_idx + 1)
5255 .copied()
5256 .unwrap_or(total_rows);
5257 let ch_len = ch_end.saturating_sub(ch_start);
5258 if ch_len == 0 {
5259 continue;
5260 }
5261
5262 if old_idx != split_idx {
5263 plan.push(PlanItem::Slice {
5264 old_idx,
5265 off: 0,
5266 len: ch_len,
5267 });
5268 continue;
5269 }
5270
5271 let left_len = split_off.min(ch_len);
5272 let right_len = ch_len.saturating_sub(left_len);
5273 if left_len > 0 {
5274 plan.push(PlanItem::Slice {
5275 old_idx,
5276 off: 0,
5277 len: left_len,
5278 });
5279 }
5280 plan.push(PlanItem::Empty { len: count });
5281 if right_len > 0 {
5282 plan.push(PlanItem::Slice {
5283 old_idx,
5284 off: left_len,
5285 len: right_len,
5286 });
5287 }
5288 }
5289
5290 let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
5291 let mut cur = 0usize;
5292 for item in &plan {
5293 let len = match *item {
5294 PlanItem::Slice { len, .. } => len,
5295 PlanItem::Empty { len } => len,
5296 };
5297 if len == 0 {
5298 continue;
5299 }
5300 new_starts.push(cur);
5301 cur = cur.saturating_add(len);
5302 }
5303
5304 debug_assert_eq!(cur, total_rows.saturating_add(count));
5305
5306 self.nrows = (total_rows + count) as u32;
5308 self.chunk_starts = new_starts;
5309
5310 for col in &mut self.columns {
5312 let old_dense = std::mem::take(&mut col.chunks);
5313 let old_sparse = std::mem::take(&mut col.sparse_chunks);
5314 let get_old = |idx: usize| -> Option<&ColumnChunk> {
5315 if idx < old_dense.len() {
5316 Some(&old_dense[idx])
5317 } else {
5318 old_sparse.get(&idx)
5319 }
5320 };
5321
5322 let mut dense: Vec<ColumnChunk> = Vec::new();
5323 let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
5324 let mut dense_prefix = true;
5325
5326 for (new_idx, item) in plan.iter().enumerate() {
5327 let produced: Option<ColumnChunk> = match *item {
5328 PlanItem::Empty { .. } => None,
5329 PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
5330 Some(orig) => {
5331 if off == 0 && len == orig.type_tag.len() {
5332 Some(orig.clone())
5333 } else {
5334 Some(Self::slice_chunk(orig, off, len))
5335 }
5336 }
5337 None => None,
5338 },
5339 };
5340
5341 if let Some(ch) = produced {
5342 if dense_prefix && new_idx == dense.len() {
5343 dense.push(ch);
5344 } else {
5345 sparse.insert(new_idx, ch);
5346 dense_prefix = false;
5347 }
5348 } else if dense_prefix && new_idx == dense.len() {
5349 dense_prefix = false;
5350 }
5351 }
5352
5353 col.chunks = dense;
5354 col.sparse_chunks = sparse;
5355 }
5356 }
5357
5358 pub fn delete_rows(&mut self, start: usize, count: usize) {
5360 if count == 0 || self.nrows == 0 {
5361 return;
5362 }
5363
5364 let total_rows = self.nrows as usize;
5365 if start >= total_rows {
5366 return;
5367 }
5368 let end = (start + count).min(total_rows);
5369 let del_len = end.saturating_sub(start);
5370 if del_len == 0 {
5371 return;
5372 }
5373
5374 if total_rows > 0 && self.chunk_starts.is_empty() {
5376 self.chunk_starts.push(0);
5377 }
5378
5379 let dense_aligned = self
5381 .columns
5382 .iter()
5383 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5384
5385 if dense_aligned {
5386 for col in &mut self.columns {
5388 let mut new_chunks: Vec<ColumnChunk> = Vec::new();
5389 let mut cur_start = 0usize;
5390 for ch in &col.chunks {
5391 let len = ch.type_tag.len();
5392 let ch_end = cur_start + len;
5393 if ch_end <= start || cur_start >= end {
5395 new_chunks.push(ch.clone());
5396 } else {
5397 let del_start = start.max(cur_start);
5399 let del_end = end.min(ch_end);
5400 let left_len = del_start.saturating_sub(cur_start);
5401 let right_len = ch_end.saturating_sub(del_end);
5402 if left_len > 0 {
5403 new_chunks.push(Self::slice_chunk(ch, 0, left_len));
5404 }
5405 if right_len > 0 {
5406 let off = len - right_len;
5407 new_chunks.push(Self::slice_chunk(ch, off, right_len));
5408 }
5409 }
5410 cur_start = ch_end;
5411 }
5412 col.chunks = new_chunks;
5413 col.sparse_chunks.clear();
5414 }
5415 self.nrows = (total_rows - del_len) as u32;
5416 self.recompute_chunk_starts();
5417 return;
5418 }
5419
5420 #[derive(Clone, Copy)]
5422 enum PlanItem {
5423 Slice {
5424 old_idx: usize,
5425 off: usize,
5426 len: usize,
5427 },
5428 }
5429
5430 let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len());
5431 for old_idx in 0..self.chunk_starts.len() {
5432 let ch_start = self.chunk_starts[old_idx];
5433 let ch_end = self
5434 .chunk_starts
5435 .get(old_idx + 1)
5436 .copied()
5437 .unwrap_or(total_rows);
5438 let ch_len = ch_end.saturating_sub(ch_start);
5439 if ch_len == 0 {
5440 continue;
5441 }
5442
5443 if ch_end <= start || ch_start >= end {
5445 plan.push(PlanItem::Slice {
5446 old_idx,
5447 off: 0,
5448 len: ch_len,
5449 });
5450 continue;
5451 }
5452
5453 if start > ch_start {
5455 let left_end = start.min(ch_end);
5456 let left_len = left_end.saturating_sub(ch_start);
5457 if left_len > 0 {
5458 plan.push(PlanItem::Slice {
5459 old_idx,
5460 off: 0,
5461 len: left_len,
5462 });
5463 }
5464 }
5465
5466 if end < ch_end {
5468 let right_off = end.saturating_sub(ch_start);
5469 let right_len = ch_end.saturating_sub(end);
5470 if right_len > 0 {
5471 plan.push(PlanItem::Slice {
5472 old_idx,
5473 off: right_off,
5474 len: right_len,
5475 });
5476 }
5477 }
5478 }
5479
5480 let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
5481 let mut cur = 0usize;
5482 for item in &plan {
5483 let len = match *item {
5484 PlanItem::Slice { len, .. } => len,
5485 };
5486 if len == 0 {
5487 continue;
5488 }
5489 new_starts.push(cur);
5490 cur = cur.saturating_add(len);
5491 }
5492
5493 debug_assert_eq!(cur, total_rows.saturating_sub(del_len));
5494
5495 self.nrows = (total_rows - del_len) as u32;
5497 self.chunk_starts = new_starts;
5498
5499 for col in &mut self.columns {
5501 let old_dense = std::mem::take(&mut col.chunks);
5502 let old_sparse = std::mem::take(&mut col.sparse_chunks);
5503 let get_old = |idx: usize| -> Option<&ColumnChunk> {
5504 if idx < old_dense.len() {
5505 Some(&old_dense[idx])
5506 } else {
5507 old_sparse.get(&idx)
5508 }
5509 };
5510
5511 let mut dense: Vec<ColumnChunk> = Vec::new();
5512 let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
5513 let mut dense_prefix = true;
5514
5515 for (new_idx, item) in plan.iter().enumerate() {
5516 let produced: Option<ColumnChunk> = match *item {
5517 PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
5518 Some(orig) => {
5519 if off == 0 && len == orig.type_tag.len() {
5520 Some(orig.clone())
5521 } else {
5522 Some(Self::slice_chunk(orig, off, len))
5523 }
5524 }
5525 None => None,
5526 },
5527 };
5528
5529 if let Some(ch) = produced {
5530 if dense_prefix && new_idx == dense.len() {
5531 dense.push(ch);
5532 } else {
5533 sparse.insert(new_idx, ch);
5534 dense_prefix = false;
5535 }
5536 } else if dense_prefix && new_idx == dense.len() {
5537 dense_prefix = false;
5538 }
5539 }
5540
5541 col.chunks = dense;
5542 col.sparse_chunks = sparse;
5543 }
5544 }
5545
5546 pub fn insert_columns(&mut self, before: usize, count: usize) {
5548 if count == 0 {
5549 return;
5550 }
5551 let empty_col = |lens: &[usize]| -> ArrowColumn {
5553 let mut chunks = Vec::with_capacity(lens.len());
5554 for &l in lens {
5555 chunks.push(Self::make_empty_chunk(l));
5556 }
5557 ArrowColumn {
5558 chunks,
5559 sparse_chunks: FxHashMap::default(),
5560 index: 0,
5561 }
5562 };
5563 let dense_aligned = !self.columns.is_empty()
5564 && self
5565 .columns
5566 .iter()
5567 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5568
5569 let lens: Vec<usize> = if dense_aligned {
5570 self.columns[0]
5571 .chunks
5572 .iter()
5573 .map(|c| c.type_tag.len())
5574 .collect()
5575 } else if self.columns.is_empty() {
5576 if self.nrows > 0 {
5578 vec![self.nrows as usize]
5579 } else {
5580 Vec::new()
5581 }
5582 } else {
5583 Vec::new()
5585 };
5586 let mut cols_new: Vec<ArrowColumn> = Vec::with_capacity(self.columns.len() + count);
5587 let before_idx = before.min(self.columns.len());
5588 for (i, col) in self.columns.iter_mut().enumerate() {
5589 if i == before_idx {
5590 for _ in 0..count {
5591 cols_new.push(empty_col(&lens));
5592 }
5593 }
5594 cols_new.push(col.clone());
5595 }
5596 if before_idx == self.columns.len() {
5597 for _ in 0..count {
5598 cols_new.push(empty_col(&lens));
5599 }
5600 }
5601 for (idx, col) in cols_new.iter_mut().enumerate() {
5603 col.index = idx as u32;
5604 }
5605 self.columns = cols_new;
5606 }
5608
5609 pub fn delete_columns(&mut self, start: usize, count: usize) {
5611 if count == 0 || self.columns.is_empty() {
5612 return;
5613 }
5614 let end = (start + count).min(self.columns.len());
5615 if start >= end {
5616 return;
5617 }
5618 self.columns.drain(start..end);
5619 for (idx, col) in self.columns.iter_mut().enumerate() {
5620 col.index = idx as u32;
5621 }
5622 }
5623}
5624
5625#[derive(Debug, Clone, Copy)]
5626pub struct ColumnShape {
5627 pub index: u32,
5628 pub chunks: usize,
5629 pub rows: usize,
5630 pub has_num: bool,
5631 pub has_bool: bool,
5632 pub has_text: bool,
5633 pub has_err: bool,
5634}
5635
5636#[cfg(test)]
5637mod tests {
5638 use super::*;
5639
5640 #[test]
5644 fn ingest_builder_chunk_boundaries_and_lane_transitions_are_preserved() {
5645 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5646 let value_at = |i: usize| match i % 7 {
5647 0 | 1 if i < 8 => LiteralValue::Number(i as f64),
5648 0 => LiteralValue::Text(format!("t{i}")),
5649 1 => LiteralValue::Boolean(i.is_multiple_of(2)),
5650 2 => LiteralValue::Empty,
5651 3 => LiteralValue::Error(ExcelError::new(ExcelErrorKind::Div)),
5652 4 => LiteralValue::Date(date),
5653 5 => LiteralValue::Pending,
5654 _ => LiteralValue::Int(i as i64),
5655 };
5656 fn cell_at(value: LiteralValue, i: usize, text: &str) -> CellIngest<'_> {
5658 match value {
5659 LiteralValue::Number(n) => CellIngest::Number(n),
5660 LiteralValue::Text(_) => CellIngest::Text(text),
5661 LiteralValue::Boolean(b) => CellIngest::Boolean(b),
5662 LiteralValue::Empty => CellIngest::Empty,
5663 LiteralValue::Error(e) => CellIngest::ErrorCode(map_error_code(e.kind)),
5664 LiteralValue::Date(_) => CellIngest::DateSerial(i as f64 + 45_000.0),
5665 LiteralValue::Pending => CellIngest::Pending,
5666 LiteralValue::Int(n) => CellIngest::Number(n as f64),
5667 other => unreachable!("{other:?}"),
5668 }
5669 }
5670 let cases = [(0usize, 4usize), (4, 4), (8, 4), (9, 4), (3, 4), (23, 5)];
5671 for ((rows, chunk_rows), path) in cases
5672 .into_iter()
5673 .flat_map(|case| ["row", "cells", "cells_iter"].map(|path| (case, path)))
5674 {
5675 let mut ingest =
5676 IngestBuilder::new("S", 2, chunk_rows, crate::engine::DateSystem::Excel1900);
5677 for i in 0..rows {
5678 let text = format!("t{i}");
5679 let number = CellIngest::Number(i as f64);
5680 match path {
5681 "row" => ingest
5682 .append_row(&[value_at(i), LiteralValue::Number(i as f64)])
5683 .unwrap(),
5684 "cells" => ingest
5685 .append_row_cells(&[cell_at(value_at(i), i, &text), number])
5686 .unwrap(),
5687 _ => ingest
5688 .append_row_cells_iter([cell_at(value_at(i), i, &text), number].into_iter())
5689 .unwrap(),
5690 }
5691 }
5692 let sheet = ingest.finish();
5693 let expected_chunks = rows.div_ceil(chunk_rows);
5694 assert_eq!(sheet.nrows as usize, rows);
5695 assert_eq!(sheet.chunk_starts.len(), expected_chunks);
5696 for (ci, col) in sheet.columns.iter().enumerate() {
5697 assert_eq!(col.chunks.len(), expected_chunks, "col {ci} rows {rows}");
5698 for (k, chunk) in col.chunks.iter().enumerate() {
5699 let start = k * chunk_rows;
5700 let len = chunk_rows.min(rows - start);
5701 assert_eq!(chunk.meta.len, len);
5702 assert_eq!(chunk.type_tag.len(), len);
5703 for lane_len in [
5704 chunk.numbers.as_ref().map(|a| a.len()),
5705 chunk.booleans.as_ref().map(|a| a.len()),
5706 chunk.text.as_ref().map(|a| a.len()),
5707 chunk.errors.as_ref().map(|a| a.len()),
5708 ]
5709 .into_iter()
5710 .flatten()
5711 {
5712 assert_eq!(lane_len, len, "materialized lanes span the chunk");
5713 }
5714 assert_eq!(chunk.text.is_some(), chunk.meta.non_null_text > 0);
5715 assert_eq!(chunk.numbers.is_some(), chunk.meta.non_null_num > 0);
5716 }
5717 }
5718 for i in 0..rows {
5719 let got = sheet.get_cell_value(i, 0);
5720 match value_at(i) {
5721 LiteralValue::Date(_) => {
5723 assert_eq!(sheet.format_id(i, 0), Some(FormatId::DATE))
5724 }
5725 LiteralValue::Error(e) => {
5726 assert!(matches!(got, LiteralValue::Error(g) if g.kind == e.kind))
5727 }
5728 LiteralValue::Int(n) => assert_eq!(got, LiteralValue::Number(n as f64)),
5729 expected => assert_eq!(
5730 expected, got,
5731 "{path} row {i} rows {rows} chunk {chunk_rows}"
5732 ),
5733 }
5734 assert_eq!(sheet.get_cell_value(i, 1), LiteralValue::Number(i as f64));
5735 }
5736 }
5737 }
5738
5739 #[test]
5740 fn explicit_format_precedence_and_general_filter_are_stable() {
5741 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5742 let mut ingest = IngestBuilder::new("Sheet1", 1, 16, crate::engine::DateSystem::Excel1900);
5743 ingest.append_row(&[LiteralValue::Date(date)]).unwrap();
5744 ingest.append_row(&[LiteralValue::Number(1.0)]).unwrap();
5745 let mut sheet = ingest.finish();
5746
5747 let chunk = sheet.columns[0].chunk_mut(0).unwrap();
5748 chunk.computed_overlay.set_format(0, Some(FormatId::TIME));
5749 assert_eq!(
5750 sheet.format_id(0, 0),
5751 Some(FormatId::DATE),
5752 "base explicit format must beat the derived overlay"
5753 );
5754
5755 sheet.columns[0]
5756 .chunk_mut(0)
5757 .unwrap()
5758 .overlay
5759 .set_format(0, Some(FormatId::DATETIME));
5760 assert_eq!(
5761 sheet.format_id(0, 0),
5762 Some(FormatId::DATETIME),
5763 "user explicit overlay must beat base and derived formats"
5764 );
5765 assert_eq!(
5766 sheet.format_id(1, 0),
5767 None,
5768 "General is absence, not an effective explicit format"
5769 );
5770 sheet.ensure_row_capacity(3);
5771 assert_eq!(
5772 sheet.format_id(2, 0),
5773 None,
5774 "growing a formatted chunk must fill new rows with General"
5775 );
5776 }
5777
5778 #[test]
5779 fn known_error_storage_codes_are_stable() {
5780 let cases = [
5781 (ExcelErrorKind::Null, 1),
5782 (ExcelErrorKind::Ref, 2),
5783 (ExcelErrorKind::Name, 3),
5784 (ExcelErrorKind::Value, 4),
5785 (ExcelErrorKind::Div, 5),
5786 (ExcelErrorKind::Na, 6),
5787 (ExcelErrorKind::Num, 7),
5788 (ExcelErrorKind::Error, 8),
5789 (ExcelErrorKind::NImpl, 9),
5790 (ExcelErrorKind::Spill, 10),
5791 (ExcelErrorKind::Calc, 11),
5792 (ExcelErrorKind::Circ, 12),
5793 (ExcelErrorKind::Cancelled, 13),
5794 ];
5795 for (kind, code) in cases {
5796 assert_eq!(map_error_code(kind), code, "{kind:?}");
5797 assert_eq!(unmap_error_code(code), kind, "code {code}");
5798 }
5799 assert_eq!(unmap_error_code(u8::MAX), ExcelErrorKind::Error);
5800 }
5801 use arrow_array::Array;
5802 use arrow_schema::DataType;
5803 use chrono::{Datelike, Timelike};
5804
5805 #[test]
5806 fn sparse_constructor_defaults_to_excel_1900_and_decodes_excel_1904() {
5807 let date = chrono::NaiveDate::from_ymd_opt(1904, 1, 1).unwrap();
5808 let _datetime = date.and_hms_opt(12, 0, 0).unwrap();
5809
5810 let mut default_sheet = ArrowSheet::new_sparse("Default", 1, 1, 16);
5811 assert_eq!(
5812 default_sheet.date_system,
5813 crate::engine::DateSystem::Excel1900
5814 );
5815 default_sheet.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(1462.5));
5816 assert_eq!(
5817 default_sheet.get_cell_value(0, 0),
5818 LiteralValue::Number(1462.5)
5819 );
5820
5821 let mut excel_1904 = ArrowSheet::new_sparse_with_date_system(
5822 "1904",
5823 1,
5824 1,
5825 16,
5826 crate::engine::DateSystem::Excel1904,
5827 );
5828 assert_eq!(excel_1904.date_system, crate::engine::DateSystem::Excel1904);
5829 excel_1904.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(0.5));
5830 assert_eq!(excel_1904.get_cell_value(0, 0), LiteralValue::Number(0.5));
5831 }
5832
5833 #[test]
5834 fn datetime_lanes_round_trip_the_sheet_date_system() {
5835 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5836 let datetime = date.and_hms_opt(12, 30, 0).unwrap();
5837 let time = chrono::NaiveTime::from_hms_opt(12, 30, 0).unwrap();
5838
5839 for system in [
5840 crate::engine::DateSystem::Excel1900,
5841 crate::engine::DateSystem::Excel1904,
5842 ] {
5843 let values = vec![
5844 LiteralValue::Date(date),
5845 LiteralValue::DateTime(datetime),
5846 LiteralValue::Time(time),
5847 ];
5848 let mut ingest = IngestBuilder::new("Sheet1", 3, 16, system);
5849 ingest.append_row(&values).unwrap();
5850 let sheet = ingest.finish();
5851
5852 assert_eq!(sheet.date_system, system);
5853 let date_serial = formualizer_common::date_to_serial_for(system, &date);
5854 let datetime_serial = formualizer_common::datetime_to_serial_for(system, &datetime);
5855 assert_eq!(
5856 sheet.get_cell_value(0, 0),
5857 LiteralValue::Number(date_serial)
5858 );
5859 assert_eq!(
5860 sheet.get_cell_value(0, 1),
5861 LiteralValue::Number(datetime_serial)
5862 );
5863 assert_eq!(sheet.format_id(0, 0), Some(FormatId::DATE));
5864 assert_eq!(sheet.format_id(0, 1), Some(FormatId::DATETIME));
5865 assert_eq!(sheet.format_id(0, 2), Some(FormatId::TIME));
5866 let view = sheet.range_view(0, 0, 0, 1);
5867 assert_eq!(view.get_cell(0, 0), LiteralValue::Number(date_serial));
5868 assert_eq!(view.get_cell(0, 1), LiteralValue::Number(datetime_serial));
5869
5870 let mut sparse = ArrowSheet::new_sparse_with_date_system("Sparse", 1, 1, 16, system);
5871 sparse.set_sparse_overlay_value(
5872 0,
5873 0,
5874 OverlayValue::from_literal_value(&values[1], system),
5875 );
5876 assert_eq!(
5877 sparse.get_cell_value(0, 0),
5878 LiteralValue::Number(datetime_serial)
5879 );
5880 }
5881 }
5882
5883 fn add_overlay_stats(into: &mut OverlayDebugStats, next: OverlayDebugStats) {
5884 into.points += next.points;
5885 into.sparse_fragments += next.sparse_fragments;
5886 into.dense_fragments += next.dense_fragments;
5887 into.run_fragments += next.run_fragments;
5888 into.covered_len += next.covered_len;
5889 }
5890
5891 fn column_overlay_stats(
5892 sheet: &ArrowSheet,
5893 col_idx: usize,
5894 computed: bool,
5895 ) -> OverlayDebugStats {
5896 let mut stats = OverlayDebugStats::default();
5897 let Some(column) = sheet.columns.get(col_idx) else {
5898 return stats;
5899 };
5900 for chunk in &column.chunks {
5901 add_overlay_stats(
5902 &mut stats,
5903 if computed {
5904 chunk.computed_overlay.debug_stats()
5905 } else {
5906 chunk.overlay.debug_stats()
5907 },
5908 );
5909 }
5910 for chunk in column.sparse_chunks.values() {
5911 add_overlay_stats(
5912 &mut stats,
5913 if computed {
5914 chunk.computed_overlay.debug_stats()
5915 } else {
5916 chunk.overlay.debug_stats()
5917 },
5918 );
5919 }
5920 stats
5921 }
5922
5923 fn assert_column_overlays_normalized(sheet: &ArrowSheet, col_idx: usize) {
5924 let column = &sheet.columns[col_idx];
5925 for chunk in &column.chunks {
5926 assert!(chunk.overlay.debug_is_normalized());
5927 assert!(chunk.computed_overlay.debug_is_normalized());
5928 assert_eq!(
5929 chunk.overlay.estimated_bytes(),
5930 chunk.overlay.debug_recomputed_estimated_bytes()
5931 );
5932 assert_eq!(
5933 chunk.computed_overlay.estimated_bytes(),
5934 chunk.computed_overlay.debug_recomputed_estimated_bytes()
5935 );
5936 }
5937 for chunk in column.sparse_chunks.values() {
5938 assert!(chunk.overlay.debug_is_normalized());
5939 assert!(chunk.computed_overlay.debug_is_normalized());
5940 assert_eq!(
5941 chunk.overlay.estimated_bytes(),
5942 chunk.overlay.debug_recomputed_estimated_bytes()
5943 );
5944 assert_eq!(
5945 chunk.computed_overlay.estimated_bytes(),
5946 chunk.computed_overlay.debug_recomputed_estimated_bytes()
5947 );
5948 }
5949 }
5950
5951 fn column_computed_overlay_estimated_bytes(sheet: &ArrowSheet, col_idx: usize) -> usize {
5952 let Some(column) = sheet.columns.get(col_idx) else {
5953 return 0;
5954 };
5955 column
5956 .chunks
5957 .iter()
5958 .map(|chunk| chunk.computed_overlay.estimated_bytes())
5959 .chain(
5960 column
5961 .sparse_chunks
5962 .values()
5963 .map(|chunk| chunk.computed_overlay.estimated_bytes()),
5964 )
5965 .fold(0usize, usize::saturating_add)
5966 }
5967
5968 #[derive(Debug, Clone, Copy)]
5969 enum Phase4ProbeFixture {
5970 PointNumeric,
5971 DenseNumeric,
5972 RunNumeric,
5973 SparseNumeric,
5974 EmptyRun,
5975 MixedDense,
5976 }
5977
5978 impl Phase4ProbeFixture {
5979 fn name(self) -> &'static str {
5980 match self {
5981 Phase4ProbeFixture::PointNumeric => "point_numeric",
5982 Phase4ProbeFixture::DenseNumeric => "dense_numeric",
5983 Phase4ProbeFixture::RunNumeric => "run_numeric",
5984 Phase4ProbeFixture::SparseNumeric => "sparse_numeric",
5985 Phase4ProbeFixture::EmptyRun => "empty_run",
5986 Phase4ProbeFixture::MixedDense => "mixed_dense",
5987 }
5988 }
5989 }
5990
5991 #[derive(Debug, serde::Serialize)]
5992 struct Phase4ProbeOp {
5993 ms: f64,
5994 segments: usize,
5995 arrays: usize,
5996 rows_scanned: usize,
5997 checksum: f64,
5998 non_null: usize,
5999 }
6000
6001 #[derive(Debug, serde::Serialize)]
6002 struct Phase4ProbeRow {
6003 fixture: &'static str,
6004 rows: usize,
6005 points: usize,
6006 sparse_fragments: usize,
6007 dense_fragments: usize,
6008 run_fragments: usize,
6009 covered_len: usize,
6010 overlay_estimated_bytes: usize,
6011 numbers: Phase4ProbeOp,
6012 type_tags: Phase4ProbeOp,
6013 lowered_text: Phase4ProbeOp,
6014 get_cell_scan: Phase4ProbeOp,
6015 select_stats: OverlaySelectStats,
6016 }
6017
6018 fn build_phase4_probe_sheet(rows: usize, fixture: Phase4ProbeFixture) -> ArrowSheet {
6019 let mut builder =
6020 IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
6021 for row in 0..rows {
6022 builder
6023 .append_row(&[LiteralValue::Number((row + 1) as f64)])
6024 .unwrap();
6025 }
6026 let mut sheet = builder.finish();
6027 let chunk = sheet.columns[0].chunk_mut(0).unwrap();
6028 match fixture {
6029 Phase4ProbeFixture::PointNumeric => {
6030 for row in 0..rows {
6031 chunk
6032 .computed_overlay
6033 .set_scalar(row, OverlayValue::Number((row + 1) as f64));
6034 }
6035 }
6036 Phase4ProbeFixture::DenseNumeric => {
6037 chunk.computed_overlay.apply_fragment(
6038 OverlayFragment::dense_range(
6039 0,
6040 (0..rows)
6041 .map(|row| OverlayValue::Number((row + 1) as f64))
6042 .collect(),
6043 )
6044 .unwrap(),
6045 );
6046 }
6047 Phase4ProbeFixture::RunNumeric => {
6048 chunk.computed_overlay.apply_fragment(
6049 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); rows]).unwrap(),
6050 );
6051 }
6052 Phase4ProbeFixture::SparseNumeric => {
6053 chunk.computed_overlay.apply_fragment(
6054 OverlayFragment::sparse_offsets(
6055 (0..rows)
6056 .step_by(10)
6057 .map(|row| (row, OverlayValue::Number(10.0)))
6058 .collect(),
6059 )
6060 .unwrap(),
6061 );
6062 }
6063 Phase4ProbeFixture::EmptyRun => {
6064 chunk.computed_overlay.apply_fragment(
6065 OverlayFragment::run_range(0, vec![OverlayValue::Empty; rows]).unwrap(),
6066 );
6067 }
6068 Phase4ProbeFixture::MixedDense => {
6069 let pattern = [
6070 OverlayValue::Number(1.0),
6071 OverlayValue::Boolean(true),
6072 OverlayValue::Text(Arc::from("Alpha")),
6073 OverlayValue::Empty,
6074 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
6075 OverlayValue::Pending,
6076 OverlayValue::DateTime(45000.25),
6077 OverlayValue::Duration(0.5),
6078 ];
6079 chunk.computed_overlay.apply_fragment(
6080 OverlayFragment::dense_range(
6081 0,
6082 (0..rows)
6083 .map(|row| pattern[row % pattern.len()].clone())
6084 .collect(),
6085 )
6086 .unwrap(),
6087 );
6088 }
6089 }
6090 sheet
6091 }
6092
6093 fn measure_probe_numbers(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6094 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6095 let start = std::time::Instant::now();
6096 let mut segments = 0usize;
6097 let mut arrays = 0usize;
6098 let mut rows_scanned = 0usize;
6099 let mut checksum = 0.0;
6100 let mut non_null = 0usize;
6101 for segment in view.numbers_slices() {
6102 let (_row_start, row_len, cols) = segment.unwrap();
6103 segments += 1;
6104 rows_scanned += row_len;
6105 for array in cols {
6106 arrays += 1;
6107 for idx in 0..array.len() {
6108 if array.is_valid(idx) {
6109 checksum += array.value(idx);
6110 non_null += 1;
6111 }
6112 }
6113 }
6114 }
6115 Phase4ProbeOp {
6116 ms: start.elapsed().as_secs_f64() * 1000.0,
6117 segments,
6118 arrays,
6119 rows_scanned,
6120 checksum,
6121 non_null,
6122 }
6123 }
6124
6125 fn measure_probe_type_tags(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6126 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6127 let start = std::time::Instant::now();
6128 let mut segments = 0usize;
6129 let mut arrays = 0usize;
6130 let mut rows_scanned = 0usize;
6131 let mut checksum = 0.0;
6132 let mut non_null = 0usize;
6133 for segment in view.type_tags_slices() {
6134 let (_row_start, row_len, cols) = segment.unwrap();
6135 segments += 1;
6136 rows_scanned += row_len;
6137 for array in cols {
6138 arrays += 1;
6139 for idx in 0..array.len() {
6140 if array.is_valid(idx) {
6141 checksum += array.value(idx) as f64;
6142 non_null += 1;
6143 }
6144 }
6145 }
6146 }
6147 Phase4ProbeOp {
6148 ms: start.elapsed().as_secs_f64() * 1000.0,
6149 segments,
6150 arrays,
6151 rows_scanned,
6152 checksum,
6153 non_null,
6154 }
6155 }
6156
6157 fn measure_probe_lowered_text(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6158 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6159 let start = std::time::Instant::now();
6160 let mut segments = 0usize;
6161 let mut arrays = 0usize;
6162 let mut rows_scanned = 0usize;
6163 let mut checksum = 0.0;
6164 let mut non_null = 0usize;
6165 for segment in view.lowered_text_slices() {
6166 let (_row_start, row_len, cols) = segment.unwrap();
6167 segments += 1;
6168 rows_scanned += row_len;
6169 for array in cols {
6170 arrays += 1;
6171 for idx in 0..array.len() {
6172 if array.is_valid(idx) {
6173 checksum += array.value(idx).len() as f64;
6174 non_null += 1;
6175 }
6176 }
6177 }
6178 }
6179 Phase4ProbeOp {
6180 ms: start.elapsed().as_secs_f64() * 1000.0,
6181 segments,
6182 arrays,
6183 rows_scanned,
6184 checksum,
6185 non_null,
6186 }
6187 }
6188
6189 fn literal_probe_weight(value: LiteralValue) -> f64 {
6190 match value {
6191 LiteralValue::Empty => 0.0,
6192 LiteralValue::Int(value) => value as f64,
6193 LiteralValue::Number(value) => value,
6194 LiteralValue::Boolean(value) => {
6195 if value {
6196 1.0
6197 } else {
6198 0.0
6199 }
6200 }
6201 LiteralValue::Text(value) => value.len() as f64,
6202 LiteralValue::Error(_) => -1.0,
6203 LiteralValue::Date(value) => value.num_days_from_ce() as f64,
6204 LiteralValue::DateTime(value) => value.and_utc().timestamp() as f64,
6205 LiteralValue::Time(value) => value.num_seconds_from_midnight() as f64,
6206 LiteralValue::Duration(value) => value.num_seconds() as f64,
6207 LiteralValue::Array(values) => values.len() as f64,
6208 LiteralValue::Pending => -2.0,
6209 }
6210 }
6211
6212 fn measure_probe_get_cell(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6213 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6214 let start = std::time::Instant::now();
6215 let mut checksum = 0.0;
6216 for row in 0..rows {
6217 checksum += literal_probe_weight(view.get_cell(row, 0));
6218 }
6219 Phase4ProbeOp {
6220 ms: start.elapsed().as_secs_f64() * 1000.0,
6221 segments: 1,
6222 arrays: 0,
6223 rows_scanned: rows,
6224 checksum,
6225 non_null: rows,
6226 }
6227 }
6228
6229 fn run_phase4_probe_fixture(rows: usize, fixture: Phase4ProbeFixture) -> Phase4ProbeRow {
6230 let sheet = build_phase4_probe_sheet(rows, fixture);
6231 assert_column_overlays_normalized(&sheet, 0);
6232 let stats = column_overlay_stats(&sheet, 0, true);
6233 reset_overlay_select_stats();
6234 let numbers = measure_probe_numbers(&sheet, rows);
6235 let type_tags = measure_probe_type_tags(&sheet, rows);
6236 let lowered_text = measure_probe_lowered_text(&sheet, rows);
6237 let select_stats = snapshot_overlay_select_stats();
6238 let get_cell_scan = measure_probe_get_cell(&sheet, rows);
6239 Phase4ProbeRow {
6240 fixture: fixture.name(),
6241 rows,
6242 points: stats.points,
6243 sparse_fragments: stats.sparse_fragments,
6244 dense_fragments: stats.dense_fragments,
6245 run_fragments: stats.run_fragments,
6246 covered_len: stats.covered_len,
6247 overlay_estimated_bytes: column_computed_overlay_estimated_bytes(&sheet, 0),
6248 numbers,
6249 type_tags,
6250 lowered_text,
6251 get_cell_scan,
6252 select_stats,
6253 }
6254 }
6255
6256 #[test]
6257 #[ignore = "manual Phase 4 observability probe; run with --ignored --nocapture"]
6258 fn phase4_overlay_rangeview_observability_probe() {
6259 let rows = std::env::var("FORMUALIZER_OVERLAY_PROBE_ROWS")
6260 .ok()
6261 .and_then(|value| value.parse::<usize>().ok())
6262 .unwrap_or(100_000)
6263 .max(1);
6264 for fixture in [
6265 Phase4ProbeFixture::PointNumeric,
6266 Phase4ProbeFixture::DenseNumeric,
6267 Phase4ProbeFixture::RunNumeric,
6268 Phase4ProbeFixture::SparseNumeric,
6269 Phase4ProbeFixture::EmptyRun,
6270 Phase4ProbeFixture::MixedDense,
6271 ] {
6272 let row = run_phase4_probe_fixture(rows, fixture);
6273 println!("{}", serde_json::to_string(&row).unwrap());
6274 }
6275 }
6276
6277 #[test]
6278 fn ingest_mixed_rows_into_lanes_and_tags() {
6279 let mut b = IngestBuilder::new("Sheet1", 1, 1024, crate::engine::DateSystem::Excel1900);
6280 let data = vec![
6281 LiteralValue::Number(42.5), LiteralValue::Empty, LiteralValue::Text(String::new()), LiteralValue::Boolean(true), LiteralValue::Error(ExcelError::new_value()), ];
6287 for v in &data {
6288 b.append_row(std::slice::from_ref(v)).unwrap();
6289 }
6290 let sheet = b.finish();
6291 assert_eq!(sheet.nrows, 5);
6292 assert_eq!(sheet.columns.len(), 1);
6293 assert_eq!(sheet.columns[0].chunks.len(), 1);
6294 let ch = &sheet.columns[0].chunks[0];
6295
6296 let tags = ch.type_tag.values();
6298 assert_eq!(tags.len(), 5);
6299 assert_eq!(tags[0], TypeTag::Number as u8);
6300 assert_eq!(tags[1], TypeTag::Empty as u8);
6301 assert_eq!(tags[2], TypeTag::Text as u8);
6302 assert_eq!(tags[3], TypeTag::Boolean as u8);
6303 assert_eq!(tags[4], TypeTag::Error as u8);
6304
6305 let nums = ch.numbers.as_ref().unwrap();
6307 assert_eq!(nums.len(), 5);
6308 assert_eq!(nums.null_count(), 4);
6309 assert!(nums.is_valid(0));
6310
6311 let bools = ch.booleans.as_ref().unwrap();
6313 assert_eq!(bools.len(), 5);
6314 assert_eq!(bools.null_count(), 4);
6315 assert!(bools.is_valid(3));
6316
6317 let txt = ch.text.as_ref().unwrap();
6319 assert_eq!(txt.len(), 5);
6320 assert_eq!(txt.null_count(), 4);
6321 assert!(txt.is_valid(2)); let errs = ch.errors.as_ref().unwrap();
6325 assert_eq!(errs.len(), 5);
6326 assert_eq!(errs.null_count(), 4);
6327 assert!(errs.is_valid(4));
6328 }
6329
6330 #[test]
6331 fn range_view_get_cell_and_padding() {
6332 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6333 b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("".into())])
6334 .unwrap();
6335 b.append_row(&[LiteralValue::Empty, LiteralValue::Text("x".into())])
6336 .unwrap();
6337 b.append_row(&[LiteralValue::Boolean(true), LiteralValue::Empty])
6338 .unwrap();
6339 let sheet = b.finish();
6340 let rv = sheet.range_view(0, 0, 2, 1);
6341 assert_eq!(rv.dims(), (3, 2));
6342 assert_eq!(rv.get_cell(0, 0), LiteralValue::Number(1.0));
6344 assert_eq!(rv.get_cell(0, 1), LiteralValue::Text(String::new())); assert_eq!(rv.get_cell(1, 0), LiteralValue::Empty); assert_eq!(rv.get_cell(2, 0), LiteralValue::Boolean(true));
6347 assert_eq!(rv.get_cell(3, 0), LiteralValue::Empty);
6349 assert_eq!(rv.get_cell(0, 2), LiteralValue::Empty);
6350
6351 let nums: Vec<_> = rv.numbers_slices().map(|r| r.unwrap()).collect();
6353 assert_eq!(nums.len(), 2);
6354 assert_eq!(nums[0].0, 0);
6355 assert_eq!(nums[0].1, 2);
6356 assert_eq!(nums[1].0, 2);
6357 assert_eq!(nums[1].1, 1);
6358 }
6359
6360 #[test]
6361 fn overlay_precedence_user_over_computed() {
6362 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
6363 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
6364 b.append_row(&[LiteralValue::Empty]).unwrap();
6365 b.append_row(&[LiteralValue::Empty]).unwrap();
6366 let mut sheet = b.finish();
6367
6368 let (ch_i, off) = sheet.chunk_of_row(0).unwrap();
6369 sheet.columns[0].chunks[ch_i]
6370 .computed_overlay
6371 .set(off, OverlayValue::Number(2.0));
6372
6373 let rv0 = sheet.range_view(0, 0, 0, 0);
6374 assert_eq!(rv0.get_cell(0, 0), LiteralValue::Number(2.0));
6375 let nums0: Vec<_> = rv0.numbers_slices().map(|r| r.unwrap()).collect();
6376 assert_eq!(nums0.len(), 1);
6377 assert_eq!(nums0[0].2[0].value(0), 2.0);
6378
6379 sheet.columns[0].chunks[ch_i]
6380 .overlay
6381 .set(off, OverlayValue::Number(3.0));
6382
6383 let rv1 = sheet.range_view(0, 0, 0, 0);
6384 assert_eq!(rv1.get_cell(0, 0), LiteralValue::Number(3.0));
6385 let nums1: Vec<_> = rv1.numbers_slices().map(|r| r.unwrap()).collect();
6386 assert_eq!(nums1.len(), 1);
6387 assert_eq!(nums1[0].2[0].value(0), 3.0);
6388 }
6389
6390 #[test]
6391 fn overlay_slice_preserves_explicit_empty_and_offsets() {
6392 let mut overlay = Overlay::new();
6393 overlay.set(2, OverlayValue::Number(2.0));
6394 overlay.set(4, OverlayValue::Empty);
6395 overlay.set(6, OverlayValue::Text(Arc::from("outside")));
6396
6397 let sliced = overlay.slice(1, 4);
6398 assert!(sliced.get_scalar(0).is_none());
6399 assert_eq!(
6400 sliced.get_scalar(1).unwrap().to_literal(),
6401 LiteralValue::Number(2.0)
6402 );
6403 assert_eq!(
6404 sliced.get_scalar(3).unwrap().to_literal(),
6405 LiteralValue::Empty
6406 );
6407 assert!(sliced.get_scalar(5).is_none());
6408 }
6409
6410 #[test]
6411 fn overlay_cascade_user_empty_masks_computed_and_base() {
6412 let mut user = Overlay::new();
6413 let mut computed = Overlay::new();
6414 computed.set(1, OverlayValue::Number(42.0));
6415 user.set(1, OverlayValue::Empty);
6416
6417 let cascade = OverlayCascade::new(&user, &computed);
6418 assert_eq!(
6419 cascade.get_scalar(1).unwrap().to_literal(),
6420 LiteralValue::Empty
6421 );
6422 assert!(cascade.has_any_in_range(1..2));
6423 }
6424
6425 #[test]
6426 fn overlay_storage_pointmap_backward_compat_get_set_remove() {
6427 let mut overlay = Overlay::new();
6428 assert!(overlay.is_empty());
6429
6430 let delta = overlay.set_scalar(1, OverlayValue::Number(10.0));
6431 assert!(delta > 0);
6432 assert_eq!(overlay.len(), 1);
6433 assert_eq!(
6434 overlay.get_scalar(1).unwrap().to_literal(),
6435 LiteralValue::Number(10.0)
6436 );
6437
6438 let replace_delta = overlay.set_scalar(1, OverlayValue::Text(Arc::from("x")));
6439 assert_ne!(replace_delta, 0);
6440 assert_eq!(overlay.len(), 1);
6441 assert_eq!(
6442 overlay.get_scalar(1).unwrap().to_literal(),
6443 LiteralValue::Text("x".into())
6444 );
6445
6446 let remove_delta = overlay.remove_scalar(1);
6447 assert!(remove_delta < 0);
6448 assert!(overlay.is_empty());
6449 assert!(overlay.get_scalar(1).is_none());
6450 }
6451
6452 #[test]
6453 fn overlay_remove_range_splits_fragments_and_points() {
6454 let mut overlay = Overlay::new();
6455 overlay.set_scalar(2, OverlayValue::Number(20.0));
6456 overlay.apply_fragment(
6457 OverlayFragment::dense_range(
6458 0,
6459 (0..6)
6460 .map(|i| OverlayValue::Number(i as f64))
6461 .collect::<Vec<_>>(),
6462 )
6463 .unwrap(),
6464 );
6465 overlay.set_scalar(3, OverlayValue::Number(30.0));
6466 overlay.set_scalar(8, OverlayValue::Number(80.0));
6467
6468 let delta = overlay.remove_range(2..5);
6469
6470 assert!(delta < 0);
6471 assert_eq!(
6472 overlay.get_scalar(0).unwrap().to_literal(),
6473 LiteralValue::Number(0.0)
6474 );
6475 assert_eq!(
6476 overlay.get_scalar(1).unwrap().to_literal(),
6477 LiteralValue::Number(1.0)
6478 );
6479 assert!(overlay.get_scalar(2).is_none());
6480 assert!(overlay.get_scalar(3).is_none());
6481 assert!(overlay.get_scalar(4).is_none());
6482 assert_eq!(
6483 overlay.get_scalar(5).unwrap().to_literal(),
6484 LiteralValue::Number(5.0)
6485 );
6486 assert_eq!(
6487 overlay.get_scalar(8).unwrap().to_literal(),
6488 LiteralValue::Number(80.0)
6489 );
6490 assert!(overlay.debug_is_normalized());
6491 assert_eq!(
6492 overlay.estimated_bytes(),
6493 overlay.debug_recomputed_estimated_bytes()
6494 );
6495 }
6496
6497 #[test]
6498 fn overlay_storage_no_fragments_behavior_matches_old_map() {
6499 let mut overlay = Overlay::new();
6500 overlay.set_scalar(0, OverlayValue::Number(1.0));
6501 overlay.set_scalar(3, OverlayValue::Empty);
6502
6503 assert!(overlay.has_any_in_range(0..1));
6504 assert!(!overlay.has_any_in_range(1..3));
6505 assert!(overlay.has_any_in_range(3..4));
6506
6507 let sliced = overlay.slice(2, 3);
6508 assert!(sliced.get_scalar(0).is_none());
6509 assert_eq!(
6510 sliced.get_scalar(1).unwrap().to_literal(),
6511 LiteralValue::Empty
6512 );
6513 }
6514
6515 #[test]
6516 fn overlay_cascade_user_layer_masks_computed_fragment_regardless_of_sequence() {
6517 let mut user = Overlay::new();
6518 let mut computed = Overlay::new();
6519
6520 user.set_scalar(0, OverlayValue::Number(3.0));
6521 computed.apply_fragment(
6522 OverlayFragment::dense_range(0, vec![OverlayValue::Number(2.0)]).unwrap(),
6523 );
6524
6525 let cascade = OverlayCascade::new(&user, &computed);
6526 assert_eq!(
6527 cascade.get_scalar(0).unwrap().to_literal(),
6528 LiteralValue::Number(3.0)
6529 );
6530 }
6531
6532 #[test]
6533 fn overlay_same_layer_later_point_replaces_fragment_cell() {
6534 let mut overlay = Overlay::new();
6535 overlay.apply_fragment(
6536 OverlayFragment::dense_range(
6537 0,
6538 vec![
6539 OverlayValue::Number(1.0),
6540 OverlayValue::Number(2.0),
6541 OverlayValue::Number(3.0),
6542 ],
6543 )
6544 .unwrap(),
6545 );
6546
6547 overlay.set_scalar(1, OverlayValue::Number(99.0));
6548
6549 assert_eq!(
6550 overlay.get_scalar(0).unwrap().to_literal(),
6551 LiteralValue::Number(1.0)
6552 );
6553 assert_eq!(
6554 overlay.get_scalar(1).unwrap().to_literal(),
6555 LiteralValue::Number(99.0)
6556 );
6557 assert_eq!(
6558 overlay.get_scalar(2).unwrap().to_literal(),
6559 LiteralValue::Number(3.0)
6560 );
6561 }
6562
6563 #[test]
6564 fn overlay_same_layer_later_fragment_replaces_point_range() {
6565 let mut overlay = Overlay::new();
6566 overlay.set_scalar(0, OverlayValue::Number(1.0));
6567 overlay.set_scalar(1, OverlayValue::Number(2.0));
6568 overlay.set_scalar(2, OverlayValue::Number(3.0));
6569
6570 overlay.apply_fragment(
6571 OverlayFragment::dense_range(
6572 0,
6573 vec![
6574 OverlayValue::Number(10.0),
6575 OverlayValue::Number(20.0),
6576 OverlayValue::Number(30.0),
6577 ],
6578 )
6579 .unwrap(),
6580 );
6581
6582 let stats = overlay.debug_stats();
6583 assert_eq!(stats.points, 0);
6584 assert_eq!(stats.dense_fragments, 1);
6585 assert!(overlay.debug_is_normalized());
6586 assert_eq!(
6587 overlay.get_scalar(0).unwrap().to_literal(),
6588 LiteralValue::Number(10.0)
6589 );
6590 assert_eq!(
6591 overlay.get_scalar(1).unwrap().to_literal(),
6592 LiteralValue::Number(20.0)
6593 );
6594 assert_eq!(
6595 overlay.get_scalar(2).unwrap().to_literal(),
6596 LiteralValue::Number(30.0)
6597 );
6598 }
6599
6600 #[test]
6601 fn overlay_sparse_far_apart_replacement_does_not_rewrite_unrelated_dense_fragment() {
6602 let mut overlay = Overlay::new();
6603 overlay.apply_fragment(
6604 OverlayFragment::dense_range(100, vec![OverlayValue::Number(1.0); 10]).unwrap(),
6605 );
6606
6607 overlay.apply_fragment(
6608 OverlayFragment::sparse_offsets(vec![
6609 (0, OverlayValue::Empty),
6610 (1000, OverlayValue::Number(1000.0)),
6611 ])
6612 .unwrap(),
6613 );
6614
6615 let stats = overlay.debug_stats();
6616 assert_eq!(stats.dense_fragments, 1);
6617 assert_eq!(stats.sparse_fragments, 1);
6618 assert_eq!(stats.run_fragments, 0);
6619 assert!(overlay.debug_is_normalized());
6620 assert_eq!(
6621 overlay.get_scalar(105).unwrap().to_literal(),
6622 LiteralValue::Number(1.0)
6623 );
6624 assert_eq!(
6625 overlay.get_scalar(0).unwrap().to_literal(),
6626 LiteralValue::Empty
6627 );
6628 assert_eq!(
6629 overlay.get_scalar(1000).unwrap().to_literal(),
6630 LiteralValue::Number(1000.0)
6631 );
6632 }
6633
6634 #[test]
6635 fn overlay_sparse_offsets_are_sorted_unique_last_write_wins() {
6636 let mut overlay = Overlay::new();
6637 overlay.apply_fragment(
6638 OverlayFragment::sparse_offsets(vec![
6639 (3, OverlayValue::Number(3.0)),
6640 (1, OverlayValue::Number(1.0)),
6641 (3, OverlayValue::Number(33.0)),
6642 ])
6643 .unwrap(),
6644 );
6645
6646 let stats = overlay.debug_stats();
6647 assert_eq!(stats.sparse_fragments, 1);
6648 assert_eq!(overlay.len(), 2);
6649 assert_eq!(
6650 overlay.get_scalar(1).unwrap().to_literal(),
6651 LiteralValue::Number(1.0)
6652 );
6653 assert_eq!(
6654 overlay.get_scalar(3).unwrap().to_literal(),
6655 LiteralValue::Number(33.0)
6656 );
6657 assert!(overlay.debug_is_normalized());
6658 }
6659
6660 #[test]
6661 fn overlay_dense_point_replacement_shadows_without_splitting() {
6662 let mut overlay = Overlay::new();
6663 overlay.apply_fragment(
6664 OverlayFragment::dense_range(
6665 0,
6666 (0..6)
6667 .map(|i| OverlayValue::Number(i as f64))
6668 .collect::<Vec<_>>(),
6669 )
6670 .unwrap(),
6671 );
6672
6673 overlay.set_scalar(3, OverlayValue::Number(99.0));
6674
6675 let stats = overlay.debug_stats();
6677 assert_eq!(stats.points, 1);
6678 assert_eq!(stats.dense_fragments, 1);
6679 assert_eq!(stats.sparse_fragments, 0);
6680 assert_eq!(stats.covered_len, 6);
6681 assert!(overlay.debug_is_normalized());
6682 assert_eq!(
6683 overlay.get_scalar(2).unwrap().to_literal(),
6684 LiteralValue::Number(2.0)
6685 );
6686 assert_eq!(
6687 overlay.get_scalar(3).unwrap().to_literal(),
6688 LiteralValue::Number(99.0)
6689 );
6690 assert_eq!(
6691 overlay.get_scalar(4).unwrap().to_literal(),
6692 LiteralValue::Number(4.0)
6693 );
6694 }
6695
6696 #[test]
6697 fn overlay_dense_fragment_replacement_splits_left_and_right_dense() {
6698 let mut overlay = Overlay::new();
6699 overlay.apply_fragment(
6700 OverlayFragment::dense_range(
6701 0,
6702 (0..8)
6703 .map(|i| OverlayValue::Number(i as f64))
6704 .collect::<Vec<_>>(),
6705 )
6706 .unwrap(),
6707 );
6708
6709 overlay.apply_fragment(
6710 OverlayFragment::dense_range(
6711 3,
6712 vec![OverlayValue::Number(30.0), OverlayValue::Number(40.0)],
6713 )
6714 .unwrap(),
6715 );
6716
6717 let stats = overlay.debug_stats();
6718 assert_eq!(stats.points, 0);
6719 assert_eq!(stats.dense_fragments, 3);
6720 assert_eq!(stats.sparse_fragments, 0);
6721 assert!(overlay.debug_is_normalized());
6722 assert_eq!(
6723 overlay.get_scalar(2).unwrap().to_literal(),
6724 LiteralValue::Number(2.0)
6725 );
6726 assert_eq!(
6727 overlay.get_scalar(3).unwrap().to_literal(),
6728 LiteralValue::Number(30.0)
6729 );
6730 assert_eq!(
6731 overlay.get_scalar(4).unwrap().to_literal(),
6732 LiteralValue::Number(40.0)
6733 );
6734 assert_eq!(
6735 overlay.get_scalar(5).unwrap().to_literal(),
6736 LiteralValue::Number(5.0)
6737 );
6738 }
6739
6740 #[test]
6741 fn overlay_run_point_replacement_shadows_without_splitting() {
6742 let mut overlay = Overlay::new();
6743 overlay.apply_fragment(
6744 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 10]).unwrap(),
6745 );
6746
6747 overlay.set_scalar(5, OverlayValue::Number(99.0));
6748
6749 let stats = overlay.debug_stats();
6750 assert_eq!(stats.points, 1);
6751 assert_eq!(stats.run_fragments, 1);
6752 assert_eq!(stats.sparse_fragments, 0);
6753 assert_eq!(stats.covered_len, 10);
6754 assert!(overlay.debug_is_normalized());
6755 assert_eq!(
6756 overlay.get_scalar(4).unwrap().to_literal(),
6757 LiteralValue::Number(1.0)
6758 );
6759 assert_eq!(
6760 overlay.get_scalar(5).unwrap().to_literal(),
6761 LiteralValue::Number(99.0)
6762 );
6763 assert_eq!(
6764 overlay.get_scalar(6).unwrap().to_literal(),
6765 LiteralValue::Number(1.0)
6766 );
6767 }
6768
6769 #[test]
6770 fn overlay_point_removal_splits_dense_and_run_zero_copy() {
6771 for run in [false, true] {
6772 let values: Vec<_> = (0..1000)
6773 .map(|i| OverlayValue::Number(if run { (i / 10) as f64 } else { i as f64 }))
6774 .collect();
6775 let fragment = if run {
6776 OverlayFragment::run_range(0, values).unwrap()
6777 } else {
6778 OverlayFragment::dense_range(0, values).unwrap()
6779 };
6780 let mut overlay = Overlay::new();
6781 overlay.apply_fragment(fragment);
6782 overlay.remove_scalar(400);
6783 overlay.remove_scalar(10);
6784 let stats = overlay.debug_stats();
6785 assert_eq!(stats.points, 0);
6786 assert_eq!(stats.covered_len, 998);
6787 assert!(overlay.debug_is_normalized());
6788 assert_eq!(
6789 overlay.estimated_bytes(),
6790 overlay.debug_recomputed_estimated_bytes()
6791 );
6792 for i in 0..1000usize {
6793 let got = overlay.get_scalar(i).map(|v| v.to_literal());
6794 if i == 400 || i == 10 {
6795 assert!(got.is_none(), "{run} {i}");
6796 } else {
6797 let want = if run { (i / 10) as f64 } else { i as f64 };
6798 assert_eq!(got, Some(LiteralValue::Number(want)), "{run} {i}");
6799 }
6800 }
6801 let mut pieces: Vec<_> = overlay
6804 .fragments
6805 .iter()
6806 .map(|f| {
6807 let p = match f {
6808 OverlayFragment::DenseRange { payload, .. }
6809 | OverlayFragment::RunRange { payload, .. } => payload,
6810 OverlayFragment::SparseOffsets { .. } => unreachable!(),
6811 };
6812 (f.coverage_len(), p.root_len)
6813 })
6814 .collect();
6815 pieces.sort();
6816 let root = if run { 100 } else { 1000 };
6817 let small = if run { 1 } else { 10 };
6818 assert_eq!(pieces[0], (10, small), "{run}");
6819 assert!(
6820 pieces[1..].iter().all(|(_, r)| *r == root),
6821 "{run} {pieces:?}"
6822 );
6823 }
6824 }
6825
6826 #[test]
6827 fn overlay_shadowed_points_fold_into_fragments() {
6828 for run in [false, true] {
6829 let n = 400usize;
6830 let base = |i: usize| if run { 7.0 } else { i as f64 };
6831 let values: Vec<_> = (0..n).map(|i| OverlayValue::Number(base(i))).collect();
6832 let fragment = if run {
6833 OverlayFragment::run_range(0, values).unwrap()
6834 } else {
6835 OverlayFragment::dense_range(0, values).unwrap()
6836 };
6837 let mut overlay = Overlay::new();
6838 overlay.apply_fragment(fragment);
6839 overlay.set_scalar(n + 5, OverlayValue::Number(-1.0));
6840 let mut delta_sum = 0isize;
6841 let start_bytes = overlay.estimated_bytes() as isize;
6842 for k in 0..49usize {
6844 delta_sum += overlay.set_scalar(k * 7, OverlayValue::Number(1000.0 + k as f64));
6845 }
6846 assert_eq!(overlay.debug_stats().points, 50);
6847 assert!(overlay.debug_is_normalized());
6848 delta_sum += overlay.set_scalar(49 * 7, OverlayValue::Text(Arc::from("t")));
6849 let stats = overlay.debug_stats();
6850 assert_eq!(stats.points, 1, "only the unshadowed point stays");
6851 assert_eq!(stats.covered_len, n + 1);
6852 assert!(overlay.debug_is_normalized());
6853 assert_eq!(
6854 overlay.estimated_bytes(),
6855 overlay.debug_recomputed_estimated_bytes()
6856 );
6857 assert_eq!(overlay.estimated_bytes() as isize, start_bytes + delta_sum);
6858 for i in 0..n {
6859 let want = if i % 7 == 0 && i / 7 < 49 {
6860 LiteralValue::Number(1000.0 + (i / 7) as f64)
6861 } else if i == 49 * 7 {
6862 LiteralValue::Text("t".into())
6863 } else {
6864 LiteralValue::Number(base(i))
6865 };
6866 assert_eq!(
6867 overlay.get_scalar(i).unwrap().to_literal(),
6868 want,
6869 "{run} {i}"
6870 );
6871 }
6872 overlay.set_scalar(3, OverlayValue::Number(3.5));
6875 overlay.set_scalar(3, OverlayValue::Number(4.5));
6876 assert!(overlay.debug_is_normalized());
6877 overlay.apply_fragment(
6878 OverlayFragment::dense_range(0, vec![OverlayValue::Number(0.0); 8]).unwrap(),
6879 );
6880 assert!(overlay.debug_is_normalized());
6881 overlay.set_scalar(20, OverlayValue::Number(1.0));
6882 overlay.remove_range(15..25);
6883 assert!(overlay.debug_is_normalized());
6884 assert!(overlay.get_scalar(20).is_none());
6885 overlay.set_scalar(30, OverlayValue::Number(1.0));
6886 overlay.remove_scalar(30);
6887 assert!(overlay.get_scalar(30).is_none());
6888 assert!(overlay.debug_is_normalized());
6889 assert_eq!(
6890 overlay.estimated_bytes(),
6891 overlay.debug_recomputed_estimated_bytes()
6892 );
6893 }
6894 }
6895
6896 #[test]
6897 fn overlay_run_fragment_replacement_splits_left_and_right_run() {
6898 let mut overlay = Overlay::new();
6899 let values = [
6900 vec![OverlayValue::Number(1.0); 4],
6901 vec![OverlayValue::Number(2.0); 4],
6902 vec![OverlayValue::Number(3.0); 4],
6903 ]
6904 .concat();
6905 overlay.apply_fragment(OverlayFragment::run_range(0, values).unwrap());
6906
6907 overlay.apply_fragment(
6908 OverlayFragment::dense_range(
6909 5,
6910 vec![OverlayValue::Number(50.0), OverlayValue::Number(60.0)],
6911 )
6912 .unwrap(),
6913 );
6914
6915 let stats = overlay.debug_stats();
6916 assert_eq!(stats.run_fragments, 2);
6917 assert_eq!(stats.dense_fragments, 1);
6918 assert_eq!(stats.sparse_fragments, 0);
6919 assert!(overlay.debug_is_normalized());
6920 assert_eq!(
6921 overlay.get_scalar(4).unwrap().to_literal(),
6922 LiteralValue::Number(2.0)
6923 );
6924 assert_eq!(
6925 overlay.get_scalar(5).unwrap().to_literal(),
6926 LiteralValue::Number(50.0)
6927 );
6928 assert_eq!(
6929 overlay.get_scalar(6).unwrap().to_literal(),
6930 LiteralValue::Number(60.0)
6931 );
6932 assert_eq!(
6933 overlay.get_scalar(7).unwrap().to_literal(),
6934 LiteralValue::Number(2.0)
6935 );
6936 }
6937
6938 #[test]
6939 fn overlay_slice_preserves_dense_and_run_encodings() {
6940 let mut overlay = Overlay::new();
6941 overlay.apply_fragment(
6942 OverlayFragment::dense_range(
6943 10,
6944 (0..5)
6945 .map(|i| OverlayValue::Number(i as f64))
6946 .collect::<Vec<_>>(),
6947 )
6948 .unwrap(),
6949 );
6950 overlay.apply_fragment(
6951 OverlayFragment::run_range(
6952 20,
6953 [
6954 vec![OverlayValue::Number(1.0); 3],
6955 vec![OverlayValue::Number(2.0); 3],
6956 ]
6957 .concat(),
6958 )
6959 .unwrap(),
6960 );
6961
6962 let dense_slice = overlay.slice(12, 2);
6963 let dense_stats = dense_slice.debug_stats();
6964 assert_eq!(dense_stats.dense_fragments, 1);
6965 assert_eq!(dense_stats.sparse_fragments, 0);
6966 assert_eq!(
6967 dense_slice.get_scalar(0).unwrap().to_literal(),
6968 LiteralValue::Number(2.0)
6969 );
6970 assert_eq!(
6971 dense_slice.get_scalar(1).unwrap().to_literal(),
6972 LiteralValue::Number(3.0)
6973 );
6974 assert!(dense_slice.debug_is_normalized());
6975
6976 let run_slice = overlay.slice(22, 3);
6977 let run_stats = run_slice.debug_stats();
6978 assert_eq!(run_stats.run_fragments, 1);
6979 assert_eq!(run_stats.sparse_fragments, 0);
6980 assert_eq!(
6981 run_slice.get_scalar(0).unwrap().to_literal(),
6982 LiteralValue::Number(1.0)
6983 );
6984 assert_eq!(
6985 run_slice.get_scalar(1).unwrap().to_literal(),
6986 LiteralValue::Number(2.0)
6987 );
6988 assert_eq!(
6989 run_slice.get_scalar(2).unwrap().to_literal(),
6990 LiteralValue::Number(2.0)
6991 );
6992 assert!(run_slice.debug_is_normalized());
6993 }
6994
6995 #[test]
6996 fn overlay_computed_empty_run_masks_non_empty_base() {
6997 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
6998 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
6999 b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
7000 b.append_row(&[LiteralValue::Number(3.0)]).unwrap();
7001 let mut sheet = b.finish();
7002
7003 let (ch_i, _) = sheet.chunk_of_row(0).unwrap();
7004 sheet.columns[0].chunks[ch_i]
7005 .computed_overlay
7006 .apply_fragment(
7007 OverlayFragment::run_range(
7008 0,
7009 vec![
7010 OverlayValue::Empty,
7011 OverlayValue::Empty,
7012 OverlayValue::Empty,
7013 ],
7014 )
7015 .unwrap(),
7016 );
7017
7018 assert_eq!(sheet.get_cell_value(0, 0), LiteralValue::Empty);
7019 assert_eq!(sheet.get_cell_value(1, 0), LiteralValue::Empty);
7020 assert_eq!(sheet.get_cell_value(2, 0), LiteralValue::Empty);
7021 }
7022
7023 #[test]
7024 fn overlay_fragments_reconstruct_scalars_from_typed_lanes() {
7025 let values = vec![
7026 OverlayValue::Empty,
7027 OverlayValue::Number(1.5),
7028 OverlayValue::DateTime(45000.25),
7029 OverlayValue::Duration(0.5),
7030 OverlayValue::Boolean(true),
7031 OverlayValue::Text(Arc::from("Hello")),
7032 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
7033 OverlayValue::Pending,
7034 ];
7035
7036 let mut dense = Overlay::new();
7037 dense.apply_fragment(OverlayFragment::dense_range(0, values.clone()).unwrap());
7038 for (idx, expected) in values.iter().enumerate() {
7039 assert_eq!(
7040 dense.get_scalar(idx).unwrap().to_overlay_value(),
7041 expected.clone()
7042 );
7043 }
7044
7045 let mut sparse = Overlay::new();
7046 sparse.apply_fragment(
7047 OverlayFragment::sparse_offsets(
7048 values
7049 .iter()
7050 .cloned()
7051 .enumerate()
7052 .map(|(idx, value)| (idx * 2, value))
7053 .collect(),
7054 )
7055 .unwrap(),
7056 );
7057 for (idx, expected) in values.iter().enumerate() {
7058 assert_eq!(
7059 sparse.get_scalar(idx * 2).unwrap().to_overlay_value(),
7060 expected.clone()
7061 );
7062 }
7063
7064 let mut run = Overlay::new();
7065 run.apply_fragment(
7066 OverlayFragment::run_range(
7067 0,
7068 vec![
7069 OverlayValue::Number(7.0),
7070 OverlayValue::Number(7.0),
7071 OverlayValue::Text(Arc::from("run")),
7072 OverlayValue::Text(Arc::from("run")),
7073 ],
7074 )
7075 .unwrap(),
7076 );
7077 assert_eq!(
7078 run.get_scalar(0).unwrap().to_overlay_value(),
7079 OverlayValue::Number(7.0)
7080 );
7081 assert_eq!(
7082 run.get_scalar(2).unwrap().to_overlay_value(),
7083 OverlayValue::Text(Arc::from("run"))
7084 );
7085 }
7086
7087 #[test]
7088 fn overlay_iter_returns_complete_logical_entries() {
7089 let mut overlay = Overlay::new();
7090 overlay.apply_fragment(
7091 OverlayFragment::dense_range(
7092 2,
7093 vec![OverlayValue::Number(2.0), OverlayValue::Number(3.0)],
7094 )
7095 .unwrap(),
7096 );
7097 overlay.set_scalar(5, OverlayValue::Text(Arc::from("point")));
7098
7099 let entries: Vec<_> = overlay.iter().collect();
7100 assert_eq!(
7101 entries,
7102 vec![
7103 (2, OverlayValue::Number(2.0)),
7104 (3, OverlayValue::Number(3.0)),
7105 (5, OverlayValue::Text(Arc::from("point"))),
7106 ]
7107 );
7108 assert_eq!(overlay.iter_points().count(), 1);
7109 }
7110
7111 #[test]
7112 fn overlay_fragment_estimates_follow_encoded_shapes() {
7113 let mut points = Overlay::new();
7114 for idx in 0..512 {
7115 points.set_scalar(idx, OverlayValue::Number(idx as f64));
7116 }
7117
7118 let mut dense = Overlay::new();
7119 dense.apply_fragment(
7120 OverlayFragment::dense_range(
7121 0,
7122 (0..512)
7123 .map(|idx| OverlayValue::Number(idx as f64))
7124 .collect::<Vec<_>>(),
7125 )
7126 .unwrap(),
7127 );
7128 assert_eq!(
7129 dense.estimated_bytes(),
7130 dense.debug_recomputed_estimated_bytes()
7131 );
7132 assert!(
7133 dense.estimated_bytes() < points.estimated_bytes(),
7134 "dense fragment should account like encoded lanes, not point-map entries"
7135 );
7136
7137 let mut short_run = Overlay::new();
7138 short_run.apply_fragment(
7139 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 8]).unwrap(),
7140 );
7141 let mut long_run = Overlay::new();
7142 long_run.apply_fragment(
7143 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 4096]).unwrap(),
7144 );
7145 assert_eq!(
7146 short_run.estimated_bytes(),
7147 short_run.debug_recomputed_estimated_bytes()
7148 );
7149 assert_eq!(
7150 long_run.estimated_bytes(),
7151 long_run.debug_recomputed_estimated_bytes()
7152 );
7153 assert_eq!(
7154 short_run.estimated_bytes(),
7155 long_run.estimated_bytes(),
7156 "single-run estimate should scale with run count, not covered rows"
7157 );
7158
7159 let sparse10 = OverlayFragment::sparse_offsets(
7160 (0..10)
7161 .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
7162 .collect(),
7163 )
7164 .unwrap();
7165 let sparse20 = OverlayFragment::sparse_offsets(
7166 (0..20)
7167 .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
7168 .collect(),
7169 )
7170 .unwrap();
7171 assert!(sparse20.estimated_bytes() > sparse10.estimated_bytes());
7172 }
7173
7174 #[test]
7175 fn overlay_estimated_bytes_stay_consistent_after_split_and_clear() {
7176 let mut overlay = Overlay::new();
7177 overlay.apply_fragment(
7178 OverlayFragment::dense_range(
7179 0,
7180 (0..16)
7181 .map(|idx| OverlayValue::Number(idx as f64))
7182 .collect::<Vec<_>>(),
7183 )
7184 .unwrap(),
7185 );
7186 assert_eq!(
7187 overlay.estimated_bytes(),
7188 overlay.debug_recomputed_estimated_bytes()
7189 );
7190
7191 overlay.set_scalar(8, OverlayValue::Text(Arc::from("split")));
7192 assert!(overlay.debug_is_normalized());
7193 assert_eq!(
7194 overlay.estimated_bytes(),
7195 overlay.debug_recomputed_estimated_bytes()
7196 );
7197
7198 overlay.apply_fragment(
7199 OverlayFragment::sparse_offsets(vec![
7200 (0, OverlayValue::Empty),
7201 (15, OverlayValue::Boolean(true)),
7202 ])
7203 .unwrap(),
7204 );
7205 assert!(overlay.debug_is_normalized());
7206 assert_eq!(
7207 overlay.estimated_bytes(),
7208 overlay.debug_recomputed_estimated_bytes()
7209 );
7210
7211 let freed = overlay.clear_all();
7212 assert!(freed > 0);
7213 assert_eq!(overlay.estimated_bytes(), 0);
7214 assert_eq!(overlay.debug_recomputed_estimated_bytes(), 0);
7215 assert!(overlay.is_empty());
7216 }
7217
7218 #[test]
7219 fn overlay_segment_numbers_masks_base_for_non_numeric_overlays() {
7220 let mut user = Overlay::new();
7221 user.set(1, OverlayValue::Text(Arc::from("x")));
7222 user.set(2, OverlayValue::Empty);
7223 user.set(3, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
7224 user.set(4, OverlayValue::Pending);
7225 let computed = Overlay::new();
7226 let cascade = OverlayCascade::new(&user, &computed);
7227
7228 let base = Float64Array::from(vec![10.0, 20.0, 30.0, 40.0, 50.0]);
7229 let selected = cascade.select_numbers(0..5, &base);
7230 assert_eq!(selected.value(0), 10.0);
7231 assert!(selected.is_null(1));
7232 assert!(selected.is_null(2));
7233 assert!(selected.is_null(3));
7234 assert!(selected.is_null(4));
7235 }
7236
7237 #[test]
7238 fn overlay_segment_type_tags_preserve_temporal_tags() {
7239 let mut computed = Overlay::new();
7240 computed.set(0, OverlayValue::DateTime(45000.5));
7241 computed.set(1, OverlayValue::Duration(0.25));
7242 let user = Overlay::new();
7243 let cascade = OverlayCascade::new(&user, &computed);
7244
7245 let base = UInt8Array::from(vec![TypeTag::Empty as u8; 2]);
7246 let selected = cascade.select_type_tags(0..2, &base);
7247 assert_eq!(selected.value(0), TypeTag::DateTime as u8);
7248 assert_eq!(selected.value(1), TypeTag::Duration as u8);
7249 }
7250
7251 #[test]
7252 fn overlay_lowered_text_matches_existing_overlay_semantics() {
7253 let mut user = Overlay::new();
7254 user.set(0, OverlayValue::Text(Arc::from("HeLLo")));
7255 user.set(1, OverlayValue::Number(1.5));
7256 user.set(2, OverlayValue::Boolean(true));
7257 user.set(3, OverlayValue::Empty);
7258 let computed = Overlay::new();
7259 let cascade = OverlayCascade::new(&user, &computed);
7260
7261 let base = StringArray::from(vec![Some("A"), Some("B"), Some("C"), Some("D")]);
7262 let selected = cascade.select_lowered_text(0..4, &base);
7263 assert_eq!(selected.value(0), "hello");
7264 assert_eq!(selected.value(1), "1.5");
7265 assert_eq!(selected.value(2), "true");
7266 assert!(selected.is_null(3));
7267 }
7268
7269 fn numeric_sheet(rows: usize) -> ArrowSheet {
7270 let mut b = IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
7271 for row in 0..rows {
7272 b.append_row(&[LiteralValue::Number((row + 1) as f64)])
7273 .unwrap();
7274 }
7275 b.finish()
7276 }
7277
7278 fn numbers_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<Float64Array> {
7279 let view = sheet.range_view(sr, 0, er, 0);
7280 let segments: Vec<_> = view.numbers_slices().map(|res| res.unwrap()).collect();
7281 assert_eq!(segments.len(), 1);
7282 assert_eq!(segments[0].2.len(), 1);
7283 segments[0].2[0].clone()
7284 }
7285
7286 fn type_tags_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<UInt8Array> {
7287 let view = sheet.range_view(sr, 0, er, 0);
7288 let segments: Vec<_> = view.type_tags_slices().map(|res| res.unwrap()).collect();
7289 assert_eq!(segments.len(), 1);
7290 assert_eq!(segments[0].2.len(), 1);
7291 segments[0].2[0].clone()
7292 }
7293
7294 fn lowered_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<StringArray> {
7295 let view = sheet.range_view(sr, 0, er, 0);
7296 let segments: Vec<_> = view.lowered_text_slices().map(|res| res.unwrap()).collect();
7297 assert_eq!(segments.len(), 1);
7298 assert_eq!(segments[0].2.len(), 1);
7299 segments[0].2[0].clone()
7300 }
7301
7302 #[test]
7303 fn rangeview_dense_text_masks_base_numbers() {
7304 let mut sheet = numeric_sheet(4);
7305 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7306 OverlayFragment::dense_range(
7307 0,
7308 vec![
7309 OverlayValue::Text(Arc::from("x")),
7310 OverlayValue::Text(Arc::from("y")),
7311 OverlayValue::Text(Arc::from("z")),
7312 OverlayValue::Text(Arc::from("w")),
7313 ],
7314 )
7315 .unwrap(),
7316 );
7317
7318 reset_overlay_select_stats();
7319 let numbers = numbers_for_range(&sheet, 0, 3);
7320 assert_eq!(numbers.null_count(), 4);
7321 let stats = snapshot_overlay_select_stats();
7322 assert_eq!(stats.direct_dense_slices, 1);
7323 assert_eq!(stats.zip_select_calls, 0);
7324 }
7325
7326 #[test]
7327 fn rangeview_empty_dense_masks_base_all_selectors() {
7328 let mut sheet = numeric_sheet(3);
7329 sheet.columns[0].chunks[0]
7330 .computed_overlay
7331 .apply_fragment(OverlayFragment::dense_range(0, vec![OverlayValue::Empty; 3]).unwrap());
7332
7333 reset_overlay_select_stats();
7334 let numbers = numbers_for_range(&sheet, 0, 2);
7335 let type_tags = type_tags_for_range(&sheet, 0, 2);
7336 let lowered = lowered_for_range(&sheet, 0, 2);
7337 assert_eq!(numbers.null_count(), 3);
7338 assert_eq!(lowered.null_count(), 3);
7339 assert_eq!(type_tags.values(), &[TypeTag::Empty as u8; 3]);
7340 let stats = snapshot_overlay_select_stats();
7341 assert_eq!(stats.direct_dense_slices, 3);
7342 assert_eq!(stats.zip_select_calls, 0);
7343 }
7344
7345 #[test]
7346 fn rangeview_pending_masks_base_type_tag_present_lanes_null() {
7347 let mut sheet = numeric_sheet(2);
7348 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7349 OverlayFragment::dense_range(0, vec![OverlayValue::Pending; 2]).unwrap(),
7350 );
7351
7352 reset_overlay_select_stats();
7353 let numbers = numbers_for_range(&sheet, 0, 1);
7354 let type_tags = type_tags_for_range(&sheet, 0, 1);
7355 let lowered = lowered_for_range(&sheet, 0, 1);
7356 assert_eq!(numbers.null_count(), 2);
7357 assert_eq!(lowered.null_count(), 2);
7358 assert_eq!(type_tags.values(), &[TypeTag::Pending as u8; 2]);
7359 let stats = snapshot_overlay_select_stats();
7360 assert_eq!(stats.direct_dense_slices, 3);
7361 assert_eq!(stats.zip_select_calls, 0);
7362 }
7363
7364 #[test]
7365 fn rangeview_subrange_inside_dense_fragment_uses_direct_path() {
7366 let mut sheet = numeric_sheet(10);
7367 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7368 OverlayFragment::dense_range(
7369 0,
7370 (0..10)
7371 .map(|row| OverlayValue::Number((row + 10) as f64))
7372 .collect(),
7373 )
7374 .unwrap(),
7375 );
7376
7377 reset_overlay_select_stats();
7378 let numbers = numbers_for_range(&sheet, 2, 6);
7379 assert_eq!(numbers.len(), 5);
7380 assert_eq!(numbers.value(0), 12.0);
7381 assert_eq!(numbers.value(4), 16.0);
7382 let stats = snapshot_overlay_select_stats();
7383 assert_eq!(stats.direct_dense_slices, 1);
7384 assert_eq!(stats.zip_select_calls, 0);
7385 }
7386
7387 #[test]
7388 fn rangeview_subrange_inside_run_fragment_uses_direct_path() {
7389 let mut sheet = numeric_sheet(10);
7390 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7391 OverlayFragment::run_range(0, vec![OverlayValue::Number(7.0); 10]).unwrap(),
7392 );
7393
7394 reset_overlay_select_stats();
7395 let numbers = numbers_for_range(&sheet, 2, 6);
7396 assert_eq!(numbers.len(), 5);
7397 for idx in 0..numbers.len() {
7398 assert_eq!(numbers.value(idx), 7.0);
7399 }
7400 let stats = snapshot_overlay_select_stats();
7401 assert_eq!(stats.direct_run_materializations, 1);
7402 assert_eq!(stats.zip_select_calls, 0);
7403 }
7404
7405 #[test]
7406 fn rangeview_user_partial_wrong_type_masks_computed_numeric() {
7407 let mut sheet = numeric_sheet(5);
7408 let chunk = &mut sheet.columns[0].chunks[0];
7409 chunk.computed_overlay.apply_fragment(
7410 OverlayFragment::dense_range(
7411 0,
7412 (0..5)
7413 .map(|row| OverlayValue::Number((row + 10) as f64))
7414 .collect(),
7415 )
7416 .unwrap(),
7417 );
7418 chunk.overlay.apply_fragment(
7419 OverlayFragment::dense_range(2, vec![OverlayValue::Text(Arc::from("mask"))]).unwrap(),
7420 );
7421
7422 reset_overlay_select_stats();
7423 let numbers = numbers_for_range(&sheet, 0, 4);
7424 assert_eq!(numbers.value(0), 10.0);
7425 assert_eq!(numbers.value(1), 11.0);
7426 assert!(numbers.is_null(2));
7427 assert_eq!(numbers.value(3), 13.0);
7428 assert_eq!(numbers.value(4), 14.0);
7429 let stats = snapshot_overlay_select_stats();
7430 assert_eq!(stats.direct_dense_slices, 0);
7431 assert_eq!(stats.zip_select_calls, 1);
7432 assert_eq!(stats.partial_dense_intersections, 2);
7433 }
7434
7435 #[test]
7436 fn rangeview_computed_full_cover_user_no_overlap_uses_computed_direct() {
7437 let mut sheet = numeric_sheet(5);
7438 let chunk = &mut sheet.columns[0].chunks[0];
7439 chunk.computed_overlay.apply_fragment(
7440 OverlayFragment::dense_range(0, vec![OverlayValue::Number(3.0); 5]).unwrap(),
7441 );
7442 chunk
7443 .overlay
7444 .set_scalar(10, OverlayValue::Text(Arc::from("outside")));
7445
7446 reset_overlay_select_stats();
7447 let numbers = numbers_for_range(&sheet, 0, 4);
7448 assert_eq!(numbers.value(0), 3.0);
7449 assert_eq!(numbers.value(4), 3.0);
7450 let stats = snapshot_overlay_select_stats();
7451 assert_eq!(stats.direct_dense_slices, 1);
7452 assert_eq!(stats.zip_select_calls, 0);
7453 }
7454
7455 #[test]
7456 fn rangeview_user_full_cover_ignores_computed() {
7457 let mut sheet = numeric_sheet(4);
7458 let chunk = &mut sheet.columns[0].chunks[0];
7459 chunk.computed_overlay.apply_fragment(
7460 OverlayFragment::dense_range(0, vec![OverlayValue::Number(99.0); 4]).unwrap(),
7461 );
7462 chunk.overlay.apply_fragment(
7463 OverlayFragment::dense_range(0, vec![OverlayValue::Text(Arc::from("user")); 4])
7464 .unwrap(),
7465 );
7466
7467 reset_overlay_select_stats();
7468 let numbers = numbers_for_range(&sheet, 0, 3);
7469 assert_eq!(numbers.null_count(), 4);
7470 let stats = snapshot_overlay_select_stats();
7471 assert_eq!(stats.direct_dense_slices, 1);
7472 assert_eq!(stats.zip_select_calls, 0);
7473 }
7474
7475 #[test]
7476 fn rangeview_point_overlay_still_matches_legacy_scalar_path() {
7477 let mut sheet = numeric_sheet(3);
7478 sheet.columns[0].chunks[0]
7479 .computed_overlay
7480 .set_scalar(1, OverlayValue::Text(Arc::from("point")));
7481
7482 reset_overlay_select_stats();
7483 let numbers = numbers_for_range(&sheet, 0, 2);
7484 assert_eq!(numbers.value(0), 1.0);
7485 assert!(numbers.is_null(1));
7486 assert_eq!(numbers.value(2), 3.0);
7487 let stats = snapshot_overlay_select_stats();
7488 assert_eq!(stats.small_point_selects, 1);
7491 assert_eq!(stats.zip_select_calls, 0);
7492 assert_eq!(stats.point_entries_applied, 1);
7493 assert_eq!(stats.row_scalar_fallbacks, 0);
7494 }
7495
7496 #[test]
7497 fn points_only_patch_matches_per_offset_layering() {
7498 use rand::{Rng, SeedableRng, rngs::SmallRng};
7499 let mut rng = SmallRng::seed_from_u64(0x5eed);
7500 let pick = |rng: &mut SmallRng| match rng.gen_range(0..7) {
7501 0 => OverlayValue::Number(rng.gen_range(-5.0..5.0)),
7502 1 => OverlayValue::DateTime(rng.gen_range(0.0..5.0)),
7503 2 => OverlayValue::Text(Arc::from("t")),
7504 3 => OverlayValue::Empty,
7505 4 => OverlayValue::Error(rng.gen_range(1..8)),
7506 5 => OverlayValue::Boolean(rng.gen_bool(0.5)),
7507 _ => OverlayValue::Number(-0.0),
7508 };
7509 reset_overlay_select_stats();
7510 for case in 0..400 {
7511 let n = rng.gen_range(17..300);
7512 let total = n + 10;
7513 let base_nulls = case % 2 == 0;
7514 let nums: Vec<Option<f64>> = (0..total)
7515 .map(|i| (!base_nulls || !rng.gen_bool(0.3)).then_some(i as f64 + 0.5))
7516 .collect();
7517 let errs: Vec<Option<u8>> = (0..total)
7518 .map(|i| (!base_nulls || rng.gen_bool(0.3)).then_some((i % 7) as u8 + 1))
7519 .collect();
7520 let (nums, errs) = (Float64Array::from(nums), UInt8Array::from(errs));
7521 let start = rng.gen_range(0..10);
7522 let range = start..start + n;
7523 let (mut user, mut computed) = (Overlay::new(), Overlay::new());
7524 for _ in 0..rng.gen_range(1..40) {
7525 let off = rng.gen_range(0..total);
7526 let v = pick(&mut rng);
7527 if rng.gen_bool(0.5) {
7528 user.set_scalar(off, v);
7529 } else {
7530 computed.set_scalar(off, v);
7531 }
7532 }
7533 let cascade = OverlayCascade::new(&user, &computed);
7534 let point = |off: usize| user.points.get(&off).or(computed.points.get(&off));
7535 let base = nums.slice(start, n);
7536 let got = cascade.select_numbers(range.clone(), &base);
7537 assert_eq!(got.len(), n);
7538 for i in 0..n {
7539 let want = match point(start + i) {
7540 Some(v) => v.numeric_lane_value(),
7541 None => base.is_valid(i).then(|| base.value(i)),
7542 };
7543 let have = got.is_valid(i).then(|| got.value(i));
7544 assert_eq!(
7545 have.map(f64::to_bits),
7546 want.map(f64::to_bits),
7547 "case {case} row {i}"
7548 );
7549 }
7550 let base = errs.slice(start, n);
7551 let got = cascade.select_errors(range.clone(), &base);
7552 for i in 0..n {
7553 let want = match point(start + i) {
7554 Some(v) => v.error_lane_value(),
7555 None => base.is_valid(i).then(|| base.value(i)),
7556 };
7557 assert_eq!(
7558 got.is_valid(i).then(|| got.value(i)),
7559 want,
7560 "case {case} row {i}"
7561 );
7562 }
7563 }
7564 let stats = snapshot_overlay_select_stats();
7565 assert!(stats.point_patch_selects > 400, "{stats:?}");
7566 assert_eq!(stats.zip_select_calls, 0);
7567 }
7568
7569 #[test]
7570 fn rangeview_multi_fragment_full_union_does_not_use_direct_path() {
7571 let mut sheet = numeric_sheet(4);
7572 let chunk = &mut sheet.columns[0].chunks[0];
7573 chunk.computed_overlay.apply_fragment(
7574 OverlayFragment::dense_range(0, vec![OverlayValue::Number(10.0); 2]).unwrap(),
7575 );
7576 chunk.computed_overlay.apply_fragment(
7577 OverlayFragment::dense_range(2, vec![OverlayValue::Number(20.0); 2]).unwrap(),
7578 );
7579
7580 reset_overlay_select_stats();
7581 let numbers = numbers_for_range(&sheet, 0, 3);
7582 assert_eq!(numbers.value(0), 10.0);
7583 assert_eq!(numbers.value(1), 10.0);
7584 assert_eq!(numbers.value(2), 20.0);
7585 assert_eq!(numbers.value(3), 20.0);
7586 let stats = snapshot_overlay_select_stats();
7587 assert_eq!(stats.direct_dense_slices, 0);
7588 assert_eq!(stats.zip_select_calls, 1);
7589 assert_eq!(stats.partial_dense_intersections, 2);
7590 }
7591
7592 #[test]
7593 fn rangeview_lowered_text_fragment_semantics_match_scalar_semantics() {
7594 let mut sheet = numeric_sheet(8);
7595 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7596 OverlayFragment::dense_range(
7597 0,
7598 vec![
7599 OverlayValue::Text(Arc::from("HeLLo")),
7600 OverlayValue::Number(1.5),
7601 OverlayValue::DateTime(45000.25),
7602 OverlayValue::Duration(0.5),
7603 OverlayValue::Boolean(true),
7604 OverlayValue::Empty,
7605 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
7606 OverlayValue::Pending,
7607 ],
7608 )
7609 .unwrap(),
7610 );
7611
7612 reset_overlay_select_stats();
7613 let lowered = lowered_for_range(&sheet, 0, 7);
7614 assert_eq!(lowered.value(0), "hello");
7615 assert_eq!(lowered.value(1), "1.5");
7616 assert_eq!(lowered.value(2), "45000.25");
7617 assert_eq!(lowered.value(3), "0.5");
7618 assert_eq!(lowered.value(4), "true");
7619 assert!(lowered.is_null(5));
7620 assert!(lowered.is_null(6));
7621 assert!(lowered.is_null(7));
7622 let stats = snapshot_overlay_select_stats();
7623 assert_eq!(stats.direct_dense_slices, 1);
7624 assert_eq!(stats.zip_select_calls, 0);
7625 }
7626
7627 #[test]
7628 fn row_chunk_slices_shape() {
7629 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7631 b.append_row(&[LiteralValue::Text("a".into()), LiteralValue::Number(1.0)])
7632 .unwrap();
7633 b.append_row(&[LiteralValue::Text("b".into()), LiteralValue::Number(2.0)])
7634 .unwrap();
7635 b.append_row(&[LiteralValue::Text("c".into()), LiteralValue::Number(3.0)])
7636 .unwrap();
7637 let sheet = b.finish();
7638 let rv = sheet.range_view(0, 0, 2, 1);
7639 let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
7640 assert_eq!(slices.len(), 2);
7641 assert_eq!(slices[0].row_start, 0);
7642 assert_eq!(slices[0].row_len, 2);
7643 assert_eq!(slices[0].cols.len(), 2);
7644 assert_eq!(slices[1].row_start, 2);
7645 assert_eq!(slices[1].row_len, 1);
7646 assert_eq!(slices[1].cols.len(), 2);
7647 }
7648
7649 #[test]
7650 fn oob_columns_are_padded() {
7651 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7653 b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("a".into())])
7654 .unwrap();
7655 b.append_row(&[LiteralValue::Number(2.0), LiteralValue::Text("b".into())])
7656 .unwrap();
7657 let sheet = b.finish();
7658 let rv = sheet.range_view(0, 0, 1, 2);
7660 assert_eq!(rv.dims(), (2, 3));
7661 let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
7662 assert!(!slices.is_empty());
7663 for cs in &slices {
7664 assert_eq!(cs.cols.len(), 3);
7665 }
7666 for res in rv.numbers_slices() {
7668 let (_rs, _rl, cols) = res.unwrap();
7669 assert_eq!(cols.len(), 3);
7670 }
7671 for res in rv.booleans_slices() {
7672 let (_rs, _rl, cols) = res.unwrap();
7673 assert_eq!(cols.len(), 3);
7674 }
7675 for res in rv.text_slices() {
7676 let (_rs, _rl, cols) = res.unwrap();
7677 assert_eq!(cols.len(), 3);
7678 }
7679 for res in rv.errors_slices() {
7680 let (_rs, _rl, cols) = res.unwrap();
7681 assert_eq!(cols.len(), 3);
7682 }
7683 for res in rv.lowered_text_slices() {
7684 let (_rs, _rl, cols) = res.unwrap();
7685 assert_eq!(cols.len(), 3);
7686 }
7687 }
7688
7689 #[test]
7690 fn reversed_range_is_empty() {
7691 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7692 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
7693 b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
7694 let sheet = b.finish();
7695 let rv = sheet.range_view(3, 0, 1, 0); assert_eq!(rv.dims(), (0, 0));
7697 assert!(rv.iter_row_chunks().next().is_none());
7698 assert_eq!(rv.get_cell(0, 0), LiteralValue::Empty);
7699 }
7700
7701 #[test]
7702 fn chunk_alignment_invariant() {
7703 let mut b = IngestBuilder::new("S", 3, 2, crate::engine::DateSystem::Excel1900);
7704 for r in 0..5 {
7706 b.append_row(&[
7707 LiteralValue::Number(r as f64),
7708 LiteralValue::Text(format!("{r}")),
7709 if r % 2 == 0 {
7710 LiteralValue::Empty
7711 } else {
7712 LiteralValue::Boolean(true)
7713 },
7714 ])
7715 .unwrap();
7716 }
7717 let sheet = b.finish();
7718 assert_eq!(sheet.chunk_starts, vec![0, 2, 4]);
7720 let lens0: Vec<usize> = sheet.columns[0]
7722 .chunks
7723 .iter()
7724 .map(|ch| ch.type_tag.len())
7725 .collect();
7726 for col in &sheet.columns[1..] {
7727 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7728 assert_eq!(lens, lens0);
7729 }
7730 }
7731
7732 #[test]
7733 fn chunking_splits_rows() {
7734 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7736 let rows = vec![
7737 vec![LiteralValue::Number(1.0), LiteralValue::Text("a".into())],
7738 vec![LiteralValue::Empty, LiteralValue::Text("b".into())],
7739 vec![LiteralValue::Boolean(true), LiteralValue::Empty],
7740 ];
7741 for r in rows {
7742 b.append_row(&r).unwrap();
7743 }
7744 let sheet = b.finish();
7745 assert_eq!(sheet.columns[0].chunks.len(), 2);
7746 assert_eq!(sheet.columns[1].chunks.len(), 2);
7747 assert_eq!(sheet.columns[0].chunks[0].numbers_or_null().len(), 2);
7748 assert_eq!(sheet.columns[0].chunks[1].numbers_or_null().len(), 1);
7749 }
7750
7751 #[test]
7752 fn pending_is_not_error() {
7753 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
7754 b.append_row(&[LiteralValue::Pending]).unwrap();
7755 let sheet = b.finish();
7756 let ch = &sheet.columns[0].chunks[0];
7757 assert_eq!(ch.type_tag.values()[0], super::TypeTag::Pending as u8);
7759 let errs = ch.errors_or_null();
7761 assert_eq!(errs.null_count(), 1);
7762 }
7763
7764 #[test]
7765 fn all_null_numeric_lane_uses_null_array() {
7766 let mut b = IngestBuilder::new("S", 1, 16, crate::engine::DateSystem::Excel1900);
7768 b.append_row(&[LiteralValue::Text("a".into())]).unwrap();
7769 b.append_row(&[LiteralValue::Text("".into())]).unwrap();
7770 b.append_row(&[LiteralValue::Text("b".into())]).unwrap();
7771 let sheet = b.finish();
7772 let ch = &sheet.columns[0].chunks[0];
7773 let nums = ch.numbers_or_null();
7774 assert_eq!(nums.len(), 3);
7775 assert_eq!(nums.null_count(), 3);
7776 assert_eq!(nums.data_type(), &DataType::Float64);
7777 }
7778
7779 #[test]
7780 fn row_insert_delete_across_chunk_boundaries_with_overlays() {
7781 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7783 for _ in 0..10 {
7784 b.append_row(&[LiteralValue::Empty]).unwrap();
7785 }
7786 let mut sheet = b.finish();
7787 {
7789 let (c0, o0) = sheet.chunk_of_row(3).unwrap();
7790 sheet.columns[0].chunks[c0]
7791 .overlay
7792 .set(o0, OverlayValue::Number(30.0));
7793 let (c1, o1) = sheet.chunk_of_row(4).unwrap();
7794 sheet.columns[0].chunks[c1]
7795 .overlay
7796 .set(o1, OverlayValue::Number(40.0));
7797 }
7798 sheet.insert_rows(4, 2);
7800 assert_eq!(sheet.nrows, 12);
7801 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7803 assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
7804 assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
7805 assert_eq!(av.get_cell(6, 0), LiteralValue::Number(40.0));
7806
7807 sheet.delete_rows(3, 3);
7809 assert_eq!(sheet.nrows, 9);
7810 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7811 assert_eq!(av2.get_cell(3, 0), LiteralValue::Number(40.0));
7812 let lens0: Vec<usize> = sheet.columns[0]
7814 .chunks
7815 .iter()
7816 .map(|ch| ch.type_tag.len())
7817 .collect();
7818 for col in &sheet.columns {
7819 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7820 assert_eq!(lens, lens0);
7821 }
7822 assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
7824 let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
7825 let last_len = sheet.columns[0]
7826 .chunks
7827 .last()
7828 .map(|c| c.type_tag.len())
7829 .unwrap_or(0);
7830 assert_eq!(last_start + last_len, sheet.nrows as usize);
7831 }
7832
7833 #[test]
7834 fn row_insert_delete_preserves_user_dense_fragments() {
7835 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7836 for _ in 0..10 {
7837 b.append_row(&[LiteralValue::Empty]).unwrap();
7838 }
7839 let mut sheet = b.finish();
7840
7841 let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
7842 sheet.columns[0]
7843 .chunk_mut(ch_idx)
7844 .unwrap()
7845 .overlay
7846 .apply_fragment(
7847 OverlayFragment::dense_range(
7848 off,
7849 vec![
7850 OverlayValue::Number(10.0),
7851 OverlayValue::Number(20.0),
7852 OverlayValue::Number(30.0),
7853 ],
7854 )
7855 .unwrap(),
7856 );
7857
7858 let before = column_overlay_stats(&sheet, 0, false);
7859 assert_eq!(before.dense_fragments, 1);
7860 assert_eq!(before.sparse_fragments, 0);
7861 assert_column_overlays_normalized(&sheet, 0);
7862
7863 sheet.insert_rows(2, 2);
7864 assert_eq!(sheet.nrows, 12);
7865 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7866 assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
7867 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7868 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7869 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(20.0));
7870 assert_eq!(av.get_cell(5, 0), LiteralValue::Number(30.0));
7871 let after_insert = column_overlay_stats(&sheet, 0, false);
7872 assert_eq!(after_insert.sparse_fragments, 0);
7873 assert!(after_insert.dense_fragments >= 2);
7874 assert_column_overlays_normalized(&sheet, 0);
7875
7876 sheet.delete_rows(2, 2);
7877 assert_eq!(sheet.nrows, 10);
7878 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7879 assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
7880 assert_eq!(av.get_cell(2, 0), LiteralValue::Number(20.0));
7881 assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
7882 let after_delete = column_overlay_stats(&sheet, 0, false);
7883 assert_eq!(after_delete.sparse_fragments, 0);
7884 assert!(after_delete.dense_fragments >= 1);
7885 assert_column_overlays_normalized(&sheet, 0);
7886 }
7887
7888 #[test]
7889 fn row_insert_delete_preserves_computed_empty_run_fragments() {
7890 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7891 for row in 0..8 {
7892 b.append_row(&[LiteralValue::Number((row + 1) as f64)])
7893 .unwrap();
7894 }
7895 let mut sheet = b.finish();
7896
7897 let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
7898 sheet.columns[0]
7899 .chunk_mut(ch_idx)
7900 .unwrap()
7901 .computed_overlay
7902 .apply_fragment(
7903 OverlayFragment::run_range(
7904 off,
7905 vec![
7906 OverlayValue::Empty,
7907 OverlayValue::Empty,
7908 OverlayValue::Empty,
7909 ],
7910 )
7911 .unwrap(),
7912 );
7913
7914 let before = column_overlay_stats(&sheet, 0, true);
7915 assert_eq!(before.run_fragments, 1);
7916 assert_eq!(before.sparse_fragments, 0);
7917 assert_column_overlays_normalized(&sheet, 0);
7918
7919 sheet.insert_rows(2, 1);
7920 assert_eq!(sheet.nrows, 9);
7921 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7922 assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
7923 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7924 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7925 assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
7926 assert_eq!(av.get_cell(5, 0), LiteralValue::Number(5.0));
7927 let after_insert = column_overlay_stats(&sheet, 0, true);
7928 assert_eq!(after_insert.sparse_fragments, 0);
7929 assert!(after_insert.run_fragments >= 2);
7930 assert_column_overlays_normalized(&sheet, 0);
7931
7932 sheet.delete_rows(2, 1);
7933 assert_eq!(sheet.nrows, 8);
7934 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7935 assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
7936 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7937 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7938 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(5.0));
7939 let after_delete = column_overlay_stats(&sheet, 0, true);
7940 assert_eq!(after_delete.sparse_fragments, 0);
7941 assert!(after_delete.run_fragments >= 1);
7942 assert_column_overlays_normalized(&sheet, 0);
7943 }
7944
7945 #[test]
7946 fn column_insert_delete_retains_chunk_alignment() {
7947 let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
7948 for _ in 0..5 {
7949 b.append_row(&[
7950 LiteralValue::Empty,
7951 LiteralValue::Empty,
7952 LiteralValue::Empty,
7953 ])
7954 .unwrap();
7955 }
7956 let mut sheet = b.finish();
7957 let ref_lens: Vec<usize> = sheet.columns[0]
7959 .chunks
7960 .iter()
7961 .map(|ch| ch.type_tag.len())
7962 .collect();
7963 sheet.insert_columns(1, 2);
7965 assert_eq!(sheet.columns.len(), 5);
7966 for col in &sheet.columns {
7967 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7968 assert_eq!(lens, ref_lens);
7969 }
7970 let starts_before = sheet.chunk_starts.clone();
7971 sheet.delete_columns(2, 2);
7973 assert_eq!(sheet.columns.len(), 3);
7974 for col in &sheet.columns {
7975 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7976 assert_eq!(lens, ref_lens);
7977 }
7978 assert_eq!(sheet.chunk_starts, starts_before);
7980 }
7981
7982 #[test]
7983 fn multiple_adjacent_row_ops_overlay_mixed_types() {
7984 use formualizer_common::ExcelErrorKind;
7985 let mut b = IngestBuilder::new("S", 2, 3, crate::engine::DateSystem::Excel1900);
7987 for _ in 0..9 {
7988 b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
7989 .unwrap();
7990 }
7991 let mut sheet = b.finish();
7992 let set_ov = |sh: &mut ArrowSheet, row: usize, ov: OverlayValue| {
7995 let (ch_i, off) = sh.chunk_of_row(row).unwrap();
7996 let _ = sh.columns[0].chunks[ch_i].overlay.set(off, ov);
7997 };
7998 set_ov(&mut sheet, 2, OverlayValue::Number(12.5));
7999 set_ov(&mut sheet, 3, OverlayValue::Text(Arc::from("hello")));
8000 set_ov(&mut sheet, 5, OverlayValue::Boolean(true));
8001 set_ov(
8002 &mut sheet,
8003 6,
8004 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
8005 );
8006 set_ov(&mut sheet, 8, OverlayValue::Empty);
8007
8008 sheet.insert_rows(3, 1);
8010 let av1 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8012 assert_eq!(av1.get_cell(2, 0), LiteralValue::Number(12.5));
8013 assert_eq!(av1.get_cell(4, 0), LiteralValue::Text("hello".into()));
8014 assert_eq!(av1.get_cell(6, 0), LiteralValue::Boolean(true));
8015 match av1.get_cell(7, 0) {
8016 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8017 other => panic!("expected error at row 7, got {other:?}"),
8018 }
8019 assert_eq!(av1.get_cell(9, 0), LiteralValue::Empty);
8020
8021 sheet.insert_rows(4, 2);
8023 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8025 assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.5));
8026 assert_eq!(av2.get_cell(6, 0), LiteralValue::Text("hello".into()));
8027 assert_eq!(av2.get_cell(8, 0), LiteralValue::Boolean(true));
8028 match av2.get_cell(9, 0) {
8029 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8030 other => panic!("expected error at row 9, got {other:?}"),
8031 }
8032 assert_eq!(av2.get_cell(11, 0), LiteralValue::Empty);
8033
8034 sheet.delete_rows(6, 2);
8036 let av3 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8037 assert_eq!(av3.get_cell(2, 0), LiteralValue::Number(12.5));
8039 assert_eq!(av3.get_cell(6, 0), LiteralValue::Boolean(true));
8040 match av3.get_cell(7, 0) {
8041 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8042 other => panic!("expected error at row 8, got {other:?}"),
8043 }
8044 assert_eq!(av3.get_cell(9, 0), LiteralValue::Empty);
8045
8046 let lens0: Vec<usize> = sheet.columns[0]
8048 .chunks
8049 .iter()
8050 .map(|ch| ch.type_tag.len())
8051 .collect();
8052 for col in &sheet.columns {
8053 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8054 assert_eq!(lens, lens0);
8055 }
8056 assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
8058 let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
8059 let last_len = sheet.columns[0]
8060 .chunks
8061 .last()
8062 .map(|c| c.type_tag.len())
8063 .unwrap_or(0);
8064 assert_eq!(last_start + last_len, sheet.nrows as usize);
8065 }
8066
8067 #[test]
8068 fn multiple_adjacent_column_ops_alignment() {
8069 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
8071 for _ in 0..5 {
8072 b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
8073 .unwrap();
8074 }
8075 let mut sheet = b.finish();
8076 let ref_lens: Vec<usize> = sheet.columns[0]
8077 .chunks
8078 .iter()
8079 .map(|ch| ch.type_tag.len())
8080 .collect();
8081 sheet.insert_columns(0, 1);
8083 sheet.insert_columns(2, 2);
8084 assert_eq!(sheet.columns.len(), 5);
8085 for col in &sheet.columns {
8086 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8087 assert_eq!(lens, ref_lens);
8088 }
8089 let starts_before = sheet.chunk_starts.clone();
8090 sheet.delete_columns(1, 1);
8092 let remain = sheet.columns.len();
8093 if remain >= 3 {
8094 sheet.delete_columns(remain - 2, 2);
8095 }
8096 for col in &sheet.columns {
8097 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8098 assert_eq!(lens, ref_lens);
8099 }
8100 assert_eq!(sheet.chunk_starts, starts_before);
8101 }
8102
8103 #[test]
8104 fn overlays_on_multiple_columns_row_col_ops() {
8105 let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
8107 for _ in 0..6 {
8108 b.append_row(&[
8109 LiteralValue::Empty,
8110 LiteralValue::Empty,
8111 LiteralValue::Empty,
8112 ])
8113 .unwrap();
8114 }
8115 let mut sheet = b.finish();
8116 let set_ov = |sh: &mut ArrowSheet, col: usize, row: usize, ov: OverlayValue| {
8118 let (ch_i, off) = sh.chunk_of_row(row).unwrap();
8119 let _ = sh.columns[col].chunks[ch_i].overlay.set(off, ov);
8120 };
8121 set_ov(&mut sheet, 0, 2, OverlayValue::Number(12.0));
8122 set_ov(&mut sheet, 1, 2, OverlayValue::Text(Arc::from("xx")));
8123 set_ov(&mut sheet, 2, 2, OverlayValue::Boolean(true));
8124 set_ov(&mut sheet, 0, 3, OverlayValue::Number(33.0));
8125 set_ov(&mut sheet, 1, 3, OverlayValue::Text(Arc::from("yy")));
8126 set_ov(&mut sheet, 2, 3, OverlayValue::Boolean(false));
8127
8128 sheet.insert_rows(3, 1);
8130 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 2);
8132 assert_eq!(av.get_cell(2, 0), LiteralValue::Number(12.0));
8134 assert_eq!(av.get_cell(2, 1), LiteralValue::Text("xx".into()));
8135 assert_eq!(av.get_cell(2, 2), LiteralValue::Boolean(true));
8136 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
8138 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(33.0));
8140 assert_eq!(av.get_cell(4, 1), LiteralValue::Text("yy".into()));
8141 assert_eq!(av.get_cell(4, 2), LiteralValue::Boolean(false));
8142
8143 sheet.delete_columns(1, 1);
8145 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 1);
8146 assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.0));
8147 assert_eq!(av2.get_cell(2, 1), LiteralValue::Boolean(true));
8149 assert_eq!(av2.get_cell(4, 0), LiteralValue::Number(33.0));
8150 assert_eq!(av2.get_cell(4, 1), LiteralValue::Boolean(false));
8151
8152 let lens0: Vec<usize> = sheet.columns[0]
8154 .chunks
8155 .iter()
8156 .map(|ch| ch.type_tag.len())
8157 .collect();
8158 for col in &sheet.columns {
8159 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8160 assert_eq!(lens, lens0);
8161 }
8162 }
8163
8164 #[test]
8165 fn effective_slices_overlay_precedence_numbers_text() {
8166 let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
8168 for i in 0..6 {
8169 b.append_row(&[LiteralValue::Number((i + 1) as f64)])
8170 .unwrap();
8171 }
8172 let mut sheet = b.finish();
8173 let (c1, o1) = sheet.chunk_of_row(1).unwrap();
8175 sheet.columns[0].chunks[c1]
8176 .overlay
8177 .set(o1, OverlayValue::Text(Arc::from("X")));
8178 let (c4, o4) = sheet.chunk_of_row(4).unwrap();
8179 sheet.columns[0].chunks[c4]
8180 .overlay
8181 .set(o4, OverlayValue::Number(99.0));
8182
8183 let av = sheet.range_view(0, 0, 5, 0);
8184 let mut numeric: Vec<Option<f64>> = vec![None; 6];
8186 for res in av.numbers_slices() {
8187 let (row_start, row_len, cols) = res.unwrap();
8188 let a = &cols[0];
8189 for i in 0..row_len {
8190 let idx = row_start + i;
8191 numeric[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8192 }
8193 }
8194 assert_eq!(numeric[0], Some(1.0));
8195 assert_eq!(numeric[1], None); assert_eq!(numeric[2], Some(3.0));
8197 assert_eq!(numeric[3], Some(4.0));
8198 assert_eq!(numeric[4], Some(99.0));
8199 assert_eq!(numeric[5], Some(6.0));
8200
8201 let mut texts: Vec<Option<String>> = vec![None; 6];
8203 for res in av.text_slices() {
8204 let (row_start, row_len, cols) = res.unwrap();
8205 let a = cols[0].as_any().downcast_ref::<StringArray>().unwrap();
8206 for i in 0..row_len {
8207 let idx = row_start + i;
8208 texts[idx] = if a.is_null(i) {
8209 None
8210 } else {
8211 Some(a.value(i).to_string())
8212 };
8213 }
8214 }
8215 assert_eq!(texts[1].as_deref(), Some("X"));
8216 assert!(texts[0].is_none());
8217 assert!(texts[2].is_none());
8218 assert!(texts[3].is_none());
8219 assert!(texts[4].is_none());
8220 assert!(texts[5].is_none());
8221 }
8222
8223 #[test]
8224 fn effective_slices_overlay_precedence_booleans() {
8225 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
8227 for i in 0..6 {
8228 let v = if i % 2 == 0 {
8229 LiteralValue::Boolean(true)
8230 } else {
8231 LiteralValue::Boolean(false)
8232 };
8233 b.append_row(&[v]).unwrap();
8234 }
8235 let mut sheet = b.finish();
8236 let (c1, o1) = sheet.chunk_of_row(1).unwrap();
8238 sheet.columns[0].chunks[c1]
8239 .overlay
8240 .set(o1, OverlayValue::Boolean(true));
8241 let (c2, o2) = sheet.chunk_of_row(2).unwrap();
8242 sheet.columns[0].chunks[c2]
8243 .overlay
8244 .set(o2, OverlayValue::Text(Arc::from("T")));
8245
8246 let av = sheet.range_view(0, 0, 5, 0);
8247 let mut bools: Vec<Option<bool>> = vec![None; 6];
8249 for res in av.booleans_slices() {
8250 let (row_start, row_len, cols) = res.unwrap();
8251 let a = &cols[0];
8252 for i in 0..row_len {
8253 let idx = row_start + i;
8254 bools[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8255 }
8256 }
8257 assert_eq!(bools[0], Some(true));
8258 assert_eq!(bools[1], Some(true)); assert_eq!(bools[2], None); assert_eq!(bools[3], Some(false));
8262 }
8263
8264 #[test]
8265 fn effective_slices_overlay_precedence_errors() {
8266 let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
8268 for i in 0..6 {
8269 b.append_row(&[LiteralValue::Number((i + 1) as f64)])
8270 .unwrap();
8271 }
8272 let mut sheet = b.finish();
8273 let (c4, o4) = sheet.chunk_of_row(4).unwrap();
8275 sheet.columns[0].chunks[c4]
8276 .overlay
8277 .set(o4, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
8278
8279 let av = sheet.range_view(0, 0, 5, 0);
8280 let mut errs: Vec<Option<u8>> = vec![None; 6];
8281 for res in av.errors_slices() {
8282 let (row_start, row_len, cols) = res.unwrap();
8283 let a = &cols[0];
8284 for i in 0..row_len {
8285 let idx = row_start + i;
8286 errs[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8287 }
8288 }
8289 assert_eq!(errs[4], Some(map_error_code(ExcelErrorKind::Div)));
8290 assert!(errs[3].is_none());
8291 }
8292}