1use std::any::Any;
19use std::marker::PhantomData;
20use std::sync::Arc;
21
22use arrow_buffer::{Buffer, NullBufferBuilder, ScalarBuffer};
23use arrow_data::{ByteView, MAX_INLINE_VIEW_LEN};
24use arrow_schema::ArrowError;
25use hashbrown::HashTable;
26use hashbrown::hash_table::Entry;
27
28use crate::builder::{ArrayBuilder, BinaryLikeArrayBuilder, StringLikeArrayBuilder};
29use crate::types::bytes::ByteArrayNativeType;
30use crate::types::{BinaryViewType, ByteViewType, StringViewType};
31use crate::{Array, ArrayRef, GenericByteViewArray};
32
33const STARTING_BLOCK_SIZE: u32 = 8 * 1024; const MAX_BLOCK_SIZE: u32 = 2 * 1024 * 1024; enum BlockSizeGrowthStrategy {
37 Fixed { size: u32 },
38 Exponential { current_size: u32 },
39}
40
41impl BlockSizeGrowthStrategy {
42 fn next_size(&mut self) -> u32 {
43 match self {
44 Self::Fixed { size } => *size,
45 Self::Exponential { current_size } => {
46 if *current_size < MAX_BLOCK_SIZE {
47 *current_size = current_size.saturating_mul(2);
49 *current_size
50 } else {
51 MAX_BLOCK_SIZE
52 }
53 }
54 }
55 }
56}
57
58pub struct GenericByteViewBuilder<T: ByteViewType + ?Sized> {
82 views_buffer: Vec<u128>,
83 null_buffer_builder: NullBufferBuilder,
84 completed: Vec<Buffer>,
85 in_progress: Vec<u8>,
86 block_size: BlockSizeGrowthStrategy,
87 string_tracker: Option<(HashTable<usize>, ahash::RandomState)>,
90 max_deduplication_len: Option<u32>,
91 phantom: PhantomData<T>,
92}
93
94impl<T: ByteViewType + ?Sized> GenericByteViewBuilder<T> {
95 pub fn new() -> Self {
97 Self::with_capacity(1024)
98 }
99
100 pub fn with_capacity(capacity: usize) -> Self {
102 Self {
103 views_buffer: Vec::with_capacity(capacity),
104 null_buffer_builder: NullBufferBuilder::new(capacity),
105 completed: vec![],
106 in_progress: vec![],
107 block_size: BlockSizeGrowthStrategy::Exponential {
108 current_size: STARTING_BLOCK_SIZE,
109 },
110 string_tracker: None,
111 max_deduplication_len: None,
112 phantom: Default::default(),
113 }
114 }
115
116 pub fn with_max_deduplication_len(self, max_deduplication_len: u32) -> Self {
123 debug_assert!(
124 max_deduplication_len > 0,
125 "max_deduplication_len must be greater than 0"
126 );
127 Self {
128 max_deduplication_len: Some(max_deduplication_len),
129 ..self
130 }
131 }
132
133 pub fn with_fixed_block_size(self, block_size: u32) -> Self {
149 debug_assert!(block_size > 0, "Block size must be greater than 0");
150 Self {
151 block_size: BlockSizeGrowthStrategy::Fixed { size: block_size },
152 ..self
153 }
154 }
155
156 pub fn with_deduplicate_strings(self) -> Self {
161 Self {
162 string_tracker: Some((
163 HashTable::with_capacity(self.views_buffer.capacity()),
164 Default::default(),
165 )),
166 ..self
167 }
168 }
169
170 pub fn append_block(&mut self, buffer: Buffer) -> u32 {
199 assert!(buffer.len() < u32::MAX as usize);
200
201 self.flush_in_progress();
202 let offset = self.completed.len();
203 self.push_completed(buffer);
204 offset as u32
205 }
206
207 pub unsafe fn append_view_unchecked(&mut self, block: u32, offset: u32, len: u32) {
214 let b = unsafe { self.completed.get_unchecked(block as usize) };
215 let start = offset as usize;
216 let end = start.saturating_add(len as usize);
217 let b = unsafe { b.get_unchecked(start..end) };
218
219 let view = make_view(b, block, offset);
220 self.views_buffer.push(view);
221 self.null_buffer_builder.append_non_null();
222 }
223
224 pub fn append_array(&mut self, array: &GenericByteViewArray<T>) {
228 self.flush_in_progress();
229 let keep_views = self.completed.is_empty() || array.data_buffers().is_empty();
231 let starting_buffer = self.completed.len() as u32;
232
233 self.completed.extend(array.data_buffers().iter().cloned());
234
235 if keep_views {
236 self.views_buffer.extend_from_slice(array.views());
237 } else {
238 self.views_buffer.extend(array.views().iter().map(|v| {
239 let mut byte_view = ByteView::from(*v);
240 if byte_view.length > MAX_INLINE_VIEW_LEN {
241 byte_view.buffer_index += starting_buffer;
243 }
244
245 byte_view.as_u128()
246 }));
247 }
248
249 if let Some(null_buffer) = array.nulls() {
250 self.null_buffer_builder.append_buffer(null_buffer);
251 } else {
252 self.null_buffer_builder.append_n_non_nulls(array.len());
253 }
254 }
255
256 pub fn try_append_view(&mut self, block: u32, offset: u32, len: u32) -> Result<(), ArrowError> {
260 let b = self.completed.get(block as usize).ok_or_else(|| {
261 ArrowError::InvalidArgumentError(format!("No block found with index {block}"))
262 })?;
263 let start = offset as usize;
264 let end = start.saturating_add(len as usize);
265
266 let b = b.get(start..end).ok_or_else(|| {
267 ArrowError::InvalidArgumentError(format!(
268 "Range {start}..{end} out of bounds for block of length {}",
269 b.len()
270 ))
271 })?;
272
273 if T::Native::from_bytes_checked(b).is_none() {
274 return Err(ArrowError::InvalidArgumentError(
275 "Invalid view data".to_string(),
276 ));
277 }
278
279 unsafe {
280 self.append_view_unchecked(block, offset, len);
281 }
282 Ok(())
283 }
284
285 #[inline]
287 fn flush_in_progress(&mut self) {
288 if !self.in_progress.is_empty() {
289 let f = Buffer::from_vec(std::mem::take(&mut self.in_progress));
290 self.push_completed(f)
291 }
292 }
293
294 #[inline]
296 fn push_completed(&mut self, block: Buffer) {
297 assert!(block.len() < u32::MAX as usize, "Block too large");
298 assert!(self.completed.len() < u32::MAX as usize, "Too many blocks");
299 self.completed.push(block);
300 }
301
302 pub fn get_value(&self, index: usize) -> &[u8] {
310 let view = self.views_buffer.as_slice().get(index).unwrap();
311 let len = *view as u32;
312 if len <= MAX_INLINE_VIEW_LEN {
313 unsafe { GenericByteViewArray::<T>::inline_value(view, len as usize) }
316 } else {
317 let view = ByteView::from(*view);
318 if view.buffer_index < self.completed.len() as u32 {
319 let block = &self.completed[view.buffer_index as usize];
320 &block[view.offset as usize..view.offset as usize + view.length as usize]
321 } else {
322 &self.in_progress[view.offset as usize..view.offset as usize + view.length as usize]
323 }
324 }
325 }
326
327 #[inline]
335 pub fn append_value(&mut self, value: impl AsRef<T::Native>) {
336 self.try_append_value(value).unwrap()
337 }
338
339 #[inline]
347 pub fn try_append_value(&mut self, value: impl AsRef<T::Native>) -> Result<(), ArrowError> {
348 let v: &[u8] = value.as_ref().as_ref();
349 let length: u32 = v.len().try_into().map_err(|_| {
350 ArrowError::InvalidArgumentError(format!("String length {} exceeds u32::MAX", v.len()))
351 })?;
352
353 let prefix = match v.first_chunk::<4>() {
356 Some(prefix) if length > MAX_INLINE_VIEW_LEN => u32::from_le_bytes(*prefix),
357 _ => {
358 let mut view_buffer = [0; 16];
359 view_buffer[0..4].copy_from_slice(&length.to_le_bytes());
360 view_buffer[4..4 + v.len()].copy_from_slice(v);
361 self.views_buffer.push(u128::from_le_bytes(view_buffer));
362 self.null_buffer_builder.append_non_null();
363 return Ok(());
364 }
365 };
366
367 let can_deduplicate = self.string_tracker.is_some()
371 && self
372 .max_deduplication_len
373 .map(|max_length| length <= max_length)
374 .unwrap_or(true);
375 if can_deduplicate && let Some((mut ht, hasher)) = self.string_tracker.take() {
376 let hash_val = hasher.hash_one(v);
377 let hasher_fn = |v: &_| hasher.hash_one(v);
378
379 let entry = ht.entry(
380 hash_val,
381 |idx| {
382 let stored_value = self.get_value(*idx);
383 v == stored_value
384 },
385 hasher_fn,
386 );
387 match entry {
388 Entry::Occupied(occupied) => {
389 let idx = occupied.get();
391 self.views_buffer.push(self.views_buffer[*idx]);
392 self.null_buffer_builder.append_non_null();
393 self.string_tracker = Some((ht, hasher));
394 return Ok(());
395 }
396 Entry::Vacant(vacant) => {
397 vacant.insert(self.views_buffer.len());
400 }
401 }
402 self.string_tracker = Some((ht, hasher));
403 }
404
405 let required_cap = self.in_progress.len() + v.len();
406 if self.in_progress.capacity() < required_cap {
407 self.flush_in_progress();
408 let to_reserve = v.len().max(self.block_size.next_size() as usize);
409 self.in_progress.reserve(to_reserve);
410 }
411
412 let offset = self.in_progress.len() as u32;
413 self.in_progress.extend_from_slice(v);
414
415 let buffer_index: u32 = self.completed.len().try_into().map_err(|_| {
416 ArrowError::InvalidArgumentError(format!(
417 "Buffer count {} exceeds u32::MAX",
418 self.completed.len()
419 ))
420 })?;
421
422 let view = ByteView {
423 length,
424 prefix,
425 buffer_index,
426 offset,
427 };
428 self.views_buffer.push(view.into());
429 self.null_buffer_builder.append_non_null();
430
431 Ok(())
432 }
433
434 #[inline]
436 pub fn append_option(&mut self, value: Option<impl AsRef<T::Native>>) {
437 match value {
438 None => self.append_null(),
439 Some(v) => self.append_value(v),
440 }
441 }
442
443 #[inline]
473 pub fn try_append_value_n(
474 &mut self,
475 value: impl AsRef<T::Native>,
476 n: usize,
477 ) -> Result<(), ArrowError> {
478 if n == 0 {
479 return Ok(());
480 }
481 self.try_append_value(value)?;
483 let view = *self.views_buffer.last().unwrap();
485 self.views_buffer.extend(std::iter::repeat_n(view, n - 1));
486 self.null_buffer_builder.append_n_non_nulls(n - 1);
487 Ok(())
488 }
489
490 #[inline]
492 pub fn append_null(&mut self) {
493 self.null_buffer_builder.append_null();
494 self.views_buffer.push(0);
495 }
496
497 pub fn finish(&mut self) -> GenericByteViewArray<T> {
499 self.flush_in_progress();
500 let completed = std::mem::take(&mut self.completed);
501 let nulls = self.null_buffer_builder.finish();
502 if let Some((ht, _)) = self.string_tracker.as_mut() {
503 ht.clear();
504 }
505 let views = std::mem::take(&mut self.views_buffer);
506 unsafe { GenericByteViewArray::new_unchecked(views.into(), completed.into(), nulls) }
508 }
509
510 pub fn finish_cloned(&self) -> GenericByteViewArray<T> {
512 let mut completed = self.completed.clone();
513 if !self.in_progress.is_empty() {
514 completed.push(Buffer::from_slice_ref(&self.in_progress));
515 }
516 let len = self.views_buffer.len();
517 let views = Buffer::from_slice_ref(self.views_buffer.as_slice());
518 let views = ScalarBuffer::new(views, 0, len);
519 let nulls = self.null_buffer_builder.finish_cloned();
520 unsafe { GenericByteViewArray::new_unchecked(views, completed.into(), nulls) }
522 }
523
524 pub fn validity_slice(&self) -> Option<&[u8]> {
526 self.null_buffer_builder.as_slice()
527 }
528
529 pub fn allocated_size(&self) -> usize {
531 let views = self.views_buffer.capacity() * std::mem::size_of::<u128>();
532 let null = self.null_buffer_builder.allocated_size();
533 let buffer_size = self.completed.iter().map(|b| b.capacity()).sum::<usize>();
534 let in_progress = self.in_progress.capacity();
535 let tracker = match &self.string_tracker {
536 Some((ht, _)) => ht.capacity() * std::mem::size_of::<usize>(),
537 None => 0,
538 };
539 buffer_size + in_progress + tracker + views + null
540 }
541}
542
543impl<T: ByteViewType + ?Sized> Default for GenericByteViewBuilder<T> {
544 fn default() -> Self {
545 Self::new()
546 }
547}
548
549impl<T: ByteViewType + ?Sized> std::fmt::Debug for GenericByteViewBuilder<T> {
550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
551 write!(f, "{}ViewBuilder", T::PREFIX)?;
552 f.debug_struct("")
553 .field("views_buffer", &self.views_buffer)
554 .field("in_progress", &self.in_progress)
555 .field("completed", &self.completed)
556 .field("null_buffer_builder", &self.null_buffer_builder)
557 .finish()
558 }
559}
560
561impl<T: ByteViewType + ?Sized> ArrayBuilder for GenericByteViewBuilder<T> {
562 fn len(&self) -> usize {
563 self.null_buffer_builder.len()
564 }
565
566 fn finish(&mut self) -> ArrayRef {
567 Arc::new(self.finish())
568 }
569
570 fn finish_cloned(&self) -> ArrayRef {
571 Arc::new(self.finish_cloned())
572 }
573
574 fn as_any(&self) -> &dyn Any {
575 self
576 }
577
578 fn as_any_mut(&mut self) -> &mut dyn Any {
579 self
580 }
581
582 fn into_box_any(self: Box<Self>) -> Box<dyn Any> {
583 self
584 }
585}
586
587impl<T: ByteViewType + ?Sized, V: AsRef<T::Native>> Extend<Option<V>>
588 for GenericByteViewBuilder<T>
589{
590 #[inline]
591 fn extend<I: IntoIterator<Item = Option<V>>>(&mut self, iter: I) {
592 for v in iter {
593 self.append_option(v)
594 }
595 }
596}
597
598pub type StringViewBuilder = GenericByteViewBuilder<StringViewType>;
618
619impl StringLikeArrayBuilder for StringViewBuilder {
620 fn type_name() -> &'static str {
621 std::any::type_name::<StringViewBuilder>()
622 }
623 fn with_capacity(capacity: usize) -> Self {
624 Self::with_capacity(capacity)
625 }
626 fn append_value(&mut self, value: &str) {
627 Self::append_value(self, value);
628 }
629 fn append_null(&mut self) {
630 Self::append_null(self);
631 }
632}
633
634pub type BinaryViewBuilder = GenericByteViewBuilder<BinaryViewType>;
655
656impl BinaryLikeArrayBuilder for BinaryViewBuilder {
657 fn type_name() -> &'static str {
658 std::any::type_name::<BinaryViewBuilder>()
659 }
660 fn with_capacity(capacity: usize) -> Self {
661 Self::with_capacity(capacity)
662 }
663 fn append_value(&mut self, value: &[u8]) {
664 Self::append_value(self, value);
665 }
666 fn append_null(&mut self) {
667 Self::append_null(self);
668 }
669}
670
671fn make_inlined_view<const LEN: usize>(data: &[u8]) -> u128 {
674 let mut view_buffer = [0; 16];
675 view_buffer[0..4].copy_from_slice(&(LEN as u32).to_le_bytes());
676 view_buffer[4..4 + LEN].copy_from_slice(&data[..LEN]);
677 u128::from_le_bytes(view_buffer)
678}
679
680#[inline(never)]
686pub fn make_view(data: &[u8], block_id: u32, offset: u32) -> u128 {
687 let len = data.len();
688
689 match len {
692 0 => make_inlined_view::<0>(data),
693 1 => make_inlined_view::<1>(data),
694 2 => make_inlined_view::<2>(data),
695 3 => make_inlined_view::<3>(data),
696 4 => make_inlined_view::<4>(data),
697 5 => make_inlined_view::<5>(data),
698 6 => make_inlined_view::<6>(data),
699 7 => make_inlined_view::<7>(data),
700 8 => make_inlined_view::<8>(data),
701 9 => make_inlined_view::<9>(data),
702 10 => make_inlined_view::<10>(data),
703 11 => make_inlined_view::<11>(data),
704 12 => make_inlined_view::<12>(data),
705 _ => {
707 let view = ByteView {
708 length: len as u32,
709 prefix: u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
711 buffer_index: block_id,
712 offset,
713 };
714 view.as_u128()
715 }
716 }
717}
718
719#[cfg(test)]
720mod tests {
721 use core::str;
722
723 use arrow_buffer::ArrowNativeType;
724
725 use super::*;
726
727 #[test]
728 fn test_string_max_deduplication_len() {
729 let value_1 = "short";
730 let value_2 = "not so similar string but long";
731 let value_3 = "1234567890123";
732
733 let max_deduplication_len = MAX_INLINE_VIEW_LEN * 2;
734
735 let mut builder = StringViewBuilder::new()
736 .with_deduplicate_strings()
737 .with_max_deduplication_len(max_deduplication_len);
738
739 assert!(value_1.len() < MAX_INLINE_VIEW_LEN.as_usize());
740 assert!(value_2.len() > max_deduplication_len.as_usize());
741 assert!(
742 value_3.len() > MAX_INLINE_VIEW_LEN.as_usize()
743 && value_3.len() < max_deduplication_len.as_usize()
744 );
745
746 builder.append_value(value_1); builder.append_value(value_1); builder.append_value(value_2); builder.append_value(value_2); builder.append_value(value_3); builder.append_value(value_3); let array = builder.finish();
757
758 let v2 = ByteView::from(array.views()[2]);
760 let v3 = ByteView::from(array.views()[3]);
761 assert_eq!(v2.buffer_index, v3.buffer_index); assert_ne!(v2.offset, v3.offset); let v4 = ByteView::from(array.views()[4]);
765 let v5 = ByteView::from(array.views()[5]);
766 assert_eq!(v4.buffer_index, v5.buffer_index); assert_eq!(v4.offset, v5.offset); }
769
770 #[test]
771 fn test_string_view_deduplicate() {
772 let value_1 = "long string to test string view";
773 let value_2 = "not so similar string but long";
774
775 let mut builder = StringViewBuilder::new()
776 .with_deduplicate_strings()
777 .with_fixed_block_size(value_1.len() as u32 * 2); let values = vec![
780 Some(value_1),
781 Some(value_2),
782 Some("short"),
783 Some(value_1),
784 None,
785 Some(value_2),
786 Some(value_1),
787 ];
788 builder.extend(values.clone());
789
790 let array = builder.finish_cloned();
791 array.to_data().validate_full().unwrap();
792 assert_eq!(array.data_buffers().len(), 1); let actual: Vec<_> = array.iter().collect();
794 assert_eq!(actual, values);
795
796 let view0 = array.views().first().unwrap();
797 let view3 = array.views().get(3).unwrap();
798 let view6 = array.views().get(6).unwrap();
799
800 assert_eq!(view0, view3);
801 assert_eq!(view0, view6);
802
803 assert_eq!(array.views().get(1), array.views().get(5));
804 }
805
806 #[test]
807 fn test_string_view_deduplicate_after_finish() {
808 let mut builder = StringViewBuilder::new().with_deduplicate_strings();
809
810 let value_1 = "long string to test string view";
811 let value_2 = "not so similar string but long";
812 builder.append_value(value_1);
813 let _array = builder.finish();
814 builder.append_value(value_2);
815 let _array = builder.finish();
816 builder.append_value(value_1);
817 let _array = builder.finish();
818 }
819
820 #[test]
821 fn test_string_view() {
822 let b1 = Buffer::from(b"world\xFFbananas\xF0\x9F\x98\x81");
823 let b2 = Buffer::from(b"cupcakes");
824 let b3 = Buffer::from(b"Many strings are here contained of great length and verbosity");
825
826 let mut v = StringViewBuilder::new();
827 assert_eq!(v.append_block(b1), 0);
828
829 v.append_value("This is a very long string that exceeds the inline length");
830 v.append_value("This is another very long string that exceeds the inline length");
831
832 assert_eq!(v.append_block(b2), 2);
833 assert_eq!(v.append_block(b3), 3);
834
835 v.try_append_view(0, 0, 5).unwrap(); v.try_append_view(0, 6, 7).unwrap(); v.try_append_view(2, 3, 5).unwrap(); v.try_append_view(2, 0, 3).unwrap(); v.try_append_view(2, 0, 8).unwrap(); v.try_append_view(0, 13, 4).unwrap(); v.try_append_view(0, 13, 0).unwrap(); v.try_append_view(3, 0, 16).unwrap(); v.try_append_view(1, 0, 19).unwrap(); v.try_append_view(3, 13, 27).unwrap(); v.append_value("I do so like long strings");
850
851 let array = v.finish_cloned();
852 array.to_data().validate_full().unwrap();
853 assert_eq!(array.data_buffers().len(), 5);
854 let actual: Vec<_> = array.iter().flatten().collect();
855 assert_eq!(
856 actual,
857 &[
858 "This is a very long string that exceeds the inline length",
859 "This is another very long string that exceeds the inline length",
860 "world",
861 "bananas",
862 "cakes",
863 "cup",
864 "cupcakes",
865 "😁",
866 "",
867 "Many strings are",
868 "This is a very long",
869 "are here contained of great",
870 "I do so like long strings"
871 ]
872 );
873
874 let err = v.try_append_view(0, u32::MAX, 1).unwrap_err();
875 assert_eq!(
876 err.to_string(),
877 "Invalid argument error: Range 4294967295..4294967296 out of bounds for block of length 17"
878 );
879
880 let err = v.try_append_view(0, 1, u32::MAX).unwrap_err();
881 assert_eq!(
882 err.to_string(),
883 "Invalid argument error: Range 1..4294967296 out of bounds for block of length 17"
884 );
885
886 let err = v.try_append_view(0, 13, 2).unwrap_err();
887 assert_eq!(err.to_string(), "Invalid argument error: Invalid view data");
888
889 let err = v.try_append_view(0, 40, 0).unwrap_err();
890 assert_eq!(
891 err.to_string(),
892 "Invalid argument error: Range 40..40 out of bounds for block of length 17"
893 );
894
895 let err = v.try_append_view(5, 0, 0).unwrap_err();
896 assert_eq!(
897 err.to_string(),
898 "Invalid argument error: No block found with index 5"
899 );
900 }
901
902 #[test]
903 fn test_string_view_with_block_size_growth() {
904 let mut exp_builder = StringViewBuilder::new();
905 let mut fixed_builder = StringViewBuilder::new().with_fixed_block_size(STARTING_BLOCK_SIZE);
906
907 let long_string = str::from_utf8(&[b'a'; STARTING_BLOCK_SIZE as usize]).unwrap();
908
909 for i in 0..9 {
910 for _ in 0..(2_u32.pow(i)) {
912 exp_builder.append_value(long_string);
913 fixed_builder.append_value(long_string);
914 }
915 exp_builder.flush_in_progress();
916 fixed_builder.flush_in_progress();
917
918 assert_eq!(exp_builder.completed.len(), i as usize + 1);
920 assert_eq!(
921 exp_builder.completed[i as usize].len(),
922 STARTING_BLOCK_SIZE as usize * 2_usize.pow(i)
923 );
924
925 assert_eq!(fixed_builder.completed.len(), 2_usize.pow(i + 1) - 1);
927
928 assert!(
930 fixed_builder
931 .completed
932 .iter()
933 .all(|b| b.len() == STARTING_BLOCK_SIZE as usize)
934 );
935 }
936
937 exp_builder.append_value(long_string);
939 exp_builder.flush_in_progress();
940 assert_eq!(
941 exp_builder.completed.last().unwrap().capacity(),
942 MAX_BLOCK_SIZE as usize
943 );
944 }
945
946 #[test]
947 fn test_append_value_n() {
948 let mut builder = StringViewBuilder::new();
950
951 builder.try_append_value_n("hello", 100).unwrap();
952 builder.append_value("world");
953 builder.try_append_value_n("foo", 50).unwrap();
954
955 let array = builder.finish();
956 assert_eq!(array.len(), 151);
957 assert_eq!(array.null_count(), 0);
958
959 for i in 0..100 {
961 assert_eq!(array.value(i), "hello");
962 }
963 assert_eq!(array.value(100), "world");
964 for i in 101..151 {
965 assert_eq!(array.value(i), "foo");
966 }
967
968 assert_eq!(array.data_buffers().len(), 0);
970 }
971
972 #[test]
973 fn test_append_value_n_with_deduplication() {
974 let long_string = "This is a very long string that exceeds the inline length";
975
976 let mut builder = StringViewBuilder::new().with_deduplicate_strings();
978
979 builder.append_value(long_string);
981
982 builder.try_append_value_n(long_string, 999).unwrap();
984
985 let array = builder.finish();
986 assert_eq!(array.len(), 1000);
987 assert_eq!(array.null_count(), 0);
988
989 for i in 0..1000 {
991 assert_eq!(array.value(i), long_string);
992 }
993
994 assert_eq!(array.data_buffers().len(), 1);
996
997 let first_view = array.views()[0];
999 for view in array.views() {
1000 assert_eq!(*view, first_view);
1001 }
1002 }
1003
1004 #[test]
1005 fn test_append_value_n_zero() {
1006 let mut builder = StringViewBuilder::new();
1007
1008 builder.append_value("first");
1009 builder.try_append_value_n("should not appear", 0).unwrap();
1010 builder.append_value("second");
1011
1012 let array = builder.finish();
1013 assert_eq!(array.len(), 2);
1014 assert_eq!(array.value(0), "first");
1015 assert_eq!(array.value(1), "second");
1016 }
1017}