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