1use std::any::Any;
5use std::ops::Range;
6use std::sync::Arc;
7
8use itertools::Itertools;
9use vortex_buffer::Alignment;
10use vortex_buffer::Buffer;
11use vortex_buffer::BufferMut;
12use vortex_buffer::ByteBuffer;
13use vortex_buffer::ByteBufferMut;
14use vortex_error::VortexExpect;
15use vortex_error::VortexResult;
16use vortex_error::vortex_bail;
17use vortex_error::vortex_ensure;
18use vortex_mask::Mask;
19use vortex_utils::aliases::hash_map::Entry;
20use vortex_utils::aliases::hash_map::HashMap;
21
22use crate::ArrayRef;
23use crate::ExecutionCtx;
24use crate::IntoArray;
25use crate::arrays::VarBinViewArray;
26use crate::arrays::varbinview::VarBinViewArrayExt;
27use crate::arrays::varbinview::build_views::BinaryView;
28use crate::arrays::varbinview::compact::BufferUtilization;
29use crate::builders::ArrayBuilder;
30use crate::builders::LazyBitBufferBuilder;
31use crate::canonical::Canonical;
32use crate::dtype::DType;
33use crate::scalar::Scalar;
34
35pub struct VarBinViewBuilder {
37 dtype: DType,
38 views_builder: BufferMut<BinaryView>,
39 nulls: LazyBitBufferBuilder,
40 completed: CompletedBuffers,
41 in_progress: Option<ByteBufferMut>,
42 growth_strategy: BufferGrowthStrategy,
43 compaction_threshold: f64,
44}
45
46impl VarBinViewBuilder {
47 pub fn with_capacity(dtype: DType, capacity: usize) -> Self {
48 Self::new(dtype, capacity, Default::default(), Default::default(), 0.0)
49 }
50
51 pub fn with_buffer_deduplication(dtype: DType, capacity: usize) -> Self {
52 Self::new(
53 dtype,
54 capacity,
55 CompletedBuffers::Deduplicated(Default::default()),
56 Default::default(),
57 0.0,
58 )
59 }
60
61 pub fn with_compaction(dtype: DType, capacity: usize, compaction_threshold: f64) -> Self {
62 Self::new(
63 dtype,
64 capacity,
65 Default::default(),
66 Default::default(),
67 compaction_threshold,
68 )
69 }
70
71 pub fn new(
72 dtype: DType,
73 capacity: usize,
74 completed: CompletedBuffers,
75 growth_strategy: BufferGrowthStrategy,
76 compaction_threshold: f64,
77 ) -> Self {
78 assert!(
79 matches!(dtype, DType::Utf8(_) | DType::Binary(_)),
80 "VarBinViewBuilder DType must be Utf8 or Binary."
81 );
82 Self {
83 views_builder: BufferMut::with_capacity_preferred_aligned(
84 capacity,
85 Alignment::of::<BinaryView>(),
86 None,
87 ),
88 nulls: LazyBitBufferBuilder::new(capacity),
89 completed,
90 in_progress: None,
91 dtype,
92 growth_strategy,
93 compaction_threshold,
94 }
95 }
96
97 fn append_value_view(&mut self, value: &[u8]) {
98 let length =
99 u32::try_from(value.len()).vortex_expect("cannot have a single string >2^32 in length");
100 if length <= 12 {
101 self.views_builder.push(BinaryView::make_view(value, 0, 0));
102 return;
103 }
104
105 let (buffer_idx, offset) = self.append_value_to_buffer(value);
106 let view = BinaryView::make_view(value, buffer_idx, offset);
107 self.views_builder.push(view);
108 }
109
110 pub fn append_value<S: AsRef<[u8]>>(&mut self, value: S) {
112 self.append_value_view(value.as_ref());
113 self.nulls.append_non_null();
114 }
115
116 pub fn append_n_values<S: AsRef<[u8]>>(&mut self, value: S, n: usize) {
118 if n == 0 {
119 return;
120 }
121 let bytes = value.as_ref();
122 let view = if bytes.len() <= BinaryView::MAX_INLINED_SIZE {
123 BinaryView::make_view(bytes, 0, 0)
124 } else {
125 let (buffer_idx, offset) = self.append_value_to_buffer(bytes);
126 BinaryView::make_view(bytes, buffer_idx, offset)
127 };
128 self.views_builder.push_n(view, n);
129 self.nulls.append_n_non_nulls(n);
130 }
131
132 fn flush_in_progress(&mut self) {
133 let Some(block) = self.in_progress.take() else {
134 return;
135 };
136
137 assert!(block.len() < u32::MAX as usize, "Block too large");
138
139 let initial_len = self.completed.len();
140 self.completed.push(block.freeze());
141 assert_eq!(
142 self.completed.len(),
143 initial_len + 1,
144 "Invalid state, just completed block already exists"
145 );
146 }
147
148 fn init_in_progress(&mut self, min_len: usize) {
149 let next_buffer_size = self.growth_strategy.next_size() as usize;
150 let to_reserve = next_buffer_size.max(min_len);
151 self.in_progress = Some(ByteBufferMut::with_capacity_preferred_aligned(
152 to_reserve,
153 Alignment::of::<u8>(),
154 None,
155 ));
156 }
157
158 fn append_value_to_buffer(&mut self, value: &[u8]) -> (u32, u32) {
160 assert!(
161 value.len() > BinaryView::MAX_INLINED_SIZE,
162 "must inline small strings"
163 );
164
165 if let Some(in_progress) = &mut self.in_progress {
166 let required_cap = in_progress.len() + value.len();
167 if in_progress.capacity() < required_cap {
168 self.flush_in_progress();
169 self.init_in_progress(value.len());
170 }
171 } else {
172 self.init_in_progress(value.len())
173 };
174
175 let in_progress = self
176 .in_progress
177 .as_mut()
178 .vortex_expect("in_progress just set");
179
180 let buffer_idx = self.completed.len();
181 let offset = u32::try_from(in_progress.len()).vortex_expect("too many buffers");
182 in_progress.extend_from_slice(value);
183
184 (buffer_idx, offset)
185 }
186
187 pub fn completed_block_count(&self) -> u32 {
188 self.completed.len()
189 }
190
191 pub fn in_progress(&self) -> bool {
194 self.in_progress.is_some()
195 }
196
197 pub fn push_buffer_and_adjusted_views(
214 &mut self,
215 buffers: &[ByteBuffer],
216 views: &Buffer<BinaryView>,
217 validity_mask: Mask,
218 ) {
219 self.flush_in_progress();
220
221 let expected_completed_len = self.completed.len() as usize + buffers.len();
222 self.completed.extend_from_slice_unchecked(buffers);
223 assert_eq!(
224 self.completed.len() as usize,
225 expected_completed_len,
226 "Some buffers already exist",
227 );
228 self.views_builder.extend_trusted(views.iter().copied());
229 self.push_only_validity_mask(&validity_mask);
230
231 debug_assert_eq!(self.nulls.len(), self.views_builder.len())
232 }
233
234 pub fn finish_into_varbinview(&mut self) -> VarBinViewArray {
236 self.flush_in_progress();
237 let buffers = std::mem::take(&mut self.completed);
238
239 assert_eq!(
240 self.views_builder.len(),
241 self.nulls.len(),
242 "View and validity length must match"
243 );
244
245 let validity = self.nulls.finish_with_nullability(self.dtype.nullability());
246
247 unsafe {
249 VarBinViewArray::new_unchecked(
250 std::mem::take(&mut self.views_builder).freeze(),
251 buffers.finish(),
252 self.dtype.clone(),
253 validity,
254 )
255 }
256 }
257
258 fn push_only_validity_mask(&mut self, validity_mask: &Mask) {
260 self.nulls.append_validity_mask(validity_mask);
261 }
262
263 pub(crate) fn append_varbinview_array(
264 &mut self,
265 array: &VarBinViewArray,
266 ctx: &mut ExecutionCtx,
267 ) -> VortexResult<()> {
268 self.flush_in_progress();
269
270 let mask = array.varbinview_validity().execute_mask(array.len(), ctx)?;
271
272 self.push_only_validity_mask(&mask);
273
274 let view_adjustment =
275 self.completed
276 .extend_from_compaction(BuffersWithOffsets::from_array(
277 array,
278 self.compaction_threshold,
279 ctx,
280 ));
281
282 match view_adjustment {
283 ViewAdjustment::Precomputed(adjustment) => self.views_builder.extend_trusted(
284 array
285 .views()
286 .iter()
287 .map(|view| adjustment.adjust_view(view)),
288 ),
289 ViewAdjustment::Rewriting(adjustment) => match mask {
290 Mask::AllTrue(_) => {
291 for (idx, &view) in array.views().iter().enumerate() {
292 let new_view = self.push_view(view, &adjustment, array, idx);
293 self.views_builder.push(new_view);
294 }
295 }
296 Mask::AllFalse(_) => {
297 self.views_builder
298 .push_n(BinaryView::empty_view(), array.len());
299 }
300 Mask::Values(v) => {
301 for (idx, (&view, is_valid)) in
302 array.views().iter().zip(v.bit_buffer().iter()).enumerate()
303 {
304 let new_view = if !is_valid {
305 BinaryView::empty_view()
306 } else {
307 self.push_view(view, &adjustment, array, idx)
308 };
309 self.views_builder.push(new_view);
310 }
311 }
312 },
313 }
314
315 Ok(())
316 }
317}
318
319impl ArrayBuilder for VarBinViewBuilder {
320 fn as_any(&self) -> &dyn Any {
321 self
322 }
323
324 fn as_any_mut(&mut self) -> &mut dyn Any {
325 self
326 }
327
328 fn dtype(&self) -> &DType {
329 &self.dtype
330 }
331
332 fn len(&self) -> usize {
333 self.nulls.len()
334 }
335
336 fn append_zeros(&mut self, n: usize) {
337 self.views_builder.push_n(BinaryView::empty_view(), n);
338 self.nulls.append_n_non_nulls(n);
339 }
340
341 unsafe fn append_nulls_unchecked(&mut self, n: usize) {
342 self.views_builder.push_n(BinaryView::empty_view(), n);
343 self.nulls.append_n_nulls(n);
344 }
345
346 fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
347 vortex_ensure!(
348 scalar.dtype() == self.dtype(),
349 "VarBinViewBuilder expected scalar with dtype {}, got {}",
350 self.dtype(),
351 scalar.dtype()
352 );
353
354 match self.dtype() {
355 DType::Utf8(_) => match scalar.as_utf8().value() {
356 Some(value) => self.append_value(value),
357 None => self.append_null(),
358 },
359 DType::Binary(_) => match scalar.as_binary().value() {
360 Some(value) => self.append_value(value),
361 None => self.append_null(),
362 },
363 _ => vortex_bail!(
364 "VarBinViewBuilder can only handle Utf8 or Binary scalars, got {:?}",
365 scalar.dtype()
366 ),
367 }
368
369 Ok(())
370 }
371
372 fn reserve_exact(&mut self, additional: usize) {
373 self.views_builder.reserve(additional);
374 self.nulls.reserve_exact(additional);
375 }
376
377 unsafe fn set_validity_unchecked(&mut self, validity: Mask) {
378 self.nulls = LazyBitBufferBuilder::from_validity_mask(validity);
379 }
380
381 fn finish(&mut self) -> ArrayRef {
382 self.finish_into_varbinview().into_array()
383 }
384
385 fn finish_into_canonical(&mut self, _ctx: &mut ExecutionCtx) -> Canonical {
386 Canonical::VarBinView(self.finish_into_varbinview())
387 }
388}
389
390impl VarBinViewBuilder {
391 #[inline]
392 fn push_view(
393 &mut self,
394 view: BinaryView,
395 adjustment: &RewritingViewAdjustment,
396 array: &VarBinViewArray,
397 idx: usize,
398 ) -> BinaryView {
399 if view.is_inlined() {
400 view
401 } else if let Some(adjusted) = adjustment.adjust_view(&view) {
402 adjusted
403 } else {
404 let bytes = array.bytes_at(idx);
405 let (new_buf_idx, new_offset) = self.append_value_to_buffer(&bytes);
406 BinaryView::make_view(bytes.as_slice(), new_buf_idx, new_offset)
407 }
408 }
409}
410
411pub enum CompletedBuffers {
412 Default(Vec<ByteBuffer>),
413 Deduplicated(DeduplicatedBuffers),
414}
415
416impl Default for CompletedBuffers {
417 fn default() -> Self {
418 Self::Default(Vec::new())
419 }
420}
421
422#[expect(clippy::cast_possible_truncation)]
424impl CompletedBuffers {
425 fn len(&self) -> u32 {
426 match self {
427 Self::Default(buffers) => buffers.len() as u32,
428 Self::Deduplicated(buffers) => buffers.len(),
429 }
430 }
431
432 fn push(&mut self, block: ByteBuffer) -> u32 {
433 match self {
434 Self::Default(buffers) => {
435 assert!(buffers.len() < u32::MAX as usize, "Too many blocks");
436 buffers.push(block);
437 self.len()
438 }
439 Self::Deduplicated(buffers) => buffers.push(block),
440 }
441 }
442
443 fn extend_from_slice_unchecked(&mut self, buffers: &[ByteBuffer]) {
445 for buffer in buffers {
446 self.push(buffer.clone());
447 }
448 }
449
450 fn extend_from_compaction(&mut self, buffers: BuffersWithOffsets) -> ViewAdjustment {
451 match (self, buffers) {
452 (
453 Self::Default(completed_buffers),
454 BuffersWithOffsets::AllKept { buffers, offsets },
455 ) => {
456 let buffer_offset = completed_buffers.len() as u32;
457 completed_buffers.extend_from_slice(&buffers);
458 ViewAdjustment::shift(buffer_offset, offsets)
459 }
460 (
461 Self::Default(completed_buffers),
462 BuffersWithOffsets::SomeCompacted { buffers, offsets },
463 ) => {
464 let lookup = buffers
465 .iter()
466 .map(|maybe_buffer| {
467 maybe_buffer.as_ref().map(|buffer| {
468 completed_buffers.push(buffer.clone());
469 completed_buffers.len() as u32 - 1
470 })
471 })
472 .collect();
473 ViewAdjustment::rewriting(lookup, offsets)
474 }
475
476 (
477 Self::Deduplicated(completed_buffers),
478 BuffersWithOffsets::AllKept { buffers, offsets },
479 ) => {
480 let buffer_lookup = completed_buffers.extend_from_iter(buffers.iter().cloned());
481 ViewAdjustment::lookup(buffer_lookup, offsets)
482 }
483 (
484 Self::Deduplicated(completed_buffers),
485 BuffersWithOffsets::SomeCompacted { buffers, offsets },
486 ) => {
487 let buffer_lookup = completed_buffers.extend_from_option_slice(&buffers);
488 ViewAdjustment::rewriting(buffer_lookup, offsets)
489 }
490 }
491 }
492
493 fn finish(self) -> Arc<[ByteBuffer]> {
494 match self {
495 Self::Default(buffers) => Arc::from(buffers),
496 Self::Deduplicated(buffers) => buffers.finish(),
497 }
498 }
499}
500
501#[derive(Default)]
502pub struct DeduplicatedBuffers {
503 buffers: Vec<ByteBuffer>,
504 buffer_to_idx: HashMap<BufferId, u32>,
505}
506
507impl DeduplicatedBuffers {
508 #[expect(clippy::cast_possible_truncation)]
510 fn len(&self) -> u32 {
511 self.buffers.len() as u32
512 }
513
514 pub(crate) fn push(&mut self, block: ByteBuffer) -> u32 {
516 assert!(self.buffers.len() < u32::MAX as usize, "Too many blocks");
517
518 let initial_len = self.len();
519 let id = BufferId::from(&block);
520 match self.buffer_to_idx.entry(id) {
521 Entry::Occupied(idx) => *idx.get(),
522 Entry::Vacant(entry) => {
523 let idx = initial_len;
524 entry.insert(idx);
525 self.buffers.push(block);
526 idx
527 }
528 }
529 }
530
531 pub(crate) fn extend_from_option_slice(
532 &mut self,
533 buffers: &[Option<ByteBuffer>],
534 ) -> Vec<Option<u32>> {
535 buffers
536 .iter()
537 .map(|buffer| buffer.as_ref().map(|buf| self.push(buf.clone())))
538 .collect()
539 }
540
541 pub(crate) fn extend_from_iter(
542 &mut self,
543 buffers: impl Iterator<Item = ByteBuffer>,
544 ) -> Vec<u32> {
545 buffers.map(|buffer| self.push(buffer)).collect()
546 }
547
548 pub(crate) fn finish(self) -> Arc<[ByteBuffer]> {
549 Arc::from(self.buffers)
550 }
551}
552
553#[derive(PartialEq, Eq, Hash)]
554struct BufferId {
555 ptr: usize,
557 len: usize,
558}
559
560impl BufferId {
561 fn from(buffer: &ByteBuffer) -> Self {
562 let slice = buffer.as_slice();
563 Self {
564 ptr: slice.as_ptr() as usize,
565 len: slice.len(),
566 }
567 }
568}
569
570#[derive(Debug, Clone)]
571pub enum BufferGrowthStrategy {
572 Fixed { size: u32 },
574 Exponential { current_size: u32, max_size: u32 },
576}
577
578impl Default for BufferGrowthStrategy {
579 fn default() -> Self {
580 Self::Exponential {
581 current_size: 4 * 1024, max_size: 2 * 1024 * 1024, }
584 }
585}
586
587impl BufferGrowthStrategy {
588 pub fn fixed(size: u32) -> Self {
589 Self::Fixed { size }
590 }
591
592 pub fn exponential(initial_size: u32, max_size: u32) -> Self {
593 Self::Exponential {
594 current_size: initial_size,
595 max_size,
596 }
597 }
598
599 pub fn next_size(&mut self) -> u32 {
601 match self {
602 Self::Fixed { size } => *size,
603 Self::Exponential {
604 current_size,
605 max_size,
606 } => {
607 let result = *current_size;
608 if *current_size < *max_size {
609 *current_size = current_size.saturating_mul(2).min(*max_size);
610 }
611 result
612 }
613 }
614 }
615}
616
617enum BuffersWithOffsets {
618 AllKept {
619 buffers: Arc<[ByteBuffer]>,
620 offsets: Option<Vec<u32>>,
621 },
622 SomeCompacted {
623 buffers: Vec<Option<ByteBuffer>>,
624 offsets: Option<Vec<u32>>,
625 },
626}
627
628impl BuffersWithOffsets {
629 pub fn from_array(
630 array: &VarBinViewArray,
631 compaction_threshold: f64,
632 ctx: &mut ExecutionCtx,
633 ) -> Self {
634 if compaction_threshold == 0.0 {
635 return Self::AllKept {
636 buffers: Arc::from(
637 array
638 .data_buffers()
639 .iter()
640 .cloned()
641 .map(|b| b.unwrap_host())
642 .collect_vec(),
643 ),
644 offsets: None,
645 };
646 }
647
648 let buffer_utilizations = array
649 .buffer_utilizations(ctx)
650 .vortex_expect("buffer_utilizations in BuffersWithOffsets::from_array");
651 let mut has_rewrite = false;
652 let mut has_nonzero_offset = false;
653 for utilization in buffer_utilizations.iter() {
654 match compaction_strategy(utilization, compaction_threshold) {
655 CompactionStrategy::KeepFull => continue,
656 CompactionStrategy::Slice { .. } => has_nonzero_offset = true,
657 CompactionStrategy::Rewrite => has_rewrite = true,
658 }
659 }
660
661 let buffers_with_offsets_iter = buffer_utilizations
662 .iter()
663 .zip(array.data_buffers().iter())
664 .map(|(utilization, buffer)| {
665 match compaction_strategy(utilization, compaction_threshold) {
666 CompactionStrategy::KeepFull => (Some(buffer.as_host().clone()), 0),
667 CompactionStrategy::Slice { start, end } => (
668 Some(buffer.as_host().slice(start as usize..end as usize)),
669 start,
670 ),
671 CompactionStrategy::Rewrite => (None, 0),
672 }
673 });
674
675 match (has_rewrite, has_nonzero_offset) {
676 (false, false) => {
678 let buffers: Vec<_> = buffers_with_offsets_iter
679 .map(|(b, _)| b.vortex_expect("already checked for rewrite"))
680 .collect();
681 Self::AllKept {
682 buffers: Arc::from(buffers),
683 offsets: None,
684 }
685 }
686 (true, false) => {
688 let buffers: Vec<_> = buffers_with_offsets_iter.map(|(b, _)| b).collect();
689 Self::SomeCompacted {
690 buffers,
691 offsets: None,
692 }
693 }
694 (false, true) => {
696 let (buffers, offsets): (Vec<_>, _) = buffers_with_offsets_iter
697 .map(|(buffer, offset)| {
698 (buffer.vortex_expect("already checked for rewrite"), offset)
699 })
700 .collect();
701 Self::AllKept {
702 buffers: Arc::from(buffers),
703 offsets: Some(offsets),
704 }
705 }
706 (true, true) => {
708 let (buffers, offsets) = buffers_with_offsets_iter.collect();
709 Self::SomeCompacted {
710 buffers,
711 offsets: Some(offsets),
712 }
713 }
714 }
715 }
716}
717
718#[derive(Debug, Clone, Copy, PartialEq, Eq)]
719enum CompactionStrategy {
720 KeepFull,
721 Slice {
723 start: u32,
724 end: u32,
725 },
726 Rewrite,
728}
729
730fn compaction_strategy(
731 buffer_utilization: &BufferUtilization,
732 threshold: f64,
733) -> CompactionStrategy {
734 match buffer_utilization.overall_utilization() {
735 0.0 => CompactionStrategy::Rewrite,
737 utilised if utilised >= threshold => CompactionStrategy::KeepFull,
738 _ if buffer_utilization.range_utilization() >= threshold => {
739 let Range { start, end } = buffer_utilization.range();
740 CompactionStrategy::Slice { start, end }
741 }
742 _ => CompactionStrategy::Rewrite,
743 }
744}
745
746enum ViewAdjustment {
747 Precomputed(PrecomputedViewAdjustment),
748 Rewriting(RewritingViewAdjustment),
749}
750
751impl ViewAdjustment {
752 fn shift(buffer_offset: u32, offsets: Option<Vec<u32>>) -> Self {
753 Self::Precomputed(PrecomputedViewAdjustment::Shift {
754 buffer_offset,
755 offsets,
756 })
757 }
758
759 fn lookup(buffer_lookup: Vec<u32>, offsets: Option<Vec<u32>>) -> Self {
760 Self::Precomputed(PrecomputedViewAdjustment::Lookup {
761 buffer_lookup,
762 offsets,
763 })
764 }
765
766 fn rewriting(buffer_lookup: Vec<Option<u32>>, offsets: Option<Vec<u32>>) -> Self {
767 Self::Rewriting(RewritingViewAdjustment {
768 buffer_lookup,
769 offsets,
770 })
771 }
772}
773
774enum PrecomputedViewAdjustment {
776 Shift {
777 buffer_offset: u32,
778 offsets: Option<Vec<u32>>,
779 },
780 Lookup {
781 buffer_lookup: Vec<u32>,
782 offsets: Option<Vec<u32>>,
783 },
784}
785
786impl PrecomputedViewAdjustment {
787 #[inline]
788 fn adjust_view(&self, view: &BinaryView) -> BinaryView {
789 if view.is_inlined() {
790 return *view;
791 }
792 let view_ref = view.as_view();
793 match self {
794 Self::Shift {
795 buffer_offset,
796 offsets,
797 } => {
798 let b_idx = view_ref.buffer_index;
799 let offset_shift = offsets
800 .as_ref()
801 .map(|o| o[b_idx as usize])
802 .unwrap_or_default();
803
804 if view_ref.offset < offset_shift {
807 return BinaryView::empty_view();
808 }
809
810 view_ref
811 .with_buffer_and_offset(b_idx + buffer_offset, view_ref.offset - offset_shift)
812 }
813 Self::Lookup {
814 buffer_lookup,
815 offsets,
816 } => {
817 let b_idx = view_ref.buffer_index;
818 let buffer = buffer_lookup[b_idx as usize];
819 let offset_shift = offsets
820 .as_ref()
821 .map(|o| o[b_idx as usize])
822 .unwrap_or_default();
823
824 if view_ref.offset < offset_shift {
827 return BinaryView::empty_view();
828 }
829
830 view_ref.with_buffer_and_offset(buffer, view_ref.offset - offset_shift)
831 }
832 }
833 .into()
834 }
835}
836
837struct RewritingViewAdjustment {
838 buffer_lookup: Vec<Option<u32>>,
839 offsets: Option<Vec<u32>>,
840}
841
842impl RewritingViewAdjustment {
843 #[inline]
846 fn adjust_view(&self, view: &BinaryView) -> Option<BinaryView> {
847 if view.is_inlined() {
848 return Some(*view);
849 }
850
851 let view_ref = view.as_view();
852 self.buffer_lookup[view_ref.buffer_index as usize].map(|buffer| {
853 let offset_shift = self
854 .offsets
855 .as_ref()
856 .map(|o| o[view_ref.buffer_index as usize])
857 .unwrap_or_default();
858 view_ref
859 .with_buffer_and_offset(buffer, view_ref.offset - offset_shift)
860 .into()
861 })
862 }
863}
864
865#[cfg(test)]
866mod tests {
867 use vortex_error::VortexResult;
868
869 use crate::IntoArray;
870 use crate::VortexSessionExecute;
871 use crate::array_session;
872 use crate::assert_arrays_eq;
873 use crate::builders::ArrayBuilder;
874 use crate::builders::VarBinViewBuilder;
875 use crate::builders::varbinview::VarBinViewArray;
876 use crate::dtype::DType;
877 use crate::dtype::Nullability;
878
879 #[test]
880 fn test_utf8_builder() {
881 let mut ctx = array_session().create_execution_ctx();
882 let mut builder = VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
883
884 builder.append_value("Hello");
885 builder.append_null();
886 builder.append_value("World");
887
888 builder.append_nulls(2);
889
890 builder.append_zeros(2);
891 builder.append_value("test");
892
893 let actual = builder.finish();
894 let expected = <VarBinViewArray as FromIterator<_>>::from_iter([
895 Some("Hello"),
896 None,
897 Some("World"),
898 None,
899 None,
900 Some(""),
901 Some(""),
902 Some("test"),
903 ]);
904 assert_arrays_eq!(actual, expected, &mut ctx);
905 }
906
907 #[test]
908 fn test_utf8_builder_with_extend() {
909 let mut ctx = array_session().create_execution_ctx();
910 let array = {
911 let mut builder =
912 VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
913 builder.append_null();
914 builder.append_value("Hello2");
915 builder.finish()
916 };
917 let mut builder = VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
918
919 builder.append_value("Hello1");
920 array.append_to_builder(&mut builder, &mut ctx).unwrap();
921 builder.append_nulls(2);
922 builder.append_value("Hello3");
923
924 let actual = builder.finish_into_canonical(&mut ctx);
925 let expected = <VarBinViewArray as FromIterator<_>>::from_iter([
926 Some("Hello1"),
927 None,
928 Some("Hello2"),
929 None,
930 None,
931 Some("Hello3"),
932 ]);
933 assert_arrays_eq!(actual.into_array(), expected.into_array(), &mut ctx);
934 }
935
936 #[test]
937 fn test_buffer_deduplication() -> VortexResult<()> {
938 let array = {
939 let mut builder =
940 VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
941 builder.append_value("This is a long string that should not be inlined");
942 builder.append_value("short string");
943 builder.finish_into_varbinview()
944 };
945
946 assert_eq!(array.data_buffers().len(), 1);
947 let mut builder =
948 VarBinViewBuilder::with_buffer_deduplication(DType::Utf8(Nullability::Nullable), 10);
949
950 let mut ctx = array_session().create_execution_ctx();
951
952 array.append_to_builder(&mut builder, &mut ctx)?;
953 assert_eq!(builder.completed_block_count(), 1);
954
955 array
956 .slice(1..2)?
957 .append_to_builder(&mut builder, &mut ctx)?;
958 array
959 .slice(0..1)?
960 .append_to_builder(&mut builder, &mut ctx)?;
961 assert_eq!(builder.completed_block_count(), 1);
962
963 let array2 = {
964 let mut builder =
965 VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
966 builder.append_value("This is a long string that should not be inlined");
967 builder.finish_into_varbinview()
968 };
969
970 array2.append_to_builder(&mut builder, &mut ctx)?;
971 assert_eq!(builder.completed_block_count(), 2);
972
973 array
974 .slice(0..1)?
975 .append_to_builder(&mut builder, &mut ctx)?;
976 array2
977 .slice(0..1)?
978 .append_to_builder(&mut builder, &mut ctx)?;
979 assert_eq!(builder.completed_block_count(), 2);
980 Ok(())
981 }
982
983 #[test]
984 fn test_append_scalar() {
985 let mut ctx = array_session().create_execution_ctx();
986 use crate::scalar::Scalar;
987
988 let mut utf8_builder =
990 VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::Nullable), 10);
991
992 let utf8_scalar1 = Scalar::utf8("hello", Nullability::Nullable);
994 utf8_builder.append_scalar(&utf8_scalar1).unwrap();
995
996 let utf8_scalar2 = Scalar::utf8("world", Nullability::Nullable);
998 utf8_builder.append_scalar(&utf8_scalar2).unwrap();
999
1000 let null_scalar = Scalar::null(DType::Utf8(Nullability::Nullable));
1002 utf8_builder.append_scalar(&null_scalar).unwrap();
1003
1004 let array = utf8_builder.finish();
1005 let expected =
1006 <VarBinViewArray as FromIterator<_>>::from_iter([Some("hello"), Some("world"), None]);
1007 assert_arrays_eq!(&array, &expected, &mut ctx);
1008
1009 let mut binary_builder =
1011 VarBinViewBuilder::with_capacity(DType::Binary(Nullability::Nullable), 10);
1012
1013 let binary_scalar = Scalar::binary(vec![1u8, 2, 3], Nullability::Nullable);
1014 binary_builder.append_scalar(&binary_scalar).unwrap();
1015
1016 let binary_null = Scalar::null(DType::Binary(Nullability::Nullable));
1017 binary_builder.append_scalar(&binary_null).unwrap();
1018
1019 let binary_array = binary_builder.finish();
1020 let expected =
1021 <VarBinViewArray as FromIterator<_>>::from_iter([Some(vec![1u8, 2, 3]), None]);
1022 assert_arrays_eq!(&binary_array, &expected, &mut ctx);
1023
1024 let mut builder =
1026 VarBinViewBuilder::with_capacity(DType::Utf8(Nullability::NonNullable), 10);
1027 let wrong_scalar = Scalar::from(42i32);
1028 assert!(builder.append_scalar(&wrong_scalar).is_err());
1029 }
1030
1031 #[test]
1032 fn test_buffer_growth_strategies() {
1033 use super::BufferGrowthStrategy;
1034
1035 let mut strategy = BufferGrowthStrategy::fixed(1024);
1037
1038 assert_eq!(strategy.next_size(), 1024);
1040 assert_eq!(strategy.next_size(), 1024);
1041 assert_eq!(strategy.next_size(), 1024);
1042
1043 let mut strategy = BufferGrowthStrategy::exponential(1024, 8192);
1045
1046 assert_eq!(strategy.next_size(), 1024); assert_eq!(strategy.next_size(), 2048); assert_eq!(strategy.next_size(), 4096); assert_eq!(strategy.next_size(), 8192); assert_eq!(strategy.next_size(), 8192); }
1053
1054 #[test]
1055 fn test_large_value_allocation() {
1056 use super::BufferGrowthStrategy;
1057 use super::VarBinViewBuilder;
1058
1059 let mut builder = VarBinViewBuilder::new(
1060 DType::Binary(Nullability::Nullable),
1061 10,
1062 Default::default(),
1063 BufferGrowthStrategy::exponential(1024, 4096),
1064 0.0,
1065 );
1066
1067 let large_value = vec![0u8; 8192];
1069
1070 builder.append_value(&large_value);
1072
1073 let array = builder.finish_into_varbinview();
1074 assert_eq!(array.len(), 1);
1075
1076 let retrieved = array
1078 .execute_scalar(0, &mut array_session().create_execution_ctx())
1079 .unwrap()
1080 .as_binary()
1081 .value()
1082 .cloned()
1083 .unwrap();
1084 assert_eq!(retrieved.len(), 8192);
1085 assert_eq!(retrieved.as_slice(), &large_value);
1086 }
1087}