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, downcast_integer};
23use arrow_array::{
24 ArrayRef, FixedSizeBinaryArray, OffsetSizeTrait, cast::AsArray, make_array,
25 types::ArrowDictionaryKeyType,
26};
27use arrow_buffer::{ArrowNativeType, Buffer, MutableBuffer};
28use arrow_data::ArrayDataBuilder;
29use arrow_schema::DataType as ArrowType;
30use hashbrown::HashMap as HbHashMap;
31use hashbrown::hash_map::Entry;
32use std::hash::{BuildHasher, Hasher};
33use std::mem::size_of;
34use std::ptr::write_unaligned;
35use std::slice::{from_raw_parts, from_raw_parts_mut};
36use std::sync::Arc;
37
38pub enum DictionaryBuffer<K: ArrowNativeType, V: OffsetSizeTrait> {
41 Dict { keys: Vec<K>, values: ArrayRef },
42 Values { values: OffsetBuffer<V> },
43}
44
45impl<K: ArrowNativeType + Ord, V: OffsetSizeTrait> DictionaryBuffer<K, V> {
46 #[cfg_attr(not(test), expect(unused))]
47 pub fn len(&self) -> usize {
48 match self {
49 Self::Dict { keys, .. } => keys.len(),
50 Self::Values { values } => values.len(),
51 }
52 }
53
54 pub fn as_keys(&mut self, dictionary: &ArrayRef) -> Option<&mut Vec<K>> {
62 assert!(K::from_usize(dictionary.len()).is_some());
63
64 match self {
65 Self::Dict { keys, values } => {
66 let values_ptr = std::ptr::from_ref(values.as_ref()).cast::<()>();
70 let dict_ptr = std::ptr::from_ref(dictionary.as_ref()).cast::<()>();
71 if values_ptr == dict_ptr {
72 Some(keys)
73 } else if keys.is_empty() {
74 *values = Arc::clone(dictionary);
75 Some(keys)
76 } else {
77 None
78 }
79 }
80 Self::Values { values } if values.is_empty() => {
81 *self = Self::Dict {
82 keys: Default::default(),
83 values: Arc::clone(dictionary),
84 };
85 match self {
86 Self::Dict { keys, .. } => Some(keys),
87 Self::Values { .. } => unreachable!(),
88 }
89 }
90 Self::Values { .. } => None,
91 }
92 }
93
94 pub fn spill_values(&mut self) -> Result<&mut OffsetBuffer<V>> {
99 match self {
100 Self::Values { values } => Ok(values),
101 Self::Dict { keys, values } => {
102 let mut spilled = OffsetBuffer::with_capacity(0);
103 let data = values.to_data();
104 let dict_buffers = data.buffers();
105 let dict_offsets = dict_buffers[0].typed_data::<V>();
106 let dict_values = dict_buffers[1].as_slice();
107
108 if values.is_empty() {
109 spilled.offsets.resize(keys.len() + 1, V::default());
111 } else {
112 spilled.extend_from_dictionary(keys.as_slice(), dict_offsets, dict_values)?;
118 }
119
120 *self = Self::Values { values: spilled };
121 match self {
122 Self::Values { values } => Ok(values),
123 Self::Dict { .. } => unreachable!(),
124 }
125 }
126 }
127 }
128
129 pub fn into_array(
131 self,
132 null_buffer: Option<Buffer>,
133 data_type: &ArrowType,
134 hash_scratch: &mut MutableBuffer,
135 ) -> Result<ArrayRef> {
136 assert!(matches!(data_type, ArrowType::Dictionary(_, _)));
137
138 match self {
139 Self::Dict { keys, values } => {
140 if !values.is_empty() {
142 let min = K::from_usize(0).unwrap();
143 let max = K::from_usize(values.len()).unwrap();
144
145 if !keys
149 .as_slice()
150 .iter()
151 .copied()
152 .fold(true, |a, x| a && x >= min && x < max)
153 {
154 return Err(general_err!(
155 "dictionary key beyond bounds of dictionary: 0..{}",
156 values.len()
157 ));
158 }
159 }
160
161 let ArrowType::Dictionary(_, value_type) = data_type else {
162 unreachable!()
163 };
164 let values = if let ArrowType::FixedSizeBinary(size) = **value_type {
165 let binary = values.as_binary::<i32>();
166 Arc::new(FixedSizeBinaryArray::new(
167 size,
168 binary.values().clone(),
169 binary.nulls().cloned(),
170 )) as _
171 } else {
172 values
173 };
174
175 let builder = ArrayDataBuilder::new(data_type.clone())
176 .len(keys.len())
177 .add_buffer(Buffer::from_vec(keys))
178 .add_child_data(values.into_data())
179 .null_bit_buffer(null_buffer);
180
181 let data = match cfg!(debug_assertions) {
182 true => builder.build().unwrap(),
183 false => unsafe { builder.build_unchecked() },
184 };
185
186 Ok(make_array(data))
187 }
188 Self::Values { values } => {
189 let (key_type, value_type) = match data_type {
190 ArrowType::Dictionary(k, v) => (k, v.as_ref().clone()),
191 _ => unreachable!(),
192 };
193
194 hash_byte_slices(&values.offsets, &values.values, hash_scratch);
195 let hashes = hashes_as_u64(hash_scratch);
196
197 pack_values_from_offsets(key_type, &value_type, &values, hashes, null_buffer)
198 }
199 }
200 }
201}
202
203impl<K: ArrowNativeType, V: OffsetSizeTrait> ValuesBuffer for DictionaryBuffer<K, V> {
204 fn with_capacity(capacity: usize) -> Self {
205 Self::Values {
206 values: OffsetBuffer::with_capacity(capacity),
207 }
208 }
209
210 fn reserve_exact(&mut self, additional: usize) {
211 match self {
212 Self::Dict { keys, .. } => keys.reserve_exact(additional),
213 Self::Values { values, .. } => values.reserve_exact(additional),
214 }
215 }
216
217 fn pad_nulls(
218 &mut self,
219 read_offset: usize,
220 values_read: usize,
221 levels_read: usize,
222 valid_mask: &[u8],
223 ) -> Result<()> {
224 match self {
225 Self::Dict { keys, .. } => {
226 keys.resize(read_offset + levels_read, K::default());
227 keys.pad_nulls(read_offset, values_read, levels_read, valid_mask)
228 }
229 Self::Values { values, .. } => {
230 values.pad_nulls(read_offset, values_read, levels_read, valid_mask)
231 }
232 }
233 }
234}
235
236macro_rules! offsets_dict_helper {
237 ($k:ty, $key_type:ident, $value_type:ident, $values:ident, $hashes:ident, $null_buffer:ident) => {
238 pack_values_from_offsets_impl::<$k, _>(
239 $values,
240 $hashes,
241 $null_buffer,
242 $key_type,
243 $value_type,
244 )
245 };
246}
247
248fn pack_values_from_offsets<V: OffsetSizeTrait>(
249 key_type: &ArrowType,
250 value_type: &ArrowType,
251 values: &OffsetBuffer<V>,
252 hashes: &[u64],
253 null_buffer: Option<Buffer>,
254) -> Result<ArrayRef> {
255 downcast_integer! {
256 key_type => (offsets_dict_helper, key_type, value_type, values, hashes, null_buffer),
257 _ => unreachable!(),
258 }
259}
260
261struct PassthroughHasher(u64);
264impl std::hash::Hasher for PassthroughHasher {
265 fn finish(&self) -> u64 {
266 self.0
267 }
268 fn write(&mut self, _: &[u8]) {
269 unreachable!()
270 }
271 fn write_u64(&mut self, value: u64) {
272 self.0 = value;
273 }
274}
275#[derive(Default)]
276struct BuildPassthroughHasher;
277impl std::hash::BuildHasher for BuildPassthroughHasher {
278 type Hasher = PassthroughHasher;
279 fn build_hasher(&self) -> PassthroughHasher {
280 PassthroughHasher(0)
281 }
282}
283
284fn pack_values_from_offsets_impl<K: ArrowDictionaryKeyType, V: OffsetSizeTrait>(
288 offset_buffer: &OffsetBuffer<V>,
289 hashes: &[u64],
290 null_buffer: Option<Buffer>,
291 key_type: &ArrowType,
292 value_type: &ArrowType,
293) -> Result<ArrayRef> {
294 let dict_type = ArrowType::Dictionary(Box::new(key_type.clone()), Box::new(value_type.clone()));
295 let num_values = offset_buffer.len();
296
297 let mut keys: Vec<K::Native> = Vec::with_capacity(num_values);
298 let mut unique_offsets: Vec<V> = Vec::with_capacity(num_values + 1);
299 unique_offsets.push(V::default());
300 let mut unique_bytes: Vec<u8> = Vec::with_capacity(offset_buffer.values.len());
301
302 let mut dedup: HbHashMap<u64, (usize, usize), BuildPassthroughHasher> =
303 HbHashMap::with_capacity_and_hasher(num_values, BuildPassthroughHasher);
304
305 let mut collision_overflow: Vec<(u64, usize, usize)> = Vec::new();
308
309 for (input_idx, &hash) in hashes.iter().enumerate() {
310 let byte_start = offset_buffer.offsets[input_idx].as_usize();
311 let byte_end = offset_buffer.offsets[input_idx + 1].as_usize();
312 let bytes = &offset_buffer.values[byte_start..byte_end];
313
314 let output_idx = match dedup.entry(hash) {
315 Entry::Occupied(entry) => {
316 let (first_input_idx, existing_output_idx) = *entry.get();
317 let first_start = offset_buffer.offsets[first_input_idx].as_usize();
318 let first_end = offset_buffer.offsets[first_input_idx + 1].as_usize();
319 if &offset_buffer.values[first_start..first_end] == bytes {
320 existing_output_idx
321 } else {
322 let existing = collision_overflow
324 .iter()
325 .find(|&&(entry_hash, collision_input_idx, _)| {
326 if entry_hash != hash {
327 return false;
328 }
329 let collision_start =
330 offset_buffer.offsets[collision_input_idx].as_usize();
331 let collision_end =
332 offset_buffer.offsets[collision_input_idx + 1].as_usize();
333 &offset_buffer.values[collision_start..collision_end] == bytes
334 })
335 .map(|&(_, _, collision_output_idx)| collision_output_idx);
336
337 match existing {
338 Some(collision_output_idx) => collision_output_idx,
339 None => {
340 let new_output_idx = unique_offsets.len() - 1;
341 unique_bytes.extend_from_slice(bytes);
342 let new_end = V::from_usize(unique_bytes.len()).ok_or_else(|| {
343 general_err!("offset overflow building dictionary")
344 })?;
345 unique_offsets.push(new_end);
346 collision_overflow.push((hash, input_idx, new_output_idx));
347 new_output_idx
348 }
349 }
350 }
351 }
352 Entry::Vacant(entry) => {
353 let output_idx = unique_offsets.len() - 1;
354 unique_bytes.extend_from_slice(bytes);
355 let new_end = V::from_usize(unique_bytes.len())
356 .ok_or_else(|| general_err!("offset overflow building dictionary"))?;
357 unique_offsets.push(new_end);
358 entry.insert((input_idx, output_idx));
359 output_idx
360 }
361 };
362
363 let key = K::Native::from_usize(output_idx)
364 .ok_or_else(|| general_err!("dictionary key overflow"))?;
365 keys.push(key);
366 }
367
368 let num_unique = unique_offsets.len() - 1;
369
370 let value_data = unsafe {
374 arrow_data::ArrayData::builder(value_type.clone())
375 .len(num_unique)
376 .add_buffer(Buffer::from_vec(unique_offsets))
377 .add_buffer(Buffer::from_vec(unique_bytes))
378 .build_unchecked()
379 };
380
381 let dict_array: DictionaryArray<K> = unsafe {
383 arrow_data::ArrayData::builder(dict_type)
384 .len(keys.len())
385 .add_buffer(Buffer::from_vec(keys))
386 .add_child_data(value_data)
387 .null_bit_buffer(null_buffer)
388 .build_unchecked()
389 .into()
390 };
391
392 Ok(Arc::new(dict_array))
393}
394
395fn hash_byte_slices<I: ArrowNativeType>(offsets: &[I], values: &[u8], scratch: &mut MutableBuffer) {
396 let count = offsets.len().saturating_sub(1);
397 scratch.clear();
398 scratch.resize(count * size_of::<u64>(), 0u8);
399
400 let state = RandomState::new();
401
402 #[expect(
404 clippy::cast_ptr_alignment,
405 reason = "MutableBuffer is 64-byte aligned"
406 )]
407 let hash_slots = unsafe { from_raw_parts_mut(scratch.as_mut_ptr().cast::<u64>(), count) };
408
409 for idx in 0..count {
410 let start = offsets[idx].as_usize();
411 let end = offsets[idx + 1].as_usize();
412 let mut hasher = state.build_hasher();
413 hasher.write(&values[start..end]);
414 unsafe { write_unaligned(hash_slots.as_mut_ptr().add(idx), hasher.finish()) };
416 }
417}
418
419#[inline]
420fn hashes_as_u64(scratch: &[u8]) -> &[u64] {
421 let n = scratch.len() / size_of::<u64>();
422 #[expect(
424 clippy::cast_ptr_alignment,
425 reason = "MutableBuffer is 64-byte aligned"
426 )]
427 unsafe {
428 from_raw_parts(scratch.as_ptr().cast::<u64>(), n)
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use arrow::compute::cast;
436 use arrow_array::StringArray;
437 use arrow_array::types::*;
438
439 #[test]
440 fn test_dictionary_buffer() {
441 let dict_type =
442 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Utf8));
443
444 let d1: ArrayRef = Arc::new(StringArray::from(vec!["hello", "world", "", "a", "b"]));
445
446 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
447
448 let values = &[1, 0, 3, 2, 4];
450 buffer.as_keys(&d1).unwrap().extend_from_slice(values);
451
452 let mut valid = vec![false, false, true, true, false, true, true, true];
453 let valid_buffer = Buffer::from_iter(valid.iter().copied());
454 buffer
455 .pad_nulls(0, values.len(), valid.len(), valid_buffer.as_slice())
456 .unwrap();
457
458 let values = buffer.spill_values().unwrap();
461 let read_offset = values.len();
462 values.try_push(b"bingo", false).unwrap();
463 values.try_push(b"bongo", false).unwrap();
464
465 valid.extend_from_slice(&[false, false, true, false, true]);
466 let null_buffer = Buffer::from_iter(valid.iter().copied());
467 buffer
468 .pad_nulls(read_offset, 2, 5, null_buffer.as_slice())
469 .unwrap();
470
471 assert_eq!(buffer.len(), 13);
472 let split = std::mem::replace(&mut buffer, DictionaryBuffer::with_capacity(0));
473
474 let array = split
475 .into_array(Some(null_buffer), &dict_type, &mut MutableBuffer::new(0))
476 .unwrap();
477 assert_eq!(array.data_type(), &dict_type);
478
479 let strings = cast(&array, &ArrowType::Utf8).unwrap();
480 let strings = strings.as_any().downcast_ref::<StringArray>().unwrap();
481 assert_eq!(
482 strings.iter().collect::<Vec<_>>(),
483 vec![
484 None,
485 None,
486 Some("world"),
487 Some("hello"),
488 None,
489 Some("a"),
490 Some(""),
491 Some("b"),
492 None,
493 None,
494 Some("bingo"),
495 None,
496 Some("bongo")
497 ]
498 );
499
500 assert!(matches!(&buffer, DictionaryBuffer::Values { .. }));
502 assert_eq!(buffer.len(), 0);
503 let d2 = Arc::new(StringArray::from(vec!["bingo", ""])) as ArrayRef;
504 buffer
505 .as_keys(&d2)
506 .unwrap()
507 .extend_from_slice(&[0, 1, 0, 1]);
508
509 let array = std::mem::replace(&mut buffer, DictionaryBuffer::with_capacity(0))
510 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
511 .unwrap();
512 assert_eq!(array.data_type(), &dict_type);
513
514 let strings = cast(&array, &ArrowType::Utf8).unwrap();
515 let strings = strings.as_any().downcast_ref::<StringArray>().unwrap();
516 assert_eq!(
517 strings.iter().collect::<Vec<_>>(),
518 vec![Some("bingo"), Some(""), Some("bingo"), Some("")]
519 );
520
521 assert!(matches!(&buffer, DictionaryBuffer::Values { .. }));
523 assert_eq!(buffer.len(), 0);
524 let d3 = Arc::new(StringArray::from(vec!["bongo"])) as ArrayRef;
525 buffer.as_keys(&d3).unwrap().extend_from_slice(&[0, 0]);
526
527 let d4 = Arc::new(StringArray::from(vec!["bananas"])) as ArrayRef;
529 assert!(buffer.as_keys(&d4).is_none());
530 }
531
532 #[test]
533 fn test_validates_keys() {
534 let dict_type =
535 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Utf8));
536
537 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
538 let d = Arc::new(StringArray::from(vec!["", "f"])) as ArrayRef;
539 buffer.as_keys(&d).unwrap().extend_from_slice(&[0, 2, 0]);
540
541 let err = buffer
542 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
543 .unwrap_err()
544 .to_string();
545 assert!(
546 err.contains("dictionary key beyond bounds of dictionary: 0..2"),
547 "{}",
548 err
549 );
550
551 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
552 let d = Arc::new(StringArray::from(vec![""])) as ArrayRef;
553 buffer.as_keys(&d).unwrap().extend_from_slice(&[0, 1, 0]);
554
555 let err = buffer.spill_values().unwrap_err().to_string();
556 assert!(
557 err.contains("dictionary key beyond bounds of dictionary: 0..1"),
558 "{}",
559 err
560 );
561 }
562
563 #[test]
565 fn test_values_path_large_utf8_type() {
566 let dict_type =
567 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::LargeUtf8));
568 let mut buffer = DictionaryBuffer::<i32, i64>::with_capacity(0);
569 let values = buffer.spill_values().unwrap();
570 for s in ["foo", "bar", "foo"] {
571 values.try_push(s.as_bytes(), false).unwrap();
572 }
573
574 let array = buffer
575 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
576 .unwrap();
577 let dict = array
578 .as_any()
579 .downcast_ref::<DictionaryArray<Int32Type>>()
580 .unwrap();
581
582 assert_eq!(dict.data_type(), &dict_type);
583 assert_eq!(dict.values().data_type(), &ArrowType::LargeUtf8);
584 }
585
586 #[test]
589 fn test_int8_key_overflow_boundary() {
590 let dict_type = ArrowType::Dictionary(Box::new(ArrowType::Int8), Box::new(ArrowType::Utf8));
591 let mut scratch = MutableBuffer::new(0);
592
593 {
594 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
595 let values = buffer.spill_values().unwrap();
596 for i in 0u32..128 {
597 values
598 .try_push(format!("val{i}").as_bytes(), false)
599 .unwrap();
600 }
601 buffer
602 .into_array(None, &dict_type, &mut scratch)
603 .expect("128 unique strings must fit: last index is 127 = i8::MAX");
604 }
605
606 {
607 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
608 let values = buffer.spill_values().unwrap();
609 for i in 0u32..129 {
610 values
611 .try_push(format!("val{i}").as_bytes(), false)
612 .unwrap();
613 }
614 let err = buffer
615 .into_array(None, &dict_type, &mut scratch)
616 .unwrap_err()
617 .to_string();
618 assert!(
619 err.contains("dictionary key overflow"),
620 "expected 'dictionary key overflow', got: {err}"
621 );
622 }
623 }
624
625 #[test]
628 fn test_hash_collision_deduplication() {
629 let key_type = ArrowType::Int32;
630 let value_type = ArrowType::Utf8;
631
632 let mut ob = OffsetBuffer::<i32>::with_capacity(3);
634 ob.try_push(b"alpha", false).unwrap();
635 ob.try_push(b"beta", false).unwrap();
636 ob.try_push(b"beta", false).unwrap();
637 let hashes = [0xdeadbeef_u64; 3];
638
639 let array = pack_values_from_offsets_impl::<Int32Type, i32>(
640 &ob,
641 &hashes,
642 None,
643 &key_type,
644 &value_type,
645 )
646 .unwrap();
647
648 let dict = array
649 .as_any()
650 .downcast_ref::<DictionaryArray<Int32Type>>()
651 .unwrap();
652
653 assert_eq!(dict.values().len(), 2);
654 let keys: Vec<i32> = dict.keys().values().iter().copied().collect();
655 assert_eq!(keys, vec![0, 1, 1]);
656 }
657
658 #[test]
660 fn test_values_path_binary_type() {
661 let dict_type =
662 ArrowType::Dictionary(Box::new(ArrowType::Int32), Box::new(ArrowType::Binary));
663 let mut buffer = DictionaryBuffer::<i32, i32>::with_capacity(0);
664 let values = buffer.spill_values().unwrap();
665 for s in [b"abc".as_ref(), b"\x00\xff", b"abc"] {
666 values.try_push(s, false).unwrap();
667 }
668
669 let array = buffer
670 .into_array(None, &dict_type, &mut MutableBuffer::new(0))
671 .unwrap();
672 let dict = array
673 .as_any()
674 .downcast_ref::<DictionaryArray<Int32Type>>()
675 .unwrap();
676
677 assert_eq!(dict.data_type(), &dict_type);
678 assert_eq!(dict.values().data_type(), &ArrowType::Binary);
679 }
680}