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 formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
11use rustc_hash::FxHashMap;
12use std::collections::{BTreeMap, HashMap};
13
14#[repr(u8)]
16#[derive(Copy, Clone, Debug, Eq, PartialEq)]
17pub enum TypeTag {
18 Empty = 0,
19 Number = 1,
20 Boolean = 2,
21 Text = 3,
22 Error = 4,
23 DateTime = 5, Duration = 6, Pending = 7,
26}
27
28impl TypeTag {
29 fn from_value(v: &LiteralValue) -> Self {
30 match v {
31 LiteralValue::Empty => TypeTag::Empty,
32 LiteralValue::Int(_) | LiteralValue::Number(_) => TypeTag::Number,
33 LiteralValue::Boolean(_) => TypeTag::Boolean,
34 LiteralValue::Text(_) => TypeTag::Text,
35 LiteralValue::Error(_) => TypeTag::Error,
36 LiteralValue::Date(_) | LiteralValue::DateTime(_) | LiteralValue::Time(_) => {
37 TypeTag::DateTime
38 }
39 LiteralValue::Duration(_) => TypeTag::Duration,
40 LiteralValue::Pending => TypeTag::Pending,
41 LiteralValue::Array(_) => TypeTag::Error, }
43 }
44}
45
46impl TypeTag {
47 #[inline]
48 pub fn from_u8(b: u8) -> Self {
49 match b {
50 x if x == TypeTag::Empty as u8 => TypeTag::Empty,
51 x if x == TypeTag::Number as u8 => TypeTag::Number,
52 x if x == TypeTag::Boolean as u8 => TypeTag::Boolean,
53 x if x == TypeTag::Text as u8 => TypeTag::Text,
54 x if x == TypeTag::Error as u8 => TypeTag::Error,
55 x if x == TypeTag::DateTime as u8 => TypeTag::DateTime,
56 x if x == TypeTag::Duration as u8 => TypeTag::Duration,
57 x if x == TypeTag::Pending as u8 => TypeTag::Pending,
58 _ => TypeTag::Empty,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, Default)]
64pub struct ColumnChunkMeta {
65 pub len: usize,
66 pub non_null_num: usize,
67 pub non_null_bool: usize,
68 pub non_null_text: usize,
69 pub non_null_err: usize,
70}
71
72#[derive(Debug, Clone)]
73pub struct ColumnChunk {
74 pub numbers: Option<Arc<Float64Array>>,
75 pub booleans: Option<Arc<BooleanArray>>,
76 pub text: Option<ArrayRef>, pub errors: Option<Arc<UInt8Array>>, pub type_tag: Arc<UInt8Array>,
79 pub formula_id: Option<Arc<UInt32Array>>, pub meta: ColumnChunkMeta,
81 lazy_null_numbers: OnceCell<Arc<Float64Array>>,
83 lazy_null_booleans: OnceCell<Arc<BooleanArray>>,
84 lazy_null_text: OnceCell<ArrayRef>,
85 lazy_null_errors: OnceCell<Arc<UInt8Array>>,
86 lowered_text: OnceCell<ArrayRef>,
88 pub overlay: Overlay,
90 pub computed_overlay: Overlay,
92}
93
94impl ColumnChunk {
95 #[inline]
96 pub fn len(&self) -> usize {
97 self.type_tag.len()
98 }
99 #[inline]
100 pub fn is_empty(&self) -> bool {
101 self.len() == 0
102 }
103 #[inline]
104 pub fn numbers_or_null(&self) -> Arc<Float64Array> {
105 if let Some(a) = &self.numbers {
106 return a.clone();
107 }
108 self.lazy_null_numbers
109 .get_or_init(|| {
110 let arr = new_null_array(&DataType::Float64, self.len());
111 Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
112 })
113 .clone()
114 }
115 #[inline]
116 pub fn booleans_or_null(&self) -> Arc<BooleanArray> {
117 if let Some(a) = &self.booleans {
118 return a.clone();
119 }
120 self.lazy_null_booleans
121 .get_or_init(|| {
122 let arr = new_null_array(&DataType::Boolean, self.len());
123 Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
124 })
125 .clone()
126 }
127 #[inline]
128 pub fn errors_or_null(&self) -> Arc<UInt8Array> {
129 if let Some(a) = &self.errors {
130 return a.clone();
131 }
132 self.lazy_null_errors
133 .get_or_init(|| {
134 let arr = new_null_array(&DataType::UInt8, self.len());
135 Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
136 })
137 .clone()
138 }
139 #[inline]
140 pub fn text_or_null(&self) -> ArrayRef {
141 if let Some(a) = &self.text {
142 return a.clone();
143 }
144 self.lazy_null_text
145 .get_or_init(|| new_null_array(&DataType::Utf8, self.len()))
146 .clone()
147 }
148
149 pub fn text_lower_or_null(&self) -> ArrayRef {
151 if let Some(a) = self.lowered_text.get() {
152 return a.clone();
153 }
154 let out: ArrayRef = if let Some(txt) = &self.text {
156 let sa = txt.as_any().downcast_ref::<StringArray>().unwrap();
157 let mut b = arrow_array::builder::StringBuilder::with_capacity(sa.len(), sa.len() * 8);
158 for i in 0..sa.len() {
159 if sa.is_null(i) {
160 b.append_null();
161 } else {
162 b.append_value(sa.value(i).to_lowercase());
163 }
164 }
165 let lowered = b.finish();
166 Arc::new(lowered)
167 } else {
168 new_null_array(&DataType::Utf8, self.len())
169 };
170 self.lowered_text.get_or_init(|| out.clone());
171 out
172 }
173
174 pub fn grow_len_to(&mut self, new_len: usize) {
179 let old_len = self.len();
180 if new_len <= old_len {
181 return;
182 }
183
184 let mut tags: Vec<u8> = self.type_tag.values().to_vec();
186 tags.resize(new_len, TypeTag::Empty as u8);
187 self.type_tag = Arc::new(UInt8Array::from(tags));
188
189 if let Some(a) = &self.numbers {
191 use arrow_array::builder::Float64Builder;
192 let mut b = Float64Builder::with_capacity(new_len);
193 for i in 0..old_len {
194 if a.is_null(i) {
195 b.append_null();
196 } else {
197 b.append_value(a.value(i));
198 }
199 }
200 for _ in old_len..new_len {
201 b.append_null();
202 }
203 self.numbers = Some(Arc::new(b.finish()));
204 }
205 if let Some(a) = &self.booleans {
206 use arrow_array::builder::BooleanBuilder;
207 let mut b = BooleanBuilder::with_capacity(new_len);
208 for i in 0..old_len {
209 if a.is_null(i) {
210 b.append_null();
211 } else {
212 b.append_value(a.value(i));
213 }
214 }
215 for _ in old_len..new_len {
216 b.append_null();
217 }
218 self.booleans = Some(Arc::new(b.finish()));
219 }
220 if let Some(a) = &self.errors {
221 use arrow_array::builder::UInt8Builder;
222 let mut b = UInt8Builder::with_capacity(new_len);
223 for i in 0..old_len {
224 if a.is_null(i) {
225 b.append_null();
226 } else {
227 b.append_value(a.value(i));
228 }
229 }
230 for _ in old_len..new_len {
231 b.append_null();
232 }
233 self.errors = Some(Arc::new(b.finish()));
234 }
235 if let Some(a) = &self.text {
236 use arrow_array::builder::StringBuilder;
237 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
238 let mut b = StringBuilder::with_capacity(new_len, 0);
239 for i in 0..old_len {
240 if sa.is_null(i) {
241 b.append_null();
242 } else {
243 b.append_value(sa.value(i));
244 }
245 }
246 for _ in old_len..new_len {
247 b.append_null();
248 }
249 self.text = Some(Arc::new(b.finish()) as ArrayRef);
250 }
251
252 self.lazy_null_numbers = OnceCell::new();
254 self.lazy_null_booleans = OnceCell::new();
255 self.lazy_null_text = OnceCell::new();
256 self.lazy_null_errors = OnceCell::new();
257 self.lowered_text = OnceCell::new();
258
259 self.meta.len = new_len;
260 }
261}
262
263#[derive(Debug, Clone)]
264pub struct ArrowColumn {
265 pub chunks: Vec<ColumnChunk>,
266 pub sparse_chunks: FxHashMap<usize, ColumnChunk>,
267 pub index: u32,
268}
269
270impl ArrowColumn {
271 #[inline]
272 pub fn chunk(&self, idx: usize) -> Option<&ColumnChunk> {
273 if idx < self.chunks.len() {
274 Some(&self.chunks[idx])
275 } else {
276 self.sparse_chunks.get(&idx)
277 }
278 }
279
280 #[inline]
281 pub fn chunk_mut(&mut self, idx: usize) -> Option<&mut ColumnChunk> {
282 if idx < self.chunks.len() {
283 Some(&mut self.chunks[idx])
284 } else {
285 self.sparse_chunks.get_mut(&idx)
286 }
287 }
288
289 #[inline]
290 pub fn has_sparse_chunks(&self) -> bool {
291 !self.sparse_chunks.is_empty()
292 }
293
294 #[inline]
295 pub fn total_chunk_count(&self) -> usize {
296 self.chunks.len() + self.sparse_chunks.len()
297 }
298}
299
300#[derive(Debug, Clone)]
301pub struct ArrowSheet {
302 pub name: Arc<str>,
303 pub date_system: crate::engine::DateSystem,
308 pub columns: Vec<ArrowColumn>,
309 pub nrows: u32,
310 pub chunk_starts: Vec<usize>,
311 pub chunk_rows: usize,
316}
317
318#[derive(Debug, Default, Clone)]
319pub struct SheetStore {
320 pub sheets: Vec<ArrowSheet>,
321}
322
323impl SheetStore {
324 pub fn sheet(&self, name: &str) -> Option<&ArrowSheet> {
325 self.sheets.iter().find(|s| s.name.as_ref() == name)
326 }
327 pub fn sheet_mut(&mut self, name: &str) -> Option<&mut ArrowSheet> {
328 self.sheets.iter_mut().find(|s| s.name.as_ref() == name)
329 }
330}
331
332pub struct IngestBuilder {
334 name: Arc<str>,
335 ncols: usize,
336 chunk_rows: usize,
337 date_system: crate::engine::DateSystem,
338
339 num_builders: Vec<Float64Builder>,
341 bool_builders: Vec<BooleanBuilder>,
342 text_builders: Vec<StringBuilder>,
343 err_builders: Vec<UInt8Builder>,
344 tag_builders: Vec<UInt8Builder>,
345
346 lane_counts: Vec<LaneCounts>,
348
349 chunks: Vec<Vec<ColumnChunk>>, row_in_chunk: usize,
352 total_rows: u32,
353}
354
355#[derive(Debug, Clone, Copy, Default)]
356struct LaneCounts {
357 n_num: usize,
358 n_bool: usize,
359 n_text: usize,
360 n_err: usize,
361}
362
363impl IngestBuilder {
364 pub fn new(
365 sheet_name: &str,
366 ncols: usize,
367 chunk_rows: usize,
368 date_system: crate::engine::DateSystem,
369 ) -> Self {
370 let mut chunks = Vec::with_capacity(ncols);
371 chunks.resize_with(ncols, Vec::new);
372 Self {
373 name: Arc::from(sheet_name.to_string()),
374 ncols,
375 chunk_rows: chunk_rows.max(1),
376 date_system,
377 num_builders: (0..ncols)
378 .map(|_| Float64Builder::with_capacity(chunk_rows))
379 .collect(),
380 bool_builders: (0..ncols)
381 .map(|_| BooleanBuilder::with_capacity(chunk_rows))
382 .collect(),
383 text_builders: (0..ncols)
384 .map(|_| StringBuilder::with_capacity(chunk_rows, chunk_rows * 12))
385 .collect(),
386 err_builders: (0..ncols)
387 .map(|_| UInt8Builder::with_capacity(chunk_rows))
388 .collect(),
389 tag_builders: (0..ncols)
390 .map(|_| UInt8Builder::with_capacity(chunk_rows))
391 .collect(),
392 lane_counts: vec![LaneCounts::default(); ncols],
393 chunks,
394 row_in_chunk: 0,
395 total_rows: 0,
396 }
397 }
398
399 pub fn append_row_cells<'a>(&mut self, row: &[CellIngest<'a>]) -> Result<(), ExcelError> {
402 assert_eq!(row.len(), self.ncols, "row width mismatch");
403 for (c, cell) in row.iter().enumerate() {
404 match cell {
405 CellIngest::Empty => {
406 self.tag_builders[c].append_value(TypeTag::Empty as u8);
407 self.num_builders[c].append_null();
408 self.bool_builders[c].append_null();
409 self.text_builders[c].append_null();
410 self.err_builders[c].append_null();
411 }
412 CellIngest::Number(n) => {
413 self.tag_builders[c].append_value(TypeTag::Number as u8);
414 self.num_builders[c].append_value(*n);
415 self.lane_counts[c].n_num += 1;
416 self.bool_builders[c].append_null();
417 self.text_builders[c].append_null();
418 self.err_builders[c].append_null();
419 }
420 CellIngest::Boolean(b) => {
421 self.tag_builders[c].append_value(TypeTag::Boolean as u8);
422 self.num_builders[c].append_null();
423 self.bool_builders[c].append_value(*b);
424 self.lane_counts[c].n_bool += 1;
425 self.text_builders[c].append_null();
426 self.err_builders[c].append_null();
427 }
428 CellIngest::Text(s) => {
429 self.tag_builders[c].append_value(TypeTag::Text as u8);
430 self.num_builders[c].append_null();
431 self.bool_builders[c].append_null();
432 self.text_builders[c].append_value(s);
433 self.lane_counts[c].n_text += 1;
434 self.err_builders[c].append_null();
435 }
436 CellIngest::ErrorCode(code) => {
437 self.tag_builders[c].append_value(TypeTag::Error as u8);
438 self.num_builders[c].append_null();
439 self.bool_builders[c].append_null();
440 self.text_builders[c].append_null();
441 self.err_builders[c].append_value(*code);
442 self.lane_counts[c].n_err += 1;
443 }
444 CellIngest::DateSerial(serial) => {
445 self.tag_builders[c].append_value(TypeTag::DateTime as u8);
446 self.num_builders[c].append_value(*serial);
447 self.lane_counts[c].n_num += 1;
448 self.bool_builders[c].append_null();
449 self.text_builders[c].append_null();
450 self.err_builders[c].append_null();
451 }
452 CellIngest::Pending => {
453 self.tag_builders[c].append_value(TypeTag::Pending as u8);
454 self.num_builders[c].append_null();
455 self.bool_builders[c].append_null();
456 self.text_builders[c].append_null();
457 self.err_builders[c].append_null();
458 }
459 }
460 }
461 self.row_in_chunk += 1;
462 self.total_rows += 1;
463 if self.row_in_chunk >= self.chunk_rows {
464 self.finish_chunk();
465 }
466 Ok(())
467 }
468
469 pub fn append_row_cells_iter<'a, I>(&mut self, iter: I) -> Result<(), ExcelError>
472 where
473 I: ExactSizeIterator<Item = CellIngest<'a>>,
474 {
475 assert_eq!(iter.len(), self.ncols, "row width mismatch");
476 for (c, cell) in iter.enumerate() {
477 match cell {
478 CellIngest::Empty => {
479 self.tag_builders[c].append_value(TypeTag::Empty as u8);
480 self.num_builders[c].append_null();
481 self.bool_builders[c].append_null();
482 self.text_builders[c].append_null();
483 self.err_builders[c].append_null();
484 }
485 CellIngest::Number(n) => {
486 self.tag_builders[c].append_value(TypeTag::Number as u8);
487 self.num_builders[c].append_value(n);
488 self.lane_counts[c].n_num += 1;
489 self.bool_builders[c].append_null();
490 self.text_builders[c].append_null();
491 self.err_builders[c].append_null();
492 }
493 CellIngest::Boolean(b) => {
494 self.tag_builders[c].append_value(TypeTag::Boolean as u8);
495 self.num_builders[c].append_null();
496 self.bool_builders[c].append_value(b);
497 self.lane_counts[c].n_bool += 1;
498 self.text_builders[c].append_null();
499 self.err_builders[c].append_null();
500 }
501 CellIngest::Text(s) => {
502 self.tag_builders[c].append_value(TypeTag::Text as u8);
503 self.num_builders[c].append_null();
504 self.bool_builders[c].append_null();
505 self.text_builders[c].append_value(s);
506 self.lane_counts[c].n_text += 1;
507 self.err_builders[c].append_null();
508 }
509 CellIngest::ErrorCode(code) => {
510 self.tag_builders[c].append_value(TypeTag::Error as u8);
511 self.num_builders[c].append_null();
512 self.bool_builders[c].append_null();
513 self.text_builders[c].append_null();
514 self.err_builders[c].append_value(code);
515 self.lane_counts[c].n_err += 1;
516 }
517 CellIngest::DateSerial(serial) => {
518 self.tag_builders[c].append_value(TypeTag::DateTime as u8);
519 self.num_builders[c].append_value(serial);
520 self.lane_counts[c].n_num += 1;
521 self.bool_builders[c].append_null();
522 self.text_builders[c].append_null();
523 self.err_builders[c].append_null();
524 }
525 CellIngest::Pending => {
526 self.tag_builders[c].append_value(TypeTag::Pending as u8);
527 self.num_builders[c].append_null();
528 self.bool_builders[c].append_null();
529 self.text_builders[c].append_null();
530 self.err_builders[c].append_null();
531 }
532 }
533 }
534 self.row_in_chunk += 1;
535 self.total_rows += 1;
536 if self.row_in_chunk >= self.chunk_rows {
537 self.finish_chunk();
538 }
539 Ok(())
540 }
541
542 pub fn append_row(&mut self, row: &[LiteralValue]) -> Result<(), ExcelError> {
544 assert_eq!(row.len(), self.ncols, "row width mismatch");
545
546 for (c, v) in row.iter().enumerate() {
547 let tag = TypeTag::from_value(v) as u8;
548 self.tag_builders[c].append_value(tag);
549
550 match v {
551 LiteralValue::Empty => {
552 self.num_builders[c].append_null();
553 self.bool_builders[c].append_null();
554 self.text_builders[c].append_null();
555 self.err_builders[c].append_null();
556 }
557 LiteralValue::Int(i) => {
558 self.num_builders[c].append_value(*i as f64);
559 self.lane_counts[c].n_num += 1;
560 self.bool_builders[c].append_null();
561 self.text_builders[c].append_null();
562 self.err_builders[c].append_null();
563 }
564 LiteralValue::Number(n) => {
565 self.num_builders[c].append_value(*n);
566 self.lane_counts[c].n_num += 1;
567 self.bool_builders[c].append_null();
568 self.text_builders[c].append_null();
569 self.err_builders[c].append_null();
570 }
571 LiteralValue::Boolean(b) => {
572 self.num_builders[c].append_null();
573 self.bool_builders[c].append_value(*b);
574 self.lane_counts[c].n_bool += 1;
575 self.text_builders[c].append_null();
576 self.err_builders[c].append_null();
577 }
578 LiteralValue::Text(s) => {
579 self.num_builders[c].append_null();
580 self.bool_builders[c].append_null();
581 self.text_builders[c].append_value(s);
582 self.lane_counts[c].n_text += 1;
583 self.err_builders[c].append_null();
584 }
585 LiteralValue::Error(e) => {
586 self.num_builders[c].append_null();
587 self.bool_builders[c].append_null();
588 self.text_builders[c].append_null();
589 self.err_builders[c].append_value(map_error_code(e.kind));
590 self.lane_counts[c].n_err += 1;
591 }
592 LiteralValue::Date(d) => {
594 let dt = d.and_hms_opt(0, 0, 0).unwrap();
595 let serial = formualizer_common::datetime_to_serial_for(self.date_system, &dt);
596 self.num_builders[c].append_value(serial);
597 self.lane_counts[c].n_num += 1;
598 self.bool_builders[c].append_null();
599 self.text_builders[c].append_null();
600 self.err_builders[c].append_null();
601 }
602 LiteralValue::DateTime(dt) => {
603 let serial = formualizer_common::datetime_to_serial_for(self.date_system, dt);
604 self.num_builders[c].append_value(serial);
605 self.lane_counts[c].n_num += 1;
606 self.bool_builders[c].append_null();
607 self.text_builders[c].append_null();
608 self.err_builders[c].append_null();
609 }
610 LiteralValue::Time(t) => {
611 let serial = formualizer_common::time_to_fraction(t);
612 self.num_builders[c].append_value(serial);
613 self.lane_counts[c].n_num += 1;
614 self.bool_builders[c].append_null();
615 self.text_builders[c].append_null();
616 self.err_builders[c].append_null();
617 }
618 LiteralValue::Duration(dur) => {
619 let serial = dur.num_seconds() as f64 / 86_400.0;
620 self.num_builders[c].append_value(serial);
621 self.lane_counts[c].n_num += 1;
622 self.bool_builders[c].append_null();
623 self.text_builders[c].append_null();
624 self.err_builders[c].append_null();
625 }
626 LiteralValue::Array(_) => {
627 self.num_builders[c].append_null();
629 self.bool_builders[c].append_null();
630 self.text_builders[c].append_null();
631 self.err_builders[c].append_value(map_error_code(ExcelErrorKind::Value));
632 self.lane_counts[c].n_err += 1;
633 }
634 LiteralValue::Pending => {
635 self.num_builders[c].append_null();
637 self.bool_builders[c].append_null();
638 self.text_builders[c].append_null();
639 self.err_builders[c].append_null();
640 }
641 }
642 }
643
644 self.row_in_chunk += 1;
645 self.total_rows += 1;
646
647 if self.row_in_chunk >= self.chunk_rows {
648 self.finish_chunk();
649 }
650
651 Ok(())
652 }
653
654 fn finish_chunk(&mut self) {
655 if self.row_in_chunk == 0 {
656 return;
657 }
658 for c in 0..self.ncols {
659 let len = self.row_in_chunk;
660 let numbers_arc: Option<Arc<Float64Array>> = if self.lane_counts[c].n_num == 0 {
661 None
662 } else {
663 Some(Arc::new(self.num_builders[c].finish()))
664 };
665 let booleans_arc: Option<Arc<BooleanArray>> = if self.lane_counts[c].n_bool == 0 {
666 None
667 } else {
668 Some(Arc::new(self.bool_builders[c].finish()))
669 };
670 let text_ref: Option<ArrayRef> = if self.lane_counts[c].n_text == 0 {
671 None
672 } else {
673 Some(Arc::new(self.text_builders[c].finish()))
674 };
675 let errors_arc: Option<Arc<UInt8Array>> = if self.lane_counts[c].n_err == 0 {
676 None
677 } else {
678 Some(Arc::new(self.err_builders[c].finish()))
679 };
680 let tags: UInt8Array = self.tag_builders[c].finish();
681
682 let chunk = ColumnChunk {
683 numbers: numbers_arc,
684 booleans: booleans_arc,
685 text: text_ref,
686 errors: errors_arc,
687 type_tag: Arc::new(tags),
688 formula_id: None,
689 meta: ColumnChunkMeta {
690 len,
691 non_null_num: self.lane_counts[c].n_num,
692 non_null_bool: self.lane_counts[c].n_bool,
693 non_null_text: self.lane_counts[c].n_text,
694 non_null_err: self.lane_counts[c].n_err,
695 },
696 lazy_null_numbers: OnceCell::new(),
697 lazy_null_booleans: OnceCell::new(),
698 lazy_null_text: OnceCell::new(),
699 lazy_null_errors: OnceCell::new(),
700 lowered_text: OnceCell::new(),
701 overlay: Overlay::new(),
702 computed_overlay: Overlay::new(),
703 };
704 self.chunks[c].push(chunk);
705
706 self.num_builders[c] = Float64Builder::with_capacity(self.chunk_rows);
708 self.bool_builders[c] = BooleanBuilder::with_capacity(self.chunk_rows);
709 self.text_builders[c] =
710 StringBuilder::with_capacity(self.chunk_rows, self.chunk_rows * 12);
711 self.err_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
712 self.tag_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
713 self.lane_counts[c] = LaneCounts::default();
714 }
715 self.row_in_chunk = 0;
716 }
717
718 pub fn finish(mut self) -> ArrowSheet {
719 if self.row_in_chunk > 0 {
721 self.finish_chunk();
722 }
723
724 let mut columns = Vec::with_capacity(self.ncols);
725 for (idx, chunks) in self.chunks.into_iter().enumerate() {
726 columns.push(ArrowColumn {
727 chunks,
728 sparse_chunks: FxHashMap::default(),
729 index: idx as u32,
730 });
731 }
732 let mut chunk_starts: Vec<usize> = Vec::new();
734 if let Some(col0) = columns.first() {
735 let chunks_len0 = col0.chunks.len();
736 for (ci, col) in columns.iter().enumerate() {
737 if col.chunks.len() != chunks_len0 {
738 panic!(
739 "ArrowSheet chunk misalignment: column {} chunks={} != {}",
740 ci,
741 col.chunks.len(),
742 chunks_len0
743 );
744 }
745 }
746 let mut cur = 0usize;
747 for i in 0..chunks_len0 {
748 let len_i = col0.chunks[i].type_tag.len();
749 for (ci, col) in columns.iter().enumerate() {
750 let got = col.chunks[i].type_tag.len();
751 if got != len_i {
752 panic!(
753 "ArrowSheet chunk row-length misalignment at chunk {i}: col {ci} len={got} != {len_i}"
754 );
755 }
756 }
757 chunk_starts.push(cur);
758 cur += len_i;
759 }
760 }
761 ArrowSheet {
762 name: self.name,
763 date_system: self.date_system,
764 columns,
765 nrows: self.total_rows,
766 chunk_starts,
767 chunk_rows: self.chunk_rows,
768 }
769 }
770}
771
772pub fn map_error_code(kind: ExcelErrorKind) -> u8 {
773 match kind {
774 ExcelErrorKind::Null => 1,
775 ExcelErrorKind::Ref => 2,
776 ExcelErrorKind::Name => 3,
777 ExcelErrorKind::Value => 4,
778 ExcelErrorKind::Div => 5,
779 ExcelErrorKind::Na => 6,
780 ExcelErrorKind::Num => 7,
781 ExcelErrorKind::Error => 8,
782 ExcelErrorKind::NImpl => 9,
783 ExcelErrorKind::Spill => 10,
784 ExcelErrorKind::Calc => 11,
785 ExcelErrorKind::Circ => 12,
786 ExcelErrorKind::Cancelled => 13,
787 _ => 8,
788 }
789}
790
791pub fn unmap_error_code(code: u8) -> ExcelErrorKind {
792 match code {
793 1 => ExcelErrorKind::Null,
794 2 => ExcelErrorKind::Ref,
795 3 => ExcelErrorKind::Name,
796 4 => ExcelErrorKind::Value,
797 5 => ExcelErrorKind::Div,
798 6 => ExcelErrorKind::Na,
799 7 => ExcelErrorKind::Num,
800 8 => ExcelErrorKind::Error,
801 9 => ExcelErrorKind::NImpl,
802 10 => ExcelErrorKind::Spill,
803 11 => ExcelErrorKind::Calc,
804 12 => ExcelErrorKind::Circ,
805 13 => ExcelErrorKind::Cancelled,
806 _ => ExcelErrorKind::Error,
807 }
808}
809
810pub enum CellIngest<'a> {
814 Empty,
815 Number(f64),
816 Boolean(bool),
817 Text(&'a str),
818 ErrorCode(u8),
819 DateSerial(f64),
820 Pending,
821}
822
823#[derive(Debug, Clone, PartialEq)]
824pub enum OverlayValue {
825 Empty,
826 Number(f64),
827 DateTime(f64),
829 Duration(f64),
831 Boolean(bool),
832 Text(Arc<str>),
833 Error(u8),
834 Pending,
835}
836
837impl OverlayValue {
838 pub fn from_literal_value(
839 value: &LiteralValue,
840 date_system: crate::engine::DateSystem,
841 ) -> Self {
842 match value {
843 LiteralValue::Empty => OverlayValue::Empty,
844 LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
845 LiteralValue::Number(n) => OverlayValue::Number(*n),
846 LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
847 LiteralValue::Text(s) => OverlayValue::Text(Arc::from(s.clone())),
848 LiteralValue::Error(e) => OverlayValue::Error(map_error_code(e.kind)),
849 LiteralValue::Date(d) => {
850 let dt = d.and_hms_opt(0, 0, 0).unwrap();
851 OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, &dt))
852 }
853 LiteralValue::DateTime(dt) => {
854 OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, dt))
855 }
856 LiteralValue::Time(t) => {
857 OverlayValue::DateTime(formualizer_common::time_to_fraction(t))
858 }
859 LiteralValue::Duration(d) => OverlayValue::Duration(d.num_seconds() as f64 / 86_400.0),
860 LiteralValue::Pending => OverlayValue::Pending,
861 LiteralValue::Array(_) => OverlayValue::Error(map_error_code(ExcelErrorKind::Value)),
862 }
863 }
864
865 #[inline]
866 pub(crate) fn estimated_payload_bytes(&self) -> usize {
867 match self {
868 OverlayValue::Empty | OverlayValue::Pending => 0,
869 OverlayValue::Number(_) | OverlayValue::DateTime(_) | OverlayValue::Duration(_) => {
870 core::mem::size_of::<f64>()
871 }
872 OverlayValue::Boolean(_) => core::mem::size_of::<bool>(),
873 OverlayValue::Error(_) => core::mem::size_of::<u8>(),
874 OverlayValue::Text(s) => s.len(),
876 }
877 }
878
879 #[inline]
880 pub(crate) fn type_tag(&self) -> TypeTag {
881 match self {
882 OverlayValue::Empty => TypeTag::Empty,
883 OverlayValue::Number(_) => TypeTag::Number,
884 OverlayValue::DateTime(_) => TypeTag::DateTime,
885 OverlayValue::Duration(_) => TypeTag::Duration,
886 OverlayValue::Boolean(_) => TypeTag::Boolean,
887 OverlayValue::Text(_) => TypeTag::Text,
888 OverlayValue::Error(_) => TypeTag::Error,
889 OverlayValue::Pending => TypeTag::Pending,
890 }
891 }
892
893 #[inline]
894 pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
895 match self {
896 OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
897 Some(*n)
898 }
899 _ => None,
900 }
901 }
902
903 #[inline]
904 pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
905 match self {
906 OverlayValue::Boolean(b) => Some(*b),
907 _ => None,
908 }
909 }
910
911 #[inline]
912 pub(crate) fn text_lane_value(&self) -> Option<&str> {
913 match self {
914 OverlayValue::Text(s) => Some(s.as_ref()),
915 _ => None,
916 }
917 }
918
919 #[inline]
920 pub(crate) fn error_lane_value(&self) -> Option<u8> {
921 match self {
922 OverlayValue::Error(code) => Some(*code),
923 _ => None,
924 }
925 }
926
927 pub(crate) fn lowered_text_value(&self) -> Option<String> {
928 match self {
929 OverlayValue::Text(s) => Some(s.to_lowercase()),
930 OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
931 Some(n.to_string())
932 }
933 OverlayValue::Boolean(b) => Some(if *b { "true" } else { "false" }.to_string()),
934 OverlayValue::Empty | OverlayValue::Error(_) | OverlayValue::Pending => None,
935 }
936 }
937
938 pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
939 match self {
940 OverlayValue::Empty => LiteralValue::Empty,
941 OverlayValue::Number(n) => LiteralValue::Number(*n),
942 OverlayValue::DateTime(serial) => {
943 LiteralValue::try_from_serial_number_for(date_system, *serial)
944 .unwrap_or_else(LiteralValue::Error)
945 }
946 OverlayValue::Duration(serial) => {
947 let nanos_f = *serial * 86_400.0 * 1_000_000_000.0;
948 let nanos = nanos_f.round().clamp(i64::MIN as f64, i64::MAX as f64) as i64;
949 LiteralValue::Duration(chrono::Duration::nanoseconds(nanos))
950 }
951 OverlayValue::Boolean(b) => LiteralValue::Boolean(*b),
952 OverlayValue::Text(s) => LiteralValue::Text((**s).to_string()),
953 OverlayValue::Error(code) => {
954 LiteralValue::Error(ExcelError::new(unmap_error_code(*code)))
955 }
956 OverlayValue::Pending => LiteralValue::Pending,
957 }
958 }
959
960 #[cfg(test)]
961 pub(crate) fn to_literal(&self) -> LiteralValue {
962 self.to_literal_for(crate::engine::DateSystem::Excel1900)
963 }
964}
965
966#[derive(Debug, Clone)]
967pub(crate) enum OverlayScalar<'a> {
968 Borrowed(&'a OverlayValue),
969 Owned(OverlayValue),
970}
971
972impl<'a> OverlayScalar<'a> {
973 #[inline]
974 fn as_value(&self) -> &OverlayValue {
975 match self {
976 OverlayScalar::Borrowed(value) => value,
977 OverlayScalar::Owned(value) => value,
978 }
979 }
980
981 #[inline]
982 pub(crate) fn to_overlay_value(&self) -> OverlayValue {
983 self.as_value().clone()
984 }
985
986 #[inline]
987 pub(crate) fn type_tag(&self) -> TypeTag {
988 self.as_value().type_tag()
989 }
990
991 #[inline]
992 pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
993 self.as_value().numeric_lane_value()
994 }
995
996 #[inline]
997 pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
998 self.as_value().boolean_lane_value()
999 }
1000
1001 #[inline]
1002 pub(crate) fn text_lane_value(&self) -> Option<&str> {
1003 self.as_value().text_lane_value()
1004 }
1005
1006 #[inline]
1007 pub(crate) fn error_lane_value(&self) -> Option<u8> {
1008 self.as_value().error_lane_value()
1009 }
1010
1011 pub(crate) fn lowered_text_value(&self) -> Option<String> {
1012 self.as_value().lowered_text_value()
1013 }
1014
1015 pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
1016 self.as_value().to_literal_for(date_system)
1017 }
1018
1019 #[cfg(test)]
1020 pub(crate) fn to_literal(&self) -> LiteralValue {
1021 self.to_literal_for(crate::engine::DateSystem::Excel1900)
1022 }
1023}
1024
1025const OVERLAY_ENTRY_BASE_BYTES: usize = 32;
1026const OVERLAY_FRAGMENT_BASE_BYTES: usize = 48;
1027
1028#[allow(dead_code)]
1029#[derive(Debug, Clone)]
1030pub(crate) struct OverlayFragmentPayload {
1031 type_tags: Arc<UInt8Array>,
1032 numbers: Option<Arc<Float64Array>>,
1033 booleans: Option<Arc<BooleanArray>>,
1034 text: Option<ArrayRef>,
1035 errors: Option<Arc<UInt8Array>>,
1036 estimated_bytes: usize,
1037}
1038
1039impl OverlayFragmentPayload {
1040 fn from_values(values: Vec<OverlayValue>) -> Self {
1041 let len = values.len();
1042 let mut tag_b = UInt8Builder::with_capacity(len);
1043 let mut nb = Float64Builder::with_capacity(len);
1044 let mut bb = BooleanBuilder::with_capacity(len);
1045 let mut sb = StringBuilder::with_capacity(len, len.saturating_mul(8));
1046 let mut eb = UInt8Builder::with_capacity(len);
1047 let mut non_num = 0usize;
1048 let mut non_bool = 0usize;
1049 let mut non_text = 0usize;
1050 let mut non_err = 0usize;
1051
1052 for value in &values {
1053 append_overlay_value_to_lane_builders(
1054 value,
1055 &mut tag_b,
1056 &mut nb,
1057 &mut bb,
1058 &mut sb,
1059 &mut eb,
1060 &mut non_num,
1061 &mut non_bool,
1062 &mut non_text,
1063 &mut non_err,
1064 );
1065 }
1066
1067 let type_tags = Arc::new(tag_b.finish());
1068 let numbers = {
1069 let a = nb.finish();
1070 (non_num > 0).then(|| Arc::new(a))
1071 };
1072 let booleans = {
1073 let a = bb.finish();
1074 (non_bool > 0).then(|| Arc::new(a))
1075 };
1076 let text = {
1077 let a = sb.finish();
1078 (non_text > 0).then(|| Arc::new(a) as ArrayRef)
1079 };
1080 let errors = {
1081 let a = eb.finish();
1082 (non_err > 0).then(|| Arc::new(a))
1083 };
1084
1085 let estimated_bytes = type_tags
1086 .get_array_memory_size()
1087 .saturating_add(
1088 numbers
1089 .as_ref()
1090 .map(|a| a.get_array_memory_size())
1091 .unwrap_or(0),
1092 )
1093 .saturating_add(
1094 booleans
1095 .as_ref()
1096 .map(|a| a.get_array_memory_size())
1097 .unwrap_or(0),
1098 )
1099 .saturating_add(
1100 text.as_ref()
1101 .map(|a| a.get_array_memory_size())
1102 .unwrap_or(0),
1103 )
1104 .saturating_add(
1105 errors
1106 .as_ref()
1107 .map(|a| a.get_array_memory_size())
1108 .unwrap_or(0),
1109 );
1110
1111 Self {
1112 type_tags,
1113 numbers,
1114 booleans,
1115 text,
1116 errors,
1117 estimated_bytes,
1118 }
1119 }
1120
1121 fn overlay_value(&self, idx: usize) -> Option<OverlayValue> {
1122 if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
1123 return None;
1124 }
1125 match TypeTag::from_u8(self.type_tags.value(idx)) {
1126 TypeTag::Empty => Some(OverlayValue::Empty),
1127 TypeTag::Number => Some(OverlayValue::Number(self.number_at(idx)?)),
1128 TypeTag::DateTime => Some(OverlayValue::DateTime(self.number_at(idx)?)),
1129 TypeTag::Duration => Some(OverlayValue::Duration(self.number_at(idx)?)),
1130 TypeTag::Boolean => Some(OverlayValue::Boolean(self.boolean_at(idx)?)),
1131 TypeTag::Text => Some(OverlayValue::Text(Arc::from(self.text_at(idx)?))),
1132 TypeTag::Error => Some(OverlayValue::Error(self.error_at(idx)?)),
1133 TypeTag::Pending => Some(OverlayValue::Pending),
1134 }
1135 }
1136
1137 #[inline]
1138 fn get_scalar(&self, idx: usize) -> Option<OverlayScalar<'_>> {
1139 self.overlay_value(idx).map(OverlayScalar::Owned)
1140 }
1141
1142 #[inline]
1143 fn number_at(&self, idx: usize) -> Option<f64> {
1144 let arr = self.numbers.as_ref()?;
1145 (!arr.is_null(idx)).then(|| arr.value(idx))
1146 }
1147
1148 #[inline]
1149 fn boolean_at(&self, idx: usize) -> Option<bool> {
1150 let arr = self.booleans.as_ref()?;
1151 (!arr.is_null(idx)).then(|| arr.value(idx))
1152 }
1153
1154 #[inline]
1155 fn text_at(&self, idx: usize) -> Option<&str> {
1156 let arr = self.text.as_ref()?;
1157 let arr = arr.as_any().downcast_ref::<StringArray>()?;
1158 (!arr.is_null(idx)).then(|| arr.value(idx))
1159 }
1160
1161 #[inline]
1162 fn error_at(&self, idx: usize) -> Option<u8> {
1163 let arr = self.errors.as_ref()?;
1164 (!arr.is_null(idx)).then(|| arr.value(idx))
1165 }
1166
1167 #[inline]
1168 fn values_slice(&self, start: usize, len: usize) -> Vec<OverlayValue> {
1169 (start..start.saturating_add(len))
1170 .filter_map(|idx| self.overlay_value(idx))
1171 .collect()
1172 }
1173
1174 #[inline]
1175 fn estimated_bytes(&self) -> usize {
1176 self.estimated_bytes
1177 }
1178}
1179#[derive(Debug, Clone)]
1180pub(crate) enum OverlayFragment {
1181 SparseOffsets {
1182 offsets: Vec<u32>,
1183 payload: OverlayFragmentPayload,
1184 },
1185 DenseRange {
1186 start: u32,
1187 len: u32,
1188 payload: OverlayFragmentPayload,
1189 },
1190 RunRange {
1191 start: u32,
1192 len: u32,
1193 run_ends: Vec<u32>,
1194 payload: OverlayFragmentPayload,
1195 },
1196}
1197
1198impl OverlayFragment {
1199 const MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK: usize = 128;
1200
1201 pub(crate) fn sparse_offsets(items: Vec<(usize, OverlayValue)>) -> Option<Self> {
1202 let mut by_offset: BTreeMap<usize, OverlayValue> = BTreeMap::new();
1203 for (offset, value) in items {
1204 by_offset.insert(offset, value);
1205 }
1206 if by_offset.is_empty() {
1207 return None;
1208 }
1209
1210 let mut offsets = Vec::with_capacity(by_offset.len());
1211 let mut values = Vec::with_capacity(by_offset.len());
1212 for (offset, value) in by_offset {
1213 offsets.push(u32::try_from(offset).expect("overlay offset fits in u32"));
1214 values.push(value);
1215 }
1216
1217 Some(Self::SparseOffsets {
1218 offsets,
1219 payload: OverlayFragmentPayload::from_values(values),
1220 })
1221 }
1222
1223 pub(crate) fn sparse_offsets_if_estimated_smaller_than_points(
1224 items: Vec<(usize, OverlayValue)>,
1225 point_estimate: usize,
1226 ) -> Option<Result<Self, Vec<(usize, OverlayValue)>>> {
1227 let fragment = Self::sparse_offsets(items)?;
1228 if fragment.estimated_bytes() < point_estimate {
1229 Some(Ok(fragment))
1230 } else {
1231 Some(Err(fragment.cells()))
1232 }
1233 }
1234
1235 pub(crate) fn dense_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1236 let len = values.len();
1237 if len == 0 {
1238 return None;
1239 }
1240 Some(Self::DenseRange {
1241 start: u32::try_from(start).expect("overlay start fits in u32"),
1242 len: u32::try_from(len).expect("overlay length fits in u32"),
1243 payload: OverlayFragmentPayload::from_values(values),
1244 })
1245 }
1246
1247 pub(crate) fn run_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1248 if values.is_empty() {
1249 return None;
1250 }
1251
1252 let mut run_ends = Vec::new();
1253 let mut run_values = Vec::new();
1254 let mut current = values[0].clone();
1255 for (idx, value) in values.iter().enumerate().skip(1) {
1256 if *value != current {
1257 run_ends.push(idx);
1258 run_values.push(current);
1259 current = value.clone();
1260 }
1261 }
1262 run_ends.push(values.len());
1263 run_values.push(current);
1264
1265 Self::run_range_from_parts(start, values.len(), run_ends, run_values)
1266 }
1267
1268 fn run_range_from_parts(
1269 start: usize,
1270 len: usize,
1271 run_ends: Vec<usize>,
1272 values: Vec<OverlayValue>,
1273 ) -> Option<Self> {
1274 if len == 0 || run_ends.is_empty() || run_ends.len() != values.len() {
1275 return None;
1276 }
1277
1278 let mut merged_ends: Vec<u32> = Vec::with_capacity(run_ends.len());
1279 let mut merged_values: Vec<OverlayValue> = Vec::with_capacity(values.len());
1280 let mut prev_end = 0usize;
1281 for (end, value) in run_ends.into_iter().zip(values.into_iter()) {
1282 if end <= prev_end || end > len {
1283 return None;
1284 }
1285 if merged_values.last().is_some_and(|last| *last == value) {
1286 if let Some(last_end) = merged_ends.last_mut() {
1287 *last_end = u32::try_from(end).expect("run end fits in u32");
1288 }
1289 } else {
1290 merged_ends.push(u32::try_from(end).expect("run end fits in u32"));
1291 merged_values.push(value);
1292 }
1293 prev_end = end;
1294 }
1295
1296 if prev_end != len || merged_ends.last().copied() != Some(len as u32) {
1297 return None;
1298 }
1299
1300 Some(Self::RunRange {
1301 start: u32::try_from(start).expect("overlay start fits in u32"),
1302 len: u32::try_from(len).expect("overlay length fits in u32"),
1303 run_ends: merged_ends,
1304 payload: OverlayFragmentPayload::from_values(merged_values),
1305 })
1306 }
1307
1308 #[inline]
1309 fn estimated_bytes(&self) -> usize {
1310 match self {
1311 OverlayFragment::SparseOffsets { offsets, payload } => OVERLAY_FRAGMENT_BASE_BYTES
1312 .saturating_add(offsets.len().saturating_mul(core::mem::size_of::<u32>()))
1313 .saturating_add(payload.estimated_bytes()),
1314 OverlayFragment::DenseRange { payload, .. } => {
1315 OVERLAY_FRAGMENT_BASE_BYTES.saturating_add(payload.estimated_bytes())
1316 }
1317 OverlayFragment::RunRange {
1318 run_ends, payload, ..
1319 } => OVERLAY_FRAGMENT_BASE_BYTES
1320 .saturating_add(run_ends.len().saturating_mul(core::mem::size_of::<u32>()))
1321 .saturating_add(payload.estimated_bytes()),
1322 }
1323 }
1324
1325 #[inline]
1326 fn coverage_len(&self) -> usize {
1327 match self {
1328 OverlayFragment::SparseOffsets { offsets, .. } => offsets.len(),
1329 OverlayFragment::DenseRange { len, .. } | OverlayFragment::RunRange { len, .. } => {
1330 *len as usize
1331 }
1332 }
1333 }
1334
1335 pub(crate) fn max_covered_offset(&self) -> usize {
1336 match self {
1337 OverlayFragment::SparseOffsets { offsets, .. } => {
1338 offsets.iter().copied().max().unwrap_or(0) as usize
1339 }
1340 OverlayFragment::DenseRange { start, len, .. }
1341 | OverlayFragment::RunRange { start, len, .. } => (*start as usize)
1342 .saturating_add(*len as usize)
1343 .saturating_sub(1),
1344 }
1345 }
1346
1347 fn interval_coverage(&self) -> Option<core::ops::Range<usize>> {
1348 match self {
1349 OverlayFragment::DenseRange { start, len, .. }
1350 | OverlayFragment::RunRange { start, len, .. } => {
1351 let start = *start as usize;
1352 Some(start..start.saturating_add(*len as usize))
1353 }
1354 OverlayFragment::SparseOffsets { .. } => None,
1355 }
1356 }
1357
1358 fn sparse_offsets_slice(&self) -> Option<&[u32]> {
1359 match self {
1360 OverlayFragment::SparseOffsets { offsets, .. } => Some(offsets.as_slice()),
1361 _ => None,
1362 }
1363 }
1364
1365 fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
1366 if range.is_empty() {
1367 return false;
1368 }
1369 match self {
1370 OverlayFragment::SparseOffsets { offsets, .. } => {
1371 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1372 let idx = offsets.partition_point(|off| *off < start);
1373 offsets
1374 .get(idx)
1375 .is_some_and(|off| (*off as usize) < range.end)
1376 }
1377 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1378 .interval_coverage()
1379 .is_some_and(|r| r.start < range.end && range.start < r.end),
1380 }
1381 }
1382
1383 fn intersects_fragment_exact(&self, replacement: &OverlayFragment) -> bool {
1384 if let Some(offsets) = replacement.sparse_offsets_slice() {
1385 self.intersects_sparse_offsets(offsets)
1386 } else if let Some(range) = replacement.interval_coverage() {
1387 self.intersects_interval(range)
1388 } else {
1389 false
1390 }
1391 }
1392
1393 fn intersects_interval(&self, range: core::ops::Range<usize>) -> bool {
1394 if range.is_empty() {
1395 return false;
1396 }
1397 match self {
1398 OverlayFragment::SparseOffsets { offsets, .. } => {
1399 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1400 let idx = offsets.partition_point(|off| *off < start);
1401 offsets
1402 .get(idx)
1403 .is_some_and(|off| (*off as usize) < range.end)
1404 }
1405 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1406 .interval_coverage()
1407 .is_some_and(|own| own.start < range.end && range.start < own.end),
1408 }
1409 }
1410
1411 fn intersects_sparse_offsets(&self, replacement_offsets: &[u32]) -> bool {
1412 if replacement_offsets.is_empty() {
1413 return false;
1414 }
1415 match self {
1416 OverlayFragment::SparseOffsets { offsets, .. } => {
1417 Self::sorted_offsets_intersect(offsets, replacement_offsets)
1418 }
1419 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
1420 self.interval_coverage().is_some_and(|range| {
1421 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1422 let idx = replacement_offsets.partition_point(|off| *off < start);
1423 replacement_offsets
1424 .get(idx)
1425 .is_some_and(|off| (*off as usize) < range.end)
1426 })
1427 }
1428 }
1429 }
1430
1431 fn sorted_offsets_intersect(a: &[u32], b: &[u32]) -> bool {
1432 let mut ai = 0usize;
1433 let mut bi = 0usize;
1434 while ai < a.len() && bi < b.len() {
1435 match a[ai].cmp(&b[bi]) {
1436 core::cmp::Ordering::Equal => return true,
1437 core::cmp::Ordering::Less => ai += 1,
1438 core::cmp::Ordering::Greater => bi += 1,
1439 }
1440 }
1441 false
1442 }
1443
1444 fn covers_offset(&self, off: usize) -> bool {
1445 self.get_scalar(off).is_some()
1446 }
1447
1448 fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
1449 match self {
1450 OverlayFragment::SparseOffsets { offsets, payload } => {
1451 let off = u32::try_from(off).ok()?;
1452 let idx = offsets.binary_search(&off).ok()?;
1453 payload.get_scalar(idx)
1454 }
1455 OverlayFragment::DenseRange {
1456 start,
1457 len,
1458 payload,
1459 } => {
1460 let start = *start as usize;
1461 let rel = off.checked_sub(start)?;
1462 if rel >= *len as usize {
1463 return None;
1464 }
1465 payload.get_scalar(rel)
1466 }
1467 OverlayFragment::RunRange {
1468 start,
1469 len,
1470 run_ends,
1471 payload,
1472 } => {
1473 let start = *start as usize;
1474 let rel = off.checked_sub(start)?;
1475 if rel >= *len as usize {
1476 return None;
1477 }
1478 let rel_u32 = u32::try_from(rel).ok()?;
1479 let run_idx = run_ends.partition_point(|end| *end <= rel_u32);
1480 payload.get_scalar(run_idx)
1481 }
1482 }
1483 }
1484
1485 fn subtract_fragment(&self, replacement: &OverlayFragment) -> Vec<OverlayFragment> {
1486 if let Some(offsets) = replacement.sparse_offsets_slice() {
1487 self.subtract_sparse_offsets(offsets)
1488 } else if let Some(range) = replacement.interval_coverage() {
1489 self.subtract_interval(range)
1490 } else {
1491 vec![self.clone()]
1492 }
1493 }
1494
1495 fn subtract_offset(&self, off: usize) -> Vec<OverlayFragment> {
1496 match self {
1497 OverlayFragment::SparseOffsets { .. } => {
1498 let Ok(off) = u32::try_from(off) else {
1499 return vec![self.clone()];
1500 };
1501 self.subtract_sparse_offsets(core::slice::from_ref(&off))
1502 }
1503 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
1504 self.subtract_interval(off..off.saturating_add(1))
1505 }
1506 }
1507 }
1508
1509 fn subtract_interval(&self, replacement: core::ops::Range<usize>) -> Vec<OverlayFragment> {
1510 if replacement.is_empty() {
1511 return vec![self.clone()];
1512 }
1513
1514 match self {
1515 OverlayFragment::SparseOffsets { offsets, payload } => {
1516 let cells: Vec<_> = offsets
1517 .iter()
1518 .enumerate()
1519 .filter_map(|(idx, off)| {
1520 let off_usize = *off as usize;
1521 (!replacement.contains(&off_usize))
1522 .then(|| payload.overlay_value(idx).map(|value| (off_usize, value)))?
1523 })
1524 .collect();
1525 OverlayFragment::sparse_offsets(cells).into_iter().collect()
1526 }
1527 OverlayFragment::DenseRange { .. } => {
1528 let Some(own) = self.interval_coverage() else {
1529 return vec![self.clone()];
1530 };
1531 if own.end <= replacement.start || replacement.end <= own.start {
1532 return vec![self.clone()];
1533 }
1534 let cut_start = replacement.start.max(own.start);
1535 let cut_end = replacement.end.min(own.end);
1536 let mut out = Vec::with_capacity(2);
1537 if own.start < cut_start
1538 && let Some(left) =
1539 self.dense_segment_with_start(own.start, own.start, cut_start)
1540 {
1541 out.push(left);
1542 }
1543 if cut_end < own.end
1544 && let Some(right) = self.dense_segment_with_start(cut_end, cut_end, own.end)
1545 {
1546 out.push(right);
1547 }
1548 out
1549 }
1550 OverlayFragment::RunRange { .. } => {
1551 let Some(own) = self.interval_coverage() else {
1552 return vec![self.clone()];
1553 };
1554 if own.end <= replacement.start || replacement.end <= own.start {
1555 return vec![self.clone()];
1556 }
1557 let cut_start = replacement.start.max(own.start);
1558 let cut_end = replacement.end.min(own.end);
1559 let mut out = Vec::with_capacity(2);
1560 if own.start < cut_start
1561 && let Some(left) = self.run_segment_with_start(own.start, own.start, cut_start)
1562 {
1563 out.push(left);
1564 }
1565 if cut_end < own.end
1566 && let Some(right) = self.run_segment_with_start(cut_end, cut_end, own.end)
1567 {
1568 out.push(right);
1569 }
1570 out
1571 }
1572 }
1573 }
1574
1575 fn subtract_sparse_offsets(&self, replacement_offsets: &[u32]) -> Vec<OverlayFragment> {
1576 if replacement_offsets.is_empty() {
1577 return vec![self.clone()];
1578 }
1579
1580 match self {
1581 OverlayFragment::SparseOffsets { offsets, payload } => {
1582 let cells: Vec<_> = offsets
1583 .iter()
1584 .enumerate()
1585 .filter_map(|(idx, off)| {
1586 replacement_offsets.binary_search(off).is_err().then(|| {
1587 payload
1588 .overlay_value(idx)
1589 .map(|value| (*off as usize, value))
1590 })?
1591 })
1592 .collect();
1593 OverlayFragment::sparse_offsets(cells).into_iter().collect()
1594 }
1595 OverlayFragment::DenseRange { .. } => {
1596 self.subtract_sparse_offsets_from_dense(replacement_offsets)
1597 }
1598 OverlayFragment::RunRange { .. } => {
1599 self.subtract_sparse_offsets_from_run(replacement_offsets)
1600 }
1601 }
1602 }
1603
1604 fn sparse_holes_in_interval(offsets: &[u32], range: core::ops::Range<usize>) -> Vec<usize> {
1605 if range.is_empty() {
1606 return Vec::new();
1607 }
1608 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1609 let mut idx = offsets.partition_point(|off| *off < start);
1610 let mut holes = Vec::new();
1611 let mut last = None;
1612 while let Some(off) = offsets.get(idx).copied() {
1613 let off_usize = off as usize;
1614 if off_usize >= range.end {
1615 break;
1616 }
1617 if last != Some(off_usize) {
1618 holes.push(off_usize);
1619 last = Some(off_usize);
1620 }
1621 idx += 1;
1622 }
1623 holes
1624 }
1625
1626 fn subtract_sparse_offsets_from_dense(
1627 &self,
1628 replacement_offsets: &[u32],
1629 ) -> Vec<OverlayFragment> {
1630 let Some(own) = self.interval_coverage() else {
1631 return vec![self.clone()];
1632 };
1633 let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
1634 if holes.is_empty() {
1635 return vec![self.clone()];
1636 }
1637 if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
1638 return self.sparse_remainder_excluding_offsets(&holes);
1639 }
1640
1641 let mut out = Vec::with_capacity(holes.len().saturating_add(1));
1642 let mut seg_start = own.start;
1643 for hole in holes {
1644 if seg_start < hole
1645 && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, hole)
1646 {
1647 out.push(segment);
1648 }
1649 seg_start = hole.saturating_add(1);
1650 }
1651 if seg_start < own.end
1652 && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, own.end)
1653 {
1654 out.push(segment);
1655 }
1656 out
1657 }
1658
1659 fn subtract_sparse_offsets_from_run(
1660 &self,
1661 replacement_offsets: &[u32],
1662 ) -> Vec<OverlayFragment> {
1663 let Some(own) = self.interval_coverage() else {
1664 return vec![self.clone()];
1665 };
1666 let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
1667 if holes.is_empty() {
1668 return vec![self.clone()];
1669 }
1670 if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
1671 return self.sparse_remainder_excluding_offsets(&holes);
1672 }
1673
1674 let mut out = Vec::with_capacity(holes.len().saturating_add(1));
1675 let mut seg_start = own.start;
1676 for hole in holes {
1677 if seg_start < hole
1678 && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, hole)
1679 {
1680 out.push(segment);
1681 }
1682 seg_start = hole.saturating_add(1);
1683 }
1684 if seg_start < own.end
1685 && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, own.end)
1686 {
1687 out.push(segment);
1688 }
1689 out
1690 }
1691
1692 fn sparse_remainder_excluding_offsets(&self, sorted_holes: &[usize]) -> Vec<OverlayFragment> {
1693 let cells: Vec<_> = self
1694 .cells()
1695 .into_iter()
1696 .filter(|(off, _)| sorted_holes.binary_search(off).is_err())
1697 .collect();
1698 OverlayFragment::sparse_offsets(cells).into_iter().collect()
1699 }
1700
1701 fn dense_segment_with_start(
1702 &self,
1703 new_start: usize,
1704 abs_start: usize,
1705 abs_end: usize,
1706 ) -> Option<OverlayFragment> {
1707 match self {
1708 OverlayFragment::DenseRange { start, payload, .. } => {
1709 if abs_start >= abs_end {
1710 return None;
1711 }
1712 let base = *start as usize;
1713 let rel_start = abs_start.checked_sub(base)?;
1714 let len = abs_end.saturating_sub(abs_start);
1715 OverlayFragment::dense_range(new_start, payload.values_slice(rel_start, len))
1716 }
1717 _ => None,
1718 }
1719 }
1720
1721 fn run_segment_with_start(
1722 &self,
1723 new_start: usize,
1724 abs_start: usize,
1725 abs_end: usize,
1726 ) -> Option<OverlayFragment> {
1727 let OverlayFragment::RunRange {
1728 start,
1729 len,
1730 run_ends,
1731 payload,
1732 } = self
1733 else {
1734 return None;
1735 };
1736 if abs_start >= abs_end {
1737 return None;
1738 }
1739 let base = *start as usize;
1740 let frag_end = base.saturating_add(*len as usize);
1741 if abs_start < base || abs_end > frag_end {
1742 return None;
1743 }
1744
1745 let rel_start = abs_start - base;
1746 let rel_end = abs_end - base;
1747 let mut new_run_ends = Vec::new();
1748 let mut new_values = Vec::new();
1749 let mut prev_end = 0usize;
1750
1751 for (run_idx, end) in run_ends.iter().enumerate() {
1752 let run_start = prev_end;
1753 let run_end = *end as usize;
1754 let inter_start = run_start.max(rel_start);
1755 let inter_end = run_end.min(rel_end);
1756 if inter_start < inter_end {
1757 new_run_ends.push(inter_end - rel_start);
1758 if let Some(value) = payload.overlay_value(run_idx) {
1759 new_values.push(value);
1760 }
1761 }
1762 prev_end = run_end;
1763 if prev_end >= rel_end {
1764 break;
1765 }
1766 }
1767
1768 OverlayFragment::run_range_from_parts(
1769 new_start,
1770 abs_end.saturating_sub(abs_start),
1771 new_run_ends,
1772 new_values,
1773 )
1774 }
1775
1776 fn cells(&self) -> Vec<(usize, OverlayValue)> {
1777 match self {
1778 OverlayFragment::SparseOffsets { offsets, payload } => offsets
1779 .iter()
1780 .enumerate()
1781 .filter_map(|(idx, off)| {
1782 payload
1783 .overlay_value(idx)
1784 .map(|value| (*off as usize, value))
1785 })
1786 .collect(),
1787 OverlayFragment::DenseRange {
1788 start,
1789 len,
1790 payload,
1791 } => {
1792 let start = *start as usize;
1793 (0..*len as usize)
1794 .filter_map(|idx| {
1795 payload
1796 .overlay_value(idx)
1797 .map(|value| (start.saturating_add(idx), value))
1798 })
1799 .collect()
1800 }
1801 OverlayFragment::RunRange { start, len, .. } => {
1802 let start = *start as usize;
1803 (0..*len as usize)
1804 .filter_map(|idx| {
1805 self.get_scalar(start.saturating_add(idx))
1806 .map(|value| (start.saturating_add(idx), value.to_overlay_value()))
1807 })
1808 .collect()
1809 }
1810 }
1811 }
1812
1813 fn slice(&self, off: usize, len: usize) -> Option<OverlayFragment> {
1814 let end = off.saturating_add(len);
1815 if len == 0 {
1816 return None;
1817 }
1818
1819 match self {
1820 OverlayFragment::SparseOffsets { offsets, payload } => {
1821 let start = u32::try_from(off).unwrap_or(u32::MAX);
1822 let lo = offsets.partition_point(|candidate| *candidate < start);
1823 let hi = offsets.partition_point(|candidate| (*candidate as usize) < end);
1824 let cells: Vec<_> = (lo..hi)
1825 .filter_map(|idx| {
1826 let rebased = (offsets[idx] as usize).saturating_sub(off);
1827 payload.overlay_value(idx).map(|value| (rebased, value))
1828 })
1829 .collect();
1830 OverlayFragment::sparse_offsets(cells)
1831 }
1832 OverlayFragment::DenseRange { .. } => {
1833 let own = self.interval_coverage()?;
1834 let seg_start = own.start.max(off);
1835 let seg_end = own.end.min(end);
1836 if seg_start >= seg_end {
1837 return None;
1838 }
1839 self.dense_segment_with_start(seg_start - off, seg_start, seg_end)
1840 }
1841 OverlayFragment::RunRange { .. } => {
1842 let own = self.interval_coverage()?;
1843 let seg_start = own.start.max(off);
1844 let seg_end = own.end.min(end);
1845 if seg_start >= seg_end {
1846 return None;
1847 }
1848 self.run_segment_with_start(seg_start - off, seg_start, seg_end)
1849 }
1850 }
1851 }
1852}
1853#[derive(Debug, Default, Clone)]
1854pub struct Overlay {
1855 points: HashMap<usize, OverlayValue>,
1856 fragments: Vec<OverlayFragment>,
1857 estimated_bytes: usize,
1861}
1862
1863impl Overlay {
1864 const ENTRY_BASE_BYTES: usize = OVERLAY_ENTRY_BASE_BYTES;
1867
1868 pub fn new() -> Self {
1869 Self {
1870 points: HashMap::new(),
1871 fragments: Vec::new(),
1872 estimated_bytes: 0,
1873 }
1874 }
1875
1876 #[inline]
1877 fn point_estimate(v: &OverlayValue) -> usize {
1878 Self::ENTRY_BASE_BYTES + v.estimated_payload_bytes()
1879 }
1880
1881 #[inline]
1882 fn adjust_estimated_bytes(&mut self, delta: isize) {
1883 if delta >= 0 {
1884 self.estimated_bytes = self.estimated_bytes.saturating_add(delta as usize);
1885 } else {
1886 self.estimated_bytes = self.estimated_bytes.saturating_sub((-delta) as usize);
1887 }
1888 }
1889
1890 #[inline]
1891 pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
1892 self.points
1893 .get(&off)
1894 .map(OverlayScalar::Borrowed)
1895 .or_else(|| self.fragments.iter().rev().find_map(|f| f.get_scalar(off)))
1896 }
1897
1898 #[inline]
1899 pub fn get(&self, off: usize) -> Option<OverlayValue> {
1900 self.get_scalar(off).map(|value| value.to_overlay_value())
1901 }
1902
1903 #[inline]
1904 pub(crate) fn set_scalar(&mut self, off: usize, v: OverlayValue) -> isize {
1905 let removed = self.remove_scalar(off);
1906 let new_est = Self::point_estimate(&v);
1907 self.points.insert(off, v);
1908 self.adjust_estimated_bytes(new_est as isize);
1909 removed.saturating_add(new_est as isize)
1910 }
1911
1912 #[inline]
1913 pub fn set(&mut self, off: usize, v: OverlayValue) -> isize {
1914 self.set_scalar(off, v)
1915 }
1916
1917 pub(crate) fn apply_fragment(&mut self, fragment: OverlayFragment) -> isize {
1918 let mut delta = self.remove_points_covered_by_fragment(&fragment);
1919 delta = delta.saturating_add(self.remove_fragments_covered_by_fragment(&fragment));
1920
1921 let fragment_est = fragment.estimated_bytes();
1922 self.fragments.push(fragment);
1923 self.adjust_estimated_bytes(fragment_est as isize);
1924 delta.saturating_add(fragment_est as isize)
1925 }
1926
1927 fn remove_points_covered_by_fragment(&mut self, fragment: &OverlayFragment) -> isize {
1928 let mut removed = 0usize;
1929 match fragment {
1930 OverlayFragment::SparseOffsets { offsets, .. } => {
1931 for off in offsets.iter().copied() {
1932 if let Some(old) = self.points.remove(&(off as usize)) {
1933 removed = removed.saturating_add(Self::point_estimate(&old));
1934 }
1935 }
1936 }
1937 OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
1938 if let Some(range) = fragment.interval_coverage() {
1939 let keys: Vec<_> = self
1940 .points
1941 .keys()
1942 .copied()
1943 .filter(|off| range.contains(off))
1944 .collect();
1945 for off in keys {
1946 if let Some(old) = self.points.remove(&off) {
1947 removed = removed.saturating_add(Self::point_estimate(&old));
1948 }
1949 }
1950 }
1951 }
1952 }
1953 self.estimated_bytes = self.estimated_bytes.saturating_sub(removed);
1954 -(removed as isize)
1955 }
1956
1957 fn remove_fragments_covered_by_fragment(&mut self, replacement: &OverlayFragment) -> isize {
1958 if self.fragments.is_empty() {
1959 return 0;
1960 }
1961
1962 let mut delta: isize = 0;
1963 let mut fragments = Vec::with_capacity(self.fragments.len());
1964 for fragment in self.fragments.drain(..) {
1965 if !fragment.intersects_fragment_exact(replacement) {
1966 fragments.push(fragment);
1967 continue;
1968 }
1969
1970 let old_est = fragment.estimated_bytes();
1971 let replacements = fragment.subtract_fragment(replacement);
1972 let new_est = replacements
1973 .iter()
1974 .map(OverlayFragment::estimated_bytes)
1975 .fold(0usize, usize::saturating_add);
1976 fragments.extend(replacements);
1977 delta = delta.saturating_add(new_est as isize - old_est as isize);
1978 }
1979 self.fragments = fragments;
1980 self.adjust_estimated_bytes(delta);
1981 delta
1982 }
1983
1984 #[inline]
1985 pub(crate) fn remove_scalar(&mut self, off: usize) -> isize {
1986 let mut delta = 0isize;
1987 if let Some(old) = self.points.remove(&off) {
1988 let old_est = Self::point_estimate(&old);
1989 self.estimated_bytes = self.estimated_bytes.saturating_sub(old_est);
1990 delta = delta.saturating_sub(old_est as isize);
1991 }
1992
1993 if !self.fragments.is_empty() {
1994 let mut fragments = Vec::with_capacity(self.fragments.len());
1995 for fragment in self.fragments.drain(..) {
1996 if fragment.get_scalar(off).is_none() {
1997 fragments.push(fragment);
1998 continue;
1999 }
2000
2001 let old_est = fragment.estimated_bytes();
2002 let replacements = fragment.subtract_offset(off);
2003 let new_est = replacements
2004 .iter()
2005 .map(OverlayFragment::estimated_bytes)
2006 .fold(0usize, usize::saturating_add);
2007 fragments.extend(replacements);
2008 delta = delta.saturating_add(new_est as isize - old_est as isize);
2009 }
2010 self.fragments = fragments;
2011 self.adjust_estimated_bytes(delta);
2012 }
2013
2014 delta
2015 }
2016
2017 #[inline]
2018 pub fn remove(&mut self, off: usize) -> isize {
2019 self.remove_scalar(off)
2020 }
2021
2022 pub(crate) fn remove_range(&mut self, range: core::ops::Range<usize>) -> isize {
2023 if range.is_empty() {
2024 return 0;
2025 }
2026
2027 let mut delta = 0isize;
2028 let removed_points: Vec<_> = self
2029 .points
2030 .keys()
2031 .copied()
2032 .filter(|off| range.contains(off))
2033 .collect();
2034 for off in removed_points {
2035 if let Some(old) = self.points.remove(&off) {
2036 let old_est = Self::point_estimate(&old);
2037 self.estimated_bytes = self.estimated_bytes.saturating_sub(old_est);
2038 delta = delta.saturating_sub(old_est as isize);
2039 }
2040 }
2041
2042 if !self.fragments.is_empty() {
2043 let mut fragment_delta = 0isize;
2044 let mut fragments = Vec::with_capacity(self.fragments.len());
2045 for fragment in self.fragments.drain(..) {
2046 let old_est = fragment.estimated_bytes();
2047 let replacements = fragment.subtract_interval(range.clone());
2048 let new_est = replacements
2049 .iter()
2050 .map(OverlayFragment::estimated_bytes)
2051 .fold(0usize, usize::saturating_add);
2052 fragments.extend(replacements);
2053 fragment_delta = fragment_delta.saturating_add(new_est as isize - old_est as isize);
2054 }
2055 self.fragments = fragments;
2056 self.adjust_estimated_bytes(fragment_delta);
2057 delta = delta.saturating_add(fragment_delta);
2058 }
2059
2060 delta
2061 }
2062
2063 #[inline]
2064 pub(crate) fn clear_all(&mut self) -> usize {
2065 let freed = self.estimated_bytes;
2066 self.points.clear();
2067 self.fragments.clear();
2068 self.estimated_bytes = 0;
2069 freed
2070 }
2071
2072 #[inline]
2073 pub fn clear(&mut self) -> usize {
2074 self.clear_all()
2075 }
2076
2077 #[inline]
2078 pub fn len(&self) -> usize {
2079 self.points.len().saturating_add(
2080 self.fragments
2081 .iter()
2082 .map(OverlayFragment::coverage_len)
2083 .sum(),
2084 )
2085 }
2086
2087 #[inline]
2088 pub fn estimated_bytes(&self) -> usize {
2089 self.estimated_bytes
2090 }
2091
2092 #[inline]
2093 pub fn is_empty(&self) -> bool {
2094 self.points.is_empty() && self.fragments.is_empty()
2095 }
2096
2097 #[inline]
2098 pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2099 self.points.keys().any(|k| range.contains(k))
2100 || self
2101 .fragments
2102 .iter()
2103 .any(|fragment| fragment.has_any_in_range(range.clone()))
2104 }
2105
2106 #[inline]
2107 pub fn any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2108 self.has_any_in_range(range)
2109 }
2110
2111 pub(crate) fn slice(&self, off: usize, len: usize) -> Overlay {
2112 let mut out = Overlay::new();
2113 let end = off.saturating_add(len);
2114 for fragment in &self.fragments {
2115 if let Some(sliced) = fragment.slice(off, len) {
2116 let _ = out.apply_fragment(sliced);
2117 }
2118 }
2119 for (k, v) in self.points.iter() {
2120 if *k >= off && *k < end {
2121 let _ = out.set_scalar(*k - off, v.clone());
2122 }
2123 }
2124 out
2125 }
2126
2127 pub fn iter(&self) -> impl Iterator<Item = (usize, OverlayValue)> {
2129 let mut cells = BTreeMap::new();
2130 for fragment in &self.fragments {
2131 for (off, value) in fragment.cells() {
2132 cells.insert(off, value);
2133 }
2134 }
2135 for (off, value) in &self.points {
2136 cells.insert(*off, value.clone());
2137 }
2138 cells.into_iter()
2139 }
2140
2141 pub(crate) fn iter_points(&self) -> impl Iterator<Item = (&usize, &OverlayValue)> {
2143 self.points.iter()
2144 }
2145}
2146
2147#[cfg(test)]
2148#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
2149pub(crate) struct OverlayDebugStats {
2150 pub(crate) points: usize,
2151 pub(crate) sparse_fragments: usize,
2152 pub(crate) dense_fragments: usize,
2153 pub(crate) run_fragments: usize,
2154 pub(crate) covered_len: usize,
2155}
2156
2157#[cfg(test)]
2158impl Overlay {
2159 pub(crate) fn debug_stats(&self) -> OverlayDebugStats {
2160 let mut stats = OverlayDebugStats {
2161 points: self.points.len(),
2162 covered_len: self.len(),
2163 ..OverlayDebugStats::default()
2164 };
2165 for fragment in &self.fragments {
2166 match fragment {
2167 OverlayFragment::SparseOffsets { .. } => stats.sparse_fragments += 1,
2168 OverlayFragment::DenseRange { .. } => stats.dense_fragments += 1,
2169 OverlayFragment::RunRange { .. } => stats.run_fragments += 1,
2170 }
2171 }
2172 stats
2173 }
2174
2175 pub(crate) fn debug_is_normalized(&self) -> bool {
2176 let mut covered = std::collections::HashSet::new();
2177 for off in self.points.keys().copied() {
2178 if !covered.insert(off) {
2179 return false;
2180 }
2181 }
2182 for fragment in &self.fragments {
2183 for (off, _) in fragment.cells() {
2184 if !covered.insert(off) {
2185 return false;
2186 }
2187 }
2188 }
2189 covered.len() == self.len()
2190 }
2191
2192 pub(crate) fn debug_recomputed_estimated_bytes(&self) -> usize {
2193 let point_bytes = self
2194 .points
2195 .values()
2196 .map(Self::point_estimate)
2197 .fold(0usize, usize::saturating_add);
2198 let fragment_bytes = self
2199 .fragments
2200 .iter()
2201 .map(OverlayFragment::estimated_bytes)
2202 .fold(0usize, usize::saturating_add);
2203 point_bytes.saturating_add(fragment_bytes)
2204 }
2205}
2206
2207#[derive(Debug, Clone, Copy, Default)]
2208#[cfg_attr(test, derive(serde::Serialize))]
2209pub(crate) struct OverlaySelectStats {
2210 pub(crate) zip_select_calls: usize,
2211 pub(crate) direct_dense_slices: usize,
2212 pub(crate) direct_run_materializations: usize,
2213 pub(crate) partial_sparse_intersections: usize,
2214 pub(crate) partial_dense_intersections: usize,
2215 pub(crate) partial_run_intersections: usize,
2216 pub(crate) partial_overlay_builds: usize,
2217 pub(crate) row_scalar_fallbacks: usize,
2218 pub(crate) point_entries_applied: usize,
2219 pub(crate) fragment_intersections: usize,
2220}
2221
2222#[cfg(test)]
2223thread_local! {
2224 static OVERLAY_SELECT_STATS: std::cell::RefCell<OverlaySelectStats> =
2225 std::cell::RefCell::new(OverlaySelectStats::default());
2226}
2227
2228#[cfg(test)]
2229pub(crate) fn reset_overlay_select_stats() {
2230 OVERLAY_SELECT_STATS.with(|stats| *stats.borrow_mut() = OverlaySelectStats::default());
2231}
2232
2233#[cfg(test)]
2234pub(crate) fn snapshot_overlay_select_stats() -> OverlaySelectStats {
2235 OVERLAY_SELECT_STATS.with(|stats| *stats.borrow())
2236}
2237
2238#[cfg(test)]
2239fn record_overlay_select_stats(f: impl FnOnce(&mut OverlaySelectStats)) {
2240 OVERLAY_SELECT_STATS.with(|stats| f(&mut stats.borrow_mut()));
2241}
2242
2243#[cfg(not(test))]
2244#[inline]
2245fn record_overlay_select_stats(_f: impl FnOnce(&mut OverlaySelectStats)) {}
2246
2247#[derive(Debug, Clone, Copy, Eq, PartialEq)]
2248enum OverlayFragmentShape {
2249 Sparse,
2250 Dense,
2251 Run,
2252}
2253
2254struct OverlaySlots<T> {
2255 present: Vec<bool>,
2256 values: Vec<Option<T>>,
2257 any_present: bool,
2258}
2259
2260impl<T> OverlaySlots<T> {
2261 fn new(len: usize) -> Self {
2262 Self {
2263 present: vec![false; len],
2264 values: (0..len).map(|_| None).collect(),
2265 any_present: false,
2266 }
2267 }
2268
2269 #[inline]
2270 fn set(&mut self, idx: usize, value: Option<T>) {
2271 if idx >= self.present.len() {
2272 return;
2273 }
2274 self.present[idx] = true;
2275 self.values[idx] = value;
2276 self.any_present = true;
2277 }
2278
2279 #[inline]
2280 fn any_present(&self) -> bool {
2281 self.any_present
2282 }
2283}
2284
2285pub(crate) struct OverlayCascade<'a> {
2286 user: &'a Overlay,
2287 computed: &'a Overlay,
2288}
2289
2290impl<'a> OverlayCascade<'a> {
2291 #[inline]
2292 pub(crate) fn new(user: &'a Overlay, computed: &'a Overlay) -> Self {
2293 Self { user, computed }
2294 }
2295
2296 #[inline]
2297 pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'a>> {
2298 self.user
2299 .get_scalar(off)
2300 .or_else(|| self.computed.get_scalar(off))
2301 }
2302
2303 #[inline]
2304 pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2305 self.user.has_any_in_range(range.clone()) || self.computed.has_any_in_range(range)
2306 }
2307
2308 pub(crate) fn select_numbers(
2309 &self,
2310 range: core::ops::Range<usize>,
2311 base: &Float64Array,
2312 ) -> Arc<Float64Array> {
2313 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2314 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2315 return Self::dense_numbers(fragment, range);
2316 }
2317 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2318 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2319 return Self::run_numbers(fragment, range);
2320 }
2321 if !self.user.has_any_in_range(range.clone()) {
2322 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2323 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2324 return Self::dense_numbers(fragment, range);
2325 }
2326 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2327 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2328 return Self::run_numbers(fragment, range);
2329 }
2330 }
2331
2332 if !self.has_any_in_range(range.clone()) {
2333 return Arc::new(base.clone());
2334 }
2335
2336 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2337 let len = range.end.saturating_sub(range.start);
2338 let mut slots = OverlaySlots::<f64>::new(len);
2339 Self::apply_number_layer(self.computed, range.clone(), &mut slots);
2340 Self::apply_number_layer(self.user, range.clone(), &mut slots);
2341 if !slots.any_present() {
2342 return Arc::new(base.clone());
2343 }
2344
2345 let mut mask_b = BooleanBuilder::with_capacity(len);
2346 let mut values_b = Float64Builder::with_capacity(len);
2347 for idx in 0..len {
2348 mask_b.append_value(slots.present[idx]);
2349 match slots.values[idx] {
2350 Some(value) => values_b.append_value(value),
2351 None => values_b.append_null(),
2352 }
2353 }
2354 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2355 let mask = mask_b.finish();
2356 let values = values_b.finish();
2357 let zipped =
2358 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip numeric overlay");
2359 Arc::new(
2360 zipped
2361 .as_any()
2362 .downcast_ref::<Float64Array>()
2363 .expect("numeric overlay zip type")
2364 .clone(),
2365 )
2366 }
2367
2368 pub(crate) fn select_booleans(
2369 &self,
2370 range: core::ops::Range<usize>,
2371 base: &BooleanArray,
2372 ) -> Arc<BooleanArray> {
2373 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2374 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2375 return Self::dense_booleans(fragment, range);
2376 }
2377 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2378 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2379 return Self::run_booleans(fragment, range);
2380 }
2381 if !self.user.has_any_in_range(range.clone()) {
2382 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2383 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2384 return Self::dense_booleans(fragment, range);
2385 }
2386 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2387 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2388 return Self::run_booleans(fragment, range);
2389 }
2390 }
2391
2392 if !self.has_any_in_range(range.clone()) {
2393 return Arc::new(base.clone());
2394 }
2395
2396 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2397 let len = range.end.saturating_sub(range.start);
2398 let mut slots = OverlaySlots::<bool>::new(len);
2399 Self::apply_boolean_layer(self.computed, range.clone(), &mut slots);
2400 Self::apply_boolean_layer(self.user, range.clone(), &mut slots);
2401 if !slots.any_present() {
2402 return Arc::new(base.clone());
2403 }
2404
2405 let mut mask_b = BooleanBuilder::with_capacity(len);
2406 let mut values_b = BooleanBuilder::with_capacity(len);
2407 for idx in 0..len {
2408 mask_b.append_value(slots.present[idx]);
2409 match slots.values[idx] {
2410 Some(value) => values_b.append_value(value),
2411 None => values_b.append_null(),
2412 }
2413 }
2414 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2415 let mask = mask_b.finish();
2416 let values = values_b.finish();
2417 let zipped =
2418 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip boolean overlay");
2419 Arc::new(
2420 zipped
2421 .as_any()
2422 .downcast_ref::<BooleanArray>()
2423 .expect("boolean overlay zip type")
2424 .clone(),
2425 )
2426 }
2427
2428 pub(crate) fn select_text(
2429 &self,
2430 range: core::ops::Range<usize>,
2431 base: &StringArray,
2432 ) -> ArrayRef {
2433 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2434 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2435 return Self::dense_text(fragment, range);
2436 }
2437 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2438 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2439 return Self::run_text(fragment, range);
2440 }
2441 if !self.user.has_any_in_range(range.clone()) {
2442 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2443 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2444 return Self::dense_text(fragment, range);
2445 }
2446 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2447 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2448 return Self::run_text(fragment, range);
2449 }
2450 }
2451
2452 if !self.has_any_in_range(range.clone()) {
2453 return Arc::new(base.clone()) as ArrayRef;
2454 }
2455
2456 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2457 let len = range.end.saturating_sub(range.start);
2458 let mut slots = OverlaySlots::<String>::new(len);
2459 Self::apply_text_layer(self.computed, range.clone(), &mut slots);
2460 Self::apply_text_layer(self.user, range.clone(), &mut slots);
2461 if !slots.any_present() {
2462 return Arc::new(base.clone()) as ArrayRef;
2463 }
2464
2465 let mut mask_b = BooleanBuilder::with_capacity(len);
2466 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
2467 for idx in 0..len {
2468 mask_b.append_value(slots.present[idx]);
2469 match &slots.values[idx] {
2470 Some(value) => values_b.append_value(value),
2471 None => values_b.append_null(),
2472 }
2473 }
2474 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2475 let mask = mask_b.finish();
2476 let values = values_b.finish();
2477 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip text overlay")
2478 }
2479
2480 pub(crate) fn select_errors(
2481 &self,
2482 range: core::ops::Range<usize>,
2483 base: &UInt8Array,
2484 ) -> Arc<UInt8Array> {
2485 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2486 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2487 return Self::dense_errors(fragment, range);
2488 }
2489 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2490 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2491 return Self::run_errors(fragment, range);
2492 }
2493 if !self.user.has_any_in_range(range.clone()) {
2494 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2495 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2496 return Self::dense_errors(fragment, range);
2497 }
2498 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2499 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2500 return Self::run_errors(fragment, range);
2501 }
2502 }
2503
2504 if !self.has_any_in_range(range.clone()) {
2505 return Arc::new(base.clone());
2506 }
2507
2508 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2509 let len = range.end.saturating_sub(range.start);
2510 let mut slots = OverlaySlots::<u8>::new(len);
2511 Self::apply_error_layer(self.computed, range.clone(), &mut slots);
2512 Self::apply_error_layer(self.user, range.clone(), &mut slots);
2513 if !slots.any_present() {
2514 return Arc::new(base.clone());
2515 }
2516
2517 let mut mask_b = BooleanBuilder::with_capacity(len);
2518 let mut values_b = UInt8Builder::with_capacity(len);
2519 for idx in 0..len {
2520 mask_b.append_value(slots.present[idx]);
2521 match slots.values[idx] {
2522 Some(value) => values_b.append_value(value),
2523 None => values_b.append_null(),
2524 }
2525 }
2526 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2527 let mask = mask_b.finish();
2528 let values = values_b.finish();
2529 let zipped =
2530 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip error overlay");
2531 Arc::new(
2532 zipped
2533 .as_any()
2534 .downcast_ref::<UInt8Array>()
2535 .expect("error overlay zip type")
2536 .clone(),
2537 )
2538 }
2539
2540 pub(crate) fn select_type_tags(
2541 &self,
2542 range: core::ops::Range<usize>,
2543 base: &UInt8Array,
2544 ) -> Arc<UInt8Array> {
2545 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2546 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2547 return Self::dense_type_tags(fragment, range);
2548 }
2549 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2550 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2551 return Self::run_type_tags(fragment, range);
2552 }
2553 if !self.user.has_any_in_range(range.clone()) {
2554 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2555 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2556 return Self::dense_type_tags(fragment, range);
2557 }
2558 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2559 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2560 return Self::run_type_tags(fragment, range);
2561 }
2562 }
2563
2564 if !self.has_any_in_range(range.clone()) {
2565 return Arc::new(base.clone());
2566 }
2567
2568 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2569 let len = range.end.saturating_sub(range.start);
2570 let mut slots = OverlaySlots::<u8>::new(len);
2571 Self::apply_type_tag_layer(self.computed, range.clone(), &mut slots);
2572 Self::apply_type_tag_layer(self.user, range.clone(), &mut slots);
2573 if !slots.any_present() {
2574 return Arc::new(base.clone());
2575 }
2576
2577 let mut mask_b = BooleanBuilder::with_capacity(len);
2578 let mut values_b = UInt8Builder::with_capacity(len);
2579 for idx in 0..len {
2580 mask_b.append_value(slots.present[idx]);
2581 match slots.values[idx] {
2582 Some(value) => values_b.append_value(value),
2583 None => values_b.append_null(),
2584 }
2585 }
2586 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2587 let mask = mask_b.finish();
2588 let values = values_b.finish();
2589 let zipped =
2590 crate::compute_prelude::zip_select(&mask, &values, base).expect("zip type-tag overlay");
2591 Arc::new(
2592 zipped
2593 .as_any()
2594 .downcast_ref::<UInt8Array>()
2595 .expect("type-tag overlay zip type")
2596 .clone(),
2597 )
2598 }
2599
2600 pub(crate) fn select_lowered_text(
2601 &self,
2602 range: core::ops::Range<usize>,
2603 base: &StringArray,
2604 ) -> Arc<StringArray> {
2605 if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
2606 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2607 return Self::dense_lowered_text(fragment, range);
2608 }
2609 if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
2610 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2611 return Self::run_lowered_text(fragment, range);
2612 }
2613 if !self.user.has_any_in_range(range.clone()) {
2614 if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
2615 record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
2616 return Self::dense_lowered_text(fragment, range);
2617 }
2618 if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
2619 record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
2620 return Self::run_lowered_text(fragment, range);
2621 }
2622 }
2623
2624 if !self.has_any_in_range(range.clone()) {
2625 return Arc::new(base.clone());
2626 }
2627 if self.user.fragments.is_empty() && self.computed.fragments.is_empty() {
2628 return self.select_lowered_text_point_scalar(range, base);
2629 }
2630
2631 record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
2632 let len = range.end.saturating_sub(range.start);
2633 let mut slots = OverlaySlots::<String>::new(len);
2634 Self::apply_lowered_text_layer(self.computed, range.clone(), &mut slots);
2635 Self::apply_lowered_text_layer(self.user, range.clone(), &mut slots);
2636 if !slots.any_present() {
2637 return Arc::new(base.clone());
2638 }
2639
2640 let mut mask_b = BooleanBuilder::with_capacity(len);
2641 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
2642 for idx in 0..len {
2643 mask_b.append_value(slots.present[idx]);
2644 match &slots.values[idx] {
2645 Some(value) => values_b.append_value(value),
2646 None => values_b.append_null(),
2647 }
2648 }
2649 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2650 let mask = mask_b.finish();
2651 let values = values_b.finish();
2652 let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
2653 .expect("zip lowered text overlay");
2654 Arc::new(
2655 zipped
2656 .as_any()
2657 .downcast_ref::<StringArray>()
2658 .expect("lowered text overlay zip type")
2659 .clone(),
2660 )
2661 }
2662
2663 fn select_lowered_text_point_scalar(
2664 &self,
2665 range: core::ops::Range<usize>,
2666 base: &StringArray,
2667 ) -> Arc<StringArray> {
2668 let len = range.end.saturating_sub(range.start);
2669 let mut mask_b = BooleanBuilder::with_capacity(len);
2670 let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
2671 record_overlay_select_stats(|stats| stats.row_scalar_fallbacks += len);
2672 for off in range {
2673 if let Some(value) = self.get_scalar(off) {
2674 mask_b.append_value(true);
2675 if let Some(s) = value.lowered_text_value() {
2676 values_b.append_value(&s);
2677 } else {
2678 values_b.append_null();
2679 }
2680 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
2681 } else {
2682 mask_b.append_value(false);
2683 values_b.append_null();
2684 }
2685 }
2686 record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
2687 let mask = mask_b.finish();
2688 let values = values_b.finish();
2689 let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
2690 .expect("zip lowered text overlay");
2691 Arc::new(
2692 zipped
2693 .as_any()
2694 .downcast_ref::<StringArray>()
2695 .expect("lowered text overlay zip type")
2696 .clone(),
2697 )
2698 }
2699
2700 fn dense_numbers(
2701 fragment: &OverlayFragment,
2702 range: core::ops::Range<usize>,
2703 ) -> Arc<Float64Array> {
2704 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2705 Self::payload_numbers_slice(payload, rel_start, len)
2706 }
2707
2708 fn dense_booleans(
2709 fragment: &OverlayFragment,
2710 range: core::ops::Range<usize>,
2711 ) -> Arc<BooleanArray> {
2712 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2713 Self::payload_booleans_slice(payload, rel_start, len)
2714 }
2715
2716 fn dense_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
2717 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2718 Self::payload_text_slice(payload, rel_start, len)
2719 }
2720
2721 fn dense_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
2722 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2723 Self::payload_errors_slice(payload, rel_start, len)
2724 }
2725
2726 fn dense_type_tags(
2727 fragment: &OverlayFragment,
2728 range: core::ops::Range<usize>,
2729 ) -> Arc<UInt8Array> {
2730 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2731 Self::payload_type_tags_slice(payload, rel_start, len)
2732 }
2733
2734 fn dense_lowered_text(
2735 fragment: &OverlayFragment,
2736 range: core::ops::Range<usize>,
2737 ) -> Arc<StringArray> {
2738 let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
2739 Self::payload_lowered_text_materialize(payload, rel_start, len)
2740 }
2741
2742 fn dense_payload_window(
2743 fragment: &OverlayFragment,
2744 range: core::ops::Range<usize>,
2745 ) -> (usize, usize, &OverlayFragmentPayload) {
2746 let OverlayFragment::DenseRange { start, payload, .. } = fragment else {
2747 unreachable!("dense payload window requires DenseRange")
2748 };
2749 let rel_start = range.start.saturating_sub(*start as usize);
2750 (rel_start, range.end.saturating_sub(range.start), payload)
2751 }
2752
2753 fn run_numbers(
2754 fragment: &OverlayFragment,
2755 range: core::ops::Range<usize>,
2756 ) -> Arc<Float64Array> {
2757 let mut b = Float64Builder::with_capacity(range.end.saturating_sub(range.start));
2758 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2759 if let Some(value) = payload.number_at(run_idx) {
2760 for _ in 0..repeat {
2761 b.append_value(value);
2762 }
2763 } else {
2764 for _ in 0..repeat {
2765 b.append_null();
2766 }
2767 }
2768 });
2769 Arc::new(b.finish())
2770 }
2771
2772 fn run_booleans(
2773 fragment: &OverlayFragment,
2774 range: core::ops::Range<usize>,
2775 ) -> Arc<BooleanArray> {
2776 let mut b = BooleanBuilder::with_capacity(range.end.saturating_sub(range.start));
2777 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2778 if let Some(value) = payload.boolean_at(run_idx) {
2779 for _ in 0..repeat {
2780 b.append_value(value);
2781 }
2782 } else {
2783 for _ in 0..repeat {
2784 b.append_null();
2785 }
2786 }
2787 });
2788 Arc::new(b.finish())
2789 }
2790
2791 fn run_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
2792 let mut b = StringBuilder::with_capacity(
2793 range.end.saturating_sub(range.start),
2794 range.end.saturating_sub(range.start).saturating_mul(8),
2795 );
2796 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2797 if let Some(value) = payload.text_at(run_idx) {
2798 for _ in 0..repeat {
2799 b.append_value(value);
2800 }
2801 } else {
2802 for _ in 0..repeat {
2803 b.append_null();
2804 }
2805 }
2806 });
2807 Arc::new(b.finish()) as ArrayRef
2808 }
2809
2810 fn run_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
2811 let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
2812 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2813 if let Some(value) = payload.error_at(run_idx) {
2814 for _ in 0..repeat {
2815 b.append_value(value);
2816 }
2817 } else {
2818 for _ in 0..repeat {
2819 b.append_null();
2820 }
2821 }
2822 });
2823 Arc::new(b.finish())
2824 }
2825
2826 fn run_type_tags(
2827 fragment: &OverlayFragment,
2828 range: core::ops::Range<usize>,
2829 ) -> Arc<UInt8Array> {
2830 let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
2831 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2832 let tag = payload.type_tag_at(run_idx).unwrap_or(TypeTag::Empty) as u8;
2833 for _ in 0..repeat {
2834 b.append_value(tag);
2835 }
2836 });
2837 Arc::new(b.finish())
2838 }
2839
2840 fn run_lowered_text(
2841 fragment: &OverlayFragment,
2842 range: core::ops::Range<usize>,
2843 ) -> Arc<StringArray> {
2844 let mut b = StringBuilder::with_capacity(
2845 range.end.saturating_sub(range.start),
2846 range.end.saturating_sub(range.start).saturating_mul(8),
2847 );
2848 Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
2849 let value = Self::payload_lowered_text_at(payload, run_idx);
2850 if let Some(value) = value {
2851 for _ in 0..repeat {
2852 b.append_value(&value);
2853 }
2854 } else {
2855 for _ in 0..repeat {
2856 b.append_null();
2857 }
2858 }
2859 });
2860 Arc::new(b.finish())
2861 }
2862
2863 fn payload_numbers_slice(
2864 payload: &OverlayFragmentPayload,
2865 start: usize,
2866 len: usize,
2867 ) -> Arc<Float64Array> {
2868 if let Some(array) = &payload.numbers {
2869 let sliced = array.slice(start, len);
2870 Arc::new(
2871 sliced
2872 .as_any()
2873 .downcast_ref::<Float64Array>()
2874 .unwrap()
2875 .clone(),
2876 )
2877 } else {
2878 Self::null_numbers(len)
2879 }
2880 }
2881
2882 fn payload_booleans_slice(
2883 payload: &OverlayFragmentPayload,
2884 start: usize,
2885 len: usize,
2886 ) -> Arc<BooleanArray> {
2887 if let Some(array) = &payload.booleans {
2888 let sliced = array.slice(start, len);
2889 Arc::new(
2890 sliced
2891 .as_any()
2892 .downcast_ref::<BooleanArray>()
2893 .unwrap()
2894 .clone(),
2895 )
2896 } else {
2897 Self::null_booleans(len)
2898 }
2899 }
2900
2901 fn payload_text_slice(payload: &OverlayFragmentPayload, start: usize, len: usize) -> ArrayRef {
2902 if let Some(array) = &payload.text {
2903 array.slice(start, len)
2904 } else {
2905 new_null_array(&DataType::Utf8, len)
2906 }
2907 }
2908
2909 fn payload_errors_slice(
2910 payload: &OverlayFragmentPayload,
2911 start: usize,
2912 len: usize,
2913 ) -> Arc<UInt8Array> {
2914 if let Some(array) = &payload.errors {
2915 let sliced = array.slice(start, len);
2916 Arc::new(
2917 sliced
2918 .as_any()
2919 .downcast_ref::<UInt8Array>()
2920 .unwrap()
2921 .clone(),
2922 )
2923 } else {
2924 Self::null_errors(len)
2925 }
2926 }
2927
2928 fn payload_type_tags_slice(
2929 payload: &OverlayFragmentPayload,
2930 start: usize,
2931 len: usize,
2932 ) -> Arc<UInt8Array> {
2933 let sliced = payload.type_tags.slice(start, len);
2934 Arc::new(
2935 sliced
2936 .as_any()
2937 .downcast_ref::<UInt8Array>()
2938 .unwrap()
2939 .clone(),
2940 )
2941 }
2942
2943 fn payload_lowered_text_materialize(
2944 payload: &OverlayFragmentPayload,
2945 start: usize,
2946 len: usize,
2947 ) -> Arc<StringArray> {
2948 let mut b = StringBuilder::with_capacity(len, len.saturating_mul(8));
2949 for idx in start..start.saturating_add(len) {
2950 if let Some(value) = Self::payload_lowered_text_at(payload, idx) {
2951 b.append_value(&value);
2952 } else {
2953 b.append_null();
2954 }
2955 }
2956 Arc::new(b.finish())
2957 }
2958
2959 fn payload_lowered_text_at(payload: &OverlayFragmentPayload, idx: usize) -> Option<String> {
2960 match payload.type_tag_at(idx)? {
2961 TypeTag::Text => payload.text_at(idx).map(|value| value.to_lowercase()),
2962 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
2963 payload.number_at(idx).map(|value| value.to_string())
2964 }
2965 TypeTag::Boolean => payload
2966 .boolean_at(idx)
2967 .map(|value| if value { "true" } else { "false" }.to_string()),
2968 TypeTag::Empty | TypeTag::Error | TypeTag::Pending => None,
2969 }
2970 }
2971
2972 fn null_numbers(len: usize) -> Arc<Float64Array> {
2973 let arr = new_null_array(&DataType::Float64, len);
2974 Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
2975 }
2976
2977 fn null_booleans(len: usize) -> Arc<BooleanArray> {
2978 let arr = new_null_array(&DataType::Boolean, len);
2979 Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
2980 }
2981
2982 fn null_errors(len: usize) -> Arc<UInt8Array> {
2983 let arr = new_null_array(&DataType::UInt8, len);
2984 Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
2985 }
2986
2987 fn apply_number_layer(
2988 layer: &Overlay,
2989 range: core::ops::Range<usize>,
2990 slots: &mut OverlaySlots<f64>,
2991 ) {
2992 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
2993 payload.number_at(idx)
2994 });
2995 for (off, value) in layer.iter_points() {
2996 if range.contains(off) {
2997 slots.set(*off - range.start, value.numeric_lane_value());
2998 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
2999 }
3000 }
3001 }
3002
3003 fn apply_boolean_layer(
3004 layer: &Overlay,
3005 range: core::ops::Range<usize>,
3006 slots: &mut OverlaySlots<bool>,
3007 ) {
3008 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3009 payload.boolean_at(idx)
3010 });
3011 for (off, value) in layer.iter_points() {
3012 if range.contains(off) {
3013 slots.set(*off - range.start, value.boolean_lane_value());
3014 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3015 }
3016 }
3017 }
3018
3019 fn apply_text_layer(
3020 layer: &Overlay,
3021 range: core::ops::Range<usize>,
3022 slots: &mut OverlaySlots<String>,
3023 ) {
3024 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3025 payload.text_at(idx).map(ToString::to_string)
3026 });
3027 for (off, value) in layer.iter_points() {
3028 if range.contains(off) {
3029 slots.set(
3030 *off - range.start,
3031 value.text_lane_value().map(ToString::to_string),
3032 );
3033 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3034 }
3035 }
3036 }
3037
3038 fn apply_error_layer(
3039 layer: &Overlay,
3040 range: core::ops::Range<usize>,
3041 slots: &mut OverlaySlots<u8>,
3042 ) {
3043 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3044 payload.error_at(idx)
3045 });
3046 for (off, value) in layer.iter_points() {
3047 if range.contains(off) {
3048 slots.set(*off - range.start, value.error_lane_value());
3049 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3050 }
3051 }
3052 }
3053
3054 fn apply_type_tag_layer(
3055 layer: &Overlay,
3056 range: core::ops::Range<usize>,
3057 slots: &mut OverlaySlots<u8>,
3058 ) {
3059 Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3060 payload.type_tag_at(idx).map(|tag| tag as u8)
3061 });
3062 for (off, value) in layer.iter_points() {
3063 if range.contains(off) {
3064 slots.set(*off - range.start, Some(value.type_tag() as u8));
3065 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3066 }
3067 }
3068 }
3069
3070 fn apply_lowered_text_layer(
3071 layer: &Overlay,
3072 range: core::ops::Range<usize>,
3073 slots: &mut OverlaySlots<String>,
3074 ) {
3075 Self::apply_fragment_layer(layer, range.clone(), slots, Self::payload_lowered_text_at);
3076 for (off, value) in layer.iter_points() {
3077 if range.contains(off) {
3078 slots.set(*off - range.start, value.lowered_text_value());
3079 record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3080 }
3081 }
3082 }
3083
3084 fn apply_fragment_layer<T>(
3085 layer: &Overlay,
3086 range: core::ops::Range<usize>,
3087 slots: &mut OverlaySlots<T>,
3088 mut value_at: impl FnMut(&OverlayFragmentPayload, usize) -> Option<T>,
3089 ) {
3090 for fragment in &layer.fragments {
3091 if !fragment.has_any_in_range(range.clone()) {
3092 continue;
3093 }
3094 Self::record_fragment_intersection(fragment);
3095 Self::for_each_fragment_payload_index(
3096 fragment,
3097 range.clone(),
3098 |out_idx, payload, payload_idx| {
3099 slots.set(out_idx, value_at(payload, payload_idx));
3100 },
3101 );
3102 }
3103 }
3104
3105 fn record_fragment_intersection(fragment: &OverlayFragment) {
3106 let shape = match fragment {
3107 OverlayFragment::SparseOffsets { .. } => OverlayFragmentShape::Sparse,
3108 OverlayFragment::DenseRange { .. } => OverlayFragmentShape::Dense,
3109 OverlayFragment::RunRange { .. } => OverlayFragmentShape::Run,
3110 };
3111 record_overlay_select_stats(|stats| {
3112 stats.fragment_intersections += 1;
3113 match shape {
3114 OverlayFragmentShape::Sparse => stats.partial_sparse_intersections += 1,
3115 OverlayFragmentShape::Dense => stats.partial_dense_intersections += 1,
3116 OverlayFragmentShape::Run => stats.partial_run_intersections += 1,
3117 }
3118 });
3119 }
3120
3121 fn for_each_fragment_payload_index(
3122 fragment: &OverlayFragment,
3123 range: core::ops::Range<usize>,
3124 mut f: impl FnMut(usize, &OverlayFragmentPayload, usize),
3125 ) {
3126 if range.is_empty() {
3127 return;
3128 }
3129 match fragment {
3130 OverlayFragment::SparseOffsets { offsets, payload } => {
3131 let start = u32::try_from(range.start).unwrap_or(u32::MAX);
3132 let lo = offsets.partition_point(|off| *off < start);
3133 let hi = offsets.partition_point(|off| (*off as usize) < range.end);
3134 for (idx, off) in offsets.iter().enumerate().take(hi).skip(lo) {
3135 let out_idx = (*off as usize).saturating_sub(range.start);
3136 f(out_idx, payload, idx);
3137 }
3138 }
3139 OverlayFragment::DenseRange {
3140 start,
3141 len,
3142 payload,
3143 } => {
3144 let frag_start = *start as usize;
3145 let frag_end = frag_start.saturating_add(*len as usize);
3146 let inter_start = frag_start.max(range.start);
3147 let inter_end = frag_end.min(range.end);
3148 if inter_start >= inter_end {
3149 return;
3150 }
3151 for abs in inter_start..inter_end {
3152 f(abs - range.start, payload, abs - frag_start);
3153 }
3154 }
3155 OverlayFragment::RunRange {
3156 start,
3157 len,
3158 run_ends,
3159 payload,
3160 } => {
3161 let frag_start = *start as usize;
3162 let frag_end = frag_start.saturating_add(*len as usize);
3163 let inter_start = frag_start.max(range.start);
3164 let inter_end = frag_end.min(range.end);
3165 if inter_start >= inter_end {
3166 return;
3167 }
3168 let mut prev_end = 0usize;
3169 for (run_idx, run_end) in run_ends.iter().enumerate() {
3170 let run_start_abs = frag_start.saturating_add(prev_end);
3171 let run_end_abs = frag_start.saturating_add(*run_end as usize);
3172 let start_abs = run_start_abs.max(inter_start);
3173 let end_abs = run_end_abs.min(inter_end);
3174 if start_abs < end_abs {
3175 for abs in start_abs..end_abs {
3176 f(abs - range.start, payload, run_idx);
3177 }
3178 }
3179 prev_end = *run_end as usize;
3180 if run_end_abs >= inter_end {
3181 break;
3182 }
3183 }
3184 }
3185 }
3186 }
3187
3188 fn for_each_run_payload_index(
3189 fragment: &OverlayFragment,
3190 range: core::ops::Range<usize>,
3191 mut f: impl FnMut(&OverlayFragmentPayload, usize, usize),
3192 ) {
3193 let OverlayFragment::RunRange {
3194 start,
3195 len,
3196 run_ends,
3197 payload,
3198 } = fragment
3199 else {
3200 unreachable!("run payload iteration requires RunRange")
3201 };
3202 let frag_start = *start as usize;
3203 let frag_end = frag_start.saturating_add(*len as usize);
3204 let inter_start = frag_start.max(range.start);
3205 let inter_end = frag_end.min(range.end);
3206 if inter_start >= inter_end {
3207 return;
3208 }
3209 let mut prev_end = 0usize;
3210 for (run_idx, run_end) in run_ends.iter().enumerate() {
3211 let run_start_abs = frag_start.saturating_add(prev_end);
3212 let run_end_abs = frag_start.saturating_add(*run_end as usize);
3213 let start_abs = run_start_abs.max(inter_start);
3214 let end_abs = run_end_abs.min(inter_end);
3215 if start_abs < end_abs {
3216 f(payload, run_idx, end_abs - start_abs);
3217 }
3218 prev_end = *run_end as usize;
3219 if run_end_abs >= inter_end {
3220 break;
3221 }
3222 }
3223 }
3224}
3225
3226impl OverlayFragmentPayload {
3227 #[inline]
3228 fn type_tag_at(&self, idx: usize) -> Option<TypeTag> {
3229 if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
3230 return None;
3231 }
3232 Some(TypeTag::from_u8(self.type_tags.value(idx)))
3233 }
3234}
3235
3236impl Overlay {
3237 fn full_cover_dense_fragment(
3238 &self,
3239 range: core::ops::Range<usize>,
3240 ) -> Option<&OverlayFragment> {
3241 self.full_cover_single_fragment(range, OverlayFragmentShape::Dense)
3242 }
3243
3244 fn full_cover_run_fragment(&self, range: core::ops::Range<usize>) -> Option<&OverlayFragment> {
3245 self.full_cover_single_fragment(range, OverlayFragmentShape::Run)
3246 }
3247
3248 fn full_cover_single_fragment(
3249 &self,
3250 range: core::ops::Range<usize>,
3251 shape: OverlayFragmentShape,
3252 ) -> Option<&OverlayFragment> {
3253 if range.is_empty() || self.points.keys().any(|off| range.contains(off)) {
3254 return None;
3255 }
3256 let mut found = None;
3257 for fragment in &self.fragments {
3258 if !fragment.has_any_in_range(range.clone()) {
3259 continue;
3260 }
3261 let shape_matches = matches!(
3262 (shape, fragment),
3263 (
3264 OverlayFragmentShape::Dense,
3265 OverlayFragment::DenseRange { .. }
3266 ) | (OverlayFragmentShape::Run, OverlayFragment::RunRange { .. })
3267 );
3268 let covers = fragment
3269 .interval_coverage()
3270 .is_some_and(|own| own.start <= range.start && range.end <= own.end);
3271 if shape_matches && covers && found.is_none() {
3272 found = Some(fragment);
3273 } else {
3274 return None;
3275 }
3276 }
3277 found
3278 }
3279}
3280fn append_overlay_value_to_lane_builders(
3281 ov: &OverlayValue,
3282 tag_b: &mut UInt8Builder,
3283 nb: &mut Float64Builder,
3284 bb: &mut BooleanBuilder,
3285 sb: &mut StringBuilder,
3286 eb: &mut UInt8Builder,
3287 non_num: &mut usize,
3288 non_bool: &mut usize,
3289 non_text: &mut usize,
3290 non_err: &mut usize,
3291) {
3292 match ov {
3293 OverlayValue::Empty => {
3294 tag_b.append_value(TypeTag::Empty as u8);
3295 nb.append_null();
3296 bb.append_null();
3297 sb.append_null();
3298 eb.append_null();
3299 }
3300 OverlayValue::Number(n) => {
3301 tag_b.append_value(TypeTag::Number as u8);
3302 nb.append_value(*n);
3303 *non_num += 1;
3304 bb.append_null();
3305 sb.append_null();
3306 eb.append_null();
3307 }
3308 OverlayValue::DateTime(serial) => {
3309 tag_b.append_value(TypeTag::DateTime as u8);
3310 nb.append_value(*serial);
3311 *non_num += 1;
3312 bb.append_null();
3313 sb.append_null();
3314 eb.append_null();
3315 }
3316 OverlayValue::Duration(serial) => {
3317 tag_b.append_value(TypeTag::Duration as u8);
3318 nb.append_value(*serial);
3319 *non_num += 1;
3320 bb.append_null();
3321 sb.append_null();
3322 eb.append_null();
3323 }
3324 OverlayValue::Boolean(b) => {
3325 tag_b.append_value(TypeTag::Boolean as u8);
3326 nb.append_null();
3327 bb.append_value(*b);
3328 *non_bool += 1;
3329 sb.append_null();
3330 eb.append_null();
3331 }
3332 OverlayValue::Text(s) => {
3333 tag_b.append_value(TypeTag::Text as u8);
3334 nb.append_null();
3335 bb.append_null();
3336 sb.append_value(s);
3337 *non_text += 1;
3338 eb.append_null();
3339 }
3340 OverlayValue::Error(code) => {
3341 tag_b.append_value(TypeTag::Error as u8);
3342 nb.append_null();
3343 bb.append_null();
3344 sb.append_null();
3345 eb.append_value(*code);
3346 *non_err += 1;
3347 }
3348 OverlayValue::Pending => {
3349 tag_b.append_value(TypeTag::Pending as u8);
3350 nb.append_null();
3351 bb.append_null();
3352 sb.append_null();
3353 eb.append_null();
3354 }
3355 }
3356}
3357
3358impl ArrowSheet {
3359 pub fn new_sparse(sheet_name: &str, ncols: usize, nrows: usize, chunk_rows: usize) -> Self {
3365 Self::new_sparse_with_date_system(
3366 sheet_name,
3367 ncols,
3368 nrows,
3369 chunk_rows,
3370 crate::engine::DateSystem::Excel1900,
3371 )
3372 }
3373
3374 pub fn new_sparse_with_date_system(
3376 sheet_name: &str,
3377 ncols: usize,
3378 nrows: usize,
3379 chunk_rows: usize,
3380 date_system: crate::engine::DateSystem,
3381 ) -> Self {
3382 let chunk_rows = chunk_rows.max(1);
3383 let columns = (0..ncols)
3384 .map(|idx| ArrowColumn {
3385 chunks: Vec::new(),
3386 sparse_chunks: FxHashMap::default(),
3387 index: idx as u32,
3388 })
3389 .collect();
3390 let mut sheet = Self {
3391 name: Arc::from(sheet_name.to_string()),
3392 date_system,
3393 columns,
3394 nrows: 0,
3395 chunk_starts: Vec::new(),
3396 chunk_rows,
3397 };
3398 sheet.ensure_row_capacity(nrows);
3399 sheet
3400 }
3401
3402 pub fn set_sparse_overlay_value(
3406 &mut self,
3407 abs_row: usize,
3408 abs_col: usize,
3409 value: OverlayValue,
3410 ) -> isize {
3411 if abs_col >= self.columns.len() {
3412 let start = self.columns.len();
3413 self.columns
3414 .extend((start..=abs_col).map(|idx| ArrowColumn {
3415 chunks: Vec::new(),
3416 sparse_chunks: FxHashMap::default(),
3417 index: idx as u32,
3418 }));
3419 }
3420 if abs_row >= self.nrows as usize {
3421 self.ensure_row_capacity(abs_row + 1);
3422 }
3423 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
3424 return 0;
3425 };
3426 let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) else {
3427 return 0;
3428 };
3429 ch.overlay.set(in_off, value)
3430 }
3431
3432 pub fn shape(&self) -> Vec<ColumnShape> {
3434 self.columns
3435 .iter()
3436 .map(|c| {
3437 let chunks = c.chunks.len();
3438 let rows = self.nrows as usize;
3439 let has_num = c.chunks.iter().any(|ch| ch.meta.non_null_num > 0);
3440 let has_bool = c.chunks.iter().any(|ch| ch.meta.non_null_bool > 0);
3441 let has_text = c.chunks.iter().any(|ch| ch.meta.non_null_text > 0);
3442 let has_err = c.chunks.iter().any(|ch| ch.meta.non_null_err > 0);
3443 ColumnShape {
3444 index: c.index,
3445 chunks,
3446 rows,
3447 has_num,
3448 has_bool,
3449 has_text,
3450 has_err,
3451 }
3452 })
3453 .collect()
3454 }
3455
3456 pub fn range_view(
3457 &self,
3458 sr: usize,
3459 sc: usize,
3460 er: usize,
3461 ec: usize,
3462 ) -> crate::engine::range_view::RangeView<'_> {
3463 let r0 = er.checked_sub(sr).map(|d| d + 1).unwrap_or(0);
3464 let c0 = ec.checked_sub(sc).map(|d| d + 1).unwrap_or(0);
3465 let (rows, cols) = if r0 == 0 || c0 == 0 { (0, 0) } else { (r0, c0) };
3466 crate::engine::range_view::RangeView::new(
3467 crate::engine::range_view::RangeBacking::Borrowed(self),
3468 sr,
3469 sc,
3470 er,
3471 ec,
3472 rows,
3473 cols,
3474 )
3475 }
3476
3477 #[inline]
3481 pub fn get_cell_value(&self, abs_row: usize, abs_col: usize) -> LiteralValue {
3482 let sheet_rows = self.nrows as usize;
3483 if abs_row >= sheet_rows {
3484 return LiteralValue::Empty;
3485 }
3486 if abs_col >= self.columns.len() {
3487 return LiteralValue::Empty;
3488 }
3489 let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
3490 return LiteralValue::Empty;
3491 };
3492 let col_ref = &self.columns[abs_col];
3493 let Some(ch) = col_ref.chunk(ch_idx) else {
3494 return LiteralValue::Empty;
3495 };
3496
3497 let cascade = OverlayCascade::new(&ch.overlay, &ch.computed_overlay);
3499 if let Some(ov) = cascade.get_scalar(in_off) {
3500 return ov.to_literal_for(self.date_system);
3501 }
3502
3503 let tag_u8 = ch.type_tag.value(in_off);
3505 match TypeTag::from_u8(tag_u8) {
3506 TypeTag::Empty => LiteralValue::Empty,
3507 TypeTag::Number => {
3508 if let Some(arr) = &ch.numbers {
3509 if arr.is_null(in_off) {
3510 return LiteralValue::Empty;
3511 }
3512 LiteralValue::Number(arr.value(in_off))
3513 } else {
3514 LiteralValue::Empty
3515 }
3516 }
3517 TypeTag::DateTime => {
3518 if let Some(arr) = &ch.numbers {
3519 if arr.is_null(in_off) {
3520 return LiteralValue::Empty;
3521 }
3522 LiteralValue::try_from_serial_number_for(self.date_system, arr.value(in_off))
3523 .unwrap_or_else(LiteralValue::Error)
3524 } else {
3525 LiteralValue::Empty
3526 }
3527 }
3528 TypeTag::Duration => {
3529 if let Some(arr) = &ch.numbers {
3530 if arr.is_null(in_off) {
3531 return LiteralValue::Empty;
3532 }
3533 let serial = arr.value(in_off);
3534 let nanos_f = serial * 86_400.0 * 1_000_000_000.0;
3535 let nanos = nanos_f.round().clamp(i64::MIN as f64, i64::MAX as f64) as i64;
3536 LiteralValue::Duration(chrono::Duration::nanoseconds(nanos))
3537 } else {
3538 LiteralValue::Empty
3539 }
3540 }
3541 TypeTag::Boolean => {
3542 if let Some(arr) = &ch.booleans {
3543 if arr.is_null(in_off) {
3544 return LiteralValue::Empty;
3545 }
3546 LiteralValue::Boolean(arr.value(in_off))
3547 } else {
3548 LiteralValue::Empty
3549 }
3550 }
3551 TypeTag::Text => {
3552 if let Some(arr) = &ch.text {
3553 if arr.is_null(in_off) {
3554 return LiteralValue::Empty;
3555 }
3556 let sa = arr
3557 .as_any()
3558 .downcast_ref::<arrow_array::StringArray>()
3559 .unwrap();
3560 LiteralValue::Text(sa.value(in_off).to_string())
3561 } else {
3562 LiteralValue::Empty
3563 }
3564 }
3565 TypeTag::Error => {
3566 if let Some(arr) = &ch.errors {
3567 if arr.is_null(in_off) {
3568 return LiteralValue::Empty;
3569 }
3570 let kind = unmap_error_code(arr.value(in_off));
3571 LiteralValue::Error(ExcelError::new(kind))
3572 } else {
3573 LiteralValue::Empty
3574 }
3575 }
3576 TypeTag::Pending => LiteralValue::Pending,
3577 }
3578 }
3579
3580 pub fn ensure_row_capacity(&mut self, target_rows: usize) {
3585 if target_rows as u32 <= self.nrows {
3586 return;
3587 }
3588
3589 let chunk_size = self.chunk_rows.max(1);
3590
3591 if self.chunk_starts.is_empty() {
3595 self.chunk_starts.push(0);
3596 }
3597
3598 let mut next_start = self
3601 .chunk_starts
3602 .last()
3603 .copied()
3604 .unwrap_or(0)
3605 .saturating_add(chunk_size);
3606 while next_start < target_rows {
3607 self.chunk_starts.push(next_start);
3608 next_start = next_start.saturating_add(chunk_size);
3609 }
3610
3611 self.nrows = target_rows as u32;
3612
3613 let starts = self.chunk_starts.clone();
3618 let nrows = self.nrows as usize;
3619 let required_len_for = |ch_idx: usize| -> Option<usize> {
3620 let start = *starts.get(ch_idx)?;
3621 let end = starts.get(ch_idx + 1).copied().unwrap_or(nrows);
3622 Some(end.saturating_sub(start))
3623 };
3624
3625 for col in &mut self.columns {
3626 for (idx, ch) in col.chunks.iter_mut().enumerate() {
3627 if let Some(req) = required_len_for(idx) {
3628 ch.grow_len_to(req);
3629 }
3630 }
3631 if !col.sparse_chunks.is_empty() {
3632 let keys: Vec<usize> = col.sparse_chunks.keys().copied().collect();
3633 for idx in keys {
3634 if let (Some(req), Some(ch)) =
3635 (required_len_for(idx), col.sparse_chunks.get_mut(&idx))
3636 {
3637 ch.grow_len_to(req);
3638 }
3639 }
3640 }
3641 }
3642 }
3643
3644 pub fn ensure_column_chunk_mut(
3648 &mut self,
3649 col_idx: usize,
3650 ch_idx: usize,
3651 ) -> Option<&mut ColumnChunk> {
3652 let start = *self.chunk_starts.get(ch_idx)?;
3653 let end = self
3654 .chunk_starts
3655 .get(ch_idx + 1)
3656 .copied()
3657 .unwrap_or(self.nrows as usize);
3658 let len = end.saturating_sub(start);
3659
3660 let col = self.columns.get_mut(col_idx)?;
3661 if ch_idx < col.chunks.len() {
3662 return Some(&mut col.chunks[ch_idx]);
3663 }
3664 Some(
3665 col.sparse_chunks
3666 .entry(ch_idx)
3667 .or_insert_with(|| Self::make_empty_chunk(len)),
3668 )
3669 }
3670
3671 pub fn chunk_of_row(&self, abs_row: usize) -> Option<(usize, usize)> {
3673 if abs_row >= self.nrows as usize {
3674 return None;
3675 }
3676 let ch_idx = match self.chunk_starts.binary_search(&abs_row) {
3677 Ok(i) => i,
3678 Err(0) => 0,
3679 Err(i) => i - 1,
3680 };
3681 let start = self.chunk_starts[ch_idx];
3682 Some((ch_idx, abs_row - start))
3683 }
3684
3685 fn recompute_chunk_starts(&mut self) {
3686 self.chunk_starts.clear();
3687 if let Some(col0) = self.columns.first() {
3688 let mut cur = 0usize;
3689 for ch in &col0.chunks {
3690 self.chunk_starts.push(cur);
3691 cur += ch.type_tag.len();
3692 }
3693 }
3694 }
3695
3696 fn make_empty_chunk(len: usize) -> ColumnChunk {
3697 ColumnChunk {
3698 numbers: None,
3699 booleans: None,
3700 text: None,
3701 errors: None,
3702 type_tag: Arc::new(UInt8Array::from(vec![TypeTag::Empty as u8; len])),
3703 formula_id: None,
3704 meta: ColumnChunkMeta {
3705 len,
3706 non_null_num: 0,
3707 non_null_bool: 0,
3708 non_null_text: 0,
3709 non_null_err: 0,
3710 },
3711 lazy_null_numbers: OnceCell::new(),
3712 lazy_null_booleans: OnceCell::new(),
3713 lazy_null_text: OnceCell::new(),
3714 lazy_null_errors: OnceCell::new(),
3715 lowered_text: OnceCell::new(),
3716 overlay: Overlay::new(),
3717 computed_overlay: Overlay::new(),
3718 }
3719 }
3720
3721 fn slice_chunk(ch: &ColumnChunk, off: usize, len: usize) -> ColumnChunk {
3722 use arrow_array::Array;
3724 let type_tag: Arc<UInt8Array> = Arc::new(
3725 Array::slice(ch.type_tag.as_ref(), off, len)
3726 .as_any()
3727 .downcast_ref::<UInt8Array>()
3728 .unwrap()
3729 .clone(),
3730 );
3731 let numbers: Option<Arc<Float64Array>> = ch.numbers.as_ref().and_then(|a| {
3733 let sl = Array::slice(a.as_ref(), off, len);
3734 let fa = sl.as_any().downcast_ref::<Float64Array>().unwrap().clone();
3735 let nn = len.saturating_sub(fa.null_count());
3736 if nn == 0 { None } else { Some(Arc::new(fa)) }
3737 });
3738 let booleans: Option<Arc<BooleanArray>> = ch.booleans.as_ref().and_then(|a| {
3739 let sl = Array::slice(a.as_ref(), off, len);
3740 let ba = sl.as_any().downcast_ref::<BooleanArray>().unwrap().clone();
3741 let nn = len.saturating_sub(ba.null_count());
3742 if nn == 0 { None } else { Some(Arc::new(ba)) }
3743 });
3744 let text: Option<ArrayRef> = ch.text.as_ref().and_then(|a| {
3745 let sl = Array::slice(a.as_ref(), off, len);
3746 let sa = sl.as_any().downcast_ref::<StringArray>().unwrap().clone();
3747 let nn = len.saturating_sub(sa.null_count());
3748 if nn == 0 {
3749 None
3750 } else {
3751 Some(Arc::new(sa) as ArrayRef)
3752 }
3753 });
3754 let errors: Option<Arc<UInt8Array>> = ch.errors.as_ref().and_then(|a| {
3755 let sl = Array::slice(a.as_ref(), off, len);
3756 let ea = sl.as_any().downcast_ref::<UInt8Array>().unwrap().clone();
3757 let nn = len.saturating_sub(ea.null_count());
3758 if nn == 0 { None } else { Some(Arc::new(ea)) }
3759 });
3760 let overlay = ch.overlay.slice(off, len);
3762 let computed_overlay = ch.computed_overlay.slice(off, len);
3763 let non_null_num = numbers.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
3764 let non_null_bool = booleans.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
3765 let non_null_text = text.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
3766 let non_null_err = errors.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
3767 ColumnChunk {
3768 numbers: numbers.clone(),
3769 booleans: booleans.clone(),
3770 text: text.clone(),
3771 errors: errors.clone(),
3772 type_tag,
3773 formula_id: None,
3774 meta: ColumnChunkMeta {
3775 len,
3776 non_null_num,
3777 non_null_bool,
3778 non_null_text,
3779 non_null_err,
3780 },
3781 lazy_null_numbers: OnceCell::new(),
3782 lazy_null_booleans: OnceCell::new(),
3783 lazy_null_text: OnceCell::new(),
3784 lazy_null_errors: OnceCell::new(),
3785 lowered_text: OnceCell::new(),
3786 overlay,
3787 computed_overlay,
3788 }
3789 }
3790
3791 pub fn maybe_compact_chunk(
3794 &mut self,
3795 col_idx: usize,
3796 ch_idx: usize,
3797 abs_threshold: usize,
3798 frac_den: usize,
3799 ) -> usize {
3800 if col_idx >= self.columns.len() {
3801 return 0;
3802 }
3803
3804 let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
3805 let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
3806 return 0;
3807 };
3808 let len = ch_ref.type_tag.len();
3809 if len == 0 {
3810 return 0;
3811 }
3812
3813 let ov_len = ch_ref.overlay.len();
3814 let den = frac_den.max(1);
3815 let trig = ov_len > (len / den) || ov_len > abs_threshold;
3816 if !trig {
3817 return 0;
3818 }
3819
3820 let mut tag_b = UInt8Builder::with_capacity(len);
3822 let mut nb = Float64Builder::with_capacity(len);
3823 let mut bb = BooleanBuilder::with_capacity(len);
3824 let mut sb = StringBuilder::with_capacity(len, len * 8);
3825 let mut eb = UInt8Builder::with_capacity(len);
3826 let mut non_num = 0usize;
3827 let mut non_bool = 0usize;
3828 let mut non_text = 0usize;
3829 let mut non_err = 0usize;
3830
3831 for i in 0..len {
3832 if let Some(ov) = ch_ref.overlay.get_scalar(i) {
3834 let ov = ov.to_overlay_value();
3835 append_overlay_value_to_lane_builders(
3836 &ov,
3837 &mut tag_b,
3838 &mut nb,
3839 &mut bb,
3840 &mut sb,
3841 &mut eb,
3842 &mut non_num,
3843 &mut non_bool,
3844 &mut non_text,
3845 &mut non_err,
3846 );
3847 } else {
3848 let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
3849 match tag {
3850 TypeTag::Empty => {
3851 tag_b.append_value(TypeTag::Empty as u8);
3852 nb.append_null();
3853 bb.append_null();
3854 sb.append_null();
3855 eb.append_null();
3856 }
3857 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
3858 tag_b.append_value(tag as u8);
3859 if let Some(a) = &ch_ref.numbers {
3860 let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
3861 if fa.is_null(i) {
3862 nb.append_null();
3863 } else {
3864 nb.append_value(fa.value(i));
3865 non_num += 1;
3866 }
3867 } else {
3868 nb.append_null();
3869 }
3870 bb.append_null();
3871 sb.append_null();
3872 eb.append_null();
3873 }
3874 TypeTag::Boolean => {
3875 tag_b.append_value(TypeTag::Boolean as u8);
3876 nb.append_null();
3877 if let Some(a) = &ch_ref.booleans {
3878 let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
3879 if ba.is_null(i) {
3880 bb.append_null();
3881 } else {
3882 bb.append_value(ba.value(i));
3883 non_bool += 1;
3884 }
3885 } else {
3886 bb.append_null();
3887 }
3888 sb.append_null();
3889 eb.append_null();
3890 }
3891 TypeTag::Text => {
3892 tag_b.append_value(TypeTag::Text as u8);
3893 nb.append_null();
3894 bb.append_null();
3895 if let Some(a) = &ch_ref.text {
3896 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
3897 if sa.is_null(i) {
3898 sb.append_null();
3899 } else {
3900 sb.append_value(sa.value(i));
3901 non_text += 1;
3902 }
3903 } else {
3904 sb.append_null();
3905 }
3906 eb.append_null();
3907 }
3908 TypeTag::Error => {
3909 tag_b.append_value(TypeTag::Error as u8);
3910 nb.append_null();
3911 bb.append_null();
3912 sb.append_null();
3913 if let Some(a) = &ch_ref.errors {
3914 let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
3915 if ea.is_null(i) {
3916 eb.append_null();
3917 } else {
3918 eb.append_value(ea.value(i));
3919 non_err += 1;
3920 }
3921 } else {
3922 eb.append_null();
3923 }
3924 }
3925 TypeTag::Pending => {
3926 tag_b.append_value(TypeTag::Pending as u8);
3927 nb.append_null();
3928 bb.append_null();
3929 sb.append_null();
3930 eb.append_null();
3931 }
3932 }
3933 }
3934 }
3935
3936 let tags = Arc::new(tag_b.finish());
3937 let numbers = {
3938 let a = nb.finish();
3939 if non_num == 0 {
3940 None
3941 } else {
3942 Some(Arc::new(a))
3943 }
3944 };
3945 let booleans = {
3946 let a = bb.finish();
3947 if non_bool == 0 {
3948 None
3949 } else {
3950 Some(Arc::new(a))
3951 }
3952 };
3953 let text = {
3954 let a = sb.finish();
3955 if non_text == 0 {
3956 None
3957 } else {
3958 Some(Arc::new(a) as ArrayRef)
3959 }
3960 };
3961 let errors = {
3962 let a = eb.finish();
3963 if non_err == 0 {
3964 None
3965 } else {
3966 Some(Arc::new(a))
3967 }
3968 };
3969
3970 (
3971 len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
3972 )
3973 };
3974
3975 let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
3976 return 0;
3977 };
3978
3979 ch_mut.type_tag = tags;
3980 ch_mut.numbers = numbers;
3981 ch_mut.booleans = booleans;
3982 ch_mut.text = text;
3983 ch_mut.errors = errors;
3984 let freed = ch_mut.overlay.clear();
3985 ch_mut.lowered_text = OnceCell::new();
3986 ch_mut.meta.len = len;
3987 ch_mut.meta.non_null_num = non_num;
3988 ch_mut.meta.non_null_bool = non_bool;
3989 ch_mut.meta.non_null_text = non_text;
3990 ch_mut.meta.non_null_err = non_err;
3991 freed
3992 }
3993
3994 pub fn compact_computed_overlay_chunk(&mut self, col_idx: usize, ch_idx: usize) -> usize {
4002 if col_idx >= self.columns.len() {
4003 return 0;
4004 }
4005
4006 let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
4007 let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
4008 return 0;
4009 };
4010 let len = ch_ref.type_tag.len();
4011 if len == 0 || ch_ref.computed_overlay.is_empty() {
4012 return 0;
4013 }
4014
4015 let mut tag_b = UInt8Builder::with_capacity(len);
4016 let mut nb = Float64Builder::with_capacity(len);
4017 let mut bb = BooleanBuilder::with_capacity(len);
4018 let mut sb = StringBuilder::with_capacity(len, len * 8);
4019 let mut eb = UInt8Builder::with_capacity(len);
4020 let mut non_num = 0usize;
4021 let mut non_bool = 0usize;
4022 let mut non_text = 0usize;
4023 let mut non_err = 0usize;
4024
4025 for i in 0..len {
4026 if let Some(ov) = ch_ref.computed_overlay.get_scalar(i) {
4027 let ov = ov.to_overlay_value();
4028 append_overlay_value_to_lane_builders(
4029 &ov,
4030 &mut tag_b,
4031 &mut nb,
4032 &mut bb,
4033 &mut sb,
4034 &mut eb,
4035 &mut non_num,
4036 &mut non_bool,
4037 &mut non_text,
4038 &mut non_err,
4039 );
4040 } else {
4041 let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
4042 match tag {
4043 TypeTag::Empty => {
4044 tag_b.append_value(TypeTag::Empty as u8);
4045 nb.append_null();
4046 bb.append_null();
4047 sb.append_null();
4048 eb.append_null();
4049 }
4050 TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
4051 tag_b.append_value(tag as u8);
4052 if let Some(a) = &ch_ref.numbers {
4053 let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
4054 if fa.is_null(i) {
4055 nb.append_null();
4056 } else {
4057 nb.append_value(fa.value(i));
4058 non_num += 1;
4059 }
4060 } else {
4061 nb.append_null();
4062 }
4063 bb.append_null();
4064 sb.append_null();
4065 eb.append_null();
4066 }
4067 TypeTag::Boolean => {
4068 tag_b.append_value(TypeTag::Boolean as u8);
4069 nb.append_null();
4070 if let Some(a) = &ch_ref.booleans {
4071 let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
4072 if ba.is_null(i) {
4073 bb.append_null();
4074 } else {
4075 bb.append_value(ba.value(i));
4076 non_bool += 1;
4077 }
4078 } else {
4079 bb.append_null();
4080 }
4081 sb.append_null();
4082 eb.append_null();
4083 }
4084 TypeTag::Text => {
4085 tag_b.append_value(TypeTag::Text as u8);
4086 nb.append_null();
4087 bb.append_null();
4088 if let Some(a) = &ch_ref.text {
4089 let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
4090 if sa.is_null(i) {
4091 sb.append_null();
4092 } else {
4093 sb.append_value(sa.value(i));
4094 non_text += 1;
4095 }
4096 } else {
4097 sb.append_null();
4098 }
4099 eb.append_null();
4100 }
4101 TypeTag::Error => {
4102 tag_b.append_value(TypeTag::Error as u8);
4103 nb.append_null();
4104 bb.append_null();
4105 sb.append_null();
4106 if let Some(a) = &ch_ref.errors {
4107 let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
4108 if ea.is_null(i) {
4109 eb.append_null();
4110 } else {
4111 eb.append_value(ea.value(i));
4112 non_err += 1;
4113 }
4114 } else {
4115 eb.append_null();
4116 }
4117 }
4118 TypeTag::Pending => {
4119 tag_b.append_value(TypeTag::Pending as u8);
4120 nb.append_null();
4121 bb.append_null();
4122 sb.append_null();
4123 eb.append_null();
4124 }
4125 }
4126 }
4127 }
4128
4129 let tags = Arc::new(tag_b.finish());
4130 let numbers = {
4131 let a = nb.finish();
4132 if non_num == 0 {
4133 None
4134 } else {
4135 Some(Arc::new(a))
4136 }
4137 };
4138 let booleans = {
4139 let a = bb.finish();
4140 if non_bool == 0 {
4141 None
4142 } else {
4143 Some(Arc::new(a))
4144 }
4145 };
4146 let text = {
4147 let a = sb.finish();
4148 if non_text == 0 {
4149 None
4150 } else {
4151 Some(Arc::new(a) as ArrayRef)
4152 }
4153 };
4154 let errors = {
4155 let a = eb.finish();
4156 if non_err == 0 {
4157 None
4158 } else {
4159 Some(Arc::new(a))
4160 }
4161 };
4162
4163 (
4164 len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
4165 )
4166 };
4167
4168 let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
4169 return 0;
4170 };
4171
4172 ch_mut.type_tag = tags;
4173 ch_mut.numbers = numbers;
4174 ch_mut.booleans = booleans;
4175 ch_mut.text = text;
4176 ch_mut.errors = errors;
4177 let freed = ch_mut.computed_overlay.clear();
4178 ch_mut.lowered_text = OnceCell::new();
4179 ch_mut.meta.len = len;
4180 ch_mut.meta.non_null_num = non_num;
4181 ch_mut.meta.non_null_bool = non_bool;
4182 ch_mut.meta.non_null_text = non_text;
4183 ch_mut.meta.non_null_err = non_err;
4184 freed
4185 }
4186
4187 pub fn compact_computed_overlay_sparse_chunk(
4190 &mut self,
4191 col_idx: usize,
4192 ch_idx: usize,
4193 ) -> usize {
4194 self.compact_computed_overlay_chunk(col_idx, ch_idx)
4197 }
4198
4199 pub fn insert_rows(&mut self, before: usize, count: usize) {
4201 if count == 0 {
4202 return;
4203 }
4204
4205 let total_rows = self.nrows as usize;
4206 if total_rows == 0 {
4207 self.nrows = count as u32;
4208 if self.nrows > 0 && self.chunk_starts.is_empty() {
4209 self.chunk_starts.push(0);
4210 }
4211 return;
4212 }
4213
4214 if self.chunk_starts.is_empty() {
4216 self.chunk_starts.push(0);
4217 }
4218
4219 let dense_aligned = self
4221 .columns
4222 .iter()
4223 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
4224
4225 let insert_at = before.min(total_rows);
4226 let (split_idx, split_off) = if insert_at == total_rows {
4227 let last_idx = self.chunk_starts.len() - 1;
4229 let last_start = self.chunk_starts[last_idx];
4230 let last_len = total_rows.saturating_sub(last_start);
4231 (last_idx, last_len)
4232 } else {
4233 self.chunk_of_row(insert_at).unwrap_or((0, 0))
4234 };
4235
4236 if dense_aligned {
4237 for col in &mut self.columns {
4239 let mut new_chunks: Vec<ColumnChunk> = Vec::with_capacity(col.chunks.len() + 2);
4240 for i in 0..col.chunks.len() {
4241 if i != split_idx {
4242 new_chunks.push(col.chunks[i].clone());
4243 } else {
4244 let orig = &col.chunks[i];
4245 let len = orig.type_tag.len();
4246 if split_off > 0 {
4247 new_chunks.push(Self::slice_chunk(orig, 0, split_off));
4248 }
4249 new_chunks.push(Self::make_empty_chunk(count));
4250 if split_off < len {
4251 new_chunks.push(Self::slice_chunk(orig, split_off, len - split_off));
4252 }
4253 }
4254 }
4255 col.chunks = new_chunks;
4256 col.sparse_chunks.clear();
4257 }
4258 self.nrows = (total_rows + count) as u32;
4259 self.recompute_chunk_starts();
4260 return;
4261 }
4262
4263 #[derive(Clone, Copy)]
4265 enum PlanItem {
4266 Slice {
4267 old_idx: usize,
4268 off: usize,
4269 len: usize,
4270 },
4271 Empty {
4272 len: usize,
4273 },
4274 }
4275
4276 let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len() + 2);
4277 for old_idx in 0..self.chunk_starts.len() {
4278 let ch_start = self.chunk_starts[old_idx];
4279 let ch_end = self
4280 .chunk_starts
4281 .get(old_idx + 1)
4282 .copied()
4283 .unwrap_or(total_rows);
4284 let ch_len = ch_end.saturating_sub(ch_start);
4285 if ch_len == 0 {
4286 continue;
4287 }
4288
4289 if old_idx != split_idx {
4290 plan.push(PlanItem::Slice {
4291 old_idx,
4292 off: 0,
4293 len: ch_len,
4294 });
4295 continue;
4296 }
4297
4298 let left_len = split_off.min(ch_len);
4299 let right_len = ch_len.saturating_sub(left_len);
4300 if left_len > 0 {
4301 plan.push(PlanItem::Slice {
4302 old_idx,
4303 off: 0,
4304 len: left_len,
4305 });
4306 }
4307 plan.push(PlanItem::Empty { len: count });
4308 if right_len > 0 {
4309 plan.push(PlanItem::Slice {
4310 old_idx,
4311 off: left_len,
4312 len: right_len,
4313 });
4314 }
4315 }
4316
4317 let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
4318 let mut cur = 0usize;
4319 for item in &plan {
4320 let len = match *item {
4321 PlanItem::Slice { len, .. } => len,
4322 PlanItem::Empty { len } => len,
4323 };
4324 if len == 0 {
4325 continue;
4326 }
4327 new_starts.push(cur);
4328 cur = cur.saturating_add(len);
4329 }
4330
4331 debug_assert_eq!(cur, total_rows.saturating_add(count));
4332
4333 self.nrows = (total_rows + count) as u32;
4335 self.chunk_starts = new_starts;
4336
4337 for col in &mut self.columns {
4339 let old_dense = std::mem::take(&mut col.chunks);
4340 let old_sparse = std::mem::take(&mut col.sparse_chunks);
4341 let get_old = |idx: usize| -> Option<&ColumnChunk> {
4342 if idx < old_dense.len() {
4343 Some(&old_dense[idx])
4344 } else {
4345 old_sparse.get(&idx)
4346 }
4347 };
4348
4349 let mut dense: Vec<ColumnChunk> = Vec::new();
4350 let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
4351 let mut dense_prefix = true;
4352
4353 for (new_idx, item) in plan.iter().enumerate() {
4354 let produced: Option<ColumnChunk> = match *item {
4355 PlanItem::Empty { .. } => None,
4356 PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
4357 Some(orig) => {
4358 if off == 0 && len == orig.type_tag.len() {
4359 Some(orig.clone())
4360 } else {
4361 Some(Self::slice_chunk(orig, off, len))
4362 }
4363 }
4364 None => None,
4365 },
4366 };
4367
4368 if let Some(ch) = produced {
4369 if dense_prefix && new_idx == dense.len() {
4370 dense.push(ch);
4371 } else {
4372 sparse.insert(new_idx, ch);
4373 dense_prefix = false;
4374 }
4375 } else if dense_prefix && new_idx == dense.len() {
4376 dense_prefix = false;
4377 }
4378 }
4379
4380 col.chunks = dense;
4381 col.sparse_chunks = sparse;
4382 }
4383 }
4384
4385 pub fn delete_rows(&mut self, start: usize, count: usize) {
4387 if count == 0 || self.nrows == 0 {
4388 return;
4389 }
4390
4391 let total_rows = self.nrows as usize;
4392 if start >= total_rows {
4393 return;
4394 }
4395 let end = (start + count).min(total_rows);
4396 let del_len = end.saturating_sub(start);
4397 if del_len == 0 {
4398 return;
4399 }
4400
4401 if total_rows > 0 && self.chunk_starts.is_empty() {
4403 self.chunk_starts.push(0);
4404 }
4405
4406 let dense_aligned = self
4408 .columns
4409 .iter()
4410 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
4411
4412 if dense_aligned {
4413 for col in &mut self.columns {
4415 let mut new_chunks: Vec<ColumnChunk> = Vec::new();
4416 let mut cur_start = 0usize;
4417 for ch in &col.chunks {
4418 let len = ch.type_tag.len();
4419 let ch_end = cur_start + len;
4420 if ch_end <= start || cur_start >= end {
4422 new_chunks.push(ch.clone());
4423 } else {
4424 let del_start = start.max(cur_start);
4426 let del_end = end.min(ch_end);
4427 let left_len = del_start.saturating_sub(cur_start);
4428 let right_len = ch_end.saturating_sub(del_end);
4429 if left_len > 0 {
4430 new_chunks.push(Self::slice_chunk(ch, 0, left_len));
4431 }
4432 if right_len > 0 {
4433 let off = len - right_len;
4434 new_chunks.push(Self::slice_chunk(ch, off, right_len));
4435 }
4436 }
4437 cur_start = ch_end;
4438 }
4439 col.chunks = new_chunks;
4440 col.sparse_chunks.clear();
4441 }
4442 self.nrows = (total_rows - del_len) as u32;
4443 self.recompute_chunk_starts();
4444 return;
4445 }
4446
4447 #[derive(Clone, Copy)]
4449 enum PlanItem {
4450 Slice {
4451 old_idx: usize,
4452 off: usize,
4453 len: usize,
4454 },
4455 }
4456
4457 let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len());
4458 for old_idx in 0..self.chunk_starts.len() {
4459 let ch_start = self.chunk_starts[old_idx];
4460 let ch_end = self
4461 .chunk_starts
4462 .get(old_idx + 1)
4463 .copied()
4464 .unwrap_or(total_rows);
4465 let ch_len = ch_end.saturating_sub(ch_start);
4466 if ch_len == 0 {
4467 continue;
4468 }
4469
4470 if ch_end <= start || ch_start >= end {
4472 plan.push(PlanItem::Slice {
4473 old_idx,
4474 off: 0,
4475 len: ch_len,
4476 });
4477 continue;
4478 }
4479
4480 if start > ch_start {
4482 let left_end = start.min(ch_end);
4483 let left_len = left_end.saturating_sub(ch_start);
4484 if left_len > 0 {
4485 plan.push(PlanItem::Slice {
4486 old_idx,
4487 off: 0,
4488 len: left_len,
4489 });
4490 }
4491 }
4492
4493 if end < ch_end {
4495 let right_off = end.saturating_sub(ch_start);
4496 let right_len = ch_end.saturating_sub(end);
4497 if right_len > 0 {
4498 plan.push(PlanItem::Slice {
4499 old_idx,
4500 off: right_off,
4501 len: right_len,
4502 });
4503 }
4504 }
4505 }
4506
4507 let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
4508 let mut cur = 0usize;
4509 for item in &plan {
4510 let len = match *item {
4511 PlanItem::Slice { len, .. } => len,
4512 };
4513 if len == 0 {
4514 continue;
4515 }
4516 new_starts.push(cur);
4517 cur = cur.saturating_add(len);
4518 }
4519
4520 debug_assert_eq!(cur, total_rows.saturating_sub(del_len));
4521
4522 self.nrows = (total_rows - del_len) as u32;
4524 self.chunk_starts = new_starts;
4525
4526 for col in &mut self.columns {
4528 let old_dense = std::mem::take(&mut col.chunks);
4529 let old_sparse = std::mem::take(&mut col.sparse_chunks);
4530 let get_old = |idx: usize| -> Option<&ColumnChunk> {
4531 if idx < old_dense.len() {
4532 Some(&old_dense[idx])
4533 } else {
4534 old_sparse.get(&idx)
4535 }
4536 };
4537
4538 let mut dense: Vec<ColumnChunk> = Vec::new();
4539 let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
4540 let mut dense_prefix = true;
4541
4542 for (new_idx, item) in plan.iter().enumerate() {
4543 let produced: Option<ColumnChunk> = match *item {
4544 PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
4545 Some(orig) => {
4546 if off == 0 && len == orig.type_tag.len() {
4547 Some(orig.clone())
4548 } else {
4549 Some(Self::slice_chunk(orig, off, len))
4550 }
4551 }
4552 None => None,
4553 },
4554 };
4555
4556 if let Some(ch) = produced {
4557 if dense_prefix && new_idx == dense.len() {
4558 dense.push(ch);
4559 } else {
4560 sparse.insert(new_idx, ch);
4561 dense_prefix = false;
4562 }
4563 } else if dense_prefix && new_idx == dense.len() {
4564 dense_prefix = false;
4565 }
4566 }
4567
4568 col.chunks = dense;
4569 col.sparse_chunks = sparse;
4570 }
4571 }
4572
4573 pub fn insert_columns(&mut self, before: usize, count: usize) {
4575 if count == 0 {
4576 return;
4577 }
4578 let empty_col = |lens: &[usize]| -> ArrowColumn {
4580 let mut chunks = Vec::with_capacity(lens.len());
4581 for &l in lens {
4582 chunks.push(Self::make_empty_chunk(l));
4583 }
4584 ArrowColumn {
4585 chunks,
4586 sparse_chunks: FxHashMap::default(),
4587 index: 0,
4588 }
4589 };
4590 let dense_aligned = !self.columns.is_empty()
4591 && self
4592 .columns
4593 .iter()
4594 .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
4595
4596 let lens: Vec<usize> = if dense_aligned {
4597 self.columns[0]
4598 .chunks
4599 .iter()
4600 .map(|c| c.type_tag.len())
4601 .collect()
4602 } else if self.columns.is_empty() {
4603 if self.nrows > 0 {
4605 vec![self.nrows as usize]
4606 } else {
4607 Vec::new()
4608 }
4609 } else {
4610 Vec::new()
4612 };
4613 let mut cols_new: Vec<ArrowColumn> = Vec::with_capacity(self.columns.len() + count);
4614 let before_idx = before.min(self.columns.len());
4615 for (i, col) in self.columns.iter_mut().enumerate() {
4616 if i == before_idx {
4617 for _ in 0..count {
4618 cols_new.push(empty_col(&lens));
4619 }
4620 }
4621 cols_new.push(col.clone());
4622 }
4623 if before_idx == self.columns.len() {
4624 for _ in 0..count {
4625 cols_new.push(empty_col(&lens));
4626 }
4627 }
4628 for (idx, col) in cols_new.iter_mut().enumerate() {
4630 col.index = idx as u32;
4631 }
4632 self.columns = cols_new;
4633 }
4635
4636 pub fn delete_columns(&mut self, start: usize, count: usize) {
4638 if count == 0 || self.columns.is_empty() {
4639 return;
4640 }
4641 let end = (start + count).min(self.columns.len());
4642 if start >= end {
4643 return;
4644 }
4645 self.columns.drain(start..end);
4646 for (idx, col) in self.columns.iter_mut().enumerate() {
4647 col.index = idx as u32;
4648 }
4649 }
4650}
4651
4652#[derive(Debug, Clone, Copy)]
4653pub struct ColumnShape {
4654 pub index: u32,
4655 pub chunks: usize,
4656 pub rows: usize,
4657 pub has_num: bool,
4658 pub has_bool: bool,
4659 pub has_text: bool,
4660 pub has_err: bool,
4661}
4662
4663#[cfg(test)]
4664mod tests {
4665 use super::*;
4666
4667 #[test]
4668 fn known_error_storage_codes_are_stable() {
4669 let cases = [
4670 (ExcelErrorKind::Null, 1),
4671 (ExcelErrorKind::Ref, 2),
4672 (ExcelErrorKind::Name, 3),
4673 (ExcelErrorKind::Value, 4),
4674 (ExcelErrorKind::Div, 5),
4675 (ExcelErrorKind::Na, 6),
4676 (ExcelErrorKind::Num, 7),
4677 (ExcelErrorKind::Error, 8),
4678 (ExcelErrorKind::NImpl, 9),
4679 (ExcelErrorKind::Spill, 10),
4680 (ExcelErrorKind::Calc, 11),
4681 (ExcelErrorKind::Circ, 12),
4682 (ExcelErrorKind::Cancelled, 13),
4683 ];
4684 for (kind, code) in cases {
4685 assert_eq!(map_error_code(kind), code, "{kind:?}");
4686 assert_eq!(unmap_error_code(code), kind, "code {code}");
4687 }
4688 assert_eq!(unmap_error_code(u8::MAX), ExcelErrorKind::Error);
4689 }
4690 use arrow_array::Array;
4691 use arrow_schema::DataType;
4692 use chrono::{Datelike, Timelike};
4693
4694 #[test]
4695 fn sparse_constructor_defaults_to_excel_1900_and_decodes_excel_1904() {
4696 let date = chrono::NaiveDate::from_ymd_opt(1904, 1, 1).unwrap();
4697 let datetime = date.and_hms_opt(12, 0, 0).unwrap();
4698
4699 let mut default_sheet = ArrowSheet::new_sparse("Default", 1, 1, 16);
4700 assert_eq!(
4701 default_sheet.date_system,
4702 crate::engine::DateSystem::Excel1900
4703 );
4704 default_sheet.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(1462.5));
4705 assert_eq!(
4706 default_sheet.get_cell_value(0, 0),
4707 LiteralValue::DateTime(datetime)
4708 );
4709
4710 let mut excel_1904 = ArrowSheet::new_sparse_with_date_system(
4711 "1904",
4712 1,
4713 1,
4714 16,
4715 crate::engine::DateSystem::Excel1904,
4716 );
4717 assert_eq!(excel_1904.date_system, crate::engine::DateSystem::Excel1904);
4718 excel_1904.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(0.5));
4719 assert_eq!(
4720 excel_1904.get_cell_value(0, 0),
4721 LiteralValue::DateTime(datetime)
4722 );
4723 }
4724
4725 #[test]
4726 fn datetime_lanes_round_trip_the_sheet_date_system() {
4727 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
4728 let datetime = date.and_hms_opt(12, 30, 0).unwrap();
4729
4730 for system in [
4731 crate::engine::DateSystem::Excel1900,
4732 crate::engine::DateSystem::Excel1904,
4733 ] {
4734 let values = vec![LiteralValue::Date(date), LiteralValue::DateTime(datetime)];
4735 let mut ingest = IngestBuilder::new("Sheet1", 2, 16, system);
4736 ingest.append_row(&values).unwrap();
4737 let sheet = ingest.finish();
4738
4739 assert_eq!(sheet.date_system, system);
4740 assert_eq!(sheet.get_cell_value(0, 0), values[0]);
4741 assert_eq!(sheet.get_cell_value(0, 1), values[1]);
4742 let view = sheet.range_view(0, 0, 0, 1);
4743 assert_eq!(view.get_cell(0, 0), values[0]);
4744 assert_eq!(view.get_cell(0, 1), values[1]);
4745
4746 let mut sparse = ArrowSheet::new_sparse_with_date_system("Sparse", 1, 1, 16, system);
4747 sparse.set_sparse_overlay_value(
4748 0,
4749 0,
4750 OverlayValue::from_literal_value(&values[1], system),
4751 );
4752 assert_eq!(sparse.get_cell_value(0, 0), values[1]);
4753 }
4754 }
4755
4756 fn add_overlay_stats(into: &mut OverlayDebugStats, next: OverlayDebugStats) {
4757 into.points += next.points;
4758 into.sparse_fragments += next.sparse_fragments;
4759 into.dense_fragments += next.dense_fragments;
4760 into.run_fragments += next.run_fragments;
4761 into.covered_len += next.covered_len;
4762 }
4763
4764 fn column_overlay_stats(
4765 sheet: &ArrowSheet,
4766 col_idx: usize,
4767 computed: bool,
4768 ) -> OverlayDebugStats {
4769 let mut stats = OverlayDebugStats::default();
4770 let Some(column) = sheet.columns.get(col_idx) else {
4771 return stats;
4772 };
4773 for chunk in &column.chunks {
4774 add_overlay_stats(
4775 &mut stats,
4776 if computed {
4777 chunk.computed_overlay.debug_stats()
4778 } else {
4779 chunk.overlay.debug_stats()
4780 },
4781 );
4782 }
4783 for chunk in column.sparse_chunks.values() {
4784 add_overlay_stats(
4785 &mut stats,
4786 if computed {
4787 chunk.computed_overlay.debug_stats()
4788 } else {
4789 chunk.overlay.debug_stats()
4790 },
4791 );
4792 }
4793 stats
4794 }
4795
4796 fn assert_column_overlays_normalized(sheet: &ArrowSheet, col_idx: usize) {
4797 let column = &sheet.columns[col_idx];
4798 for chunk in &column.chunks {
4799 assert!(chunk.overlay.debug_is_normalized());
4800 assert!(chunk.computed_overlay.debug_is_normalized());
4801 assert_eq!(
4802 chunk.overlay.estimated_bytes(),
4803 chunk.overlay.debug_recomputed_estimated_bytes()
4804 );
4805 assert_eq!(
4806 chunk.computed_overlay.estimated_bytes(),
4807 chunk.computed_overlay.debug_recomputed_estimated_bytes()
4808 );
4809 }
4810 for chunk in column.sparse_chunks.values() {
4811 assert!(chunk.overlay.debug_is_normalized());
4812 assert!(chunk.computed_overlay.debug_is_normalized());
4813 assert_eq!(
4814 chunk.overlay.estimated_bytes(),
4815 chunk.overlay.debug_recomputed_estimated_bytes()
4816 );
4817 assert_eq!(
4818 chunk.computed_overlay.estimated_bytes(),
4819 chunk.computed_overlay.debug_recomputed_estimated_bytes()
4820 );
4821 }
4822 }
4823
4824 fn column_computed_overlay_estimated_bytes(sheet: &ArrowSheet, col_idx: usize) -> usize {
4825 let Some(column) = sheet.columns.get(col_idx) else {
4826 return 0;
4827 };
4828 column
4829 .chunks
4830 .iter()
4831 .map(|chunk| chunk.computed_overlay.estimated_bytes())
4832 .chain(
4833 column
4834 .sparse_chunks
4835 .values()
4836 .map(|chunk| chunk.computed_overlay.estimated_bytes()),
4837 )
4838 .fold(0usize, usize::saturating_add)
4839 }
4840
4841 #[derive(Debug, Clone, Copy)]
4842 enum Phase4ProbeFixture {
4843 PointNumeric,
4844 DenseNumeric,
4845 RunNumeric,
4846 SparseNumeric,
4847 EmptyRun,
4848 MixedDense,
4849 }
4850
4851 impl Phase4ProbeFixture {
4852 fn name(self) -> &'static str {
4853 match self {
4854 Phase4ProbeFixture::PointNumeric => "point_numeric",
4855 Phase4ProbeFixture::DenseNumeric => "dense_numeric",
4856 Phase4ProbeFixture::RunNumeric => "run_numeric",
4857 Phase4ProbeFixture::SparseNumeric => "sparse_numeric",
4858 Phase4ProbeFixture::EmptyRun => "empty_run",
4859 Phase4ProbeFixture::MixedDense => "mixed_dense",
4860 }
4861 }
4862 }
4863
4864 #[derive(Debug, serde::Serialize)]
4865 struct Phase4ProbeOp {
4866 ms: f64,
4867 segments: usize,
4868 arrays: usize,
4869 rows_scanned: usize,
4870 checksum: f64,
4871 non_null: usize,
4872 }
4873
4874 #[derive(Debug, serde::Serialize)]
4875 struct Phase4ProbeRow {
4876 fixture: &'static str,
4877 rows: usize,
4878 points: usize,
4879 sparse_fragments: usize,
4880 dense_fragments: usize,
4881 run_fragments: usize,
4882 covered_len: usize,
4883 overlay_estimated_bytes: usize,
4884 numbers: Phase4ProbeOp,
4885 type_tags: Phase4ProbeOp,
4886 lowered_text: Phase4ProbeOp,
4887 get_cell_scan: Phase4ProbeOp,
4888 select_stats: OverlaySelectStats,
4889 }
4890
4891 fn build_phase4_probe_sheet(rows: usize, fixture: Phase4ProbeFixture) -> ArrowSheet {
4892 let mut builder =
4893 IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
4894 for row in 0..rows {
4895 builder
4896 .append_row(&[LiteralValue::Number((row + 1) as f64)])
4897 .unwrap();
4898 }
4899 let mut sheet = builder.finish();
4900 let chunk = sheet.columns[0].chunk_mut(0).unwrap();
4901 match fixture {
4902 Phase4ProbeFixture::PointNumeric => {
4903 for row in 0..rows {
4904 chunk
4905 .computed_overlay
4906 .set_scalar(row, OverlayValue::Number((row + 1) as f64));
4907 }
4908 }
4909 Phase4ProbeFixture::DenseNumeric => {
4910 chunk.computed_overlay.apply_fragment(
4911 OverlayFragment::dense_range(
4912 0,
4913 (0..rows)
4914 .map(|row| OverlayValue::Number((row + 1) as f64))
4915 .collect(),
4916 )
4917 .unwrap(),
4918 );
4919 }
4920 Phase4ProbeFixture::RunNumeric => {
4921 chunk.computed_overlay.apply_fragment(
4922 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); rows]).unwrap(),
4923 );
4924 }
4925 Phase4ProbeFixture::SparseNumeric => {
4926 chunk.computed_overlay.apply_fragment(
4927 OverlayFragment::sparse_offsets(
4928 (0..rows)
4929 .step_by(10)
4930 .map(|row| (row, OverlayValue::Number(10.0)))
4931 .collect(),
4932 )
4933 .unwrap(),
4934 );
4935 }
4936 Phase4ProbeFixture::EmptyRun => {
4937 chunk.computed_overlay.apply_fragment(
4938 OverlayFragment::run_range(0, vec![OverlayValue::Empty; rows]).unwrap(),
4939 );
4940 }
4941 Phase4ProbeFixture::MixedDense => {
4942 let pattern = [
4943 OverlayValue::Number(1.0),
4944 OverlayValue::Boolean(true),
4945 OverlayValue::Text(Arc::from("Alpha")),
4946 OverlayValue::Empty,
4947 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
4948 OverlayValue::Pending,
4949 OverlayValue::DateTime(45000.25),
4950 OverlayValue::Duration(0.5),
4951 ];
4952 chunk.computed_overlay.apply_fragment(
4953 OverlayFragment::dense_range(
4954 0,
4955 (0..rows)
4956 .map(|row| pattern[row % pattern.len()].clone())
4957 .collect(),
4958 )
4959 .unwrap(),
4960 );
4961 }
4962 }
4963 sheet
4964 }
4965
4966 fn measure_probe_numbers(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
4967 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
4968 let start = std::time::Instant::now();
4969 let mut segments = 0usize;
4970 let mut arrays = 0usize;
4971 let mut rows_scanned = 0usize;
4972 let mut checksum = 0.0;
4973 let mut non_null = 0usize;
4974 for segment in view.numbers_slices() {
4975 let (_row_start, row_len, cols) = segment.unwrap();
4976 segments += 1;
4977 rows_scanned += row_len;
4978 for array in cols {
4979 arrays += 1;
4980 for idx in 0..array.len() {
4981 if array.is_valid(idx) {
4982 checksum += array.value(idx);
4983 non_null += 1;
4984 }
4985 }
4986 }
4987 }
4988 Phase4ProbeOp {
4989 ms: start.elapsed().as_secs_f64() * 1000.0,
4990 segments,
4991 arrays,
4992 rows_scanned,
4993 checksum,
4994 non_null,
4995 }
4996 }
4997
4998 fn measure_probe_type_tags(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
4999 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
5000 let start = std::time::Instant::now();
5001 let mut segments = 0usize;
5002 let mut arrays = 0usize;
5003 let mut rows_scanned = 0usize;
5004 let mut checksum = 0.0;
5005 let mut non_null = 0usize;
5006 for segment in view.type_tags_slices() {
5007 let (_row_start, row_len, cols) = segment.unwrap();
5008 segments += 1;
5009 rows_scanned += row_len;
5010 for array in cols {
5011 arrays += 1;
5012 for idx in 0..array.len() {
5013 if array.is_valid(idx) {
5014 checksum += array.value(idx) as f64;
5015 non_null += 1;
5016 }
5017 }
5018 }
5019 }
5020 Phase4ProbeOp {
5021 ms: start.elapsed().as_secs_f64() * 1000.0,
5022 segments,
5023 arrays,
5024 rows_scanned,
5025 checksum,
5026 non_null,
5027 }
5028 }
5029
5030 fn measure_probe_lowered_text(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
5031 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
5032 let start = std::time::Instant::now();
5033 let mut segments = 0usize;
5034 let mut arrays = 0usize;
5035 let mut rows_scanned = 0usize;
5036 let mut checksum = 0.0;
5037 let mut non_null = 0usize;
5038 for segment in view.lowered_text_slices() {
5039 let (_row_start, row_len, cols) = segment.unwrap();
5040 segments += 1;
5041 rows_scanned += row_len;
5042 for array in cols {
5043 arrays += 1;
5044 for idx in 0..array.len() {
5045 if array.is_valid(idx) {
5046 checksum += array.value(idx).len() as f64;
5047 non_null += 1;
5048 }
5049 }
5050 }
5051 }
5052 Phase4ProbeOp {
5053 ms: start.elapsed().as_secs_f64() * 1000.0,
5054 segments,
5055 arrays,
5056 rows_scanned,
5057 checksum,
5058 non_null,
5059 }
5060 }
5061
5062 fn literal_probe_weight(value: LiteralValue) -> f64 {
5063 match value {
5064 LiteralValue::Empty => 0.0,
5065 LiteralValue::Int(value) => value as f64,
5066 LiteralValue::Number(value) => value,
5067 LiteralValue::Boolean(value) => {
5068 if value {
5069 1.0
5070 } else {
5071 0.0
5072 }
5073 }
5074 LiteralValue::Text(value) => value.len() as f64,
5075 LiteralValue::Error(_) => -1.0,
5076 LiteralValue::Date(value) => value.num_days_from_ce() as f64,
5077 LiteralValue::DateTime(value) => value.and_utc().timestamp() as f64,
5078 LiteralValue::Time(value) => value.num_seconds_from_midnight() as f64,
5079 LiteralValue::Duration(value) => value.num_seconds() as f64,
5080 LiteralValue::Array(values) => values.len() as f64,
5081 LiteralValue::Pending => -2.0,
5082 }
5083 }
5084
5085 fn measure_probe_get_cell(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
5086 let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
5087 let start = std::time::Instant::now();
5088 let mut checksum = 0.0;
5089 for row in 0..rows {
5090 checksum += literal_probe_weight(view.get_cell(row, 0));
5091 }
5092 Phase4ProbeOp {
5093 ms: start.elapsed().as_secs_f64() * 1000.0,
5094 segments: 1,
5095 arrays: 0,
5096 rows_scanned: rows,
5097 checksum,
5098 non_null: rows,
5099 }
5100 }
5101
5102 fn run_phase4_probe_fixture(rows: usize, fixture: Phase4ProbeFixture) -> Phase4ProbeRow {
5103 let sheet = build_phase4_probe_sheet(rows, fixture);
5104 assert_column_overlays_normalized(&sheet, 0);
5105 let stats = column_overlay_stats(&sheet, 0, true);
5106 reset_overlay_select_stats();
5107 let numbers = measure_probe_numbers(&sheet, rows);
5108 let type_tags = measure_probe_type_tags(&sheet, rows);
5109 let lowered_text = measure_probe_lowered_text(&sheet, rows);
5110 let select_stats = snapshot_overlay_select_stats();
5111 let get_cell_scan = measure_probe_get_cell(&sheet, rows);
5112 Phase4ProbeRow {
5113 fixture: fixture.name(),
5114 rows,
5115 points: stats.points,
5116 sparse_fragments: stats.sparse_fragments,
5117 dense_fragments: stats.dense_fragments,
5118 run_fragments: stats.run_fragments,
5119 covered_len: stats.covered_len,
5120 overlay_estimated_bytes: column_computed_overlay_estimated_bytes(&sheet, 0),
5121 numbers,
5122 type_tags,
5123 lowered_text,
5124 get_cell_scan,
5125 select_stats,
5126 }
5127 }
5128
5129 #[test]
5130 #[ignore = "manual Phase 4 observability probe; run with --ignored --nocapture"]
5131 fn phase4_overlay_rangeview_observability_probe() {
5132 let rows = std::env::var("FORMUALIZER_OVERLAY_PROBE_ROWS")
5133 .ok()
5134 .and_then(|value| value.parse::<usize>().ok())
5135 .unwrap_or(100_000)
5136 .max(1);
5137 for fixture in [
5138 Phase4ProbeFixture::PointNumeric,
5139 Phase4ProbeFixture::DenseNumeric,
5140 Phase4ProbeFixture::RunNumeric,
5141 Phase4ProbeFixture::SparseNumeric,
5142 Phase4ProbeFixture::EmptyRun,
5143 Phase4ProbeFixture::MixedDense,
5144 ] {
5145 let row = run_phase4_probe_fixture(rows, fixture);
5146 println!("{}", serde_json::to_string(&row).unwrap());
5147 }
5148 }
5149
5150 #[test]
5151 fn ingest_mixed_rows_into_lanes_and_tags() {
5152 let mut b = IngestBuilder::new("Sheet1", 1, 1024, crate::engine::DateSystem::Excel1900);
5153 let data = vec![
5154 LiteralValue::Number(42.5), LiteralValue::Empty, LiteralValue::Text(String::new()), LiteralValue::Boolean(true), LiteralValue::Error(ExcelError::new_value()), ];
5160 for v in &data {
5161 b.append_row(std::slice::from_ref(v)).unwrap();
5162 }
5163 let sheet = b.finish();
5164 assert_eq!(sheet.nrows, 5);
5165 assert_eq!(sheet.columns.len(), 1);
5166 assert_eq!(sheet.columns[0].chunks.len(), 1);
5167 let ch = &sheet.columns[0].chunks[0];
5168
5169 let tags = ch.type_tag.values();
5171 assert_eq!(tags.len(), 5);
5172 assert_eq!(tags[0], TypeTag::Number as u8);
5173 assert_eq!(tags[1], TypeTag::Empty as u8);
5174 assert_eq!(tags[2], TypeTag::Text as u8);
5175 assert_eq!(tags[3], TypeTag::Boolean as u8);
5176 assert_eq!(tags[4], TypeTag::Error as u8);
5177
5178 let nums = ch.numbers.as_ref().unwrap();
5180 assert_eq!(nums.len(), 5);
5181 assert_eq!(nums.null_count(), 4);
5182 assert!(nums.is_valid(0));
5183
5184 let bools = ch.booleans.as_ref().unwrap();
5186 assert_eq!(bools.len(), 5);
5187 assert_eq!(bools.null_count(), 4);
5188 assert!(bools.is_valid(3));
5189
5190 let txt = ch.text.as_ref().unwrap();
5192 assert_eq!(txt.len(), 5);
5193 assert_eq!(txt.null_count(), 4);
5194 assert!(txt.is_valid(2)); let errs = ch.errors.as_ref().unwrap();
5198 assert_eq!(errs.len(), 5);
5199 assert_eq!(errs.null_count(), 4);
5200 assert!(errs.is_valid(4));
5201 }
5202
5203 #[test]
5204 fn range_view_get_cell_and_padding() {
5205 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
5206 b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("".into())])
5207 .unwrap();
5208 b.append_row(&[LiteralValue::Empty, LiteralValue::Text("x".into())])
5209 .unwrap();
5210 b.append_row(&[LiteralValue::Boolean(true), LiteralValue::Empty])
5211 .unwrap();
5212 let sheet = b.finish();
5213 let rv = sheet.range_view(0, 0, 2, 1);
5214 assert_eq!(rv.dims(), (3, 2));
5215 assert_eq!(rv.get_cell(0, 0), LiteralValue::Number(1.0));
5217 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));
5220 assert_eq!(rv.get_cell(3, 0), LiteralValue::Empty);
5222 assert_eq!(rv.get_cell(0, 2), LiteralValue::Empty);
5223
5224 let nums: Vec<_> = rv.numbers_slices().map(|r| r.unwrap()).collect();
5226 assert_eq!(nums.len(), 2);
5227 assert_eq!(nums[0].0, 0);
5228 assert_eq!(nums[0].1, 2);
5229 assert_eq!(nums[1].0, 2);
5230 assert_eq!(nums[1].1, 1);
5231 }
5232
5233 #[test]
5234 fn overlay_precedence_user_over_computed() {
5235 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
5236 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
5237 b.append_row(&[LiteralValue::Empty]).unwrap();
5238 b.append_row(&[LiteralValue::Empty]).unwrap();
5239 let mut sheet = b.finish();
5240
5241 let (ch_i, off) = sheet.chunk_of_row(0).unwrap();
5242 sheet.columns[0].chunks[ch_i]
5243 .computed_overlay
5244 .set(off, OverlayValue::Number(2.0));
5245
5246 let rv0 = sheet.range_view(0, 0, 0, 0);
5247 assert_eq!(rv0.get_cell(0, 0), LiteralValue::Number(2.0));
5248 let nums0: Vec<_> = rv0.numbers_slices().map(|r| r.unwrap()).collect();
5249 assert_eq!(nums0.len(), 1);
5250 assert_eq!(nums0[0].2[0].value(0), 2.0);
5251
5252 sheet.columns[0].chunks[ch_i]
5253 .overlay
5254 .set(off, OverlayValue::Number(3.0));
5255
5256 let rv1 = sheet.range_view(0, 0, 0, 0);
5257 assert_eq!(rv1.get_cell(0, 0), LiteralValue::Number(3.0));
5258 let nums1: Vec<_> = rv1.numbers_slices().map(|r| r.unwrap()).collect();
5259 assert_eq!(nums1.len(), 1);
5260 assert_eq!(nums1[0].2[0].value(0), 3.0);
5261 }
5262
5263 #[test]
5264 fn overlay_slice_preserves_explicit_empty_and_offsets() {
5265 let mut overlay = Overlay::new();
5266 overlay.set(2, OverlayValue::Number(2.0));
5267 overlay.set(4, OverlayValue::Empty);
5268 overlay.set(6, OverlayValue::Text(Arc::from("outside")));
5269
5270 let sliced = overlay.slice(1, 4);
5271 assert!(sliced.get_scalar(0).is_none());
5272 assert_eq!(
5273 sliced.get_scalar(1).unwrap().to_literal(),
5274 LiteralValue::Number(2.0)
5275 );
5276 assert_eq!(
5277 sliced.get_scalar(3).unwrap().to_literal(),
5278 LiteralValue::Empty
5279 );
5280 assert!(sliced.get_scalar(5).is_none());
5281 }
5282
5283 #[test]
5284 fn overlay_cascade_user_empty_masks_computed_and_base() {
5285 let mut user = Overlay::new();
5286 let mut computed = Overlay::new();
5287 computed.set(1, OverlayValue::Number(42.0));
5288 user.set(1, OverlayValue::Empty);
5289
5290 let cascade = OverlayCascade::new(&user, &computed);
5291 assert_eq!(
5292 cascade.get_scalar(1).unwrap().to_literal(),
5293 LiteralValue::Empty
5294 );
5295 assert!(cascade.has_any_in_range(1..2));
5296 }
5297
5298 #[test]
5299 fn overlay_storage_pointmap_backward_compat_get_set_remove() {
5300 let mut overlay = Overlay::new();
5301 assert!(overlay.is_empty());
5302
5303 let delta = overlay.set_scalar(1, OverlayValue::Number(10.0));
5304 assert!(delta > 0);
5305 assert_eq!(overlay.len(), 1);
5306 assert_eq!(
5307 overlay.get_scalar(1).unwrap().to_literal(),
5308 LiteralValue::Number(10.0)
5309 );
5310
5311 let replace_delta = overlay.set_scalar(1, OverlayValue::Text(Arc::from("x")));
5312 assert_ne!(replace_delta, 0);
5313 assert_eq!(overlay.len(), 1);
5314 assert_eq!(
5315 overlay.get_scalar(1).unwrap().to_literal(),
5316 LiteralValue::Text("x".into())
5317 );
5318
5319 let remove_delta = overlay.remove_scalar(1);
5320 assert!(remove_delta < 0);
5321 assert!(overlay.is_empty());
5322 assert!(overlay.get_scalar(1).is_none());
5323 }
5324
5325 #[test]
5326 fn overlay_remove_range_splits_fragments_and_points() {
5327 let mut overlay = Overlay::new();
5328 overlay.set_scalar(2, OverlayValue::Number(20.0));
5329 overlay.apply_fragment(
5330 OverlayFragment::dense_range(
5331 0,
5332 (0..6)
5333 .map(|i| OverlayValue::Number(i as f64))
5334 .collect::<Vec<_>>(),
5335 )
5336 .unwrap(),
5337 );
5338 overlay.set_scalar(3, OverlayValue::Number(30.0));
5339 overlay.set_scalar(8, OverlayValue::Number(80.0));
5340
5341 let delta = overlay.remove_range(2..5);
5342
5343 assert!(delta < 0);
5344 assert_eq!(
5345 overlay.get_scalar(0).unwrap().to_literal(),
5346 LiteralValue::Number(0.0)
5347 );
5348 assert_eq!(
5349 overlay.get_scalar(1).unwrap().to_literal(),
5350 LiteralValue::Number(1.0)
5351 );
5352 assert!(overlay.get_scalar(2).is_none());
5353 assert!(overlay.get_scalar(3).is_none());
5354 assert!(overlay.get_scalar(4).is_none());
5355 assert_eq!(
5356 overlay.get_scalar(5).unwrap().to_literal(),
5357 LiteralValue::Number(5.0)
5358 );
5359 assert_eq!(
5360 overlay.get_scalar(8).unwrap().to_literal(),
5361 LiteralValue::Number(80.0)
5362 );
5363 assert!(overlay.debug_is_normalized());
5364 assert_eq!(
5365 overlay.estimated_bytes(),
5366 overlay.debug_recomputed_estimated_bytes()
5367 );
5368 }
5369
5370 #[test]
5371 fn overlay_storage_no_fragments_behavior_matches_old_map() {
5372 let mut overlay = Overlay::new();
5373 overlay.set_scalar(0, OverlayValue::Number(1.0));
5374 overlay.set_scalar(3, OverlayValue::Empty);
5375
5376 assert!(overlay.has_any_in_range(0..1));
5377 assert!(!overlay.has_any_in_range(1..3));
5378 assert!(overlay.has_any_in_range(3..4));
5379
5380 let sliced = overlay.slice(2, 3);
5381 assert!(sliced.get_scalar(0).is_none());
5382 assert_eq!(
5383 sliced.get_scalar(1).unwrap().to_literal(),
5384 LiteralValue::Empty
5385 );
5386 }
5387
5388 #[test]
5389 fn overlay_cascade_user_layer_masks_computed_fragment_regardless_of_sequence() {
5390 let mut user = Overlay::new();
5391 let mut computed = Overlay::new();
5392
5393 user.set_scalar(0, OverlayValue::Number(3.0));
5394 computed.apply_fragment(
5395 OverlayFragment::dense_range(0, vec![OverlayValue::Number(2.0)]).unwrap(),
5396 );
5397
5398 let cascade = OverlayCascade::new(&user, &computed);
5399 assert_eq!(
5400 cascade.get_scalar(0).unwrap().to_literal(),
5401 LiteralValue::Number(3.0)
5402 );
5403 }
5404
5405 #[test]
5406 fn overlay_same_layer_later_point_replaces_fragment_cell() {
5407 let mut overlay = Overlay::new();
5408 overlay.apply_fragment(
5409 OverlayFragment::dense_range(
5410 0,
5411 vec![
5412 OverlayValue::Number(1.0),
5413 OverlayValue::Number(2.0),
5414 OverlayValue::Number(3.0),
5415 ],
5416 )
5417 .unwrap(),
5418 );
5419
5420 overlay.set_scalar(1, OverlayValue::Number(99.0));
5421
5422 assert_eq!(
5423 overlay.get_scalar(0).unwrap().to_literal(),
5424 LiteralValue::Number(1.0)
5425 );
5426 assert_eq!(
5427 overlay.get_scalar(1).unwrap().to_literal(),
5428 LiteralValue::Number(99.0)
5429 );
5430 assert_eq!(
5431 overlay.get_scalar(2).unwrap().to_literal(),
5432 LiteralValue::Number(3.0)
5433 );
5434 }
5435
5436 #[test]
5437 fn overlay_same_layer_later_fragment_replaces_point_range() {
5438 let mut overlay = Overlay::new();
5439 overlay.set_scalar(0, OverlayValue::Number(1.0));
5440 overlay.set_scalar(1, OverlayValue::Number(2.0));
5441 overlay.set_scalar(2, OverlayValue::Number(3.0));
5442
5443 overlay.apply_fragment(
5444 OverlayFragment::dense_range(
5445 0,
5446 vec![
5447 OverlayValue::Number(10.0),
5448 OverlayValue::Number(20.0),
5449 OverlayValue::Number(30.0),
5450 ],
5451 )
5452 .unwrap(),
5453 );
5454
5455 let stats = overlay.debug_stats();
5456 assert_eq!(stats.points, 0);
5457 assert_eq!(stats.dense_fragments, 1);
5458 assert!(overlay.debug_is_normalized());
5459 assert_eq!(
5460 overlay.get_scalar(0).unwrap().to_literal(),
5461 LiteralValue::Number(10.0)
5462 );
5463 assert_eq!(
5464 overlay.get_scalar(1).unwrap().to_literal(),
5465 LiteralValue::Number(20.0)
5466 );
5467 assert_eq!(
5468 overlay.get_scalar(2).unwrap().to_literal(),
5469 LiteralValue::Number(30.0)
5470 );
5471 }
5472
5473 #[test]
5474 fn overlay_sparse_far_apart_replacement_does_not_rewrite_unrelated_dense_fragment() {
5475 let mut overlay = Overlay::new();
5476 overlay.apply_fragment(
5477 OverlayFragment::dense_range(100, vec![OverlayValue::Number(1.0); 10]).unwrap(),
5478 );
5479
5480 overlay.apply_fragment(
5481 OverlayFragment::sparse_offsets(vec![
5482 (0, OverlayValue::Empty),
5483 (1000, OverlayValue::Number(1000.0)),
5484 ])
5485 .unwrap(),
5486 );
5487
5488 let stats = overlay.debug_stats();
5489 assert_eq!(stats.dense_fragments, 1);
5490 assert_eq!(stats.sparse_fragments, 1);
5491 assert_eq!(stats.run_fragments, 0);
5492 assert!(overlay.debug_is_normalized());
5493 assert_eq!(
5494 overlay.get_scalar(105).unwrap().to_literal(),
5495 LiteralValue::Number(1.0)
5496 );
5497 assert_eq!(
5498 overlay.get_scalar(0).unwrap().to_literal(),
5499 LiteralValue::Empty
5500 );
5501 assert_eq!(
5502 overlay.get_scalar(1000).unwrap().to_literal(),
5503 LiteralValue::Number(1000.0)
5504 );
5505 }
5506
5507 #[test]
5508 fn overlay_sparse_offsets_are_sorted_unique_last_write_wins() {
5509 let mut overlay = Overlay::new();
5510 overlay.apply_fragment(
5511 OverlayFragment::sparse_offsets(vec![
5512 (3, OverlayValue::Number(3.0)),
5513 (1, OverlayValue::Number(1.0)),
5514 (3, OverlayValue::Number(33.0)),
5515 ])
5516 .unwrap(),
5517 );
5518
5519 let stats = overlay.debug_stats();
5520 assert_eq!(stats.sparse_fragments, 1);
5521 assert_eq!(overlay.len(), 2);
5522 assert_eq!(
5523 overlay.get_scalar(1).unwrap().to_literal(),
5524 LiteralValue::Number(1.0)
5525 );
5526 assert_eq!(
5527 overlay.get_scalar(3).unwrap().to_literal(),
5528 LiteralValue::Number(33.0)
5529 );
5530 assert!(overlay.debug_is_normalized());
5531 }
5532
5533 #[test]
5534 fn overlay_dense_point_replacement_splits_dense_not_sparse() {
5535 let mut overlay = Overlay::new();
5536 overlay.apply_fragment(
5537 OverlayFragment::dense_range(
5538 0,
5539 (0..6)
5540 .map(|i| OverlayValue::Number(i as f64))
5541 .collect::<Vec<_>>(),
5542 )
5543 .unwrap(),
5544 );
5545
5546 overlay.set_scalar(3, OverlayValue::Number(99.0));
5547
5548 let stats = overlay.debug_stats();
5549 assert_eq!(stats.points, 1);
5550 assert_eq!(stats.dense_fragments, 2);
5551 assert_eq!(stats.sparse_fragments, 0);
5552 assert!(overlay.debug_is_normalized());
5553 assert_eq!(
5554 overlay.get_scalar(2).unwrap().to_literal(),
5555 LiteralValue::Number(2.0)
5556 );
5557 assert_eq!(
5558 overlay.get_scalar(3).unwrap().to_literal(),
5559 LiteralValue::Number(99.0)
5560 );
5561 assert_eq!(
5562 overlay.get_scalar(4).unwrap().to_literal(),
5563 LiteralValue::Number(4.0)
5564 );
5565 }
5566
5567 #[test]
5568 fn overlay_dense_fragment_replacement_splits_left_and_right_dense() {
5569 let mut overlay = Overlay::new();
5570 overlay.apply_fragment(
5571 OverlayFragment::dense_range(
5572 0,
5573 (0..8)
5574 .map(|i| OverlayValue::Number(i as f64))
5575 .collect::<Vec<_>>(),
5576 )
5577 .unwrap(),
5578 );
5579
5580 overlay.apply_fragment(
5581 OverlayFragment::dense_range(
5582 3,
5583 vec![OverlayValue::Number(30.0), OverlayValue::Number(40.0)],
5584 )
5585 .unwrap(),
5586 );
5587
5588 let stats = overlay.debug_stats();
5589 assert_eq!(stats.points, 0);
5590 assert_eq!(stats.dense_fragments, 3);
5591 assert_eq!(stats.sparse_fragments, 0);
5592 assert!(overlay.debug_is_normalized());
5593 assert_eq!(
5594 overlay.get_scalar(2).unwrap().to_literal(),
5595 LiteralValue::Number(2.0)
5596 );
5597 assert_eq!(
5598 overlay.get_scalar(3).unwrap().to_literal(),
5599 LiteralValue::Number(30.0)
5600 );
5601 assert_eq!(
5602 overlay.get_scalar(4).unwrap().to_literal(),
5603 LiteralValue::Number(40.0)
5604 );
5605 assert_eq!(
5606 overlay.get_scalar(5).unwrap().to_literal(),
5607 LiteralValue::Number(5.0)
5608 );
5609 }
5610
5611 #[test]
5612 fn overlay_run_point_replacement_splits_run_not_sparse() {
5613 let mut overlay = Overlay::new();
5614 overlay.apply_fragment(
5615 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 10]).unwrap(),
5616 );
5617
5618 overlay.set_scalar(5, OverlayValue::Number(99.0));
5619
5620 let stats = overlay.debug_stats();
5621 assert_eq!(stats.points, 1);
5622 assert_eq!(stats.run_fragments, 2);
5623 assert_eq!(stats.sparse_fragments, 0);
5624 assert!(overlay.debug_is_normalized());
5625 assert_eq!(
5626 overlay.get_scalar(4).unwrap().to_literal(),
5627 LiteralValue::Number(1.0)
5628 );
5629 assert_eq!(
5630 overlay.get_scalar(5).unwrap().to_literal(),
5631 LiteralValue::Number(99.0)
5632 );
5633 assert_eq!(
5634 overlay.get_scalar(6).unwrap().to_literal(),
5635 LiteralValue::Number(1.0)
5636 );
5637 }
5638
5639 #[test]
5640 fn overlay_run_fragment_replacement_splits_left_and_right_run() {
5641 let mut overlay = Overlay::new();
5642 let values = [
5643 vec![OverlayValue::Number(1.0); 4],
5644 vec![OverlayValue::Number(2.0); 4],
5645 vec![OverlayValue::Number(3.0); 4],
5646 ]
5647 .concat();
5648 overlay.apply_fragment(OverlayFragment::run_range(0, values).unwrap());
5649
5650 overlay.apply_fragment(
5651 OverlayFragment::dense_range(
5652 5,
5653 vec![OverlayValue::Number(50.0), OverlayValue::Number(60.0)],
5654 )
5655 .unwrap(),
5656 );
5657
5658 let stats = overlay.debug_stats();
5659 assert_eq!(stats.run_fragments, 2);
5660 assert_eq!(stats.dense_fragments, 1);
5661 assert_eq!(stats.sparse_fragments, 0);
5662 assert!(overlay.debug_is_normalized());
5663 assert_eq!(
5664 overlay.get_scalar(4).unwrap().to_literal(),
5665 LiteralValue::Number(2.0)
5666 );
5667 assert_eq!(
5668 overlay.get_scalar(5).unwrap().to_literal(),
5669 LiteralValue::Number(50.0)
5670 );
5671 assert_eq!(
5672 overlay.get_scalar(6).unwrap().to_literal(),
5673 LiteralValue::Number(60.0)
5674 );
5675 assert_eq!(
5676 overlay.get_scalar(7).unwrap().to_literal(),
5677 LiteralValue::Number(2.0)
5678 );
5679 }
5680
5681 #[test]
5682 fn overlay_slice_preserves_dense_and_run_encodings() {
5683 let mut overlay = Overlay::new();
5684 overlay.apply_fragment(
5685 OverlayFragment::dense_range(
5686 10,
5687 (0..5)
5688 .map(|i| OverlayValue::Number(i as f64))
5689 .collect::<Vec<_>>(),
5690 )
5691 .unwrap(),
5692 );
5693 overlay.apply_fragment(
5694 OverlayFragment::run_range(
5695 20,
5696 [
5697 vec![OverlayValue::Number(1.0); 3],
5698 vec![OverlayValue::Number(2.0); 3],
5699 ]
5700 .concat(),
5701 )
5702 .unwrap(),
5703 );
5704
5705 let dense_slice = overlay.slice(12, 2);
5706 let dense_stats = dense_slice.debug_stats();
5707 assert_eq!(dense_stats.dense_fragments, 1);
5708 assert_eq!(dense_stats.sparse_fragments, 0);
5709 assert_eq!(
5710 dense_slice.get_scalar(0).unwrap().to_literal(),
5711 LiteralValue::Number(2.0)
5712 );
5713 assert_eq!(
5714 dense_slice.get_scalar(1).unwrap().to_literal(),
5715 LiteralValue::Number(3.0)
5716 );
5717 assert!(dense_slice.debug_is_normalized());
5718
5719 let run_slice = overlay.slice(22, 3);
5720 let run_stats = run_slice.debug_stats();
5721 assert_eq!(run_stats.run_fragments, 1);
5722 assert_eq!(run_stats.sparse_fragments, 0);
5723 assert_eq!(
5724 run_slice.get_scalar(0).unwrap().to_literal(),
5725 LiteralValue::Number(1.0)
5726 );
5727 assert_eq!(
5728 run_slice.get_scalar(1).unwrap().to_literal(),
5729 LiteralValue::Number(2.0)
5730 );
5731 assert_eq!(
5732 run_slice.get_scalar(2).unwrap().to_literal(),
5733 LiteralValue::Number(2.0)
5734 );
5735 assert!(run_slice.debug_is_normalized());
5736 }
5737
5738 #[test]
5739 fn overlay_computed_empty_run_masks_non_empty_base() {
5740 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
5741 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
5742 b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
5743 b.append_row(&[LiteralValue::Number(3.0)]).unwrap();
5744 let mut sheet = b.finish();
5745
5746 let (ch_i, _) = sheet.chunk_of_row(0).unwrap();
5747 sheet.columns[0].chunks[ch_i]
5748 .computed_overlay
5749 .apply_fragment(
5750 OverlayFragment::run_range(
5751 0,
5752 vec![
5753 OverlayValue::Empty,
5754 OverlayValue::Empty,
5755 OverlayValue::Empty,
5756 ],
5757 )
5758 .unwrap(),
5759 );
5760
5761 assert_eq!(sheet.get_cell_value(0, 0), LiteralValue::Empty);
5762 assert_eq!(sheet.get_cell_value(1, 0), LiteralValue::Empty);
5763 assert_eq!(sheet.get_cell_value(2, 0), LiteralValue::Empty);
5764 }
5765
5766 #[test]
5767 fn overlay_fragments_reconstruct_scalars_from_typed_lanes() {
5768 let values = vec![
5769 OverlayValue::Empty,
5770 OverlayValue::Number(1.5),
5771 OverlayValue::DateTime(45000.25),
5772 OverlayValue::Duration(0.5),
5773 OverlayValue::Boolean(true),
5774 OverlayValue::Text(Arc::from("Hello")),
5775 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
5776 OverlayValue::Pending,
5777 ];
5778
5779 let mut dense = Overlay::new();
5780 dense.apply_fragment(OverlayFragment::dense_range(0, values.clone()).unwrap());
5781 for (idx, expected) in values.iter().enumerate() {
5782 assert_eq!(
5783 dense.get_scalar(idx).unwrap().to_overlay_value(),
5784 expected.clone()
5785 );
5786 }
5787
5788 let mut sparse = Overlay::new();
5789 sparse.apply_fragment(
5790 OverlayFragment::sparse_offsets(
5791 values
5792 .iter()
5793 .cloned()
5794 .enumerate()
5795 .map(|(idx, value)| (idx * 2, value))
5796 .collect(),
5797 )
5798 .unwrap(),
5799 );
5800 for (idx, expected) in values.iter().enumerate() {
5801 assert_eq!(
5802 sparse.get_scalar(idx * 2).unwrap().to_overlay_value(),
5803 expected.clone()
5804 );
5805 }
5806
5807 let mut run = Overlay::new();
5808 run.apply_fragment(
5809 OverlayFragment::run_range(
5810 0,
5811 vec![
5812 OverlayValue::Number(7.0),
5813 OverlayValue::Number(7.0),
5814 OverlayValue::Text(Arc::from("run")),
5815 OverlayValue::Text(Arc::from("run")),
5816 ],
5817 )
5818 .unwrap(),
5819 );
5820 assert_eq!(
5821 run.get_scalar(0).unwrap().to_overlay_value(),
5822 OverlayValue::Number(7.0)
5823 );
5824 assert_eq!(
5825 run.get_scalar(2).unwrap().to_overlay_value(),
5826 OverlayValue::Text(Arc::from("run"))
5827 );
5828 }
5829
5830 #[test]
5831 fn overlay_iter_returns_complete_logical_entries() {
5832 let mut overlay = Overlay::new();
5833 overlay.apply_fragment(
5834 OverlayFragment::dense_range(
5835 2,
5836 vec![OverlayValue::Number(2.0), OverlayValue::Number(3.0)],
5837 )
5838 .unwrap(),
5839 );
5840 overlay.set_scalar(5, OverlayValue::Text(Arc::from("point")));
5841
5842 let entries: Vec<_> = overlay.iter().collect();
5843 assert_eq!(
5844 entries,
5845 vec![
5846 (2, OverlayValue::Number(2.0)),
5847 (3, OverlayValue::Number(3.0)),
5848 (5, OverlayValue::Text(Arc::from("point"))),
5849 ]
5850 );
5851 assert_eq!(overlay.iter_points().count(), 1);
5852 }
5853
5854 #[test]
5855 fn overlay_fragment_estimates_follow_encoded_shapes() {
5856 let mut points = Overlay::new();
5857 for idx in 0..512 {
5858 points.set_scalar(idx, OverlayValue::Number(idx as f64));
5859 }
5860
5861 let mut dense = Overlay::new();
5862 dense.apply_fragment(
5863 OverlayFragment::dense_range(
5864 0,
5865 (0..512)
5866 .map(|idx| OverlayValue::Number(idx as f64))
5867 .collect::<Vec<_>>(),
5868 )
5869 .unwrap(),
5870 );
5871 assert_eq!(
5872 dense.estimated_bytes(),
5873 dense.debug_recomputed_estimated_bytes()
5874 );
5875 assert!(
5876 dense.estimated_bytes() < points.estimated_bytes(),
5877 "dense fragment should account like encoded lanes, not point-map entries"
5878 );
5879
5880 let mut short_run = Overlay::new();
5881 short_run.apply_fragment(
5882 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 8]).unwrap(),
5883 );
5884 let mut long_run = Overlay::new();
5885 long_run.apply_fragment(
5886 OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 4096]).unwrap(),
5887 );
5888 assert_eq!(
5889 short_run.estimated_bytes(),
5890 short_run.debug_recomputed_estimated_bytes()
5891 );
5892 assert_eq!(
5893 long_run.estimated_bytes(),
5894 long_run.debug_recomputed_estimated_bytes()
5895 );
5896 assert_eq!(
5897 short_run.estimated_bytes(),
5898 long_run.estimated_bytes(),
5899 "single-run estimate should scale with run count, not covered rows"
5900 );
5901
5902 let sparse10 = OverlayFragment::sparse_offsets(
5903 (0..10)
5904 .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
5905 .collect(),
5906 )
5907 .unwrap();
5908 let sparse20 = OverlayFragment::sparse_offsets(
5909 (0..20)
5910 .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
5911 .collect(),
5912 )
5913 .unwrap();
5914 assert!(sparse20.estimated_bytes() > sparse10.estimated_bytes());
5915 }
5916
5917 #[test]
5918 fn overlay_estimated_bytes_stay_consistent_after_split_and_clear() {
5919 let mut overlay = Overlay::new();
5920 overlay.apply_fragment(
5921 OverlayFragment::dense_range(
5922 0,
5923 (0..16)
5924 .map(|idx| OverlayValue::Number(idx as f64))
5925 .collect::<Vec<_>>(),
5926 )
5927 .unwrap(),
5928 );
5929 assert_eq!(
5930 overlay.estimated_bytes(),
5931 overlay.debug_recomputed_estimated_bytes()
5932 );
5933
5934 overlay.set_scalar(8, OverlayValue::Text(Arc::from("split")));
5935 assert!(overlay.debug_is_normalized());
5936 assert_eq!(
5937 overlay.estimated_bytes(),
5938 overlay.debug_recomputed_estimated_bytes()
5939 );
5940
5941 overlay.apply_fragment(
5942 OverlayFragment::sparse_offsets(vec![
5943 (0, OverlayValue::Empty),
5944 (15, OverlayValue::Boolean(true)),
5945 ])
5946 .unwrap(),
5947 );
5948 assert!(overlay.debug_is_normalized());
5949 assert_eq!(
5950 overlay.estimated_bytes(),
5951 overlay.debug_recomputed_estimated_bytes()
5952 );
5953
5954 let freed = overlay.clear_all();
5955 assert!(freed > 0);
5956 assert_eq!(overlay.estimated_bytes(), 0);
5957 assert_eq!(overlay.debug_recomputed_estimated_bytes(), 0);
5958 assert!(overlay.is_empty());
5959 }
5960
5961 #[test]
5962 fn overlay_segment_numbers_masks_base_for_non_numeric_overlays() {
5963 let mut user = Overlay::new();
5964 user.set(1, OverlayValue::Text(Arc::from("x")));
5965 user.set(2, OverlayValue::Empty);
5966 user.set(3, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
5967 user.set(4, OverlayValue::Pending);
5968 let computed = Overlay::new();
5969 let cascade = OverlayCascade::new(&user, &computed);
5970
5971 let base = Float64Array::from(vec![10.0, 20.0, 30.0, 40.0, 50.0]);
5972 let selected = cascade.select_numbers(0..5, &base);
5973 assert_eq!(selected.value(0), 10.0);
5974 assert!(selected.is_null(1));
5975 assert!(selected.is_null(2));
5976 assert!(selected.is_null(3));
5977 assert!(selected.is_null(4));
5978 }
5979
5980 #[test]
5981 fn overlay_segment_type_tags_preserve_temporal_tags() {
5982 let mut computed = Overlay::new();
5983 computed.set(0, OverlayValue::DateTime(45000.5));
5984 computed.set(1, OverlayValue::Duration(0.25));
5985 let user = Overlay::new();
5986 let cascade = OverlayCascade::new(&user, &computed);
5987
5988 let base = UInt8Array::from(vec![TypeTag::Empty as u8; 2]);
5989 let selected = cascade.select_type_tags(0..2, &base);
5990 assert_eq!(selected.value(0), TypeTag::DateTime as u8);
5991 assert_eq!(selected.value(1), TypeTag::Duration as u8);
5992 }
5993
5994 #[test]
5995 fn overlay_lowered_text_matches_existing_overlay_semantics() {
5996 let mut user = Overlay::new();
5997 user.set(0, OverlayValue::Text(Arc::from("HeLLo")));
5998 user.set(1, OverlayValue::Number(1.5));
5999 user.set(2, OverlayValue::Boolean(true));
6000 user.set(3, OverlayValue::Empty);
6001 let computed = Overlay::new();
6002 let cascade = OverlayCascade::new(&user, &computed);
6003
6004 let base = StringArray::from(vec![Some("A"), Some("B"), Some("C"), Some("D")]);
6005 let selected = cascade.select_lowered_text(0..4, &base);
6006 assert_eq!(selected.value(0), "hello");
6007 assert_eq!(selected.value(1), "1.5");
6008 assert_eq!(selected.value(2), "true");
6009 assert!(selected.is_null(3));
6010 }
6011
6012 fn numeric_sheet(rows: usize) -> ArrowSheet {
6013 let mut b = IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
6014 for row in 0..rows {
6015 b.append_row(&[LiteralValue::Number((row + 1) as f64)])
6016 .unwrap();
6017 }
6018 b.finish()
6019 }
6020
6021 fn numbers_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<Float64Array> {
6022 let view = sheet.range_view(sr, 0, er, 0);
6023 let segments: Vec<_> = view.numbers_slices().map(|res| res.unwrap()).collect();
6024 assert_eq!(segments.len(), 1);
6025 assert_eq!(segments[0].2.len(), 1);
6026 segments[0].2[0].clone()
6027 }
6028
6029 fn type_tags_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<UInt8Array> {
6030 let view = sheet.range_view(sr, 0, er, 0);
6031 let segments: Vec<_> = view.type_tags_slices().map(|res| res.unwrap()).collect();
6032 assert_eq!(segments.len(), 1);
6033 assert_eq!(segments[0].2.len(), 1);
6034 segments[0].2[0].clone()
6035 }
6036
6037 fn lowered_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<StringArray> {
6038 let view = sheet.range_view(sr, 0, er, 0);
6039 let segments: Vec<_> = view.lowered_text_slices().map(|res| res.unwrap()).collect();
6040 assert_eq!(segments.len(), 1);
6041 assert_eq!(segments[0].2.len(), 1);
6042 segments[0].2[0].clone()
6043 }
6044
6045 #[test]
6046 fn rangeview_dense_text_masks_base_numbers() {
6047 let mut sheet = numeric_sheet(4);
6048 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
6049 OverlayFragment::dense_range(
6050 0,
6051 vec![
6052 OverlayValue::Text(Arc::from("x")),
6053 OverlayValue::Text(Arc::from("y")),
6054 OverlayValue::Text(Arc::from("z")),
6055 OverlayValue::Text(Arc::from("w")),
6056 ],
6057 )
6058 .unwrap(),
6059 );
6060
6061 reset_overlay_select_stats();
6062 let numbers = numbers_for_range(&sheet, 0, 3);
6063 assert_eq!(numbers.null_count(), 4);
6064 let stats = snapshot_overlay_select_stats();
6065 assert_eq!(stats.direct_dense_slices, 1);
6066 assert_eq!(stats.zip_select_calls, 0);
6067 }
6068
6069 #[test]
6070 fn rangeview_empty_dense_masks_base_all_selectors() {
6071 let mut sheet = numeric_sheet(3);
6072 sheet.columns[0].chunks[0]
6073 .computed_overlay
6074 .apply_fragment(OverlayFragment::dense_range(0, vec![OverlayValue::Empty; 3]).unwrap());
6075
6076 reset_overlay_select_stats();
6077 let numbers = numbers_for_range(&sheet, 0, 2);
6078 let type_tags = type_tags_for_range(&sheet, 0, 2);
6079 let lowered = lowered_for_range(&sheet, 0, 2);
6080 assert_eq!(numbers.null_count(), 3);
6081 assert_eq!(lowered.null_count(), 3);
6082 assert_eq!(type_tags.values(), &[TypeTag::Empty as u8; 3]);
6083 let stats = snapshot_overlay_select_stats();
6084 assert_eq!(stats.direct_dense_slices, 3);
6085 assert_eq!(stats.zip_select_calls, 0);
6086 }
6087
6088 #[test]
6089 fn rangeview_pending_masks_base_type_tag_present_lanes_null() {
6090 let mut sheet = numeric_sheet(2);
6091 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
6092 OverlayFragment::dense_range(0, vec![OverlayValue::Pending; 2]).unwrap(),
6093 );
6094
6095 reset_overlay_select_stats();
6096 let numbers = numbers_for_range(&sheet, 0, 1);
6097 let type_tags = type_tags_for_range(&sheet, 0, 1);
6098 let lowered = lowered_for_range(&sheet, 0, 1);
6099 assert_eq!(numbers.null_count(), 2);
6100 assert_eq!(lowered.null_count(), 2);
6101 assert_eq!(type_tags.values(), &[TypeTag::Pending as u8; 2]);
6102 let stats = snapshot_overlay_select_stats();
6103 assert_eq!(stats.direct_dense_slices, 3);
6104 assert_eq!(stats.zip_select_calls, 0);
6105 }
6106
6107 #[test]
6108 fn rangeview_subrange_inside_dense_fragment_uses_direct_path() {
6109 let mut sheet = numeric_sheet(10);
6110 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
6111 OverlayFragment::dense_range(
6112 0,
6113 (0..10)
6114 .map(|row| OverlayValue::Number((row + 10) as f64))
6115 .collect(),
6116 )
6117 .unwrap(),
6118 );
6119
6120 reset_overlay_select_stats();
6121 let numbers = numbers_for_range(&sheet, 2, 6);
6122 assert_eq!(numbers.len(), 5);
6123 assert_eq!(numbers.value(0), 12.0);
6124 assert_eq!(numbers.value(4), 16.0);
6125 let stats = snapshot_overlay_select_stats();
6126 assert_eq!(stats.direct_dense_slices, 1);
6127 assert_eq!(stats.zip_select_calls, 0);
6128 }
6129
6130 #[test]
6131 fn rangeview_subrange_inside_run_fragment_uses_direct_path() {
6132 let mut sheet = numeric_sheet(10);
6133 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
6134 OverlayFragment::run_range(0, vec![OverlayValue::Number(7.0); 10]).unwrap(),
6135 );
6136
6137 reset_overlay_select_stats();
6138 let numbers = numbers_for_range(&sheet, 2, 6);
6139 assert_eq!(numbers.len(), 5);
6140 for idx in 0..numbers.len() {
6141 assert_eq!(numbers.value(idx), 7.0);
6142 }
6143 let stats = snapshot_overlay_select_stats();
6144 assert_eq!(stats.direct_run_materializations, 1);
6145 assert_eq!(stats.zip_select_calls, 0);
6146 }
6147
6148 #[test]
6149 fn rangeview_user_partial_wrong_type_masks_computed_numeric() {
6150 let mut sheet = numeric_sheet(5);
6151 let chunk = &mut sheet.columns[0].chunks[0];
6152 chunk.computed_overlay.apply_fragment(
6153 OverlayFragment::dense_range(
6154 0,
6155 (0..5)
6156 .map(|row| OverlayValue::Number((row + 10) as f64))
6157 .collect(),
6158 )
6159 .unwrap(),
6160 );
6161 chunk.overlay.apply_fragment(
6162 OverlayFragment::dense_range(2, vec![OverlayValue::Text(Arc::from("mask"))]).unwrap(),
6163 );
6164
6165 reset_overlay_select_stats();
6166 let numbers = numbers_for_range(&sheet, 0, 4);
6167 assert_eq!(numbers.value(0), 10.0);
6168 assert_eq!(numbers.value(1), 11.0);
6169 assert!(numbers.is_null(2));
6170 assert_eq!(numbers.value(3), 13.0);
6171 assert_eq!(numbers.value(4), 14.0);
6172 let stats = snapshot_overlay_select_stats();
6173 assert_eq!(stats.direct_dense_slices, 0);
6174 assert_eq!(stats.zip_select_calls, 1);
6175 assert_eq!(stats.partial_dense_intersections, 2);
6176 }
6177
6178 #[test]
6179 fn rangeview_computed_full_cover_user_no_overlap_uses_computed_direct() {
6180 let mut sheet = numeric_sheet(5);
6181 let chunk = &mut sheet.columns[0].chunks[0];
6182 chunk.computed_overlay.apply_fragment(
6183 OverlayFragment::dense_range(0, vec![OverlayValue::Number(3.0); 5]).unwrap(),
6184 );
6185 chunk
6186 .overlay
6187 .set_scalar(10, OverlayValue::Text(Arc::from("outside")));
6188
6189 reset_overlay_select_stats();
6190 let numbers = numbers_for_range(&sheet, 0, 4);
6191 assert_eq!(numbers.value(0), 3.0);
6192 assert_eq!(numbers.value(4), 3.0);
6193 let stats = snapshot_overlay_select_stats();
6194 assert_eq!(stats.direct_dense_slices, 1);
6195 assert_eq!(stats.zip_select_calls, 0);
6196 }
6197
6198 #[test]
6199 fn rangeview_user_full_cover_ignores_computed() {
6200 let mut sheet = numeric_sheet(4);
6201 let chunk = &mut sheet.columns[0].chunks[0];
6202 chunk.computed_overlay.apply_fragment(
6203 OverlayFragment::dense_range(0, vec![OverlayValue::Number(99.0); 4]).unwrap(),
6204 );
6205 chunk.overlay.apply_fragment(
6206 OverlayFragment::dense_range(0, vec![OverlayValue::Text(Arc::from("user")); 4])
6207 .unwrap(),
6208 );
6209
6210 reset_overlay_select_stats();
6211 let numbers = numbers_for_range(&sheet, 0, 3);
6212 assert_eq!(numbers.null_count(), 4);
6213 let stats = snapshot_overlay_select_stats();
6214 assert_eq!(stats.direct_dense_slices, 1);
6215 assert_eq!(stats.zip_select_calls, 0);
6216 }
6217
6218 #[test]
6219 fn rangeview_point_overlay_still_matches_legacy_scalar_path() {
6220 let mut sheet = numeric_sheet(3);
6221 sheet.columns[0].chunks[0]
6222 .computed_overlay
6223 .set_scalar(1, OverlayValue::Text(Arc::from("point")));
6224
6225 reset_overlay_select_stats();
6226 let numbers = numbers_for_range(&sheet, 0, 2);
6227 assert_eq!(numbers.value(0), 1.0);
6228 assert!(numbers.is_null(1));
6229 assert_eq!(numbers.value(2), 3.0);
6230 let stats = snapshot_overlay_select_stats();
6231 assert_eq!(stats.zip_select_calls, 1);
6232 assert_eq!(stats.point_entries_applied, 1);
6233 assert_eq!(stats.row_scalar_fallbacks, 0);
6234 }
6235
6236 #[test]
6237 fn rangeview_multi_fragment_full_union_does_not_use_direct_path() {
6238 let mut sheet = numeric_sheet(4);
6239 let chunk = &mut sheet.columns[0].chunks[0];
6240 chunk.computed_overlay.apply_fragment(
6241 OverlayFragment::dense_range(0, vec![OverlayValue::Number(10.0); 2]).unwrap(),
6242 );
6243 chunk.computed_overlay.apply_fragment(
6244 OverlayFragment::dense_range(2, vec![OverlayValue::Number(20.0); 2]).unwrap(),
6245 );
6246
6247 reset_overlay_select_stats();
6248 let numbers = numbers_for_range(&sheet, 0, 3);
6249 assert_eq!(numbers.value(0), 10.0);
6250 assert_eq!(numbers.value(1), 10.0);
6251 assert_eq!(numbers.value(2), 20.0);
6252 assert_eq!(numbers.value(3), 20.0);
6253 let stats = snapshot_overlay_select_stats();
6254 assert_eq!(stats.direct_dense_slices, 0);
6255 assert_eq!(stats.zip_select_calls, 1);
6256 assert_eq!(stats.partial_dense_intersections, 2);
6257 }
6258
6259 #[test]
6260 fn rangeview_lowered_text_fragment_semantics_match_scalar_semantics() {
6261 let mut sheet = numeric_sheet(8);
6262 sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
6263 OverlayFragment::dense_range(
6264 0,
6265 vec![
6266 OverlayValue::Text(Arc::from("HeLLo")),
6267 OverlayValue::Number(1.5),
6268 OverlayValue::DateTime(45000.25),
6269 OverlayValue::Duration(0.5),
6270 OverlayValue::Boolean(true),
6271 OverlayValue::Empty,
6272 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
6273 OverlayValue::Pending,
6274 ],
6275 )
6276 .unwrap(),
6277 );
6278
6279 reset_overlay_select_stats();
6280 let lowered = lowered_for_range(&sheet, 0, 7);
6281 assert_eq!(lowered.value(0), "hello");
6282 assert_eq!(lowered.value(1), "1.5");
6283 assert_eq!(lowered.value(2), "45000.25");
6284 assert_eq!(lowered.value(3), "0.5");
6285 assert_eq!(lowered.value(4), "true");
6286 assert!(lowered.is_null(5));
6287 assert!(lowered.is_null(6));
6288 assert!(lowered.is_null(7));
6289 let stats = snapshot_overlay_select_stats();
6290 assert_eq!(stats.direct_dense_slices, 1);
6291 assert_eq!(stats.zip_select_calls, 0);
6292 }
6293
6294 #[test]
6295 fn row_chunk_slices_shape() {
6296 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6298 b.append_row(&[LiteralValue::Text("a".into()), LiteralValue::Number(1.0)])
6299 .unwrap();
6300 b.append_row(&[LiteralValue::Text("b".into()), LiteralValue::Number(2.0)])
6301 .unwrap();
6302 b.append_row(&[LiteralValue::Text("c".into()), LiteralValue::Number(3.0)])
6303 .unwrap();
6304 let sheet = b.finish();
6305 let rv = sheet.range_view(0, 0, 2, 1);
6306 let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
6307 assert_eq!(slices.len(), 2);
6308 assert_eq!(slices[0].row_start, 0);
6309 assert_eq!(slices[0].row_len, 2);
6310 assert_eq!(slices[0].cols.len(), 2);
6311 assert_eq!(slices[1].row_start, 2);
6312 assert_eq!(slices[1].row_len, 1);
6313 assert_eq!(slices[1].cols.len(), 2);
6314 }
6315
6316 #[test]
6317 fn oob_columns_are_padded() {
6318 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6320 b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("a".into())])
6321 .unwrap();
6322 b.append_row(&[LiteralValue::Number(2.0), LiteralValue::Text("b".into())])
6323 .unwrap();
6324 let sheet = b.finish();
6325 let rv = sheet.range_view(0, 0, 1, 2);
6327 assert_eq!(rv.dims(), (2, 3));
6328 let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
6329 assert!(!slices.is_empty());
6330 for cs in &slices {
6331 assert_eq!(cs.cols.len(), 3);
6332 }
6333 for res in rv.numbers_slices() {
6335 let (_rs, _rl, cols) = res.unwrap();
6336 assert_eq!(cols.len(), 3);
6337 }
6338 for res in rv.booleans_slices() {
6339 let (_rs, _rl, cols) = res.unwrap();
6340 assert_eq!(cols.len(), 3);
6341 }
6342 for res in rv.text_slices() {
6343 let (_rs, _rl, cols) = res.unwrap();
6344 assert_eq!(cols.len(), 3);
6345 }
6346 for res in rv.errors_slices() {
6347 let (_rs, _rl, cols) = res.unwrap();
6348 assert_eq!(cols.len(), 3);
6349 }
6350 for res in rv.lowered_text_slices() {
6351 let (_rs, _rl, cols) = res.unwrap();
6352 assert_eq!(cols.len(), 3);
6353 }
6354 }
6355
6356 #[test]
6357 fn reversed_range_is_empty() {
6358 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
6359 b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
6360 b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
6361 let sheet = b.finish();
6362 let rv = sheet.range_view(3, 0, 1, 0); assert_eq!(rv.dims(), (0, 0));
6364 assert!(rv.iter_row_chunks().next().is_none());
6365 assert_eq!(rv.get_cell(0, 0), LiteralValue::Empty);
6366 }
6367
6368 #[test]
6369 fn chunk_alignment_invariant() {
6370 let mut b = IngestBuilder::new("S", 3, 2, crate::engine::DateSystem::Excel1900);
6371 for r in 0..5 {
6373 b.append_row(&[
6374 LiteralValue::Number(r as f64),
6375 LiteralValue::Text(format!("{r}")),
6376 if r % 2 == 0 {
6377 LiteralValue::Empty
6378 } else {
6379 LiteralValue::Boolean(true)
6380 },
6381 ])
6382 .unwrap();
6383 }
6384 let sheet = b.finish();
6385 assert_eq!(sheet.chunk_starts, vec![0, 2, 4]);
6387 let lens0: Vec<usize> = sheet.columns[0]
6389 .chunks
6390 .iter()
6391 .map(|ch| ch.type_tag.len())
6392 .collect();
6393 for col in &sheet.columns[1..] {
6394 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6395 assert_eq!(lens, lens0);
6396 }
6397 }
6398
6399 #[test]
6400 fn chunking_splits_rows() {
6401 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6403 let rows = vec![
6404 vec![LiteralValue::Number(1.0), LiteralValue::Text("a".into())],
6405 vec![LiteralValue::Empty, LiteralValue::Text("b".into())],
6406 vec![LiteralValue::Boolean(true), LiteralValue::Empty],
6407 ];
6408 for r in rows {
6409 b.append_row(&r).unwrap();
6410 }
6411 let sheet = b.finish();
6412 assert_eq!(sheet.columns[0].chunks.len(), 2);
6413 assert_eq!(sheet.columns[1].chunks.len(), 2);
6414 assert_eq!(sheet.columns[0].chunks[0].numbers_or_null().len(), 2);
6415 assert_eq!(sheet.columns[0].chunks[1].numbers_or_null().len(), 1);
6416 }
6417
6418 #[test]
6419 fn pending_is_not_error() {
6420 let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
6421 b.append_row(&[LiteralValue::Pending]).unwrap();
6422 let sheet = b.finish();
6423 let ch = &sheet.columns[0].chunks[0];
6424 assert_eq!(ch.type_tag.values()[0], super::TypeTag::Pending as u8);
6426 let errs = ch.errors_or_null();
6428 assert_eq!(errs.null_count(), 1);
6429 }
6430
6431 #[test]
6432 fn all_null_numeric_lane_uses_null_array() {
6433 let mut b = IngestBuilder::new("S", 1, 16, crate::engine::DateSystem::Excel1900);
6435 b.append_row(&[LiteralValue::Text("a".into())]).unwrap();
6436 b.append_row(&[LiteralValue::Text("".into())]).unwrap();
6437 b.append_row(&[LiteralValue::Text("b".into())]).unwrap();
6438 let sheet = b.finish();
6439 let ch = &sheet.columns[0].chunks[0];
6440 let nums = ch.numbers_or_null();
6441 assert_eq!(nums.len(), 3);
6442 assert_eq!(nums.null_count(), 3);
6443 assert_eq!(nums.data_type(), &DataType::Float64);
6444 }
6445
6446 #[test]
6447 fn row_insert_delete_across_chunk_boundaries_with_overlays() {
6448 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
6450 for _ in 0..10 {
6451 b.append_row(&[LiteralValue::Empty]).unwrap();
6452 }
6453 let mut sheet = b.finish();
6454 {
6456 let (c0, o0) = sheet.chunk_of_row(3).unwrap();
6457 sheet.columns[0].chunks[c0]
6458 .overlay
6459 .set(o0, OverlayValue::Number(30.0));
6460 let (c1, o1) = sheet.chunk_of_row(4).unwrap();
6461 sheet.columns[0].chunks[c1]
6462 .overlay
6463 .set(o1, OverlayValue::Number(40.0));
6464 }
6465 sheet.insert_rows(4, 2);
6467 assert_eq!(sheet.nrows, 12);
6468 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6470 assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
6471 assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
6472 assert_eq!(av.get_cell(6, 0), LiteralValue::Number(40.0));
6473
6474 sheet.delete_rows(3, 3);
6476 assert_eq!(sheet.nrows, 9);
6477 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6478 assert_eq!(av2.get_cell(3, 0), LiteralValue::Number(40.0));
6479 let lens0: Vec<usize> = sheet.columns[0]
6481 .chunks
6482 .iter()
6483 .map(|ch| ch.type_tag.len())
6484 .collect();
6485 for col in &sheet.columns {
6486 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6487 assert_eq!(lens, lens0);
6488 }
6489 assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
6491 let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
6492 let last_len = sheet.columns[0]
6493 .chunks
6494 .last()
6495 .map(|c| c.type_tag.len())
6496 .unwrap_or(0);
6497 assert_eq!(last_start + last_len, sheet.nrows as usize);
6498 }
6499
6500 #[test]
6501 fn row_insert_delete_preserves_user_dense_fragments() {
6502 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
6503 for _ in 0..10 {
6504 b.append_row(&[LiteralValue::Empty]).unwrap();
6505 }
6506 let mut sheet = b.finish();
6507
6508 let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
6509 sheet.columns[0]
6510 .chunk_mut(ch_idx)
6511 .unwrap()
6512 .overlay
6513 .apply_fragment(
6514 OverlayFragment::dense_range(
6515 off,
6516 vec![
6517 OverlayValue::Number(10.0),
6518 OverlayValue::Number(20.0),
6519 OverlayValue::Number(30.0),
6520 ],
6521 )
6522 .unwrap(),
6523 );
6524
6525 let before = column_overlay_stats(&sheet, 0, false);
6526 assert_eq!(before.dense_fragments, 1);
6527 assert_eq!(before.sparse_fragments, 0);
6528 assert_column_overlays_normalized(&sheet, 0);
6529
6530 sheet.insert_rows(2, 2);
6531 assert_eq!(sheet.nrows, 12);
6532 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6533 assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
6534 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
6535 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
6536 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(20.0));
6537 assert_eq!(av.get_cell(5, 0), LiteralValue::Number(30.0));
6538 let after_insert = column_overlay_stats(&sheet, 0, false);
6539 assert_eq!(after_insert.sparse_fragments, 0);
6540 assert!(after_insert.dense_fragments >= 2);
6541 assert_column_overlays_normalized(&sheet, 0);
6542
6543 sheet.delete_rows(2, 2);
6544 assert_eq!(sheet.nrows, 10);
6545 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6546 assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
6547 assert_eq!(av.get_cell(2, 0), LiteralValue::Number(20.0));
6548 assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
6549 let after_delete = column_overlay_stats(&sheet, 0, false);
6550 assert_eq!(after_delete.sparse_fragments, 0);
6551 assert!(after_delete.dense_fragments >= 1);
6552 assert_column_overlays_normalized(&sheet, 0);
6553 }
6554
6555 #[test]
6556 fn row_insert_delete_preserves_computed_empty_run_fragments() {
6557 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
6558 for row in 0..8 {
6559 b.append_row(&[LiteralValue::Number((row + 1) as f64)])
6560 .unwrap();
6561 }
6562 let mut sheet = b.finish();
6563
6564 let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
6565 sheet.columns[0]
6566 .chunk_mut(ch_idx)
6567 .unwrap()
6568 .computed_overlay
6569 .apply_fragment(
6570 OverlayFragment::run_range(
6571 off,
6572 vec![
6573 OverlayValue::Empty,
6574 OverlayValue::Empty,
6575 OverlayValue::Empty,
6576 ],
6577 )
6578 .unwrap(),
6579 );
6580
6581 let before = column_overlay_stats(&sheet, 0, true);
6582 assert_eq!(before.run_fragments, 1);
6583 assert_eq!(before.sparse_fragments, 0);
6584 assert_column_overlays_normalized(&sheet, 0);
6585
6586 sheet.insert_rows(2, 1);
6587 assert_eq!(sheet.nrows, 9);
6588 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6589 assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
6590 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
6591 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
6592 assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
6593 assert_eq!(av.get_cell(5, 0), LiteralValue::Number(5.0));
6594 let after_insert = column_overlay_stats(&sheet, 0, true);
6595 assert_eq!(after_insert.sparse_fragments, 0);
6596 assert!(after_insert.run_fragments >= 2);
6597 assert_column_overlays_normalized(&sheet, 0);
6598
6599 sheet.delete_rows(2, 1);
6600 assert_eq!(sheet.nrows, 8);
6601 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6602 assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
6603 assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
6604 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
6605 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(5.0));
6606 let after_delete = column_overlay_stats(&sheet, 0, true);
6607 assert_eq!(after_delete.sparse_fragments, 0);
6608 assert!(after_delete.run_fragments >= 1);
6609 assert_column_overlays_normalized(&sheet, 0);
6610 }
6611
6612 #[test]
6613 fn column_insert_delete_retains_chunk_alignment() {
6614 let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
6615 for _ in 0..5 {
6616 b.append_row(&[
6617 LiteralValue::Empty,
6618 LiteralValue::Empty,
6619 LiteralValue::Empty,
6620 ])
6621 .unwrap();
6622 }
6623 let mut sheet = b.finish();
6624 let ref_lens: Vec<usize> = sheet.columns[0]
6626 .chunks
6627 .iter()
6628 .map(|ch| ch.type_tag.len())
6629 .collect();
6630 sheet.insert_columns(1, 2);
6632 assert_eq!(sheet.columns.len(), 5);
6633 for col in &sheet.columns {
6634 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6635 assert_eq!(lens, ref_lens);
6636 }
6637 let starts_before = sheet.chunk_starts.clone();
6638 sheet.delete_columns(2, 2);
6640 assert_eq!(sheet.columns.len(), 3);
6641 for col in &sheet.columns {
6642 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6643 assert_eq!(lens, ref_lens);
6644 }
6645 assert_eq!(sheet.chunk_starts, starts_before);
6647 }
6648
6649 #[test]
6650 fn multiple_adjacent_row_ops_overlay_mixed_types() {
6651 use formualizer_common::ExcelErrorKind;
6652 let mut b = IngestBuilder::new("S", 2, 3, crate::engine::DateSystem::Excel1900);
6654 for _ in 0..9 {
6655 b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
6656 .unwrap();
6657 }
6658 let mut sheet = b.finish();
6659 let set_ov = |sh: &mut ArrowSheet, row: usize, ov: OverlayValue| {
6662 let (ch_i, off) = sh.chunk_of_row(row).unwrap();
6663 let _ = sh.columns[0].chunks[ch_i].overlay.set(off, ov);
6664 };
6665 set_ov(&mut sheet, 2, OverlayValue::Number(12.5));
6666 set_ov(&mut sheet, 3, OverlayValue::Text(Arc::from("hello")));
6667 set_ov(&mut sheet, 5, OverlayValue::Boolean(true));
6668 set_ov(
6669 &mut sheet,
6670 6,
6671 OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
6672 );
6673 set_ov(&mut sheet, 8, OverlayValue::Empty);
6674
6675 sheet.insert_rows(3, 1);
6677 let av1 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6679 assert_eq!(av1.get_cell(2, 0), LiteralValue::Number(12.5));
6680 assert_eq!(av1.get_cell(4, 0), LiteralValue::Text("hello".into()));
6681 assert_eq!(av1.get_cell(6, 0), LiteralValue::Boolean(true));
6682 match av1.get_cell(7, 0) {
6683 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
6684 other => panic!("expected error at row 7, got {other:?}"),
6685 }
6686 assert_eq!(av1.get_cell(9, 0), LiteralValue::Empty);
6687
6688 sheet.insert_rows(4, 2);
6690 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6692 assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.5));
6693 assert_eq!(av2.get_cell(6, 0), LiteralValue::Text("hello".into()));
6694 assert_eq!(av2.get_cell(8, 0), LiteralValue::Boolean(true));
6695 match av2.get_cell(9, 0) {
6696 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
6697 other => panic!("expected error at row 9, got {other:?}"),
6698 }
6699 assert_eq!(av2.get_cell(11, 0), LiteralValue::Empty);
6700
6701 sheet.delete_rows(6, 2);
6703 let av3 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
6704 assert_eq!(av3.get_cell(2, 0), LiteralValue::Number(12.5));
6706 assert_eq!(av3.get_cell(6, 0), LiteralValue::Boolean(true));
6707 match av3.get_cell(7, 0) {
6708 LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
6709 other => panic!("expected error at row 8, got {other:?}"),
6710 }
6711 assert_eq!(av3.get_cell(9, 0), LiteralValue::Empty);
6712
6713 let lens0: Vec<usize> = sheet.columns[0]
6715 .chunks
6716 .iter()
6717 .map(|ch| ch.type_tag.len())
6718 .collect();
6719 for col in &sheet.columns {
6720 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6721 assert_eq!(lens, lens0);
6722 }
6723 assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
6725 let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
6726 let last_len = sheet.columns[0]
6727 .chunks
6728 .last()
6729 .map(|c| c.type_tag.len())
6730 .unwrap_or(0);
6731 assert_eq!(last_start + last_len, sheet.nrows as usize);
6732 }
6733
6734 #[test]
6735 fn multiple_adjacent_column_ops_alignment() {
6736 let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6738 for _ in 0..5 {
6739 b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
6740 .unwrap();
6741 }
6742 let mut sheet = b.finish();
6743 let ref_lens: Vec<usize> = sheet.columns[0]
6744 .chunks
6745 .iter()
6746 .map(|ch| ch.type_tag.len())
6747 .collect();
6748 sheet.insert_columns(0, 1);
6750 sheet.insert_columns(2, 2);
6751 assert_eq!(sheet.columns.len(), 5);
6752 for col in &sheet.columns {
6753 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6754 assert_eq!(lens, ref_lens);
6755 }
6756 let starts_before = sheet.chunk_starts.clone();
6757 sheet.delete_columns(1, 1);
6759 let remain = sheet.columns.len();
6760 if remain >= 3 {
6761 sheet.delete_columns(remain - 2, 2);
6762 }
6763 for col in &sheet.columns {
6764 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6765 assert_eq!(lens, ref_lens);
6766 }
6767 assert_eq!(sheet.chunk_starts, starts_before);
6768 }
6769
6770 #[test]
6771 fn overlays_on_multiple_columns_row_col_ops() {
6772 let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
6774 for _ in 0..6 {
6775 b.append_row(&[
6776 LiteralValue::Empty,
6777 LiteralValue::Empty,
6778 LiteralValue::Empty,
6779 ])
6780 .unwrap();
6781 }
6782 let mut sheet = b.finish();
6783 let set_ov = |sh: &mut ArrowSheet, col: usize, row: usize, ov: OverlayValue| {
6785 let (ch_i, off) = sh.chunk_of_row(row).unwrap();
6786 let _ = sh.columns[col].chunks[ch_i].overlay.set(off, ov);
6787 };
6788 set_ov(&mut sheet, 0, 2, OverlayValue::Number(12.0));
6789 set_ov(&mut sheet, 1, 2, OverlayValue::Text(Arc::from("xx")));
6790 set_ov(&mut sheet, 2, 2, OverlayValue::Boolean(true));
6791 set_ov(&mut sheet, 0, 3, OverlayValue::Number(33.0));
6792 set_ov(&mut sheet, 1, 3, OverlayValue::Text(Arc::from("yy")));
6793 set_ov(&mut sheet, 2, 3, OverlayValue::Boolean(false));
6794
6795 sheet.insert_rows(3, 1);
6797 let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 2);
6799 assert_eq!(av.get_cell(2, 0), LiteralValue::Number(12.0));
6801 assert_eq!(av.get_cell(2, 1), LiteralValue::Text("xx".into()));
6802 assert_eq!(av.get_cell(2, 2), LiteralValue::Boolean(true));
6803 assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
6805 assert_eq!(av.get_cell(4, 0), LiteralValue::Number(33.0));
6807 assert_eq!(av.get_cell(4, 1), LiteralValue::Text("yy".into()));
6808 assert_eq!(av.get_cell(4, 2), LiteralValue::Boolean(false));
6809
6810 sheet.delete_columns(1, 1);
6812 let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 1);
6813 assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.0));
6814 assert_eq!(av2.get_cell(2, 1), LiteralValue::Boolean(true));
6816 assert_eq!(av2.get_cell(4, 0), LiteralValue::Number(33.0));
6817 assert_eq!(av2.get_cell(4, 1), LiteralValue::Boolean(false));
6818
6819 let lens0: Vec<usize> = sheet.columns[0]
6821 .chunks
6822 .iter()
6823 .map(|ch| ch.type_tag.len())
6824 .collect();
6825 for col in &sheet.columns {
6826 let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
6827 assert_eq!(lens, lens0);
6828 }
6829 }
6830
6831 #[test]
6832 fn effective_slices_overlay_precedence_numbers_text() {
6833 let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
6835 for i in 0..6 {
6836 b.append_row(&[LiteralValue::Number((i + 1) as f64)])
6837 .unwrap();
6838 }
6839 let mut sheet = b.finish();
6840 let (c1, o1) = sheet.chunk_of_row(1).unwrap();
6842 sheet.columns[0].chunks[c1]
6843 .overlay
6844 .set(o1, OverlayValue::Text(Arc::from("X")));
6845 let (c4, o4) = sheet.chunk_of_row(4).unwrap();
6846 sheet.columns[0].chunks[c4]
6847 .overlay
6848 .set(o4, OverlayValue::Number(99.0));
6849
6850 let av = sheet.range_view(0, 0, 5, 0);
6851 let mut numeric: Vec<Option<f64>> = vec![None; 6];
6853 for res in av.numbers_slices() {
6854 let (row_start, row_len, cols) = res.unwrap();
6855 let a = &cols[0];
6856 for i in 0..row_len {
6857 let idx = row_start + i;
6858 numeric[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
6859 }
6860 }
6861 assert_eq!(numeric[0], Some(1.0));
6862 assert_eq!(numeric[1], None); assert_eq!(numeric[2], Some(3.0));
6864 assert_eq!(numeric[3], Some(4.0));
6865 assert_eq!(numeric[4], Some(99.0));
6866 assert_eq!(numeric[5], Some(6.0));
6867
6868 let mut texts: Vec<Option<String>> = vec![None; 6];
6870 for res in av.text_slices() {
6871 let (row_start, row_len, cols) = res.unwrap();
6872 let a = cols[0].as_any().downcast_ref::<StringArray>().unwrap();
6873 for i in 0..row_len {
6874 let idx = row_start + i;
6875 texts[idx] = if a.is_null(i) {
6876 None
6877 } else {
6878 Some(a.value(i).to_string())
6879 };
6880 }
6881 }
6882 assert_eq!(texts[1].as_deref(), Some("X"));
6883 assert!(texts[0].is_none());
6884 assert!(texts[2].is_none());
6885 assert!(texts[3].is_none());
6886 assert!(texts[4].is_none());
6887 assert!(texts[5].is_none());
6888 }
6889
6890 #[test]
6891 fn effective_slices_overlay_precedence_booleans() {
6892 let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
6894 for i in 0..6 {
6895 let v = if i % 2 == 0 {
6896 LiteralValue::Boolean(true)
6897 } else {
6898 LiteralValue::Boolean(false)
6899 };
6900 b.append_row(&[v]).unwrap();
6901 }
6902 let mut sheet = b.finish();
6903 let (c1, o1) = sheet.chunk_of_row(1).unwrap();
6905 sheet.columns[0].chunks[c1]
6906 .overlay
6907 .set(o1, OverlayValue::Boolean(true));
6908 let (c2, o2) = sheet.chunk_of_row(2).unwrap();
6909 sheet.columns[0].chunks[c2]
6910 .overlay
6911 .set(o2, OverlayValue::Text(Arc::from("T")));
6912
6913 let av = sheet.range_view(0, 0, 5, 0);
6914 let mut bools: Vec<Option<bool>> = vec![None; 6];
6916 for res in av.booleans_slices() {
6917 let (row_start, row_len, cols) = res.unwrap();
6918 let a = &cols[0];
6919 for i in 0..row_len {
6920 let idx = row_start + i;
6921 bools[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
6922 }
6923 }
6924 assert_eq!(bools[0], Some(true));
6925 assert_eq!(bools[1], Some(true)); assert_eq!(bools[2], None); assert_eq!(bools[3], Some(false));
6929 }
6930
6931 #[test]
6932 fn effective_slices_overlay_precedence_errors() {
6933 let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
6935 for i in 0..6 {
6936 b.append_row(&[LiteralValue::Number((i + 1) as f64)])
6937 .unwrap();
6938 }
6939 let mut sheet = b.finish();
6940 let (c4, o4) = sheet.chunk_of_row(4).unwrap();
6942 sheet.columns[0].chunks[c4]
6943 .overlay
6944 .set(o4, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
6945
6946 let av = sheet.range_view(0, 0, 5, 0);
6947 let mut errs: Vec<Option<u8>> = vec![None; 6];
6948 for res in av.errors_slices() {
6949 let (row_start, row_len, cols) = res.unwrap();
6950 let a = &cols[0];
6951 for i in 0..row_len {
6952 let idx = row_start + i;
6953 errs[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
6954 }
6955 }
6956 assert_eq!(errs[4], Some(map_error_code(ExcelErrorKind::Div)));
6957 assert!(errs[3].is_none());
6958 }
6959}