1use crate::arrow::buffer::offset_buffer::OffsetBuffer;
19use crate::arrow::record_reader::buffer::ValuesBuffer;
20use crate::errors::{ParquetError, Result};
21use ahash::RandomState;
22use arrow_array::{Array, DictionaryArray, PrimitiveArray, downcast_integer};
23use arrow_array::{
24 ArrayRef, FixedSizeBinaryArray, GenericBinaryArray, OffsetSizeTrait,
25 builder::FixedSizeBinaryDictionaryBuilder,
26 cast::AsArray,
27 types::{
28 ArrowDictionaryKeyType, ArrowPrimitiveType, Int8Type, Int16Type, Int32Type, Int64Type,
29 UInt8Type, UInt16Type, UInt32Type, UInt64Type,
30 },
31};
32use arrow_buffer::{
33 ArrowNativeType, BooleanBuffer, Buffer, MutableBuffer, NullBuffer, ScalarBuffer,
34};
35use arrow_schema::DataType as ArrowType;
36use hashbrown::HashMap as HbHashMap;
37use hashbrown::hash_map::Entry;
38use std::hash::{BuildHasher, Hasher};
39use std::mem::size_of;
40use std::ptr::write_unaligned;
41use std::slice::{from_raw_parts, from_raw_parts_mut};
42use std::sync::Arc;
43
44pub enum DictionaryBuffer<K: ArrowNativeType, V: OffsetSizeTrait> {
47 Dict {
48 keys: Vec<K>,
49 values: ArrayRef,
50 key_upper_bound: Option<u64>,
52 },
53 Values {
54 values: OffsetBuffer<V>,
55 },
56}
57
58fn widen_key_bound(existing: &mut Option<u64>, incoming: Option<u64>) {
59 *existing = match (*existing, incoming) {
60 (Some(current), Some(new)) => Some(current.max(new)),
61 _ => None,
62 };
63}
64
65impl<K: ArrowNativeType + Ord, V: OffsetSizeTrait> DictionaryBuffer<K, V> {
66 #[cfg_attr(not(test), expect(unused))]
67 pub fn len(&self) -> usize {
68 match self {
69 Self::Dict { keys, .. } => keys.len(),
70 Self::Values { values } => values.len(),
71 }
72 }
73
74 pub fn as_keys_with_bit_width(
83 &mut self,
84 dictionary: &ArrayRef,
85 rle_bit_width: Option<u8>,
86 ) -> Option<&mut Vec<K>> {
87 assert!(K::from_usize(dictionary.len()).is_some());
88
89 let incoming_bound = rle_bit_width.map(|bit_width| 1u64 << bit_width);
91
92 match self {
93 Self::Dict {
94 keys,
95 values,
96 key_upper_bound,
97 } => {
98 let values_ptr = std::ptr::from_ref(values.as_ref()).cast::<()>();
102 let dict_ptr = std::ptr::from_ref(dictionary.as_ref()).cast::<()>();
103 if values_ptr == dict_ptr {
104 widen_key_bound(key_upper_bound, incoming_bound);
105 Some(keys)
106 } else if keys.is_empty() {
107 *values = Arc::clone(dictionary);
108 *key_upper_bound = incoming_bound;
109 Some(keys)
110 } else {
111 None
112 }
113 }
114 Self::Values { values } if values.is_empty() => {
115 *self = Self::Dict {
116 keys: Default::default(),
117 values: Arc::clone(dictionary),
118 key_upper_bound: incoming_bound,
119 };
120 match self {
121 Self::Dict { keys, .. } => Some(keys),
122 Self::Values { .. } => unreachable!(),
123 }
124 }
125 Self::Values { .. } => None,
126 }
127 }
128
129 pub fn spill_values(&mut self) -> Result<&mut OffsetBuffer<V>> {
134 match self {
135 Self::Values { values } => Ok(values),
136 Self::Dict { keys, values, .. } => {
137 let mut spilled = OffsetBuffer::with_capacity(0);
138 let data = values.to_data();
139 let dict_buffers = data.buffers();
140 let dict_offsets = dict_buffers[0].typed_data::<V>();
141 let dict_values = dict_buffers[1].as_slice();
142
143 if values.is_empty() {
144 spilled.offsets.resize(keys.len() + 1, V::default());
146 } else {
147 spilled.extend_from_dictionary(keys.as_slice(), dict_offsets, dict_values)?;
153 }
154
155 *self = Self::Values { values: spilled };
156 match self {
157 Self::Values { values } => Ok(values),
158 Self::Dict { .. } => unreachable!(),
159 }
160 }
161 }
162 }
163
164 pub fn into_array(
166 self,
167 null_buffer: Option<Buffer>,
168 data_type: &ArrowType,
169 hash_scratch: &mut MutableBuffer,
170 ) -> Result<ArrayRef> {
171 assert!(matches!(data_type, ArrowType::Dictionary(_, _)));
172
173 match self {
174 Self::Dict {
175 keys,
176 values,
177 key_upper_bound,
178 } => {
179 let keys_provably_in_range = key_upper_bound
180 .map(|bound| bound <= values.len() as u64)
181 .unwrap_or(false);
182
183 if !values.is_empty() && !keys_provably_in_range {
184 let min = K::from_usize(0).unwrap();
185 let max = K::from_usize(values.len()).unwrap();
186
187 let (observed_min, observed_max) = keys.as_slice().iter().copied().fold(
188 (max, min),
189 |(current_min, current_max), key| {
190 (current_min.min(key), current_max.max(key))
191 },
192 );
193
194 if observed_min < min || observed_max >= max {
195 return Err(general_err!(
196 "dictionary key beyond bounds of dictionary: 0..{}",
197 values.len()
198 ));
199 }
200 }
201
202 let ArrowType::Dictionary(key_type, value_type) = data_type else {
203 unreachable!()
204 };
205 let values = if let ArrowType::FixedSizeBinary(size) = **value_type {
206 let binary = values.as_binary::<i32>();
207 Arc::new(FixedSizeBinaryArray::new(
208 size,
209 binary.values().clone(),
210 binary.nulls().cloned(),
211 )) as _
212 } else {
213 values
214 };
215
216 let num_keys = keys.len();
217 let keys_buffer = Buffer::from_vec(keys);
218 let null_buffer =
219 null_buffer.map(|b| NullBuffer::new(BooleanBuffer::new(b, 0, num_keys)));
220
221 build_dictionary_array(key_type, keys_buffer, num_keys, null_buffer, values)
222 }
223 Self::Values { values } => {
224 let (key_type, value_type) = match data_type {
225 ArrowType::Dictionary(k, v) => (k, v.as_ref().clone()),
226 _ => unreachable!(),
227 };
228
229 if let ArrowType::FixedSizeBinary(size) = value_type {
233 let binary = values.into_array(null_buffer, GenericBinaryArray::<V>::DATA_TYPE);
234 let binary = binary.as_binary::<V>();
235 let array =
236 FixedSizeBinaryArray::try_from_sparse_iter_with_size(binary.iter(), size)?;
237 return pack_fixed_values(key_type, &array);
238 }
239
240 hash_byte_slices(&values.offsets, &values.values, hash_scratch);
241 let hashes = hashes_as_u64(hash_scratch);
242
243 pack_values_from_offsets(key_type, &value_type, &values, hashes, null_buffer)
244 }
245 }
246 }
247}
248
249impl<K: ArrowNativeType, V: OffsetSizeTrait> ValuesBuffer for DictionaryBuffer<K, V> {
250 fn with_capacity(capacity: usize) -> Self {
251 Self::Values {
252 values: OffsetBuffer::with_capacity(capacity),
253 }
254 }
255
256 fn reserve_exact(&mut self, additional: usize) {
257 match self {
258 Self::Dict { keys, .. } => keys.reserve_exact(additional),
259 Self::Values { values, .. } => values.reserve_exact(additional),
260 }
261 }
262
263 fn pad_nulls(
264 &mut self,
265 read_offset: usize,
266 values_read: usize,
267 levels_read: usize,
268 valid_mask: &[u8],
269 ) -> Result<()> {
270 match self {
271 Self::Dict { keys, .. } => {
272 keys.resize(read_offset + levels_read, K::default());
273 keys.pad_nulls(read_offset, values_read, levels_read, valid_mask)
274 }
275 Self::Values { values, .. } => {
276 values.pad_nulls(read_offset, values_read, levels_read, valid_mask)
277 }
278 }
279 }
280}
281
282fn build_dictionary_array(
285 key_type: &ArrowType,
286 keys_buffer: Buffer,
287 num_keys: usize,
288 null_buffer: Option<NullBuffer>,
289 values: ArrayRef,
290) -> Result<ArrayRef> {
291 macro_rules! build {
292 ($kt:ty) => {{
293 let scalars =
294 ScalarBuffer::<<$kt as ArrowPrimitiveType>::Native>::new(keys_buffer, 0, num_keys);
295 let keys = PrimitiveArray::<$kt>::new(scalars, null_buffer);
296 let dict = unsafe { DictionaryArray::<$kt>::new_unchecked(keys, values) };
298 Ok(Arc::new(dict) as ArrayRef)
299 }};
300 }
301 match key_type {
302 ArrowType::Int8 => build!(Int8Type),
303 ArrowType::Int16 => build!(Int16Type),
304 ArrowType::Int32 => build!(Int32Type),
305 ArrowType::Int64 => build!(Int64Type),
306 ArrowType::UInt8 => build!(UInt8Type),
307 ArrowType::UInt16 => build!(UInt16Type),
308 ArrowType::UInt32 => build!(UInt32Type),
309 ArrowType::UInt64 => build!(UInt64Type),
310 _ => Err(general_err!("unsupported dictionary key type: {key_type}")),
311 }
312}
313
314macro_rules! fixed_dict_helper {
315 ($k:ty, $array:ident) => {
316 pack_fixed_values_impl::<$k>($array)
317 };
318}
319
320fn pack_fixed_values(key_type: &ArrowType, array: &FixedSizeBinaryArray) -> Result<ArrayRef> {
321 downcast_integer! {
322 key_type => (fixed_dict_helper, array),
323 _ => unreachable!(),
324 }
325}
326
327fn pack_fixed_values_impl<K: ArrowDictionaryKeyType>(
328 array: &FixedSizeBinaryArray,
329) -> Result<ArrayRef> {
330 let mut builder = FixedSizeBinaryDictionaryBuilder::<K>::with_capacity(
331 array.len(),
332 1024,
333 array.value_length(),
334 );
335 for x in array {
336 match x {
337 Some(x) => {
338 builder.append(x)?;
339 }
340 None => builder.append_null(),
341 }
342 }
343 Ok(Arc::new(builder.finish()))
344}
345
346macro_rules! offsets_dict_helper {
347 ($k:ty, $key_type:ident, $value_type:ident, $values:ident, $hashes:ident, $null_buffer:ident) => {
348 pack_values_from_offsets_impl::<$k, _>(
349 $values,
350 $hashes,
351 $null_buffer,
352 $key_type,
353 $value_type,
354 )
355 };
356}
357
358fn pack_values_from_offsets<V: OffsetSizeTrait>(
359 key_type: &ArrowType,
360 value_type: &ArrowType,
361 values: &OffsetBuffer<V>,
362 hashes: &[u64],
363 null_buffer: Option<Buffer>,
364) -> Result<ArrayRef> {
365 downcast_integer! {
366 key_type => (offsets_dict_helper, key_type, value_type, values, hashes, null_buffer),
367 _ => unreachable!(),
368 }
369}
370
371struct PassthroughHasher(u64);
374impl std::hash::Hasher for PassthroughHasher {
375 fn finish(&self) -> u64 {
376 self.0
377 }
378 fn write(&mut self, _: &[u8]) {
379 unreachable!()
380 }
381 fn write_u64(&mut self, value: u64) {
382 self.0 = value;
383 }
384}
385#[derive(Default)]
386struct BuildPassthroughHasher;
387impl std::hash::BuildHasher for BuildPassthroughHasher {
388 type Hasher = PassthroughHasher;
389 fn build_hasher(&self) -> PassthroughHasher {
390 PassthroughHasher(0)
391 }
392}
393
394fn pack_values_from_offsets_impl<K: ArrowDictionaryKeyType, V: OffsetSizeTrait>(
398 offset_buffer: &OffsetBuffer<V>,
399 hashes: &[u64],
400 null_buffer: Option<Buffer>,
401 key_type: &ArrowType,
402 value_type: &ArrowType,
403) -> Result<ArrayRef> {
404 let dict_type = ArrowType::Dictionary(Box::new(key_type.clone()), Box::new(value_type.clone()));
405 let num_values = offset_buffer.len();
406
407 let mut keys: Vec<K::Native> = Vec::with_capacity(num_values);
408 let mut unique_offsets: Vec<V> = Vec::with_capacity(num_values + 1);
409 unique_offsets.push(V::default());
410 let mut unique_bytes: Vec<u8> = Vec::with_capacity(offset_buffer.values.len());
411
412 let mut dedup: HbHashMap<u64, (usize, usize), BuildPassthroughHasher> =
413 HbHashMap::with_capacity_and_hasher(num_values, BuildPassthroughHasher);
414
415 let mut collision_overflow: Vec<(u64, usize, usize)> = Vec::new();
418
419 for (input_idx, &hash) in hashes.iter().enumerate() {
420 let byte_start = offset_buffer.offsets[input_idx].as_usize();
421 let byte_end = offset_buffer.offsets[input_idx + 1].as_usize();
422 let bytes = &offset_buffer.values[byte_start..byte_end];
423
424 let output_idx = match dedup.entry(hash) {
425 Entry::Occupied(entry) => {
426 let (first_input_idx, existing_output_idx) = *entry.get();
427 let first_start = offset_buffer.offsets[first_input_idx].as_usize();
428 let first_end = offset_buffer.offsets[first_input_idx + 1].as_usize();
429 if &offset_buffer.values[first_start..first_end] == bytes {
430 existing_output_idx
431 } else {
432 let existing = collision_overflow
434 .iter()
435 .find(|&&(entry_hash, collision_input_idx, _)| {
436 if entry_hash != hash {
437 return false;
438 }
439 let collision_start =
440 offset_buffer.offsets[collision_input_idx].as_usize();
441 let collision_end =
442 offset_buffer.offsets[collision_input_idx + 1].as_usize();
443 &offset_buffer.values[collision_start..collision_end] == bytes
444 })
445 .map(|&(_, _, collision_output_idx)| collision_output_idx);
446
447 match existing {
448 Some(collision_output_idx) => collision_output_idx,
449 None => {
450 let new_output_idx = unique_offsets.len() - 1;
451 unique_bytes.extend_from_slice(bytes);
452 let new_end = V::from_usize(unique_bytes.len()).ok_or_else(|| {
453 general_err!("offset overflow building dictionary")
454 })?;
455 unique_offsets.push(new_end);
456 collision_overflow.push((hash, input_idx, new_output_idx));
457 new_output_idx
458 }
459 }
460 }
461 }
462 Entry::Vacant(entry) => {
463 let output_idx = unique_offsets.len() - 1;
464 unique_bytes.extend_from_slice(bytes);
465 let new_end = V::from_usize(unique_bytes.len())
466 .ok_or_else(|| general_err!("offset overflow building dictionary"))?;
467 unique_offsets.push(new_end);
468 entry.insert((input_idx, output_idx));
469 output_idx
470 }
471 };
472
473 let key = K::Native::from_usize(output_idx)
474 .ok_or_else(|| general_err!("dictionary key overflow"))?;
475 keys.push(key);
476 }
477
478 let num_unique = unique_offsets.len() - 1;
479
480 let value_data = unsafe {
484 arrow_data::ArrayData::builder(value_type.clone())
485 .len(num_unique)
486 .add_buffer(Buffer::from_vec(unique_offsets))
487 .add_buffer(Buffer::from_vec(unique_bytes))
488 .build_unchecked()
489 };
490
491 let dict_array: DictionaryArray<K> = unsafe {
493 arrow_data::ArrayData::builder(dict_type)
494 .len(keys.len())
495 .add_buffer(Buffer::from_vec(keys))
496 .add_child_data(value_data)
497 .null_bit_buffer(null_buffer)
498 .build_unchecked()
499 .into()
500 };
501
502 Ok(Arc::new(dict_array))
503}
504
505fn hash_byte_slices<I: ArrowNativeType>(offsets: &[I], values: &[u8], scratch: &mut MutableBuffer) {
506 let count = offsets.len().saturating_sub(1);
507 scratch.clear();
508 scratch.resize(count * size_of::<u64>(), 0u8);
509
510 let state = RandomState::new();
511
512 #[expect(
514 clippy::cast_ptr_alignment,
515 reason = "MutableBuffer is 64-byte aligned"
516 )]
517 let hash_slots = unsafe { from_raw_parts_mut(scratch.as_mut_ptr().cast::<u64>(), count) };
518
519 for idx in 0..count {
520 let start = offsets[idx].as_usize();
521 let end = offsets[idx + 1].as_usize();
522 let mut hasher = state.build_hasher();
523 hasher.write(&values[start..end]);
524 unsafe { write_unaligned(hash_slots.as_mut_ptr().add(idx), hasher.finish()) };
526 }
527}
528
529#[inline]
530fn hashes_as_u64(scratch: &[u8]) -> &[u64] {
531 let n = scratch.len() / size_of::<u64>();
532 #[expect(
534 clippy::cast_ptr_alignment,
535 reason = "MutableBuffer is 64-byte aligned"
536 )]
537 unsafe {
538 from_raw_parts(scratch.as_ptr().cast::<u64>(), n)
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use arrow::compute::cast;
546 use arrow_array::StringArray;
547
548 #[test]
549 fn test_dictionary_buffer() {
550 let dict_type =
551 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Utf8));
552
553 let d1: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "", "a", "b"]));
554
555 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
556
557 let values = &[1, 0, 3, 2, 4];
559 buffer
560 .as_keys_with_bit_width(&d1, None)
561 .unwrap()
562 .extend_from_slice(values);
563
564 let mut valid = vec![false, false, true, true, false, true, true, true];
565 let valid_buffer = Buffer::from_iter(valid.iter().copied());
566 buffer
567 .pad_nulls(0, values.len(), valid.len(), valid_buffer.as_slice())
568 .unwrap();
569
570 let values = buffer.spill_values().unwrap();
573 let read_offset = values.len();
574 values.try_push(b"bingo", false).unwrap();
575 values.try_push(b"bongo", false).unwrap();
576
577 valid.extend_from_slice(&[false, false, true, false, true]);
578 let null_buffer = Buffer::from_iter(valid.iter().copied());
579 buffer
580 .pad_nulls(read_offset, 2, 5, null_buffer.as_slice())
581 .unwrap();
582
583 assert_eq!(buffer.len(), 13);
584 let split = std::mem::replace(&mut buffer, DictionaryBuffer::with_capacity(0));
585
586 let array = split
587 .into_array(Some(null_buffer), &dict_type, &mut MutableBuffer::new(0))
588 .unwrap();
589 assert_eq!(array.data_type(), &dict_type);
590
591 let strings = cast(&array, &ArrowType::Utf8).unwrap();
592 let strings = strings.as_any().downcast_ref::<StringArray>().unwrap();
593 assert_eq!(
594 strings.iter().collect::<Vec<_>>(),
595 vec![
596 None,
597 None,
598 Some("world"),
599 Some("hello"),
600 None,
601 Some("a"),
602 Some(""),
603 Some("b"),
604 None,
605 None,
606 Some("bingo"),
607 None,
608 Some("bongo")
609 ]
610 );
611
612 assert!(matches!(&buffer, DictionaryBuffer::Values { .. }));
614 assert_eq!(buffer.len(), 0);
615 let d2 = Arc::new(StringArray::from(vec!["bingo", ""])) as ArrayRef;
616 buffer
617 .as_keys_with_bit_width(&d2, None)
618 .unwrap()
619 .extend_from_slice(&[0, 1, 0, 1]);
620
621 let array = std::mem::replace(&mut buffer, DictionaryBuffer::with_capacity(0))
622 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
623 .unwrap();
624 assert_eq!(array.data_type(), &dict_type);
625
626 let strings = cast(&array, &ArrowType::Utf8).unwrap();
627 let strings = strings.as_any().downcast_ref::<StringArray>().unwrap();
628 assert_eq!(
629 strings.iter().collect::<Vec<_>>(),
630 vec![Some("bingo"), Some(""), Some("bingo"), Some("")]
631 );
632
633 assert!(matches!(&buffer, DictionaryBuffer::Values { .. }));
635 assert_eq!(buffer.len(), 0);
636 let d3 = Arc::new(StringArray::from(vec!["bongo"])) as ArrayRef;
637 buffer
638 .as_keys_with_bit_width(&d3, None)
639 .unwrap()
640 .extend_from_slice(&[0, 0]);
641
642 let d4 = Arc::new(StringArray::from(vec!["bananas"])) as ArrayRef;
644 assert!(buffer.as_keys_with_bit_width(&d4, None).is_none());
645 }
646
647 #[test]
648 fn test_validates_keys() {
649 let dict_type =
650 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Utf8));
651
652 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
653 let d = Arc::new(StringArray::from(vec!["", "f"])) as ArrayRef;
654 buffer
655 .as_keys_with_bit_width(&d, None)
656 .unwrap()
657 .extend_from_slice(&[0, 2, 0]);
658
659 let err = buffer
660 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
661 .unwrap_err()
662 .to_string();
663 assert!(
664 err.contains("dictionary key beyond bounds of dictionary: 0..2"),
665 "{}",
666 err
667 );
668
669 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
670 let d = Arc::new(StringArray::from(vec![""])) as ArrayRef;
671 buffer
672 .as_keys_with_bit_width(&d, None)
673 .unwrap()
674 .extend_from_slice(&[0, 1, 0]);
675
676 let err = buffer.spill_values().unwrap_err().to_string();
677 assert!(
678 err.contains("dictionary key beyond bounds of dictionary: 0..1"),
679 "{}",
680 err
681 );
682 }
683
684 #[test]
686 fn test_values_path_large_utf8_type() {
687 let dict_type =
688 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::LargeUtf8));
689 let mut buffer = DictionaryBuffer::<i32, i64>::with_capacity(0);
690 let values = buffer.spill_values().unwrap();
691 for s in ["foo", "bar", "foo"] {
692 values.try_push(s.as_bytes(), false).unwrap();
693 }
694
695 let array = buffer
696 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
697 .unwrap();
698 let dict = array
699 .as_any()
700 .downcast_ref::<DictionaryArray<Int32Type>>()
701 .unwrap();
702
703 assert_eq!(dict.data_type(), &dict_type);
704 assert_eq!(dict.values().data_type(), &ArrowType::LargeUtf8);
705 }
706
707 #[test]
710 fn test_int8_key_overflow_boundary() {
711 let dict_type = ArrowType::Dictionary(Box::new(ArrowType::Int8), Box::new(ArrowType::Utf8));
712 let mut scratch = MutableBuffer::new(0);
713
714 {
715 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
716 let values = buffer.spill_values().unwrap();
717 for i in 0u32..128 {
718 values
719 .try_push(format!("val{i}").as_bytes(), false)
720 .unwrap();
721 }
722 buffer
723 .into_array(None, &dict_type, &mut scratch)
724 .expect("128 unique strings must fit: last index is 127 = i8::MAX");
725 }
726
727 {
728 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
729 let values = buffer.spill_values().unwrap();
730 for i in 0u32..129 {
731 values
732 .try_push(format!("val{i}").as_bytes(), false)
733 .unwrap();
734 }
735 let err = buffer
736 .into_array(None, &dict_type, &mut scratch)
737 .unwrap_err()
738 .to_string();
739 assert!(
740 err.contains("dictionary key overflow"),
741 "expected 'dictionary key overflow', got: {err}"
742 );
743 }
744 }
745
746 #[test]
747 fn test_fixed_size_binary_key_overflow_boundary() {
748 for (key_type, capacity) in [(ArrowType::Int8, 128u32), (ArrowType::UInt8, 256)] {
749 for unique in [capacity, capacity + 1] {
750 let values = (0..unique)
751 .map(|value| Some(value.to_le_bytes()))
752 .chain([None, Some(0u32.to_le_bytes())]);
753 let values =
754 FixedSizeBinaryArray::try_from_sparse_iter_with_size(values, 4).unwrap();
755 let result = pack_fixed_values(&key_type, &values);
756 if unique > capacity {
757 let error = result.unwrap_err();
758 let ParquetError::External(source) = &error else {
759 panic!("expected an Arrow dictionary overflow error, got {error}");
760 };
761 assert!(
762 matches!(
763 source.downcast_ref::<arrow_schema::ArrowError>(),
764 Some(arrow_schema::ArrowError::DictionaryKeyOverflowError)
765 ),
766 "{error}"
767 );
768 } else {
769 let array = result.unwrap();
770 assert_eq!(array.len(), unique as usize + 2);
771 assert_eq!(array.null_count(), 1);
772 assert!(array.is_null(unique as usize));
773 assert!(array.is_valid(unique as usize + 1));
774 let dictionary = array.as_any_dictionary();
775 assert_eq!(dictionary.values().len(), unique as usize);
776 assert_eq!(
777 dictionary.values().data_type(),
778 &ArrowType::FixedSizeBinary(4)
779 );
780 }
781 }
782 }
783 }
784
785 #[test]
788 fn test_hash_collision_deduplication() {
789 let key_type = ArrowType::Int32;
790 let value_type = ArrowType::Utf8;
791
792 let mut ob = OffsetBuffer::<i32>::with_capacity(3);
794 ob.try_push(b"alpha", false).unwrap();
795 ob.try_push(b"beta", false).unwrap();
796 ob.try_push(b"beta", false).unwrap();
797 let hashes = [0xdeadbeef_u64; 3];
798
799 let array = pack_values_from_offsets_impl::<Int32Type, i32>(
800 &ob,
801 &hashes,
802 None,
803 &key_type,
804 &value_type,
805 )
806 .unwrap();
807
808 let dict = array
809 .as_any()
810 .downcast_ref::<DictionaryArray<Int32Type>>()
811 .unwrap();
812
813 assert_eq!(dict.values().len(), 2);
814 let keys: Vec<i32> = dict.keys().values().iter().copied().collect();
815 assert_eq!(keys, vec![0, 1, 1]);
816 }
817
818 #[test]
820 fn test_values_path_binary_type() {
821 let dict_type =
822 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Binary));
823 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
824 let values = buffer.spill_values().unwrap();
825 for s in [b"abc".as_ref(), b"\x00\xff", b"abc"] {
826 values.try_push(s, false).unwrap();
827 }
828
829 let array = buffer
830 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
831 .unwrap();
832 let dict = array
833 .as_any()
834 .downcast_ref::<DictionaryArray<Int32Type>>()
835 .unwrap();
836
837 assert_eq!(dict.data_type(), &dict_type);
838 assert_eq!(dict.values().data_type(), &ArrowType::Binary);
839 }
840
841 #[test]
842 fn test_key_upper_bound_skip_scan_when_bound_fits_otherwise_scan() {
843 let dict_type =
844 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Utf8));
845 let dict_5 = Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e"])) as ArrayRef;
846
847 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
848 buffer
849 .as_keys_with_bit_width(&dict_5, Some(2))
850 .unwrap()
851 .extend_from_slice(&[0, 1, 2, 3]);
852 assert!(matches!(
853 buffer,
854 DictionaryBuffer::Dict {
855 key_upper_bound: Some(4),
856 ..
857 }
858 ));
859 let array = buffer
860 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
861 .unwrap();
862 assert_eq!(array.len(), 4);
863
864 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
865 buffer
866 .as_keys_with_bit_width(&dict_5, Some(4))
867 .unwrap()
868 .extend_from_slice(&[0, 10]);
869 let err = buffer
870 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
871 .unwrap_err()
872 .to_string();
873 assert!(err.contains("dictionary key beyond bounds"), "{err}");
874
875 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
876 buffer
877 .as_keys_with_bit_width(&dict_5, None)
878 .unwrap()
879 .extend_from_slice(&[0, 7]);
880 buffer
881 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
882 .unwrap_err();
883
884 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
885 buffer
886 .as_keys_with_bit_width(&dict_5, Some(2))
887 .unwrap()
888 .extend_from_slice(&[0, 1]);
889 buffer
890 .as_keys_with_bit_width(&dict_5, Some(4))
891 .unwrap()
892 .extend_from_slice(&[10]);
893 assert!(matches!(
894 buffer,
895 DictionaryBuffer::Dict {
896 key_upper_bound: Some(16),
897 ..
898 }
899 ));
900 buffer
901 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
902 .unwrap_err();
903 }
904}