1use crate::basic::Encoding;
19use crate::bloom_filter::Sbbf;
20use crate::column::writer::encoder::{
21 ColumnValueEncoder, DataPageValues, DictionaryPage, create_bloom_filter,
22};
23use crate::column::writer::{compare_greater_byte_array_decimals, is_decimal_descr};
24use crate::data_type::{AsBytes, ByteArray, Int32Type};
25use crate::encodings::encoding::{DeltaBitPackEncoder, Encoder};
26use crate::encodings::rle::RleEncoder;
27use crate::errors::{ParquetError, Result};
28use crate::file::properties::{
29 EnabledStatistics, ResolvedColumnProperties, WriterProperties, WriterVersion,
30};
31use crate::geospatial::accumulator::{GeoStatsAccumulator, try_new_geo_stats_accumulator};
32use crate::geospatial::statistics::GeospatialStatistics;
33use crate::schema::types::ColumnDescPtr;
34use crate::util::bit_util::num_required_bits;
35use crate::util::prefix::common_prefix_length;
36use arrow_array::types::ByteArrayType;
37use arrow_array::{
38 Array, ArrayAccessor, BinaryArray, BinaryViewArray, DictionaryArray, FixedSizeBinaryArray,
39 GenericByteArray, LargeBinaryArray, LargeStringArray, StringArray, StringViewArray,
40};
41use arrow_buffer::{ArrowNativeType, Buffer};
42use arrow_schema::DataType;
43
44macro_rules! downcast_dict_impl {
45 ($array:ident, $key:ident, $val:ident, $op:expr $(, $arg:expr)*) => {{
46 $op($array
47 .as_any()
48 .downcast_ref::<DictionaryArray<arrow_array::types::$key>>()
49 .unwrap()
50 .downcast_dict::<$val>()
51 .unwrap()$(, $arg)*)
52 }};
53}
54
55macro_rules! downcast_dict_op {
56 ($key_type:expr, $val:ident, $array:ident, $op:expr $(, $arg:expr)*) => {
57 match $key_type.as_ref() {
58 DataType::UInt8 => downcast_dict_impl!($array, UInt8Type, $val, $op$(, $arg)*),
59 DataType::UInt16 => downcast_dict_impl!($array, UInt16Type, $val, $op$(, $arg)*),
60 DataType::UInt32 => downcast_dict_impl!($array, UInt32Type, $val, $op$(, $arg)*),
61 DataType::UInt64 => downcast_dict_impl!($array, UInt64Type, $val, $op$(, $arg)*),
62 DataType::Int8 => downcast_dict_impl!($array, Int8Type, $val, $op$(, $arg)*),
63 DataType::Int16 => downcast_dict_impl!($array, Int16Type, $val, $op$(, $arg)*),
64 DataType::Int32 => downcast_dict_impl!($array, Int32Type, $val, $op$(, $arg)*),
65 DataType::Int64 => downcast_dict_impl!($array, Int64Type, $val, $op$(, $arg)*),
66 _ => unreachable!(),
67 }
68 };
69}
70
71macro_rules! downcast_op {
72 ($data_type:expr, $array:ident, $op:expr $(, $arg:expr)*) => {
73 match $data_type {
74 DataType::Utf8 => $op($array.as_any().downcast_ref::<StringArray>().unwrap()$(, $arg)*),
75 DataType::LargeUtf8 => {
76 $op($array.as_any().downcast_ref::<LargeStringArray>().unwrap()$(, $arg)*)
77 }
78 DataType::Utf8View => $op($array.as_any().downcast_ref::<StringViewArray>().unwrap()$(, $arg)*),
79 DataType::Binary => {
80 $op($array.as_any().downcast_ref::<BinaryArray>().unwrap()$(, $arg)*)
81 }
82 DataType::LargeBinary => {
83 $op($array.as_any().downcast_ref::<LargeBinaryArray>().unwrap()$(, $arg)*)
84 }
85 DataType::BinaryView => {
86 $op($array.as_any().downcast_ref::<BinaryViewArray>().unwrap()$(, $arg)*)
87 }
88 DataType::Dictionary(key, value) => match value.as_ref() {
89 DataType::Utf8 => downcast_dict_op!(key, StringArray, $array, $op$(, $arg)*),
90 DataType::LargeUtf8 => {
91 downcast_dict_op!(key, LargeStringArray, $array, $op$(, $arg)*)
92 }
93 DataType::Utf8View => {
94 downcast_dict_op!(key, StringViewArray, $array, $op$(, $arg)*)
95 }
96 DataType::Binary => downcast_dict_op!(key, BinaryArray, $array, $op$(, $arg)*),
97 DataType::LargeBinary => {
98 downcast_dict_op!(key, LargeBinaryArray, $array, $op$(, $arg)*)
99 }
100 DataType::BinaryView => {
101 downcast_dict_op!(key, BinaryViewArray, $array, $op$(, $arg)*)
102 }
103 DataType::FixedSizeBinary(_) => {
104 downcast_dict_op!(key, FixedSizeBinaryArray, $array, $op$(, $arg)*)
105 }
106 d => unreachable!("cannot downcast {} dictionary value to byte array", d),
107 },
108 d => unreachable!("cannot downcast {} to byte array", d),
109 }
110 };
111}
112
113struct FallbackEncoder {
115 encoder: FallbackEncoderImpl,
116 num_values: usize,
117 variable_length_bytes: i64,
118}
119
120enum FallbackEncoderImpl {
124 Plain {
125 buffer: Vec<u8>,
126 },
127 DeltaLength {
128 buffer: Vec<u8>,
129 lengths: Box<DeltaBitPackEncoder<Int32Type>>,
130 },
131 Delta {
132 buffer: Vec<u8>,
133 last_value: Vec<u8>,
134 prefix_lengths: Box<DeltaBitPackEncoder<Int32Type>>,
135 suffix_lengths: Box<DeltaBitPackEncoder<Int32Type>>,
136 },
137}
138
139impl FallbackEncoder {
140 fn new(props: &WriterProperties, column_props: &ResolvedColumnProperties) -> Result<Self> {
143 let encoding = column_props
145 .encoding
146 .unwrap_or_else(|| match props.writer_version() {
147 WriterVersion::PARQUET_1_0 => Encoding::PLAIN,
148 WriterVersion::PARQUET_2_0 => Encoding::DELTA_BYTE_ARRAY,
149 });
150
151 let encoder = match encoding {
152 Encoding::PLAIN => FallbackEncoderImpl::Plain { buffer: vec![] },
153 Encoding::DELTA_LENGTH_BYTE_ARRAY => FallbackEncoderImpl::DeltaLength {
154 buffer: vec![],
155 lengths: Box::new(DeltaBitPackEncoder::new()),
156 },
157 Encoding::DELTA_BYTE_ARRAY => FallbackEncoderImpl::Delta {
158 buffer: vec![],
159 last_value: vec![],
160 prefix_lengths: Box::new(DeltaBitPackEncoder::new()),
161 suffix_lengths: Box::new(DeltaBitPackEncoder::new()),
162 },
163 _ => {
164 return Err(general_err!(
165 "unsupported encoding {} for byte array",
166 encoding
167 ));
168 }
169 };
170
171 Ok(Self {
172 encoder,
173 num_values: 0,
174 variable_length_bytes: 0,
175 })
176 }
177
178 fn encode<T>(&mut self, values: T, indices: impl ExactSizeIterator<Item = usize>)
180 where
181 T: ArrayAccessor + Copy,
182 T::Item: AsRef<[u8]>,
183 {
184 self.num_values += indices.len();
185 match &mut self.encoder {
186 FallbackEncoderImpl::Plain { buffer } => {
187 for idx in indices {
188 let value = values.value(idx);
189 let value = value.as_ref();
190 buffer.extend_from_slice((value.len() as u32).as_bytes());
191 buffer.extend_from_slice(value);
192 self.variable_length_bytes += value.len() as i64;
193 }
194 }
195 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
196 for idx in indices {
197 let value = values.value(idx);
198 let value = value.as_ref();
199 lengths.put(&[value.len() as i32]).unwrap();
200 buffer.extend_from_slice(value);
201 self.variable_length_bytes += value.len() as i64;
202 }
203 }
204 FallbackEncoderImpl::Delta {
205 buffer,
206 last_value,
207 prefix_lengths,
208 suffix_lengths,
209 } => {
210 for idx in indices {
211 let value = values.value(idx);
212 let value = value.as_ref();
213
214 let prefix_length = common_prefix_length(last_value, value);
215 let suffix_length = value.len() - prefix_length;
216
217 last_value.clear();
218 last_value.extend_from_slice(value);
219
220 buffer.extend_from_slice(&value[prefix_length..]);
221 prefix_lengths.put(&[prefix_length as i32]).unwrap();
222 suffix_lengths.put(&[suffix_length as i32]).unwrap();
223 self.variable_length_bytes += value.len() as i64;
224 }
225 }
226 }
227 }
228
229 fn estimated_data_page_size(&self) -> usize {
234 match &self.encoder {
235 FallbackEncoderImpl::Plain { buffer, .. } => buffer.len(),
236 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
237 buffer.len() + lengths.estimated_data_encoded_size()
238 }
239 FallbackEncoderImpl::Delta {
240 buffer,
241 prefix_lengths,
242 suffix_lengths,
243 ..
244 } => {
245 buffer.len()
246 + prefix_lengths.estimated_data_encoded_size()
247 + suffix_lengths.estimated_data_encoded_size()
248 }
249 }
250 }
251
252 fn flush_data_page(
253 &mut self,
254 min_value: Option<ByteArray>,
255 max_value: Option<ByteArray>,
256 ) -> Result<DataPageValues<ByteArray>> {
257 let (buf, encoding) = match &mut self.encoder {
258 FallbackEncoderImpl::Plain { buffer } => (std::mem::take(buffer), Encoding::PLAIN),
259 FallbackEncoderImpl::DeltaLength { buffer, lengths } => {
260 let lengths = lengths.flush_buffer()?;
261
262 let mut out = Vec::with_capacity(lengths.len() + buffer.len());
263 out.extend_from_slice(&lengths);
264 out.extend_from_slice(buffer);
265 buffer.clear();
266 (out, Encoding::DELTA_LENGTH_BYTE_ARRAY)
267 }
268 FallbackEncoderImpl::Delta {
269 buffer,
270 prefix_lengths,
271 suffix_lengths,
272 last_value,
273 } => {
274 let prefix_lengths = prefix_lengths.flush_buffer()?;
275 let suffix_lengths = suffix_lengths.flush_buffer()?;
276
277 let mut out =
278 Vec::with_capacity(prefix_lengths.len() + suffix_lengths.len() + buffer.len());
279 out.extend_from_slice(&prefix_lengths);
280 out.extend_from_slice(&suffix_lengths);
281 out.extend_from_slice(buffer);
282 buffer.clear();
283 last_value.clear();
284 (out, Encoding::DELTA_BYTE_ARRAY)
285 }
286 };
287
288 let variable_length_bytes = Some(self.variable_length_bytes);
290 self.variable_length_bytes = 0;
291
292 Ok(DataPageValues {
293 buf: buf.into(),
294 num_values: std::mem::take(&mut self.num_values),
295 encoding,
296 min_value,
297 max_value,
298 nan_count: None,
299 variable_length_bytes,
300 })
301 }
302}
303
304const SHORT_VALUE_LEN: usize = size_of::<u64>() - 1;
306
307#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
309struct Entry {
310 hash: u64,
312 offset_or_inline: OffsetOrInlineView,
315 index_in_dict: u64,
316}
317
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
334struct OffsetOrInlineView(u64);
335
336impl OffsetOrInlineView {
337 const INLINE_FLAG: u64 = 1 << 63;
338 const LEN_SHIFT: u32 = 56;
339
340 #[inline]
341 fn is_non_inlined(&self) -> bool {
342 (self.0 & Self::INLINE_FLAG) == 0
343 }
344
345 #[inline]
346 fn is_inlined(&self) -> bool {
347 !self.is_non_inlined()
348 }
349
350 #[inline]
351 fn new_inlined(value: &[u8]) -> Self {
352 debug_assert!(value.len() <= 7);
353
354 let mut buf = [0u8; 8];
356 buf[..value.len()].copy_from_slice(value);
357 let packed = u64::from_le_bytes(buf);
358
359 Self(Self::INLINE_FLAG | ((value.len() as u64) << Self::LEN_SHIFT) | packed)
360 }
361
362 #[inline]
363 fn new_non_inlined(offset: u64) -> Self {
364 debug_assert_eq!(
369 offset & Self::INLINE_FLAG,
370 0,
371 "dictionary page offset must fit in 63 bits"
372 );
373 let non_inlined_value = offset;
374 Self(non_inlined_value)
375 }
376
377 fn maybe_non_inlined_value(&self) -> Option<u64> {
378 if self.is_non_inlined() {
379 Some(self.0)
381 } else {
382 None
383 }
384 }
385}
386
387#[derive(Debug, Default)]
388struct ByteArrayInterner {
389 state: ahash::RandomState,
390 map: hashbrown::hash_table::HashTable<Entry>,
392 page: Vec<u8>,
394}
395
396impl ByteArrayInterner {
397 fn intern(&mut self, value: &[u8]) -> Result<u64> {
398 let hash = self.state.hash_one(value);
399
400 let (found_entry, inline) = if let Some(inline) = maybe_inline_value(value) {
402 let entry = self.map.find(hash, |header| {
404 let is_same = inline == header.offset_or_inline;
409
410 if is_same {
411 debug_assert_eq!(
412 header.hash, hash,
413 "hashes must match if the values are the same"
414 );
415 }
416
417 is_same
418 });
419
420 (entry, Some(inline))
421 } else {
422 let entry = self.map.find(hash, |header| {
425 if header.hash != hash {
427 return false;
428 }
429
430 let Some(offset) = header.offset_or_inline.maybe_non_inlined_value() else {
432 return false;
434 };
435
436 self.is_at(offset, value)
437 });
438
439 (entry, None)
440 };
441
442 if let Some(entry) = found_entry {
443 return Ok(entry.index_in_dict);
444 }
445
446 self.insert(value, hash, inline)
448 }
449
450 #[inline(always)]
452 fn is_at(&self, offset: u64, value: &[u8]) -> bool {
453 let offset = offset as usize;
454 let stored_len = unsafe { self.page.get_unchecked(offset - 4..offset) };
456 if u32::from_le_bytes(stored_len.try_into().unwrap()) as usize != value.len() {
459 return false;
460 }
461 let existing = unsafe { self.page.get_unchecked(offset..offset + value.len()) };
463 existing == value
464 }
465
466 fn num_unique_values(&self) -> usize {
467 self.map.len()
468 }
469
470 fn insert(
472 &mut self,
473 value: &[u8],
474 hash: u64,
475 inline: Option<OffsetOrInlineView>,
476 ) -> Result<u64> {
477 debug_assert!(inline.is_none_or(|i| i.is_inlined()));
479
480 if value.len() > u32::MAX as usize {
496 return Err(ParquetError::General(format!(
497 "Byte array value is too large to be written in dictionary: {} bytes",
498 value.len()
499 )));
500 }
501
502 let key = self.num_unique_values() as u64;
503 let len = value.len();
504
505 self.page.reserve(4 + value.len());
506 self.page.extend_from_slice(&(len as u32).to_le_bytes());
507 let offset = self.page.len() as u64;
508 self.page.extend_from_slice(value);
509
510 let new_header = Entry {
512 hash,
513 offset_or_inline: inline.unwrap_or_else(|| OffsetOrInlineView::new_non_inlined(offset)),
514 index_in_dict: key,
515 };
516 self.map
517 .insert_unique(hash, new_header, |header| header.hash);
518
519 Ok(key)
520 }
521
522 fn estimated_memory_size(&self) -> usize {
523 self.page.capacity() * std::mem::size_of::<u8>() + self.map.allocation_size()
524 }
525
526 fn iter(&self) -> impl Iterator<Item = &[u8]> {
527 ByteArrayInternerIterator {
528 values: &self.page,
529 current_index: 0,
530 }
531 }
532}
533
534struct ByteArrayInternerIterator<'a> {
540 values: &'a [u8],
541 current_index: usize,
542}
543
544impl<'a> Iterator for ByteArrayInternerIterator<'a> {
545 type Item = &'a [u8];
546
547 fn next(&mut self) -> Option<Self::Item> {
548 if self.current_index >= self.values.len() {
549 return None;
550 }
551
552 let len_bytes = unsafe {
555 self.values
556 .get_unchecked(self.current_index..self.current_index + 4)
557 };
558 let len = u32::from_le_bytes(len_bytes.try_into().unwrap()) as usize;
559
560 let start = self.current_index + 4;
561 let end = start + len;
562
563 let value = unsafe { self.values.get_unchecked(start..end) };
567
568 self.current_index = end;
569
570 Some(value)
571 }
572}
573
574#[derive(Debug, Default)]
576struct DictEncoder {
577 interner: ByteArrayInterner,
578 indices: Vec<u64>,
579 variable_length_bytes: i64,
580}
581
582impl DictEncoder {
583 fn encode<T>(&mut self, values: T, indices: impl ExactSizeIterator<Item = usize>) -> Result<()>
585 where
586 T: ArrayAccessor + Copy,
587 T::Item: AsRef<[u8]>,
588 {
589 self.indices.reserve(indices.len());
590
591 for idx in indices {
592 let value = values.value(idx);
593 let interned = self.interner.intern(value.as_ref())?;
594 self.indices.push(interned);
595 self.variable_length_bytes += value.as_ref().len() as i64;
596 }
597
598 Ok(())
599 }
600
601 fn bit_width(&self) -> u8 {
602 let length = self.interner.num_unique_values();
603 num_required_bits(length.saturating_sub(1) as u64)
604 }
605
606 fn estimated_memory_size(&self) -> usize {
607 self.interner.estimated_memory_size() + self.indices.capacity() * std::mem::size_of::<u64>()
608 }
609
610 fn estimated_data_page_size(&self) -> usize {
611 let bit_width = self.bit_width();
612 1 + RleEncoder::max_buffer_size(bit_width, self.indices.len())
613 }
614
615 fn estimated_dict_page_size(&self) -> usize {
616 self.interner.page.len()
617 }
618
619 fn flush_dict_page(self) -> DictionaryPage {
620 let unique_values = self.interner.num_unique_values();
621 let bytes = self.interner.page;
622
623 DictionaryPage {
624 buf: bytes.into(),
625 num_values: unique_values,
626 is_sorted: false,
627 }
628 }
629
630 fn flush_data_page(
631 &mut self,
632 min_value: Option<ByteArray>,
633 max_value: Option<ByteArray>,
634 ) -> DataPageValues<ByteArray> {
635 let num_values = self.indices.len();
636 let buffer_len = self.estimated_data_page_size();
637 let mut buffer = Vec::with_capacity(buffer_len);
638 buffer.push(self.bit_width());
639
640 let mut encoder = RleEncoder::new_from_buf(self.bit_width(), buffer);
641 encoder.put_batch(&self.indices);
642
643 self.indices.clear();
644
645 let variable_length_bytes = Some(self.variable_length_bytes);
647 self.variable_length_bytes = 0;
648
649 DataPageValues {
650 buf: encoder.consume().into(),
651 num_values,
652 encoding: Encoding::RLE_DICTIONARY,
653 min_value,
654 max_value,
655 nan_count: None,
656 variable_length_bytes,
657 }
658 }
659}
660
661fn maybe_inline_value(value: &[u8]) -> Option<OffsetOrInlineView> {
662 if value.len() <= SHORT_VALUE_LEN {
663 Some(OffsetOrInlineView::new_inlined(value))
664 } else {
665 None
666 }
667}
668
669pub struct ByteArrayEncoder {
670 fallback: FallbackEncoder,
671 dict_encoder: Option<DictEncoder>,
672 statistics_enabled: EnabledStatistics,
673 min_value: Option<ByteArray>,
674 max_value: Option<ByteArray>,
675 bloom_filter: Option<Sbbf>,
676 bloom_filter_target_fpp: f64,
677 geo_stats_accumulator: Option<Box<dyn GeoStatsAccumulator>>,
678 is_decimal: bool,
679}
680
681impl ColumnValueEncoder for ByteArrayEncoder {
682 type T = ByteArray;
683 type Values = dyn Array;
684 fn flush_bloom_filter(&mut self) -> Option<Sbbf> {
685 let mut sbbf = self.bloom_filter.take()?;
686 sbbf.fold_to_target_fpp(self.bloom_filter_target_fpp);
687 Some(sbbf)
688 }
689
690 fn try_new(
691 descr: &ColumnDescPtr,
692 props: &WriterProperties,
693 column_props: &ResolvedColumnProperties,
694 ) -> Result<Self>
695 where
696 Self: Sized,
697 {
698 let dictionary = column_props.dictionary_enabled.then(DictEncoder::default);
699
700 let fallback = FallbackEncoder::new(props, column_props)?;
701
702 let (bloom_filter, bloom_filter_target_fpp) = create_bloom_filter(column_props)?;
703
704 let statistics_enabled = column_props.statistics_enabled;
705
706 let geo_stats_accumulator = try_new_geo_stats_accumulator(descr);
707
708 let is_decimal = is_decimal_descr(descr.get_basic_info());
709
710 Ok(Self {
711 fallback,
712 statistics_enabled,
713 bloom_filter,
714 bloom_filter_target_fpp,
715 dict_encoder: dictionary,
716 min_value: None,
717 max_value: None,
718 geo_stats_accumulator,
719 is_decimal,
720 })
721 }
722
723 fn write(&mut self, _values: &Self::Values, _offset: usize, _len: usize) -> Result<()> {
724 unreachable!("should call write_gather instead")
725 }
726
727 fn write_gather(&mut self, values: &Self::Values, indices: &[usize]) -> Result<()> {
728 downcast_op!(
729 values.data_type(),
730 values,
731 encode,
732 indices.iter().copied(),
733 self
734 )
735 }
736
737 fn count_values_within_byte_budget_gather(
738 values: &Self::Values,
739 indices: &[usize],
740 byte_budget: usize,
741 ) -> Option<usize> {
742 let count = match values.data_type() {
756 DataType::Utf8 => count_within_budget_offsets(
757 values.as_any().downcast_ref::<StringArray>().unwrap(),
758 indices,
759 byte_budget,
760 ),
761 DataType::LargeUtf8 => count_within_budget_offsets(
762 values.as_any().downcast_ref::<LargeStringArray>().unwrap(),
763 indices,
764 byte_budget,
765 ),
766 DataType::Binary => count_within_budget_offsets(
767 values.as_any().downcast_ref::<BinaryArray>().unwrap(),
768 indices,
769 byte_budget,
770 ),
771 DataType::LargeBinary => count_within_budget_offsets(
772 values.as_any().downcast_ref::<LargeBinaryArray>().unwrap(),
773 indices,
774 byte_budget,
775 ),
776 DataType::Utf8View => {
781 let array = values.as_any().downcast_ref::<StringViewArray>().unwrap();
782 count_within_budget_views(
783 array.views(),
784 indices,
785 byte_budget,
786 max_view_value_len(array.data_buffers()),
787 )
788 }
789 DataType::BinaryView => {
790 let array = values.as_any().downcast_ref::<BinaryViewArray>().unwrap();
791 count_within_budget_views(
792 array.views(),
793 indices,
794 byte_budget,
795 max_view_value_len(array.data_buffers()),
796 )
797 }
798 DataType::Dictionary(_, _) => indices.len(),
804 data_type => unreachable!("ByteArrayEncoder cannot be constructed for {data_type:?}"),
811 };
812 Some(count)
813 }
814
815 fn num_values(&self) -> usize {
816 match &self.dict_encoder {
817 Some(encoder) => encoder.indices.len(),
818 None => self.fallback.num_values,
819 }
820 }
821
822 fn has_dictionary(&self) -> bool {
823 self.dict_encoder.is_some()
824 }
825
826 fn compresses_against_previous_value(&self) -> bool {
827 self.dict_encoder.is_none()
831 && matches!(self.fallback.encoder, FallbackEncoderImpl::Delta { .. })
832 }
833
834 fn estimated_memory_size(&self) -> usize {
835 let encoder_size = match &self.dict_encoder {
836 Some(encoder) => encoder.estimated_memory_size(),
837 None => self.fallback.estimated_data_page_size(),
840 };
841
842 let bloom_filter_size = self
843 .bloom_filter
844 .as_ref()
845 .map(|bf| bf.estimated_memory_size())
846 .unwrap_or_default();
847
848 let stats_size = self.min_value.as_ref().map(|v| v.len()).unwrap_or_default()
849 + self.max_value.as_ref().map(|v| v.len()).unwrap_or_default();
850
851 encoder_size + bloom_filter_size + stats_size
852 }
853
854 fn estimated_dict_page_size(&self) -> Option<usize> {
855 Some(self.dict_encoder.as_ref()?.estimated_dict_page_size())
856 }
857
858 fn estimated_data_page_size(&self) -> usize {
863 match &self.dict_encoder {
864 Some(encoder) => encoder.estimated_data_page_size(),
865 None => self.fallback.estimated_data_page_size(),
866 }
867 }
868
869 fn flush_dict_page(&mut self) -> Result<Option<DictionaryPage>> {
870 match self.dict_encoder.take() {
871 Some(encoder) => {
872 if !encoder.indices.is_empty() {
873 return Err(general_err!(
874 "Must flush data pages before flushing dictionary"
875 ));
876 }
877
878 if let Some(bloom_filter) = &mut self.bloom_filter {
879 for value in encoder.interner.iter() {
880 bloom_filter.insert(value);
881 }
882 }
883
884 Ok(Some(encoder.flush_dict_page()))
885 }
886 _ => Ok(None),
887 }
888 }
889
890 fn flush_data_page(&mut self) -> Result<DataPageValues<ByteArray>> {
891 let min_value = self.min_value.take();
892 let max_value = self.max_value.take();
893
894 match &mut self.dict_encoder {
895 Some(encoder) => Ok(encoder.flush_data_page(min_value, max_value)),
896 _ => self.fallback.flush_data_page(min_value, max_value),
897 }
898 }
899
900 fn flush_geospatial_statistics(&mut self) -> Option<Box<GeospatialStatistics>> {
901 self.geo_stats_accumulator.as_mut().map(|a| a.finish())?
902 }
903}
904
905fn encode<T, I>(values: T, indices: I, encoder: &mut ByteArrayEncoder) -> Result<()>
909where
910 T: ArrayAccessor + Copy,
911 T::Item: Copy + Ord + AsRef<[u8]>,
912 I: ExactSizeIterator<Item = usize> + Clone,
913{
914 if encoder.statistics_enabled != EnabledStatistics::None {
915 if let Some(accumulator) = encoder.geo_stats_accumulator.as_mut() {
916 update_geo_stats_accumulator(accumulator.as_mut(), values, indices.clone());
917 } else if let Some((min, max)) =
918 compute_min_max(values, indices.clone(), encoder.is_decimal)
919 {
920 let min = min.as_ref();
925 if encoder
926 .min_value
927 .as_ref()
928 .is_none_or(|m| is_greater(encoder.is_decimal, m.data(), min))
929 {
930 encoder.min_value = Some(min.to_vec().into());
931 }
932
933 let max = max.as_ref();
934 if encoder
935 .max_value
936 .as_ref()
937 .is_none_or(|m| is_greater(encoder.is_decimal, max, m.data()))
938 {
939 encoder.max_value = Some(max.to_vec().into());
940 }
941 }
942 }
943
944 match &mut encoder.dict_encoder {
947 Some(dict_encoder) => dict_encoder.encode(values, indices),
948 None => {
949 if let Some(bloom_filter) = &mut encoder.bloom_filter {
950 for idx in indices.clone() {
951 bloom_filter.insert(values.value(idx).as_ref());
952 }
953 }
954 encoder.fallback.encode(values, indices);
955
956 Ok(())
957 }
958 }
959}
960
961fn max_view_value_len(buffers: &[Buffer]) -> usize {
963 const MAX_INLINE_VIEW_LEN: usize = 12;
965 buffers
970 .iter()
971 .map(|b| b.len())
972 .max()
973 .unwrap_or(0)
974 .max(MAX_INLINE_VIEW_LEN)
975}
976
977fn count_within_budget_views(
981 views: &[u128],
982 indices: &[usize],
983 byte_budget: usize,
984 max_value_len: usize,
985) -> usize {
986 let per_value = max_value_len + std::mem::size_of::<u32>();
997 if indices.len().saturating_mul(per_value) <= byte_budget {
998 return indices.len();
999 }
1000 let mut cum: usize = 0;
1003 for (i, idx) in indices.iter().enumerate() {
1004 let len = (views[*idx] as u32) as usize;
1005 cum = cum.saturating_add(len + std::mem::size_of::<u32>());
1006 if cum > byte_budget {
1007 return i + 1;
1008 }
1009 }
1010 indices.len()
1011}
1012
1013fn count_within_budget_offsets<T: ByteArrayType>(
1020 values: &GenericByteArray<T>,
1021 indices: &[usize],
1022 byte_budget: usize,
1023) -> usize {
1024 if indices.is_empty() {
1025 return 0;
1026 }
1027 let n = indices.len();
1028 let first = indices[0];
1029 let last = indices[n - 1];
1030 let offsets = values.value_offsets();
1031 let prefix_overhead = std::mem::size_of::<u32>();
1033
1034 if last >= first {
1042 let payload = (offsets[last + 1] - offsets[first]).as_usize();
1043 if payload + n * prefix_overhead <= byte_budget {
1044 return n;
1045 }
1046 }
1047
1048 let mut cum: usize = 0;
1050 for (i, idx) in indices.iter().enumerate() {
1051 let len = (offsets[idx + 1] - offsets[*idx]).as_usize() + prefix_overhead;
1052 cum = cum.saturating_add(len);
1053 if cum > byte_budget {
1054 return i + 1;
1055 }
1056 }
1057 n
1058}
1059
1060#[inline]
1062fn is_greater(is_decimal: bool, a: &[u8], b: &[u8]) -> bool {
1063 if is_decimal {
1064 compare_greater_byte_array_decimals(a, b)
1065 } else {
1066 a > b
1067 }
1068}
1069
1070fn compute_min_max<T>(
1074 array: T,
1075 mut valid: impl Iterator<Item = usize>,
1076 is_decimal: bool,
1077) -> Option<(T::Item, T::Item)>
1078where
1079 T: ArrayAccessor,
1080 T::Item: Copy + Ord + AsRef<[u8]>,
1081{
1082 let first_idx = valid.next()?;
1083
1084 let first_val = array.value(first_idx);
1085 let mut min = first_val;
1086 let mut max = first_val;
1087 for idx in valid {
1088 let val = array.value(idx);
1089 if is_greater(is_decimal, min.as_ref(), val.as_ref()) {
1090 min = val;
1091 }
1092 if is_greater(is_decimal, val.as_ref(), max.as_ref()) {
1093 max = val;
1094 }
1095 }
1096 Some((min, max))
1097}
1098
1099fn update_geo_stats_accumulator<T>(
1101 bounder: &mut dyn GeoStatsAccumulator,
1102 array: T,
1103 valid: impl Iterator<Item = usize>,
1104) where
1105 T: ArrayAccessor,
1106 T::Item: Copy + Ord + AsRef<[u8]>,
1107{
1108 if bounder.is_valid() {
1109 for idx in valid {
1110 let val = array.value(idx);
1111 bounder.update_wkb(val.as_ref());
1112 }
1113 }
1114}