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