1use std::cmp::min;
28use std::collections::HashMap;
29use std::io::{BufWriter, Write};
30use std::mem::size_of;
31use std::sync::Arc;
32
33use flatbuffers::FlatBufferBuilder;
34
35use arrow_array::cast::*;
36use arrow_array::types::{Int16Type, Int32Type, Int64Type, RunEndIndexType};
37use arrow_array::*;
38use arrow_buffer::{ArrowNativeType, Buffer, MutableBuffer, ToByteSlice};
39use arrow_data::{ArrayData, ArrayDataBuilder, BufferSpec, layout};
40use arrow_schema::*;
41
42use crate::CONTINUATION_MARKER;
43use crate::compression::CompressionCodec;
44#[expect(deprecated)]
45pub use crate::compression::{CompressionContext, IpcWriteContext};
46use crate::convert::IpcSchemaEncoder;
47
48#[derive(Debug, Clone)]
50pub struct IpcWriteOptions {
51 alignment: u8,
54 write_legacy_ipc_format: bool,
56 metadata_version: crate::MetadataVersion,
65 batch_compression_type: Option<crate::CompressionType>,
68 batch_compression_level: Option<i32>,
70 dictionary_handling: DictionaryHandling,
72}
73
74enum EncodedBuffer {
80 Raw(Buffer),
82 Compressed(Vec<u8>),
84}
85
86impl EncodedBuffer {
87 fn as_slice(&self) -> &[u8] {
88 match self {
89 EncodedBuffer::Raw(b) => b.as_slice(),
90 EncodedBuffer::Compressed(v) => v.as_slice(),
91 }
92 }
93
94 fn len(&self) -> usize {
95 match self {
96 EncodedBuffer::Raw(b) => b.len(),
97 EncodedBuffer::Compressed(v) => v.len(),
98 }
99 }
100}
101#[derive(Default)]
106struct IpcMetadataBuilder {
107 nodes: Vec<crate::FieldNode>,
108 buffers: Vec<crate::Buffer>,
109}
110
111enum IpcBodySink<'a> {
116 Write(&'a mut Vec<u8>),
118 Collect(&'a mut Vec<EncodedBuffer>),
120}
121impl IpcBodySink<'_> {
122 pub fn write(&mut self, pad_len: usize, buffer: EncodedBuffer) {
124 match self {
125 IpcBodySink::Write(vec) => {
126 vec.extend_from_slice(buffer.as_slice());
127 vec.extend_from_slice(&PADDING[..pad_len]);
128 }
129 IpcBodySink::Collect(vec) => {
130 vec.push(buffer);
131 }
132 }
133 }
134}
135
136struct MetadataLayout {
137 padded_header_len: usize,
138 padded_metadata_len: usize,
139 metadata_padding: usize,
140}
141
142impl MetadataLayout {
143 fn new(metadata_len: usize, write_options: &IpcWriteOptions) -> MetadataLayout {
144 let prefix_size = if write_options.write_legacy_ipc_format {
145 4
146 } else {
147 8
148 };
149 let alignment_mask = usize::from(write_options.alignment - 1);
150 let padded_header_len = (metadata_len + prefix_size + alignment_mask) & !alignment_mask;
151 let padded_metadata_len = padded_header_len - prefix_size;
152 let metadata_padding = padded_metadata_len - metadata_len;
153
154 MetadataLayout {
155 padded_header_len,
156 padded_metadata_len,
157 metadata_padding,
158 }
159 }
160}
161
162trait IpcMessageSink {
168 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError>;
169
170 fn write_vec(&mut self, bytes: Vec<u8>) -> Result<(), ArrowError> {
171 self.write_slice(&bytes)
172 }
173
174 fn write_encoded_buffer(&mut self, buffer: EncodedBuffer) -> Result<(), ArrowError> {
175 self.write_slice(buffer.as_slice())
176 }
177}
178
179trait IpcMessageSinkExt: IpcMessageSink {
181 fn write_padding(&mut self, len: usize) -> Result<(), ArrowError> {
182 self.write_slice(&PADDING[..len])
183 }
184
185 fn write_continuation(
187 &mut self,
188 write_options: &IpcWriteOptions,
189 metadata_len: i32,
190 ) -> Result<(), ArrowError> {
191 let mut buffer = [0; 8];
192 let len = match write_options.metadata_version {
193 crate::MetadataVersion::V1
194 | crate::MetadataVersion::V2
195 | crate::MetadataVersion::V3 => {
196 unreachable!("Options with the metadata version cannot be created")
197 }
198 crate::MetadataVersion::V4 => {
199 let metadata_len_bytes = metadata_len.to_le_bytes();
200 if !write_options.write_legacy_ipc_format {
201 buffer[..4].copy_from_slice(&CONTINUATION_MARKER);
203 buffer[4..].copy_from_slice(&metadata_len_bytes);
204 8
205 } else {
206 buffer[..4].copy_from_slice(&metadata_len_bytes);
207 4
208 }
209 }
210 crate::MetadataVersion::V5 => {
211 buffer[..4].copy_from_slice(&CONTINUATION_MARKER);
212 buffer[4..].copy_from_slice(&metadata_len.to_le_bytes());
213 8
214 }
215 z => panic!("Unsupported crate::MetadataVersion {z:?}"),
216 };
217 self.write_slice(&buffer[..len])
218 }
219
220 fn write_body_data(&mut self, data: Vec<u8>, alignment: u8) -> Result<usize, ArrowError> {
221 let len = data.len();
222 let pad_len = pad_to_alignment(alignment, len);
223 self.write_vec(data)?;
224 self.write_padding(pad_len)?;
225 Ok(len + pad_len)
226 }
227
228 fn write_encoded_data(
233 &mut self,
234 encoded: EncodedData,
235 write_options: &IpcWriteOptions,
236 ) -> Result<(usize, usize), ArrowError> {
237 let arrow_data_len = encoded.arrow_data.len();
238 if !arrow_data_len.is_multiple_of(usize::from(write_options.alignment)) {
239 return Err(ArrowError::MemoryError(
240 "Arrow data not aligned".to_string(),
241 ));
242 }
243
244 let metadata = encoded.ipc_message;
245 let metadata_len = metadata.len();
246 let layout = MetadataLayout::new(metadata_len, write_options);
247
248 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
249 self.write_vec(metadata)?;
250 self.write_padding(layout.metadata_padding)?;
251
252 let body_len = if arrow_data_len > 0 {
253 self.write_body_data(encoded.arrow_data, write_options.alignment)?
254 } else {
255 0
256 };
257
258 Ok((layout.padded_header_len, body_len))
259 }
260
261 fn write_eos(&mut self, write_options: &IpcWriteOptions) -> Result<(), ArrowError> {
263 self.write_continuation(write_options, 0)?;
264 Ok(())
265 }
266}
267
268impl<T: IpcMessageSink + ?Sized> IpcMessageSinkExt for T {}
269
270trait IpcRecordBatchSink: IpcMessageSinkExt {
272 fn write_record_batch(
286 &mut self,
287 metadata: &[u8],
288 encoded_buffers: &mut Vec<EncodedBuffer>,
289 body_len: usize,
290 write_options: &IpcWriteOptions,
291 ) -> Result<(usize, usize), ArrowError> {
292 let alignment = write_options.alignment;
293 let layout = MetadataLayout::new(metadata.len(), write_options);
294
295 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
296 self.write_slice(metadata)?;
297 self.write_padding(layout.metadata_padding)?;
298 for enc in encoded_buffers.drain(..) {
299 let len = enc.len();
300 self.write_encoded_buffer(enc)?;
301 self.write_padding(pad_to_alignment(alignment, len))?;
302 }
303
304 Ok((layout.padded_header_len, body_len))
305 }
306}
307
308impl<W> IpcMessageSink for W
309where
310 W: Write,
311{
312 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError> {
313 if !bytes.is_empty() {
314 self.write_all(bytes)?;
315 }
316 Ok(())
317 }
318}
319
320impl<W> IpcRecordBatchSink for W
321where
322 W: Write,
323{
324 fn write_record_batch(
325 &mut self,
326 metadata: &[u8],
327 encoded_buffers: &mut Vec<EncodedBuffer>,
328 body_len: usize,
329 write_options: &IpcWriteOptions,
330 ) -> Result<(usize, usize), ArrowError> {
331 let alignment = write_options.alignment;
332 let layout = MetadataLayout::new(metadata.len(), write_options);
333
334 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
335 self.write_all(metadata)?;
336 self.write_all(&PADDING[..layout.metadata_padding])?;
337 for enc in encoded_buffers.iter_mut() {
338 self.write_all(enc.as_slice())?;
339 self.write_all(&PADDING[..pad_to_alignment(alignment, enc.len())])?;
340 }
341 encoded_buffers.clear();
347
348 Ok((layout.padded_header_len, body_len))
349 }
350}
351
352struct Buffers<'a> {
354 out: &'a mut Vec<Buffer>,
355}
356
357impl IpcMessageSink for Buffers<'_> {
358 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError> {
359 if !bytes.is_empty() {
360 self.out.push(Buffer::from(bytes));
361 }
362 Ok(())
363 }
364
365 fn write_vec(&mut self, bytes: Vec<u8>) -> Result<(), ArrowError> {
366 if !bytes.is_empty() {
367 self.out.push(Buffer::from(bytes));
368 }
369 Ok(())
370 }
371
372 fn write_encoded_buffer(&mut self, buffer: EncodedBuffer) -> Result<(), ArrowError> {
373 match buffer {
374 EncodedBuffer::Raw(buffer) => self.out.push(buffer),
375 EncodedBuffer::Compressed(bytes) => self.out.push(Buffer::from(bytes)),
376 }
377 Ok(())
378 }
379}
380
381impl IpcRecordBatchSink for Buffers<'_> {}
382
383struct IpcWriteMetadata {
388 dictionary_block_sizes: Vec<(usize, usize)>,
391 padded_header_len: usize,
393 body_len: usize,
395}
396
397impl IpcWriteOptions {
398 pub fn try_with_compression(
403 mut self,
404 batch_compression_type: Option<crate::CompressionType>,
405 ) -> Result<Self, ArrowError> {
406 self.batch_compression_type = batch_compression_type;
407
408 if self.batch_compression_type.is_some()
409 && self.metadata_version < crate::MetadataVersion::V5
410 {
411 return Err(ArrowError::InvalidArgumentError(
412 "Compression only supported in metadata v5 and above".to_string(),
413 ));
414 }
415 Ok(self)
416 }
417
418 pub fn try_with_compression_level(
423 mut self,
424 batch_compression_level: Option<i32>,
425 ) -> Result<Self, ArrowError> {
426 self.batch_compression_level = batch_compression_level;
427
428 if self.batch_compression_level.is_some()
429 && self.metadata_version < crate::MetadataVersion::V5
430 {
431 return Err(ArrowError::InvalidArgumentError(
432 "Compression only supported in metadata v5 and above".to_string(),
433 ));
434 }
435
436 match (self.batch_compression_type, self.batch_compression_level) {
437 (Some(crate::CompressionType::ZSTD), Some(level)) => {
438 return self.check_zstd_level(level);
439 }
440 (Some(crate::CompressionType::LZ4_FRAME), Some(_)) => {
441 return Err(ArrowError::InvalidArgumentError(
442 "LZ4 Frame compression does not support configurable compression levels"
443 .to_string(),
444 ));
445 }
446 _ => {}
447 }
448
449 Ok(self)
450 }
451
452 #[cfg(not(feature = "zstd"))]
453 fn check_zstd_level(self, _level: i32) -> Result<Self, ArrowError> {
454 Err(ArrowError::InvalidArgumentError(
455 "zstd IPC compression requires the zstd feature".to_string(),
456 ))
457 }
458
459 #[cfg(feature = "zstd")]
460 fn check_zstd_level(self, level: i32) -> Result<Self, ArrowError> {
461 let range = zstd::compression_level_range();
462 if !range.contains(&level) {
463 return Err(ArrowError::InvalidArgumentError(format!(
464 "ZSTD compression level must be between {} and {}, got {}",
465 range.start(),
466 range.end(),
467 level,
468 )));
469 }
470
471 Ok(self)
472 }
473
474 pub fn try_new(
476 alignment: usize,
477 write_legacy_ipc_format: bool,
478 metadata_version: crate::MetadataVersion,
479 ) -> Result<Self, ArrowError> {
480 let alignment: u8 = match alignment {
481 8 => 8,
482 16 => 16,
483 32 => 32,
484 64 => 64,
485 _ => {
486 return Err(ArrowError::InvalidArgumentError(
487 "Alignment should be 8, 16, 32, or 64.".to_string(),
488 ));
489 }
490 };
491 match metadata_version {
492 crate::MetadataVersion::V1
493 | crate::MetadataVersion::V2
494 | crate::MetadataVersion::V3 => Err(ArrowError::InvalidArgumentError(
495 "Writing IPC metadata version 3 and lower not supported".to_string(),
496 )),
497 crate::MetadataVersion::V4 => Ok(Self {
498 alignment,
499 write_legacy_ipc_format,
500 metadata_version,
501 batch_compression_type: None,
502 batch_compression_level: None,
503 dictionary_handling: DictionaryHandling::default(),
504 }),
505 crate::MetadataVersion::V5 => {
506 if write_legacy_ipc_format {
507 Err(ArrowError::InvalidArgumentError(
508 "Legacy IPC format only supported on metadata version 4".to_string(),
509 ))
510 } else {
511 Ok(Self {
512 alignment,
513 write_legacy_ipc_format,
514 metadata_version,
515 batch_compression_type: None,
516 batch_compression_level: None,
517 dictionary_handling: DictionaryHandling::default(),
518 })
519 }
520 }
521 z => Err(ArrowError::InvalidArgumentError(format!(
522 "Unsupported crate::MetadataVersion {z:?}"
523 ))),
524 }
525 }
526
527 pub fn with_dictionary_handling(mut self, dictionary_handling: DictionaryHandling) -> Self {
529 self.dictionary_handling = dictionary_handling;
530 self
531 }
532}
533
534impl Default for IpcWriteOptions {
535 fn default() -> Self {
536 Self {
537 alignment: 64,
538 write_legacy_ipc_format: false,
539 metadata_version: crate::MetadataVersion::V5,
540 batch_compression_type: None,
541 batch_compression_level: None,
542 dictionary_handling: DictionaryHandling::default(),
543 }
544 }
545}
546
547#[derive(Debug, Default)]
548pub struct IpcDataGenerator {}
582
583struct DictionaryToEncode {
585 id: i64,
586 data: ArrayData,
587 is_delta: bool,
588}
589
590struct EncodedRecordBatchMeta {
597 fb_offset: flatbuffers::WIPOffset<crate::RecordBatch<'static>>,
599 body_len: usize,
602}
603
604impl IpcDataGenerator {
605 pub fn schema_to_bytes_with_dictionary_tracker(
608 &self,
609 schema: &Schema,
610 dictionary_tracker: &mut DictionaryTracker,
611 write_options: &IpcWriteOptions,
612 ) -> EncodedData {
613 let mut fbb = FlatBufferBuilder::new();
614 let schema = {
615 let fb = IpcSchemaEncoder::new()
616 .with_dictionary_tracker(dictionary_tracker)
617 .schema_to_fb_offset(&mut fbb, schema);
618 fb.as_union_value()
619 };
620
621 let mut message = crate::MessageBuilder::new(&mut fbb);
622 message.add_version(write_options.metadata_version);
623 message.add_header_type(crate::MessageHeader::Schema);
624 message.add_bodyLength(0);
625 message.add_header(schema);
626 let root = message.finish();
628 fbb.finish(root, None);
629
630 let metadata = fbb.finished_data();
631 EncodedData {
632 ipc_message: metadata.to_vec(),
633 arrow_data: vec![],
634 }
635 }
636
637 fn _collect_dict_updates<I: Iterator<Item = i64>>(
638 &self,
639 column: &ArrayRef,
640 dictionaries_to_encode: &mut Vec<DictionaryToEncode>,
641 dictionary_tracker: &mut DictionaryTracker,
642 write_options: &IpcWriteOptions,
643 dict_id: &mut I,
644 ) -> Result<(), ArrowError> {
645 match column.data_type() {
646 DataType::Struct(fields) => {
647 let s = as_struct_array(column);
648 for (field, column) in fields.iter().zip(s.columns()) {
649 self.collect_dict_updates(
650 field,
651 column,
652 dictionaries_to_encode,
653 dictionary_tracker,
654 write_options,
655 dict_id,
656 )?;
657 }
658 }
659 DataType::RunEndEncoded(_, values) => {
660 let data = column.to_data();
661 if data.child_data().len() != 2 {
662 return Err(ArrowError::InvalidArgumentError(format!(
663 "The run encoded array should have exactly two child arrays. Found {}",
664 data.child_data().len()
665 )));
666 }
667 let values_array = make_array(data.child_data()[1].clone());
670 self.collect_dict_updates(
671 values,
672 &values_array,
673 dictionaries_to_encode,
674 dictionary_tracker,
675 write_options,
676 dict_id,
677 )?;
678 }
679 DataType::List(field) => {
680 let list = as_list_array(column);
681 self.collect_dict_updates(
682 field,
683 list.values(),
684 dictionaries_to_encode,
685 dictionary_tracker,
686 write_options,
687 dict_id,
688 )?;
689 }
690 DataType::LargeList(field) => {
691 let list = as_large_list_array(column);
692 self.collect_dict_updates(
693 field,
694 list.values(),
695 dictionaries_to_encode,
696 dictionary_tracker,
697 write_options,
698 dict_id,
699 )?;
700 }
701 DataType::ListView(field) => {
702 let list = column.as_list_view::<i32>();
703 self.collect_dict_updates(
704 field,
705 list.values(),
706 dictionaries_to_encode,
707 dictionary_tracker,
708 write_options,
709 dict_id,
710 )?;
711 }
712 DataType::LargeListView(field) => {
713 let list = column.as_list_view::<i64>();
714 self.collect_dict_updates(
715 field,
716 list.values(),
717 dictionaries_to_encode,
718 dictionary_tracker,
719 write_options,
720 dict_id,
721 )?;
722 }
723 DataType::FixedSizeList(field, _) => {
724 let list = column
725 .as_any()
726 .downcast_ref::<FixedSizeListArray>()
727 .expect("Unable to downcast to fixed size list array");
728 self.collect_dict_updates(
729 field,
730 list.values(),
731 dictionaries_to_encode,
732 dictionary_tracker,
733 write_options,
734 dict_id,
735 )?;
736 }
737 DataType::Map(field, _) => {
738 let map_array = as_map_array(column);
739
740 let (keys, values) = match field.data_type() {
741 DataType::Struct(fields) if fields.len() == 2 => (&fields[0], &fields[1]),
742 _ => panic!("Incorrect field data type {:?}", field.data_type()),
743 };
744
745 self.collect_dict_updates(
747 keys,
748 map_array.keys(),
749 dictionaries_to_encode,
750 dictionary_tracker,
751 write_options,
752 dict_id,
753 )?;
754
755 self.collect_dict_updates(
757 values,
758 map_array.values(),
759 dictionaries_to_encode,
760 dictionary_tracker,
761 write_options,
762 dict_id,
763 )?;
764 }
765 DataType::Union(fields, _) => {
766 let union = as_union_array(column);
767 for (type_id, field) in fields.iter() {
768 let column = union.child(type_id);
769 self.collect_dict_updates(
770 field,
771 column,
772 dictionaries_to_encode,
773 dictionary_tracker,
774 write_options,
775 dict_id,
776 )?;
777 }
778 }
779 _ => (),
780 }
781
782 Ok(())
783 }
784
785 fn collect_dict_updates<I: Iterator<Item = i64>>(
789 &self,
790 field: &Field,
791 column: &ArrayRef,
792 dictionaries_to_encode: &mut Vec<DictionaryToEncode>,
793 dictionary_tracker: &mut DictionaryTracker,
794 write_options: &IpcWriteOptions,
795 dict_id_seq: &mut I,
796 ) -> Result<(), ArrowError> {
797 match column.data_type() {
798 DataType::Dictionary(_key_type, value_type) => {
799 if matches!(value_type.as_ref(), DataType::Dictionary(_, _)) {
800 return Err(ArrowError::InvalidArgumentError(format!(
801 "Arrow IPC field metadata cannot encode direct dictionary-of-dictionary values for field {:?}",
802 field.name()
803 )));
804 }
805
806 let dict_data = column.to_data();
807 let dict_values = &dict_data.child_data()[0];
808
809 let values = make_array(dict_data.child_data()[0].clone());
810
811 self._collect_dict_updates(
812 &values,
813 dictionaries_to_encode,
814 dictionary_tracker,
815 write_options,
816 dict_id_seq,
817 )?;
818
819 let dict_id = dict_id_seq.next().ok_or_else(|| {
823 ArrowError::IpcError(format!(
824 "no dict id for field {:?}: field.data_type={:?}, column.data_type={:?}",
825 field.name(),
826 field.data_type(),
827 column.data_type()
828 ))
829 })?;
830
831 match dictionary_tracker.insert_column(
832 dict_id,
833 column,
834 write_options.dictionary_handling,
835 )? {
836 DictionaryUpdate::None => {}
837 DictionaryUpdate::New | DictionaryUpdate::Replaced => {
838 dictionaries_to_encode.push(DictionaryToEncode {
839 id: dict_id,
840 data: dict_values.clone(),
841 is_delta: false,
842 });
843 }
844 DictionaryUpdate::Delta(data) => {
845 dictionaries_to_encode.push(DictionaryToEncode {
846 id: dict_id,
847 data,
848 is_delta: true,
849 });
850 }
851 }
852 }
853 _ => self._collect_dict_updates(
854 column,
855 dictionaries_to_encode,
856 dictionary_tracker,
857 write_options,
858 dict_id_seq,
859 )?,
860 }
861
862 Ok(())
863 }
864
865 pub fn encode(
869 &self,
870 batch: &RecordBatch,
871 dictionary_tracker: &mut DictionaryTracker,
872 write_options: &IpcWriteOptions,
873 ipc_write_context: &mut IpcWriteContext,
874 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
875 let dictionaries = self.collect_all_dicts(batch, dictionary_tracker, write_options)?;
876 let mut encoded_dictionaries = Vec::with_capacity(dictionaries.len());
877 for dict in dictionaries {
878 encoded_dictionaries.push(self.dictionary_batch_to_bytes(
879 dict,
880 write_options,
881 ipc_write_context,
882 )?);
883 }
884 let mut arrow_data = ipc_write_context.scratch();
885 self.record_batch_to_bytes(
886 batch,
887 write_options,
888 ipc_write_context,
889 &mut IpcBodySink::Write(&mut arrow_data),
890 )?;
891 ipc_write_context.reserve_scratch_with_capacity(arrow_data.capacity());
892 Ok((
893 encoded_dictionaries,
894 EncodedData {
895 ipc_message: ipc_write_context.mut_fbb().finished_data().to_vec(),
896 arrow_data,
897 },
898 ))
899 }
900
901 fn collect_all_dicts(
903 &self,
904 batch: &RecordBatch,
905 dictionary_tracker: &mut DictionaryTracker,
906 write_options: &IpcWriteOptions,
907 ) -> Result<Vec<DictionaryToEncode>, ArrowError> {
908 let schema = batch.schema();
909 let mut dictionaries = Vec::with_capacity(schema.flattened_fields().len());
910 let mut dict_id = dictionary_tracker.dict_ids.clone().into_iter();
911 for (i, field) in schema.fields().iter().enumerate() {
912 self.collect_dict_updates(
913 field,
914 batch.column(i),
915 &mut dictionaries,
916 dictionary_tracker,
917 write_options,
918 &mut dict_id,
919 )?;
920 }
921 Ok(dictionaries)
922 }
923
924 fn write<W: Write>(
928 &self,
929 batch: &RecordBatch,
930 dictionary_tracker: &mut DictionaryTracker,
931 write_options: &IpcWriteOptions,
932 ipc_write_context: &mut IpcWriteContext,
933 writer: &mut W,
934 ) -> Result<IpcWriteMetadata, ArrowError> {
935 self.write_to_sink(
936 batch,
937 dictionary_tracker,
938 write_options,
939 ipc_write_context,
940 writer,
941 )
942 }
943
944 fn encode_to_buffers(
947 &self,
948 batch: &RecordBatch,
949 dictionary_tracker: &mut DictionaryTracker,
950 write_options: &IpcWriteOptions,
951 ipc_write_context: &mut IpcWriteContext,
952 out: &mut Vec<Buffer>,
953 ) -> Result<IpcWriteMetadata, ArrowError> {
954 let mut sink = Buffers { out };
955 self.write_to_sink(
956 batch,
957 dictionary_tracker,
958 write_options,
959 ipc_write_context,
960 &mut sink,
961 )
962 }
963
964 fn write_to_sink<S: IpcRecordBatchSink>(
965 &self,
966 batch: &RecordBatch,
967 dictionary_tracker: &mut DictionaryTracker,
968 write_options: &IpcWriteOptions,
969 ipc_write_context: &mut IpcWriteContext,
970 sink: &mut S,
971 ) -> Result<IpcWriteMetadata, ArrowError> {
972 let dictionaries = self.collect_all_dicts(batch, dictionary_tracker, write_options)?;
973 let mut dictionary_block_sizes = Vec::with_capacity(dictionaries.len());
974
975 let capacity = batch
976 .columns()
977 .iter()
978 .map(|a| estimate_encoded_buffer_count(a.data_type()))
979 .sum();
980 let mut encoded_buffers: Vec<EncodedBuffer> = Vec::with_capacity(capacity);
981
982 for dict in &dictionaries {
983 encoded_buffers.clear();
984
985 let body_len = self.dictionary_batch_to_sink(
986 dict,
987 write_options,
988 ipc_write_context,
989 &mut IpcBodySink::Collect(&mut encoded_buffers),
990 )?;
991
992 dictionary_block_sizes.push(sink.write_record_batch(
993 ipc_write_context.mut_fbb().finished_data(),
994 &mut encoded_buffers,
995 body_len,
996 write_options,
997 )?);
998 }
999
1000 encoded_buffers.clear();
1001 let body_len = self.record_batch_to_bytes(
1002 batch,
1003 write_options,
1004 ipc_write_context,
1005 &mut IpcBodySink::Collect(&mut encoded_buffers),
1006 )?;
1007
1008 let (padded_header_len, body_len) = sink.write_record_batch(
1009 ipc_write_context.mut_fbb().finished_data(),
1010 &mut encoded_buffers,
1011 body_len,
1012 write_options,
1013 )?;
1014
1015 Ok(IpcWriteMetadata {
1016 dictionary_block_sizes,
1017 padded_header_len,
1018 body_len,
1019 })
1020 }
1021
1022 #[deprecated(since = "57.0.0", note = "Use `encode` instead")]
1026 pub fn encoded_batch(
1027 &self,
1028 batch: &RecordBatch,
1029 dictionary_tracker: &mut DictionaryTracker,
1030 write_options: &IpcWriteOptions,
1031 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
1032 self.encode(
1033 batch,
1034 dictionary_tracker,
1035 write_options,
1036 &mut Default::default(),
1037 )
1038 }
1039
1040 fn record_batch_to_bytes(
1051 &self,
1052 batch: &RecordBatch,
1053 write_options: &IpcWriteOptions,
1054 ipc_write_context: &mut IpcWriteContext,
1055 sink: &mut IpcBodySink<'_>,
1056 ) -> Result<usize, ArrowError> {
1057 ipc_write_context.mut_fbb().reset();
1059
1060 let EncodedRecordBatchMeta {
1061 fb_offset: record_batch,
1062 body_len,
1063 } = self.encode_record_batch_data(
1064 batch.columns().iter().map(|array| array.to_data()),
1065 batch.num_rows() as i64,
1066 write_options,
1067 ipc_write_context,
1068 sink,
1069 )?;
1070
1071 let fbb = ipc_write_context.mut_fbb();
1072 let mut message = crate::MessageBuilder::new(fbb);
1073 message.add_version(write_options.metadata_version);
1074 message.add_header_type(crate::MessageHeader::RecordBatch);
1075 message.add_bodyLength(body_len as i64);
1076 message.add_header(record_batch.as_union_value());
1077 let root = message.finish();
1078 fbb.finish(root, None);
1079
1080 Ok(body_len)
1081 }
1082
1083 fn dictionary_batch_to_bytes(
1086 &self,
1087 dict: DictionaryToEncode,
1088 write_options: &IpcWriteOptions,
1089 ipc_write_context: &mut IpcWriteContext,
1090 ) -> Result<EncodedData, ArrowError> {
1091 let mut arrow_data: Vec<u8> = vec![];
1092 self.dictionary_batch_to_sink(
1093 &dict,
1094 write_options,
1095 ipc_write_context,
1096 &mut IpcBodySink::Write(&mut arrow_data),
1097 )?;
1098
1099 Ok(EncodedData {
1100 ipc_message: ipc_write_context.mut_fbb().finished_data().to_vec(),
1101 arrow_data,
1102 })
1103 }
1104
1105 fn dictionary_batch_to_sink(
1112 &self,
1113 dict: &DictionaryToEncode,
1114 write_options: &IpcWriteOptions,
1115 ipc_write_context: &mut IpcWriteContext,
1116 sink: &mut IpcBodySink<'_>,
1117 ) -> Result<usize, ArrowError> {
1118 ipc_write_context.mut_fbb().reset();
1120
1121 let EncodedRecordBatchMeta {
1125 fb_offset: record_batch,
1126 body_len,
1127 } = self.encode_record_batch_data(
1128 std::iter::once(dict.data.clone()),
1129 dict.data.len() as i64,
1130 write_options,
1131 ipc_write_context,
1132 sink,
1133 )?;
1134
1135 let fbb = ipc_write_context.mut_fbb();
1136 let root = {
1137 let mut batch_builder = crate::DictionaryBatchBuilder::new(fbb);
1138 batch_builder.add_id(dict.id);
1139 batch_builder.add_data(record_batch);
1140 batch_builder.add_isDelta(dict.is_delta);
1141 batch_builder.finish().as_union_value()
1142 };
1143
1144 let root = {
1145 let mut message_builder = crate::MessageBuilder::new(fbb);
1146 message_builder.add_version(write_options.metadata_version);
1147 message_builder.add_header_type(crate::MessageHeader::DictionaryBatch);
1148 message_builder.add_bodyLength(body_len as i64);
1149 message_builder.add_header(root);
1150 message_builder.finish()
1151 };
1152
1153 fbb.finish(root, None);
1154
1155 Ok(body_len)
1156 }
1157
1158 #[inline]
1171 fn encode_record_batch_data(
1172 &self,
1173 columns: impl IntoIterator<Item = ArrayData>,
1174 row_count: i64,
1175 write_options: &IpcWriteOptions,
1176 ipc_write_context: &mut IpcWriteContext,
1177 sink: &mut IpcBodySink<'_>,
1178 ) -> Result<EncodedRecordBatchMeta, ArrowError> {
1179 let batch_compression_type = write_options.batch_compression_type;
1180
1181 let compression = batch_compression_type.map(|batch_compression_type| {
1182 let fbb = ipc_write_context.mut_fbb();
1183 let mut c = crate::BodyCompressionBuilder::new(fbb);
1184 c.add_method(crate::BodyCompressionMethod::BUFFER);
1185 c.add_codec(batch_compression_type);
1186 c.finish()
1187 });
1188
1189 let batch_compression_level = write_options.batch_compression_level;
1190 let compression_codec: Option<CompressionCodec> = batch_compression_type
1191 .map(|compression_type| match batch_compression_level {
1192 Some(level) => {
1193 CompressionCodec::try_new_with_compression_level(compression_type, level)
1194 }
1195 None => compression_type.try_into(),
1196 })
1197 .transpose()?;
1198
1199 let mut meta = IpcMetadataBuilder::default();
1200 let mut variadic_buffer_counts: Vec<i64> = vec![];
1201 let mut offset = 0i64;
1202
1203 for array_data in columns {
1204 offset = write_array_data(
1205 &array_data,
1206 &mut meta,
1207 sink,
1208 offset,
1209 compression_codec,
1210 ipc_write_context,
1211 write_options,
1212 )?;
1213 append_variadic_buffer_counts(&mut variadic_buffer_counts, &array_data);
1214 }
1215
1216 debug_assert_eq!(offset % write_options.alignment as i64, 0);
1219 let body_len = offset as usize;
1220
1221 let fbb = ipc_write_context.mut_fbb();
1222 let buffers = fbb.create_vector(&meta.buffers);
1223 let nodes = fbb.create_vector(&meta.nodes);
1224 let variadic_buffer = if variadic_buffer_counts.is_empty() {
1225 None
1226 } else {
1227 Some(fbb.create_vector(&variadic_buffer_counts))
1228 };
1229
1230 let mut batch_builder = crate::RecordBatchBuilder::new(fbb);
1231 batch_builder.add_length(row_count);
1232 batch_builder.add_nodes(nodes);
1233 batch_builder.add_buffers(buffers);
1234 if let Some(c) = compression {
1235 batch_builder.add_compression(c);
1236 }
1237 if let Some(v) = variadic_buffer {
1238 batch_builder.add_variadicBufferCounts(v);
1239 }
1240 let record_batch = batch_builder.finish();
1241
1242 Ok(EncodedRecordBatchMeta {
1243 fb_offset: record_batch,
1244 body_len,
1245 })
1246 }
1247}
1248
1249fn ensure_supported_ipc_schema(schema: &Schema) -> Result<(), ArrowError> {
1250 schema
1251 .fields()
1252 .iter()
1253 .try_for_each(|field| ensure_supported_ipc_data_type(field.name(), field.data_type()))
1254}
1255
1256fn ensure_supported_ipc_data_type(
1257 field_name: &str,
1258 data_type: &DataType,
1259) -> Result<(), ArrowError> {
1260 match data_type {
1261 DataType::Dictionary(_, value_type)
1262 if matches!(value_type.as_ref(), DataType::Dictionary(_, _)) =>
1263 {
1264 Err(ArrowError::InvalidArgumentError(format!(
1265 "Arrow IPC field metadata cannot encode direct dictionary-of-dictionary values for field {field_name:?}"
1266 )))
1267 }
1268 DataType::Dictionary(_, value_type) => {
1269 ensure_supported_ipc_data_type(field_name, value_type)
1270 }
1271 DataType::Struct(fields) => fields
1272 .iter()
1273 .try_for_each(|field| ensure_supported_ipc_data_type(field.name(), field.data_type())),
1274 DataType::RunEndEncoded(_, field)
1275 | DataType::List(field)
1276 | DataType::LargeList(field)
1277 | DataType::ListView(field)
1278 | DataType::LargeListView(field)
1279 | DataType::FixedSizeList(field, _)
1280 | DataType::Map(field, _) => {
1281 ensure_supported_ipc_data_type(field.name(), field.data_type())
1282 }
1283 DataType::Union(fields, _) => fields.iter().try_for_each(|(_, field)| {
1284 ensure_supported_ipc_data_type(field.name(), field.data_type())
1285 }),
1286 _ => Ok(()),
1287 }
1288}
1289
1290fn append_variadic_buffer_counts(counts: &mut Vec<i64>, array: &ArrayData) {
1291 match array.data_type() {
1292 DataType::BinaryView | DataType::Utf8View => {
1293 counts.push(array.buffers().len() as i64 - 1);
1296 }
1297 DataType::Dictionary(_, _) => {
1298 }
1301 _ => {
1302 for child in array.child_data() {
1303 append_variadic_buffer_counts(counts, child)
1304 }
1305 }
1306 }
1307}
1308
1309pub(crate) fn unslice_run_array(arr: ArrayData) -> Result<ArrayData, ArrowError> {
1310 match arr.data_type() {
1311 DataType::RunEndEncoded(k, _) => match k.data_type() {
1312 DataType::Int16 => {
1313 Ok(into_zero_offset_run_array(RunArray::<Int16Type>::from(arr))?.into_data())
1314 }
1315 DataType::Int32 => {
1316 Ok(into_zero_offset_run_array(RunArray::<Int32Type>::from(arr))?.into_data())
1317 }
1318 DataType::Int64 => {
1319 Ok(into_zero_offset_run_array(RunArray::<Int64Type>::from(arr))?.into_data())
1320 }
1321 d => unreachable!("Unexpected data type {d}"),
1322 },
1323 d => Err(ArrowError::InvalidArgumentError(format!(
1324 "The given array is not a run array. Data type of given array: {d}"
1325 ))),
1326 }
1327}
1328
1329fn into_zero_offset_run_array<R: RunEndIndexType>(
1332 run_array: RunArray<R>,
1333) -> Result<RunArray<R>, ArrowError> {
1334 if run_array.is_empty() {
1336 return Ok(ArrayData::new_empty(run_array.data_type()).into());
1337 }
1338
1339 let run_ends = run_array.run_ends();
1340 if run_ends.offset() == 0 && run_ends.max_value() == run_ends.len() {
1341 return Ok(run_array);
1342 }
1343
1344 let start_physical_index = run_ends.get_start_physical_index();
1346
1347 let end_physical_index = run_ends.get_end_physical_index();
1349
1350 let physical_length = end_physical_index - start_physical_index + 1;
1351
1352 let offset = R::Native::usize_as(run_ends.offset());
1354 let mut run_ends_values = Vec::<R::Native>::with_capacity(physical_length);
1355 for run_end_value in &run_ends.values()[start_physical_index..end_physical_index] {
1356 run_ends_values.push(run_end_value.sub_wrapping(offset));
1357 }
1358 run_ends_values.push(R::Native::from_usize(run_array.len()).unwrap());
1359 let offset_buffer = Buffer::from_vec(run_ends_values);
1360 let new_run_ends = unsafe {
1361 ArrayDataBuilder::new(R::DATA_TYPE)
1364 .len(physical_length)
1365 .add_buffer(offset_buffer)
1366 .build_unchecked()
1367 };
1368
1369 let new_values = run_array
1371 .values()
1372 .slice(start_physical_index, physical_length)
1373 .into_data();
1374
1375 let builder = ArrayDataBuilder::new(run_array.data_type().clone())
1376 .len(run_array.len())
1377 .add_child_data(new_run_ends)
1378 .add_child_data(new_values);
1379 let array_data = unsafe {
1380 builder.build_unchecked()
1383 };
1384 Ok(array_data.into())
1385}
1386
1387#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1389pub enum DictionaryHandling {
1390 #[default]
1392 Resend,
1393 Delta,
1399}
1400
1401#[derive(Debug, Clone)]
1403pub enum DictionaryUpdate {
1404 None,
1407 New,
1409 Replaced,
1411 Delta(ArrayData),
1413}
1414
1415#[derive(Debug)]
1421pub struct DictionaryTracker {
1422 written: HashMap<i64, ArrayData>,
1424 dict_ids: Vec<i64>,
1425 error_on_replacement: bool,
1426}
1427
1428impl DictionaryTracker {
1429 pub fn new(error_on_replacement: bool) -> Self {
1435 Self {
1436 written: HashMap::new(),
1437 dict_ids: Vec::new(),
1438 error_on_replacement,
1439 }
1440 }
1441
1442 pub fn next_dict_id(&mut self) -> i64 {
1444 let next = self
1445 .dict_ids
1446 .last()
1447 .copied()
1448 .map(|i| i + 1)
1449 .unwrap_or_default();
1450
1451 self.dict_ids.push(next);
1452 next
1453 }
1454
1455 pub fn dict_id(&mut self) -> &[i64] {
1458 &self.dict_ids
1459 }
1460
1461 #[deprecated(since = "56.1.0", note = "Use `insert_column` instead")]
1471 pub fn insert(&mut self, dict_id: i64, column: &ArrayRef) -> Result<bool, ArrowError> {
1472 let dict_data = column.to_data();
1473 let dict_values = &dict_data.child_data()[0];
1474
1475 if let Some(last) = self.written.get(&dict_id) {
1477 if ArrayData::ptr_eq(&last.child_data()[0], dict_values) {
1478 return Ok(false);
1480 }
1481 if self.error_on_replacement {
1482 if last.child_data()[0] == *dict_values {
1484 return Ok(false);
1486 }
1487 return Err(ArrowError::InvalidArgumentError(
1488 "Dictionary replacement detected when writing IPC file format. \
1489 Arrow IPC files only support a single dictionary for a given field \
1490 across all batches."
1491 .to_string(),
1492 ));
1493 }
1494 }
1495
1496 self.written.insert(dict_id, dict_data);
1497 Ok(true)
1498 }
1499
1500 pub fn insert_column(
1516 &mut self,
1517 dict_id: i64,
1518 column: &ArrayRef,
1519 dict_handling: DictionaryHandling,
1520 ) -> Result<DictionaryUpdate, ArrowError> {
1521 let new_data = column.to_data();
1522 let new_values = &new_data.child_data()[0];
1523
1524 let Some(old) = self.written.get(&dict_id) else {
1526 self.written.insert(dict_id, new_data);
1527 return Ok(DictionaryUpdate::New);
1528 };
1529
1530 let old_values = &old.child_data()[0];
1533 if ArrayData::ptr_eq(old_values, new_values) {
1534 return Ok(DictionaryUpdate::None);
1535 }
1536
1537 let comparison = compare_dictionaries(old_values, new_values);
1539 if matches!(comparison, DictionaryComparison::Equal) {
1540 return Ok(DictionaryUpdate::None);
1541 }
1542
1543 const REPLACEMENT_ERROR: &str = "Dictionary replacement detected when writing IPC file format. \
1544 Arrow IPC files only support a single dictionary for a given field \
1545 across all batches.";
1546
1547 match comparison {
1548 DictionaryComparison::NotEqual => {
1549 if self.error_on_replacement {
1550 return Err(ArrowError::InvalidArgumentError(
1551 REPLACEMENT_ERROR.to_string(),
1552 ));
1553 }
1554
1555 self.written.insert(dict_id, new_data);
1556 Ok(DictionaryUpdate::Replaced)
1557 }
1558 DictionaryComparison::Delta => match dict_handling {
1559 DictionaryHandling::Resend => {
1560 if self.error_on_replacement {
1561 return Err(ArrowError::InvalidArgumentError(
1562 REPLACEMENT_ERROR.to_string(),
1563 ));
1564 }
1565
1566 self.written.insert(dict_id, new_data);
1567 Ok(DictionaryUpdate::Replaced)
1568 }
1569 DictionaryHandling::Delta => {
1570 let delta =
1571 new_values.slice(old_values.len(), new_values.len() - old_values.len());
1572 self.written.insert(dict_id, new_data);
1573 Ok(DictionaryUpdate::Delta(delta))
1574 }
1575 },
1576 DictionaryComparison::Equal => unreachable!("Already checked equal case"),
1577 }
1578 }
1579
1580 pub fn clear(&mut self) {
1586 self.dict_ids.clear();
1587 self.written.clear();
1588 }
1589}
1590
1591#[derive(Debug, Clone)]
1593enum DictionaryComparison {
1594 NotEqual,
1596 Equal,
1598 Delta,
1601}
1602
1603fn compare_dictionaries(old: &ArrayData, new: &ArrayData) -> DictionaryComparison {
1605 let existing_len = old.len();
1607 let new_len = new.len();
1608 if existing_len == new_len {
1609 return if *old == *new {
1610 DictionaryComparison::Equal
1611 } else {
1612 DictionaryComparison::NotEqual
1613 };
1614 }
1615
1616 if new_len < existing_len {
1618 return DictionaryComparison::NotEqual;
1619 }
1620
1621 if new.slice(0, existing_len) == *old {
1623 return DictionaryComparison::Delta;
1624 }
1625
1626 DictionaryComparison::NotEqual
1627}
1628
1629pub struct FileWriter<W> {
1652 writer: W,
1654 write_options: IpcWriteOptions,
1656 schema: SchemaRef,
1658 block_offsets: usize,
1660 dictionary_blocks: Vec<crate::Block>,
1662 record_blocks: Vec<crate::Block>,
1664 finished: bool,
1666 dictionary_tracker: DictionaryTracker,
1668 custom_metadata: Metadata,
1670
1671 data_gen: IpcDataGenerator,
1672
1673 ipc_write_context: IpcWriteContext,
1677}
1678
1679impl<W: Write> FileWriter<BufWriter<W>> {
1680 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1684 Self::try_new(BufWriter::new(writer), schema)
1685 }
1686}
1687
1688impl<W: Write> FileWriter<W> {
1689 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1697 let write_options = IpcWriteOptions::default();
1698 Self::try_new_with_options(writer, schema, write_options)
1699 }
1700
1701 pub fn try_new_with_options(
1709 mut writer: W,
1710 schema: &Schema,
1711 write_options: IpcWriteOptions,
1712 ) -> Result<Self, ArrowError> {
1713 ensure_supported_ipc_schema(schema)?;
1714
1715 let data_gen = IpcDataGenerator::default();
1716 let pad_len = pad_to_alignment(write_options.alignment, super::ARROW_MAGIC.len());
1718 let header_size = super::ARROW_MAGIC.len() + pad_len;
1719 writer.write_all(&super::ARROW_MAGIC)?;
1720 writer.write_all(&PADDING[..pad_len])?;
1721 let mut dictionary_tracker = DictionaryTracker::new(true);
1723 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
1724 schema,
1725 &mut dictionary_tracker,
1726 &write_options,
1727 );
1728 let (meta, data) = write_message(&mut writer, encoded_message, &write_options)?;
1729 Ok(Self {
1730 writer,
1731 write_options,
1732 schema: Arc::new(schema.clone()),
1733 block_offsets: meta + data + header_size,
1734 dictionary_blocks: vec![],
1735 record_blocks: vec![],
1736 finished: false,
1737 dictionary_tracker,
1738 custom_metadata: Default::default(),
1739 data_gen,
1740 ipc_write_context: IpcWriteContext::default(),
1741 })
1742 }
1743
1744 pub fn write_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
1746 self.custom_metadata.insert(key.into(), value.into());
1747 }
1748
1749 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1751 if self.finished {
1752 return Err(ArrowError::IpcError(
1753 "Cannot write record batch to file writer as it is closed".to_string(),
1754 ));
1755 }
1756
1757 let meta = self.data_gen.write(
1758 batch,
1759 &mut self.dictionary_tracker,
1760 &self.write_options,
1761 &mut self.ipc_write_context,
1762 &mut self.writer,
1763 )?;
1764
1765 for (header_len, body_len) in meta.dictionary_block_sizes {
1766 let block = crate::Block::new(
1767 self.block_offsets as i64,
1768 header_len as i32,
1769 body_len as i64,
1770 );
1771 self.dictionary_blocks.push(block);
1772 self.block_offsets += header_len + body_len;
1773 }
1774
1775 let block = crate::Block::new(
1777 self.block_offsets as i64,
1778 meta.padded_header_len as i32,
1779 meta.body_len as i64,
1780 );
1781 self.record_blocks.push(block);
1782 self.block_offsets += meta.padded_header_len + meta.body_len;
1783 Ok(())
1784 }
1785
1786 pub fn finish(&mut self) -> Result<(), ArrowError> {
1788 if self.finished {
1789 return Err(ArrowError::IpcError(
1790 "Cannot write footer to file writer as it is closed".to_string(),
1791 ));
1792 }
1793
1794 self.writer.write_eos(&self.write_options)?;
1796
1797 let mut fbb = FlatBufferBuilder::new();
1798 let dictionaries = fbb.create_vector(&self.dictionary_blocks);
1799 let record_batches = fbb.create_vector(&self.record_blocks);
1800
1801 self.dictionary_tracker.clear();
1803 let schema = IpcSchemaEncoder::new()
1804 .with_dictionary_tracker(&mut self.dictionary_tracker)
1805 .schema_to_fb_offset(&mut fbb, &self.schema);
1806 let fb_custom_metadata = (!self.custom_metadata.is_empty())
1807 .then(|| crate::convert::metadata_to_fb(&mut fbb, &self.custom_metadata));
1808
1809 let root = {
1810 let mut footer_builder = crate::FooterBuilder::new(&mut fbb);
1811 footer_builder.add_version(self.write_options.metadata_version);
1812 footer_builder.add_schema(schema);
1813 footer_builder.add_dictionaries(dictionaries);
1814 footer_builder.add_recordBatches(record_batches);
1815 if let Some(fb_custom_metadata) = fb_custom_metadata {
1816 footer_builder.add_custom_metadata(fb_custom_metadata);
1817 }
1818 footer_builder.finish()
1819 };
1820 fbb.finish(root, None);
1821 let footer_data = fbb.finished_data();
1822 self.writer.write_all(footer_data)?;
1823 self.writer
1824 .write_all(&(footer_data.len() as i32).to_le_bytes())?;
1825 self.writer.write_all(&super::ARROW_MAGIC)?;
1826 self.writer.flush()?;
1827 self.finished = true;
1828
1829 Ok(())
1830 }
1831
1832 pub fn schema(&self) -> &SchemaRef {
1834 &self.schema
1835 }
1836
1837 pub fn get_ref(&self) -> &W {
1839 &self.writer
1840 }
1841
1842 pub fn get_mut(&mut self) -> &mut W {
1846 &mut self.writer
1847 }
1848
1849 pub fn flush(&mut self) -> Result<(), ArrowError> {
1853 self.writer.flush()?;
1854 Ok(())
1855 }
1856
1857 pub fn into_inner(mut self) -> Result<W, ArrowError> {
1866 if !self.finished {
1867 self.finish()?;
1869 }
1870 Ok(self.writer)
1871 }
1872}
1873
1874impl<W: Write> RecordBatchWriter for FileWriter<W> {
1875 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1876 self.write(batch)
1877 }
1878
1879 fn close(mut self) -> Result<(), ArrowError> {
1880 self.finish()
1881 }
1882}
1883
1884pub struct StreamEncoder {
1913 schema: Schema,
1914 write_options: IpcWriteOptions,
1916 schema_encoded: bool,
1918 dictionary_tracker: DictionaryTracker,
1920 data_gen: IpcDataGenerator,
1921 ipc_write_context: IpcWriteContext,
1922}
1923
1924impl StreamEncoder {
1925 pub fn try_new(schema: &Schema) -> Result<Self, ArrowError> {
1927 let write_options = IpcWriteOptions::default();
1928 Self::try_new_with_options(schema, write_options)
1929 }
1930
1931 pub fn try_new_with_options(
1933 schema: &Schema,
1934 write_options: IpcWriteOptions,
1935 ) -> Result<Self, ArrowError> {
1936 ensure_supported_ipc_schema(schema)?;
1937
1938 Ok(Self {
1939 schema: schema.clone(),
1940 write_options,
1941 schema_encoded: false,
1942 dictionary_tracker: DictionaryTracker::new(false),
1943 data_gen: IpcDataGenerator::default(),
1944 ipc_write_context: IpcWriteContext::default(),
1945 })
1946 }
1947
1948 pub fn encode(&mut self, batch: &RecordBatch) -> Result<Vec<Buffer>, ArrowError> {
1958 let mut out = vec![];
1959 self.encode_schema(&mut out)?;
1960 self.data_gen.encode_to_buffers(
1961 batch,
1962 &mut self.dictionary_tracker,
1963 &self.write_options,
1964 &mut self.ipc_write_context,
1965 &mut out,
1966 )?;
1967 Ok(out)
1968 }
1969
1970 pub fn finish(mut self) -> Result<Vec<Buffer>, ArrowError> {
1979 let mut out = vec![];
1980 self.encode_schema(&mut out)?;
1981 let mut sink = Buffers { out: &mut out };
1982 sink.write_eos(&self.write_options)?;
1983 Ok(out)
1984 }
1985
1986 fn encode_schema(&mut self, out: &mut Vec<Buffer>) -> Result<(), ArrowError> {
1987 if !self.schema_encoded {
1988 let encoded_message = self.data_gen.schema_to_bytes_with_dictionary_tracker(
1989 &self.schema,
1990 &mut self.dictionary_tracker,
1991 &self.write_options,
1992 );
1993 let mut sink = Buffers { out };
1994 sink.write_encoded_data(encoded_message, &self.write_options)?;
1995 self.schema_encoded = true;
1996 }
1997 Ok(())
1998 }
1999}
2000
2001pub struct StreamWriter<W> {
2075 writer: W,
2077 write_options: IpcWriteOptions,
2079 finished: bool,
2081 dictionary_tracker: DictionaryTracker,
2083
2084 data_gen: IpcDataGenerator,
2085
2086 ipc_write_context: IpcWriteContext,
2088}
2089
2090impl<W: Write> StreamWriter<BufWriter<W>> {
2091 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
2095 Self::try_new(BufWriter::new(writer), schema)
2096 }
2097}
2098
2099impl<W: Write> StreamWriter<W> {
2100 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
2108 let write_options = IpcWriteOptions::default();
2109 Self::try_new_with_options(writer, schema, write_options)
2110 }
2111
2112 pub fn try_new_with_options(
2118 mut writer: W,
2119 schema: &Schema,
2120 write_options: IpcWriteOptions,
2121 ) -> Result<Self, ArrowError> {
2122 ensure_supported_ipc_schema(schema)?;
2123
2124 let data_gen = IpcDataGenerator::default();
2125 let mut dictionary_tracker = DictionaryTracker::new(false);
2126
2127 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
2129 schema,
2130 &mut dictionary_tracker,
2131 &write_options,
2132 );
2133 write_message(&mut writer, encoded_message, &write_options)?;
2134 Ok(Self {
2135 writer,
2136 write_options,
2137 finished: false,
2138 dictionary_tracker,
2139 data_gen,
2140 ipc_write_context: IpcWriteContext::default(),
2141 })
2142 }
2143
2144 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
2146 if self.finished {
2147 return Err(ArrowError::IpcError(
2148 "Cannot write record batch to stream writer as it is closed".to_string(),
2149 ));
2150 }
2151
2152 self.data_gen.write(
2153 batch,
2154 &mut self.dictionary_tracker,
2155 &self.write_options,
2156 &mut self.ipc_write_context,
2157 &mut self.writer,
2158 )?;
2159 Ok(())
2160 }
2161
2162 pub fn finish(&mut self) -> Result<(), ArrowError> {
2164 if self.finished {
2165 return Err(ArrowError::IpcError(
2166 "Cannot write footer to stream writer as it is closed".to_string(),
2167 ));
2168 }
2169
2170 self.writer.write_eos(&self.write_options)?;
2171 self.writer.flush()?;
2172
2173 self.finished = true;
2174
2175 Ok(())
2176 }
2177
2178 pub fn get_ref(&self) -> &W {
2180 &self.writer
2181 }
2182
2183 pub fn get_mut(&mut self) -> &mut W {
2187 &mut self.writer
2188 }
2189
2190 pub fn flush(&mut self) -> Result<(), ArrowError> {
2194 self.writer.flush()?;
2195 Ok(())
2196 }
2197
2198 pub fn into_inner(mut self) -> Result<W, ArrowError> {
2236 if !self.finished {
2237 self.finish()?;
2239 }
2240 Ok(self.writer)
2241 }
2242}
2243
2244impl<W: Write> RecordBatchWriter for StreamWriter<W> {
2245 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
2246 self.write(batch)
2247 }
2248
2249 fn close(mut self) -> Result<(), ArrowError> {
2250 self.finish()
2251 }
2252}
2253
2254pub struct EncodedData {
2256 pub ipc_message: Vec<u8>,
2258 pub arrow_data: Vec<u8>,
2260}
2261
2262pub fn write_message<W: Write>(
2264 mut writer: W,
2265 encoded: EncodedData,
2266 write_options: &IpcWriteOptions,
2267) -> Result<(usize, usize), ArrowError> {
2268 writer.write_encoded_data(encoded, write_options)
2269}
2270
2271fn has_validity_bitmap(data_type: &DataType, write_options: &IpcWriteOptions) -> bool {
2275 if write_options.metadata_version < crate::MetadataVersion::V5 {
2276 !matches!(data_type, DataType::Null)
2277 } else {
2278 !matches!(
2279 data_type,
2280 DataType::Null | DataType::Union(_, _) | DataType::RunEndEncoded(_, _)
2281 )
2282 }
2283}
2284
2285#[inline]
2287fn buffer_need_truncate(
2288 array_offset: usize,
2289 buffer: &Buffer,
2290 spec: &BufferSpec,
2291 min_length: usize,
2292) -> bool {
2293 spec != &BufferSpec::AlwaysNull && (array_offset != 0 || min_length < buffer.len())
2294}
2295
2296#[inline]
2298fn get_buffer_element_width(spec: &BufferSpec) -> usize {
2299 match spec {
2300 BufferSpec::FixedWidth { byte_width, .. } => *byte_width,
2301 _ => 0,
2302 }
2303}
2304
2305fn reencode_offsets<O: OffsetSizeTrait>(
2308 offsets: &Buffer,
2309 data: &ArrayData,
2310) -> (Buffer, usize, usize) {
2311 let offsets_slice: &[O] = offsets.typed_data::<O>();
2312 let offset_slice = &offsets_slice[data.offset()..data.offset() + data.len() + 1];
2313
2314 let start_offset = offset_slice.first().unwrap();
2315 let end_offset = offset_slice.last().unwrap();
2316
2317 let offsets = match start_offset.as_usize() {
2318 0 => {
2319 let size = size_of::<O>();
2320 offsets.slice_with_length(data.offset() * size, (data.len() + 1) * size)
2321 }
2322 _ => offset_slice.iter().map(|x| *x - *start_offset).collect(),
2323 };
2324
2325 let start_offset = start_offset.as_usize();
2326 let end_offset = end_offset.as_usize();
2327
2328 (offsets, start_offset, end_offset - start_offset)
2329}
2330
2331fn get_byte_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> [Buffer; 2] {
2338 if data.is_empty() {
2339 let mut offsets = MutableBuffer::new(size_of::<O>());
2342 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
2343 return [offsets.into(), MutableBuffer::new(0).into()];
2344 }
2345
2346 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
2347 let values = data.buffers()[1].slice_with_length(original_start_offset, len);
2348 [offsets, values]
2349}
2350
2351fn get_list_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, ArrayData) {
2354 if data.is_empty() {
2355 let mut offsets = MutableBuffer::new(size_of::<O>());
2358 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
2359 return (offsets.into(), data.child_data()[0].slice(0, 0));
2360 }
2361
2362 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
2363 let child_data = data.child_data()[0].slice(original_start_offset, len);
2364 (offsets, child_data)
2365}
2366
2367fn get_list_view_array_buffers<O: OffsetSizeTrait>(
2373 data: &ArrayData,
2374) -> (Buffer, Buffer, ArrayData) {
2375 if data.is_empty() {
2376 return (
2377 MutableBuffer::new(0).into(),
2378 MutableBuffer::new(0).into(),
2379 data.child_data()[0].slice(0, 0),
2380 );
2381 }
2382
2383 let offsets = &data.buffers()[0];
2384 let sizes = &data.buffers()[1];
2385
2386 let element_size = std::mem::size_of::<O>();
2387 let offsets_slice =
2388 offsets.slice_with_length(data.offset() * element_size, data.len() * element_size);
2389 let sizes_slice =
2390 sizes.slice_with_length(data.offset() * element_size, data.len() * element_size);
2391
2392 let child_data = data.child_data()[0].clone();
2393
2394 (offsets_slice, sizes_slice, child_data)
2395}
2396
2397fn get_or_truncate_buffer(array_data: &ArrayData) -> Buffer {
2404 let buffer = &array_data.buffers()[0];
2405 let layout = layout(array_data.data_type());
2406 let spec = &layout.buffers[0];
2407
2408 let byte_width = get_buffer_element_width(spec);
2409 let min_length = array_data.len() * byte_width;
2410 if buffer_need_truncate(array_data.offset(), buffer, spec, min_length) {
2411 let byte_offset = array_data.offset() * byte_width;
2412 let buffer_length = min(min_length, buffer.len() - byte_offset);
2413 buffer.slice_with_length(byte_offset, buffer_length)
2414 } else {
2415 buffer.clone()
2416 }
2417}
2418
2419fn write_array_data(
2425 array_data: &ArrayData,
2426 meta: &mut IpcMetadataBuilder,
2427 sink: &mut IpcBodySink<'_>,
2428 offset: i64,
2429 compression_codec: Option<CompressionCodec>,
2430 ipc_write_context: &mut IpcWriteContext,
2431 write_options: &IpcWriteOptions,
2432) -> Result<i64, ArrowError> {
2433 let mut offset = offset;
2434 let num_rows = array_data.len();
2435 let null_count = array_data.null_count();
2436 if !matches!(array_data.data_type(), DataType::Null) {
2437 meta.nodes
2438 .push(crate::FieldNode::new(num_rows as i64, null_count as i64));
2439 } else {
2440 meta.nodes
2442 .push(crate::FieldNode::new(num_rows as i64, num_rows as i64));
2443 }
2444 if has_validity_bitmap(array_data.data_type(), write_options) {
2445 let null_buffer = match array_data.nulls() {
2448 Some(buffer) if null_count > 0 => buffer.inner().sliced(),
2449 _ => MutableBuffer::new(0).into(),
2450 };
2451
2452 offset = encode_sink_buffer(
2453 null_buffer,
2454 meta,
2455 sink,
2456 offset,
2457 compression_codec,
2458 ipc_write_context,
2459 write_options.alignment,
2460 )?;
2461 }
2462
2463 let data_type = array_data.data_type();
2464 if matches!(data_type, DataType::Binary | DataType::Utf8) {
2465 for buffer in get_byte_array_buffers::<i32>(array_data) {
2466 offset = encode_sink_buffer(
2467 buffer,
2468 meta,
2469 sink,
2470 offset,
2471 compression_codec,
2472 ipc_write_context,
2473 write_options.alignment,
2474 )?;
2475 }
2476 } else if matches!(data_type, DataType::BinaryView | DataType::Utf8View) {
2477 let views = get_or_truncate_buffer(array_data);
2484 offset = encode_sink_buffer(
2485 views,
2486 meta,
2487 sink,
2488 offset,
2489 compression_codec,
2490 ipc_write_context,
2491 write_options.alignment,
2492 )?;
2493
2494 for buffer in array_data.buffers().iter().skip(1) {
2495 offset = encode_sink_buffer(
2496 buffer.clone(),
2497 meta,
2498 sink,
2499 offset,
2500 compression_codec,
2501 ipc_write_context,
2502 write_options.alignment,
2503 )?;
2504 }
2505 } else if matches!(data_type, DataType::LargeBinary | DataType::LargeUtf8) {
2506 for buffer in get_byte_array_buffers::<i64>(array_data) {
2507 offset = encode_sink_buffer(
2508 buffer,
2509 meta,
2510 sink,
2511 offset,
2512 compression_codec,
2513 ipc_write_context,
2514 write_options.alignment,
2515 )?;
2516 }
2517 } else if DataType::is_numeric(data_type)
2518 || DataType::is_temporal(data_type)
2519 || matches!(
2520 array_data.data_type(),
2521 DataType::FixedSizeBinary(_) | DataType::Dictionary(_, _)
2522 )
2523 {
2524 assert_eq!(array_data.buffers().len(), 1);
2526
2527 let buffer = get_or_truncate_buffer(array_data);
2528 offset = encode_sink_buffer(
2529 buffer,
2530 meta,
2531 sink,
2532 offset,
2533 compression_codec,
2534 ipc_write_context,
2535 write_options.alignment,
2536 )?;
2537 } else if matches!(data_type, DataType::Boolean) {
2538 assert_eq!(array_data.buffers().len(), 1);
2541
2542 let buffer = &array_data.buffers()[0];
2543 let buffer = buffer.bit_slice(array_data.offset(), array_data.len());
2544 offset = encode_sink_buffer(
2545 buffer,
2546 meta,
2547 sink,
2548 offset,
2549 compression_codec,
2550 ipc_write_context,
2551 write_options.alignment,
2552 )?;
2553 } else if matches!(
2554 data_type,
2555 DataType::List(_) | DataType::LargeList(_) | DataType::Map(_, _)
2556 ) {
2557 assert_eq!(array_data.buffers().len(), 1);
2558 assert_eq!(array_data.child_data().len(), 1);
2559
2560 let (offsets, sliced_child_data) = match data_type {
2562 DataType::List(_) => get_list_array_buffers::<i32>(array_data),
2563 DataType::Map(_, _) => get_list_array_buffers::<i32>(array_data),
2564 DataType::LargeList(_) => get_list_array_buffers::<i64>(array_data),
2565 _ => unreachable!(),
2566 };
2567 offset = encode_sink_buffer(
2568 offsets,
2569 meta,
2570 sink,
2571 offset,
2572 compression_codec,
2573 ipc_write_context,
2574 write_options.alignment,
2575 )?;
2576 offset = write_array_data(
2577 &sliced_child_data,
2578 meta,
2579 sink,
2580 offset,
2581 compression_codec,
2582 ipc_write_context,
2583 write_options,
2584 )?;
2585 return Ok(offset);
2586 } else if matches!(
2587 data_type,
2588 DataType::ListView(_) | DataType::LargeListView(_)
2589 ) {
2590 assert_eq!(array_data.buffers().len(), 2); assert_eq!(array_data.child_data().len(), 1);
2592
2593 let (offsets, sizes, child_data) = match data_type {
2594 DataType::ListView(_) => get_list_view_array_buffers::<i32>(array_data),
2595 DataType::LargeListView(_) => get_list_view_array_buffers::<i64>(array_data),
2596 _ => unreachable!(),
2597 };
2598
2599 offset = encode_sink_buffer(
2600 offsets,
2601 meta,
2602 sink,
2603 offset,
2604 compression_codec,
2605 ipc_write_context,
2606 write_options.alignment,
2607 )?;
2608 offset = encode_sink_buffer(
2609 sizes,
2610 meta,
2611 sink,
2612 offset,
2613 compression_codec,
2614 ipc_write_context,
2615 write_options.alignment,
2616 )?;
2617
2618 offset = write_array_data(
2619 &child_data,
2620 meta,
2621 sink,
2622 offset,
2623 compression_codec,
2624 ipc_write_context,
2625 write_options,
2626 )?;
2627 return Ok(offset);
2628 } else if let DataType::FixedSizeList(_, fixed_size) = data_type {
2629 assert_eq!(array_data.child_data().len(), 1);
2630 let fixed_size = *fixed_size as usize;
2631
2632 let child_offset = array_data.offset() * fixed_size;
2633 let child_length = array_data.len() * fixed_size;
2634 let child_data = array_data.child_data()[0].slice(child_offset, child_length);
2635
2636 offset = write_array_data(
2637 &child_data,
2638 meta,
2639 sink,
2640 offset,
2641 compression_codec,
2642 ipc_write_context,
2643 write_options,
2644 )?;
2645 return Ok(offset);
2646 } else if matches!(data_type, DataType::Union(_, _)) {
2647 for (buffer, byte_width) in array_data
2648 .buffers()
2649 .iter()
2650 .zip([size_of::<i8>(), size_of::<i32>()])
2651 {
2652 let buffer =
2653 buffer.slice_with_length(array_data.offset() * byte_width, num_rows * byte_width);
2654 offset = encode_sink_buffer(
2655 buffer,
2656 meta,
2657 sink,
2658 offset,
2659 compression_codec,
2660 ipc_write_context,
2661 write_options.alignment,
2662 )?;
2663 }
2664 } else {
2665 for buffer in array_data.buffers() {
2666 offset = encode_sink_buffer(
2667 buffer.clone(),
2668 meta,
2669 sink,
2670 offset,
2671 compression_codec,
2672 ipc_write_context,
2673 write_options.alignment,
2674 )?;
2675 }
2676 }
2677
2678 match array_data.data_type() {
2679 DataType::Dictionary(_, _) => {}
2680 DataType::RunEndEncoded(_, _) => {
2681 let arr = unslice_run_array(array_data.clone())?;
2683 for data_ref in arr.child_data() {
2685 offset = write_array_data(
2687 data_ref,
2688 meta,
2689 sink,
2690 offset,
2691 compression_codec,
2692 ipc_write_context,
2693 write_options,
2694 )?;
2695 }
2696 }
2697 DataType::Union(_, UnionMode::Sparse) => {
2698 for data_ref in array_data.child_data() {
2699 let child_data = data_ref.slice(array_data.offset(), num_rows);
2700 offset = write_array_data(
2701 &child_data,
2702 meta,
2703 sink,
2704 offset,
2705 compression_codec,
2706 ipc_write_context,
2707 write_options,
2708 )?;
2709 }
2710 }
2711 _ => {
2712 for data_ref in array_data.child_data() {
2714 offset = write_array_data(
2716 data_ref,
2717 meta,
2718 sink,
2719 offset,
2720 compression_codec,
2721 ipc_write_context,
2722 write_options,
2723 )?;
2724 }
2725 }
2726 }
2727 Ok(offset)
2728}
2729
2730fn encode_sink_buffer(
2744 buffer: Buffer,
2745 ipc_meta_data: &mut IpcMetadataBuilder,
2746 sink: &mut IpcBodySink<'_>,
2747 offset: i64,
2748 compression_codec: Option<CompressionCodec>,
2749 ipc_write_context: &mut IpcWriteContext,
2750 alignment: u8,
2751) -> Result<i64, ArrowError> {
2752 let (encoded, len) = match compression_codec {
2753 None => {
2754 let len = buffer.len() as i64;
2755 (EncodedBuffer::Raw(buffer), len)
2756 }
2757 Some(codec) => {
2758 let mut scratch = Vec::new();
2759 let written =
2760 codec.compress_to_vec(buffer.as_slice(), &mut scratch, ipc_write_context)?;
2761 let len = i64::try_from(written)
2762 .map_err(|e| ArrowError::InvalidArgumentError(format!("{e}")))?;
2763 (EncodedBuffer::Compressed(scratch), len)
2764 }
2765 };
2766
2767 let pad_len = pad_to_alignment(alignment, len as usize);
2768 sink.write(pad_len, encoded);
2769 ipc_meta_data.buffers.push(crate::Buffer::new(offset, len));
2770 Ok(offset + len + pad_len as i64)
2771}
2772
2773const PADDING: [u8; 64] = [0; 64];
2774
2775#[inline]
2781fn estimate_encoded_buffer_count(dt: &DataType) -> usize {
2782 match dt {
2783 DataType::Null => 0,
2784
2785 DataType::Binary | DataType::Utf8 | DataType::LargeBinary | DataType::LargeUtf8 => 3,
2786
2787 DataType::BinaryView | DataType::Utf8View => 3,
2788
2789 DataType::List(f) | DataType::LargeList(f) | DataType::Map(f, _) => {
2790 2 + estimate_encoded_buffer_count(f.data_type())
2791 }
2792
2793 DataType::ListView(f) | DataType::LargeListView(f) => {
2794 3 + estimate_encoded_buffer_count(f.data_type())
2795 }
2796
2797 DataType::FixedSizeList(f, _) => 1 + estimate_encoded_buffer_count(f.data_type()),
2798
2799 DataType::Struct(fields) => {
2800 1 + fields
2801 .iter()
2802 .map(|f| estimate_encoded_buffer_count(f.data_type()))
2803 .sum::<usize>()
2804 }
2805
2806 DataType::Dictionary(_, _) => 2,
2808
2809 DataType::Union(fields, UnionMode::Sparse) => {
2810 1 + fields
2811 .iter()
2812 .map(|(_, f)| estimate_encoded_buffer_count(f.data_type()))
2813 .sum::<usize>()
2814 }
2815 DataType::Union(fields, UnionMode::Dense) => {
2816 2 + fields
2817 .iter()
2818 .map(|(_, f)| estimate_encoded_buffer_count(f.data_type()))
2819 .sum::<usize>()
2820 }
2821
2822 DataType::RunEndEncoded(run_ends, values) => {
2823 estimate_encoded_buffer_count(run_ends.data_type())
2824 + estimate_encoded_buffer_count(values.data_type())
2825 }
2826 _ => 2,
2828 }
2829}
2830
2831#[inline]
2833fn pad_to_alignment(alignment: u8, len: usize) -> usize {
2834 let a = usize::from(alignment - 1);
2835 ((len + a) & !a) - len
2836}
2837
2838#[cfg(test)]
2839mod tests {
2840 use std::hash::Hasher;
2841 use std::io::Cursor;
2842 use std::io::Seek;
2843
2844 use arrow_array::builder::FixedSizeListBuilder;
2845 use arrow_array::builder::Float32Builder;
2846 use arrow_array::builder::Int64Builder;
2847 use arrow_array::builder::MapBuilder;
2848 use arrow_array::builder::StringViewBuilder;
2849 use arrow_array::builder::UnionBuilder;
2850 use arrow_array::builder::{
2851 GenericListBuilder, GenericListViewBuilder, ListBuilder, StringBuilder,
2852 };
2853 use arrow_array::builder::{PrimitiveRunBuilder, UInt32Builder};
2854 use arrow_array::types::*;
2855 use arrow_buffer::ScalarBuffer;
2856
2857 use crate::MetadataVersion;
2858 use crate::convert::try_fb_to_schema;
2859 use crate::reader::*;
2860 use crate::root_as_footer;
2861
2862 use super::*;
2863
2864 #[test]
2865 fn test_roundtrip_sliced_list_of_union() {
2866 fn decoded(array: &ArrayRef) -> Vec<Vec<Option<i32>>> {
2867 (0..array.len())
2868 .map(|row| {
2869 let values = array.as_list::<i32>().value(row);
2870 let union = values.as_union();
2871 (0..union.len())
2872 .map(|index| {
2873 union
2874 .value(index)
2875 .as_primitive::<Int32Type>()
2876 .iter()
2877 .next()
2878 .unwrap()
2879 })
2880 .collect()
2881 })
2882 .collect()
2883 }
2884
2885 for mode in [UnionMode::Dense, UnionMode::Sparse] {
2886 let fields = UnionFields::try_new(
2887 [3, 7],
2888 [
2889 Field::new("a", DataType::Int32, true),
2890 Field::new("b", DataType::Int32, true),
2891 ],
2892 )
2893 .unwrap();
2894 let offsets = (mode == UnionMode::Dense).then(|| vec![2, 0, 1].into());
2895 let union: ArrayRef = Arc::new(
2896 UnionArray::try_new(
2897 fields,
2898 vec![3_i8, 7, 3].into(),
2899 offsets,
2900 vec![
2901 Arc::new(Int32Array::from(vec![Some(11), None, Some(33)])),
2902 Arc::new(Int32Array::from(vec![Some(101), Some(202), None])),
2903 ],
2904 )
2905 .unwrap(),
2906 );
2907 let item = Arc::new(Field::new("item", union.data_type().clone(), true));
2908 let list: ArrayRef = Arc::new(ListArray::new(
2909 item,
2910 arrow_buffer::OffsetBuffer::from_lengths([1, 2]),
2911 union,
2912 None,
2913 ));
2914 for (offset, len) in [(0, 2), (1, 1), (2, 0)] {
2915 let array = list.slice(offset, len);
2916 array.to_data().validate_full().unwrap();
2917 let expected = decoded(&array);
2918 let schema = Arc::new(Schema::new(vec![Field::new(
2919 "v",
2920 array.data_type().clone(),
2921 true,
2922 )]));
2923 let batch = RecordBatch::try_new(schema, vec![array]).unwrap();
2924 for output in [
2925 deserialize_file(serialize_file(&batch)),
2926 deserialize_stream(serialize_stream(&batch)),
2927 ] {
2928 output.column(0).to_data().validate_full().unwrap();
2929 assert_eq!(decoded(output.column(0)), expected);
2930 }
2931 }
2932 }
2933 }
2934
2935 fn serialize_file(rb: &RecordBatch) -> Vec<u8> {
2936 let mut writer = FileWriter::try_new(vec![], rb.schema_ref()).unwrap();
2937 writer.write(rb).unwrap();
2938 writer.finish().unwrap();
2939 writer.into_inner().unwrap()
2940 }
2941
2942 fn deserialize_file(bytes: Vec<u8>) -> RecordBatch {
2943 let mut reader = FileReader::try_new(Cursor::new(bytes), None).unwrap();
2944 reader.next().unwrap().unwrap()
2945 }
2946
2947 fn serialize_stream(record: &RecordBatch) -> Vec<u8> {
2948 const IPC_ALIGNMENT: usize = 8;
2952
2953 let mut stream_writer = StreamWriter::try_new_with_options(
2954 vec![],
2955 record.schema_ref(),
2956 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
2957 )
2958 .unwrap();
2959 stream_writer.write(record).unwrap();
2960 stream_writer.finish().unwrap();
2961 stream_writer.into_inner().unwrap()
2962 }
2963
2964 fn deserialize_stream(bytes: Vec<u8>) -> RecordBatch {
2965 let mut stream_reader = StreamReader::try_new(Cursor::new(bytes), None).unwrap();
2966 stream_reader.next().unwrap().unwrap()
2967 }
2968
2969 fn encode_stream(
2974 schema: &Schema,
2975 batches: &[RecordBatch],
2976 options: IpcWriteOptions,
2977 ) -> Vec<u8> {
2978 let mut encoder = StreamEncoder::try_new_with_options(schema, options).unwrap();
2979 let mut bytes = Vec::new();
2980 for batch in batches {
2981 for buffer in encoder.encode(batch).unwrap() {
2982 bytes.write_all(buffer.as_slice()).unwrap();
2983 }
2984 }
2985 for buffer in encoder.finish().unwrap() {
2986 bytes.write_all(buffer.as_slice()).unwrap();
2987 }
2988 bytes
2989 }
2990
2991 fn write_stream(schema: &Schema, batches: &[RecordBatch], options: IpcWriteOptions) -> Vec<u8> {
2992 let mut bytes = Vec::new();
2993 let mut writer = StreamWriter::try_new_with_options(&mut bytes, schema, options).unwrap();
2994 for batch in batches {
2995 writer.write(batch).unwrap();
2996 }
2997 writer.finish().unwrap();
2998 bytes
2999 }
3000
3001 #[tokio::test]
3004 async fn test_stream_encoder_async_writer_matches_stream_writer() {
3005 use tokio::io::{AsyncReadExt, AsyncWriteExt};
3006
3007 let batch = record_batch!(("a", Int32, [1, 2, 3]), ("b", Utf8, ["x", "y", "z"])).unwrap();
3008 let options = IpcWriteOptions::default();
3009 let expected = write_stream(
3010 batch.schema_ref(),
3011 std::slice::from_ref(&batch),
3012 options.clone(),
3013 );
3014
3015 let (mut sink, mut source) = tokio::io::duplex(64);
3016 let read = tokio::spawn(async move {
3017 let mut bytes = Vec::new();
3018 source.read_to_end(&mut bytes).await.unwrap();
3019 bytes
3020 });
3021
3022 let mut encoder = StreamEncoder::try_new_with_options(batch.schema_ref(), options).unwrap();
3023 for buffer in encoder.encode(&batch).unwrap() {
3024 sink.write_all(buffer.as_slice()).await.unwrap();
3025 }
3026 for buffer in encoder.finish().unwrap() {
3027 sink.write_all(buffer.as_slice()).await.unwrap();
3028 }
3029 sink.shutdown().await.unwrap();
3030
3031 let encoded = read.await.unwrap();
3032 assert_eq!(encoded, expected);
3033
3034 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
3035 assert_eq!(reader.next().unwrap().unwrap(), batch);
3036 assert!(reader.next().is_none());
3037 }
3038
3039 #[test]
3040 fn test_stream_encoder_empty_stream_matches_stream_writer() {
3041 let schema = Schema::new(vec![Field::new("a", DataType::Int32, true)]);
3042 let options = IpcWriteOptions::default();
3043 let encoded = encode_stream(&schema, &[], options.clone());
3044 let written = write_stream(&schema, &[], options);
3045
3046 assert_eq!(encoded, written);
3047
3048 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
3049 assert!(reader.next().is_none());
3050 }
3051
3052 #[test]
3053 fn test_stream_encoder_dictionary_batches_match_stream_writer() {
3054 let schema = Arc::new(Schema::new(vec![Field::new(
3055 "a",
3056 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
3057 false,
3058 )]));
3059 let batch = RecordBatch::try_new(
3060 schema.clone(),
3061 vec![Arc::new(DictionaryArray::new(
3062 UInt8Array::from_iter_values([0, 1, 0]),
3063 Arc::new(StringArray::from_iter_values(["a", "b"])),
3064 ))],
3065 )
3066 .unwrap();
3067 let options = IpcWriteOptions::default();
3068 let encoded = encode_stream(&schema, std::slice::from_ref(&batch), options.clone());
3069 let written = write_stream(&schema, std::slice::from_ref(&batch), options);
3070
3071 assert_eq!(encoded, written);
3072
3073 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
3074 assert_eq!(reader.next().unwrap().unwrap(), batch);
3075 assert!(reader.next().is_none());
3076 }
3077
3078 #[test]
3079 #[cfg(feature = "lz4")]
3080 fn test_write_empty_record_batch_lz4_compression() {
3081 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
3082 let values: Vec<Option<i32>> = vec![];
3083 let array = Int32Array::from(values);
3084 let record_batch =
3085 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
3086
3087 let mut file = tempfile::tempfile().unwrap();
3088
3089 {
3090 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
3091 .unwrap()
3092 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
3093 .unwrap();
3094
3095 let mut writer =
3096 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
3097 writer.write(&record_batch).unwrap();
3098 writer.finish().unwrap();
3099 }
3100 file.rewind().unwrap();
3101 {
3102 let reader = FileReader::try_new(file, None).unwrap();
3104 for read_batch in reader {
3105 read_batch
3106 .unwrap()
3107 .columns()
3108 .iter()
3109 .zip(record_batch.columns())
3110 .for_each(|(a, b)| {
3111 assert_eq!(a.data_type(), b.data_type());
3112 assert_eq!(a.len(), b.len());
3113 assert_eq!(a.null_count(), b.null_count());
3114 });
3115 }
3116 }
3117 }
3118
3119 #[test]
3120 #[cfg(feature = "lz4")]
3121 fn test_write_file_with_lz4_compression() {
3122 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
3123 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
3124 let array = Int32Array::from(values);
3125 let record_batch =
3126 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
3127
3128 let mut file = tempfile::tempfile().unwrap();
3129 {
3130 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
3131 .unwrap()
3132 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
3133 .unwrap();
3134
3135 let mut writer =
3136 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
3137 writer.write(&record_batch).unwrap();
3138 writer.finish().unwrap();
3139 }
3140 file.rewind().unwrap();
3141 {
3142 let reader = FileReader::try_new(file, None).unwrap();
3144 for read_batch in reader {
3145 read_batch
3146 .unwrap()
3147 .columns()
3148 .iter()
3149 .zip(record_batch.columns())
3150 .for_each(|(a, b)| {
3151 assert_eq!(a.data_type(), b.data_type());
3152 assert_eq!(a.len(), b.len());
3153 assert_eq!(a.null_count(), b.null_count());
3154 });
3155 }
3156 }
3157 }
3158
3159 #[test]
3160 #[cfg(feature = "zstd")]
3161 fn test_write_file_with_zstd_compression() {
3162 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
3163 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
3164 let array = Int32Array::from(values);
3165 let record_batch =
3166 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
3167 let mut file = tempfile::tempfile().unwrap();
3168 {
3169 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
3170 .unwrap()
3171 .try_with_compression(Some(crate::CompressionType::ZSTD))
3172 .unwrap()
3173 .try_with_compression_level(Some(1))
3174 .unwrap();
3175
3176 let mut writer =
3177 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
3178 writer.write(&record_batch).unwrap();
3179 writer.finish().unwrap();
3180 }
3181 file.rewind().unwrap();
3182 {
3183 let reader = FileReader::try_new(file, None).unwrap();
3185 for read_batch in reader {
3186 read_batch
3187 .unwrap()
3188 .columns()
3189 .iter()
3190 .zip(record_batch.columns())
3191 .for_each(|(a, b)| {
3192 assert_eq!(a.data_type(), b.data_type());
3193 assert_eq!(a.len(), b.len());
3194 assert_eq!(a.null_count(), b.null_count());
3195 });
3196 }
3197 }
3198 }
3199
3200 #[test]
3201 fn test_write_file() {
3202 let schema = Schema::new(vec![Field::new("field1", DataType::UInt32, true)]);
3203 let values: Vec<Option<u32>> = vec![
3204 Some(999),
3205 None,
3206 Some(235),
3207 Some(123),
3208 None,
3209 None,
3210 None,
3211 None,
3212 None,
3213 ];
3214 let array1 = UInt32Array::from(values);
3215 let batch =
3216 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array1) as ArrayRef])
3217 .unwrap();
3218 let mut file = tempfile::tempfile().unwrap();
3219 {
3220 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
3221
3222 writer.write(&batch).unwrap();
3223 writer.finish().unwrap();
3224 }
3225 file.rewind().unwrap();
3226
3227 {
3228 let mut reader = FileReader::try_new(file, None).unwrap();
3229 while let Some(Ok(read_batch)) = reader.next() {
3230 read_batch
3231 .columns()
3232 .iter()
3233 .zip(batch.columns())
3234 .for_each(|(a, b)| {
3235 assert_eq!(a.data_type(), b.data_type());
3236 assert_eq!(a.len(), b.len());
3237 assert_eq!(a.null_count(), b.null_count());
3238 });
3239 }
3240 }
3241 }
3242
3243 #[test]
3244 fn test_empty_utf8_ipc_writes_nonempty_offsets_buffer() {
3245 let name = StringArray::from(Vec::<String>::new());
3246 let [offsets, values] = get_byte_array_buffers::<i32>(&name.to_data());
3247
3248 assert_eq!(name.len(), 0);
3249 assert_eq!(
3250 offsets.len(),
3251 std::mem::size_of::<i32>(),
3252 "offsets buffer should contain one zero i32 offset"
3253 );
3254 assert_eq!(values.len(), 0, "values buffer should remain empty");
3255 }
3256
3257 #[test]
3258 fn test_empty_large_utf8_ipc_writes_nonempty_offsets_buffer() {
3259 let name = LargeStringArray::from(Vec::<String>::new());
3260 let [offsets, values] = get_byte_array_buffers::<i64>(&name.to_data());
3261
3262 assert_eq!(name.len(), 0);
3263 assert_eq!(
3264 offsets.len(),
3265 std::mem::size_of::<i64>(),
3266 "offsets buffer should contain one zero i64 offset"
3267 );
3268 assert_eq!(values.len(), 0, "values buffer should remain empty");
3269 }
3270
3271 #[test]
3272 fn test_empty_list_ipc_writes_nonempty_offsets_buffer() {
3273 let list = GenericListBuilder::<i32, _>::new(UInt32Builder::new()).finish();
3274 let (offsets, child_data) = get_list_array_buffers::<i32>(&list.to_data());
3275
3276 assert_eq!(list.len(), 0);
3277 assert_eq!(
3278 offsets.len(),
3279 std::mem::size_of::<i32>(),
3280 "offsets buffer should contain one zero i32 offset"
3281 );
3282 assert_eq!(child_data.len(), 0, "child data should remain empty");
3283 }
3284
3285 #[test]
3286 fn test_empty_large_list_ipc_writes_nonempty_offsets_buffer() {
3287 let list = GenericListBuilder::<i64, _>::new(UInt32Builder::new()).finish();
3288 let (offsets, child_data) = get_list_array_buffers::<i64>(&list.to_data());
3289
3290 assert_eq!(list.len(), 0);
3291 assert_eq!(
3292 offsets.len(),
3293 std::mem::size_of::<i64>(),
3294 "offsets buffer should contain one zero i64 offset"
3295 );
3296 assert_eq!(child_data.len(), 0, "child data should remain empty");
3297 }
3298
3299 fn write_null_file(options: IpcWriteOptions) {
3300 let schema = Schema::new(vec![
3301 Field::new("nulls", DataType::Null, true),
3302 Field::new("int32s", DataType::Int32, false),
3303 Field::new("nulls2", DataType::Null, true),
3304 Field::new("f64s", DataType::Float64, false),
3305 ]);
3306 let array1 = NullArray::new(32);
3307 let array2 = Int32Array::from(vec![1; 32]);
3308 let array3 = NullArray::new(32);
3309 let array4 = Float64Array::from(vec![f64::NAN; 32]);
3310 let batch = RecordBatch::try_new(
3311 Arc::new(schema.clone()),
3312 vec![
3313 Arc::new(array1) as ArrayRef,
3314 Arc::new(array2) as ArrayRef,
3315 Arc::new(array3) as ArrayRef,
3316 Arc::new(array4) as ArrayRef,
3317 ],
3318 )
3319 .unwrap();
3320 let mut file = tempfile::tempfile().unwrap();
3321 {
3322 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
3323
3324 writer.write(&batch).unwrap();
3325 writer.finish().unwrap();
3326 }
3327
3328 file.rewind().unwrap();
3329
3330 {
3331 let reader = FileReader::try_new(file, None).unwrap();
3332 reader.for_each(|maybe_batch| {
3333 maybe_batch
3334 .unwrap()
3335 .columns()
3336 .iter()
3337 .zip(batch.columns())
3338 .for_each(|(a, b)| {
3339 assert_eq!(a.data_type(), b.data_type());
3340 assert_eq!(a.len(), b.len());
3341 assert_eq!(a.null_count(), b.null_count());
3342 });
3343 });
3344 }
3345 }
3346 #[test]
3347 fn test_write_null_file_v4() {
3348 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
3349 write_null_file(IpcWriteOptions::try_new(8, true, MetadataVersion::V4).unwrap());
3350 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V4).unwrap());
3351 write_null_file(IpcWriteOptions::try_new(64, true, MetadataVersion::V4).unwrap());
3352 }
3353
3354 #[test]
3355 fn test_write_null_file_v5() {
3356 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
3357 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V5).unwrap());
3358 }
3359
3360 #[test]
3361 fn track_union_nested_dict() {
3362 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3363
3364 let array = Arc::new(inner) as ArrayRef;
3365
3366 #[expect(deprecated)]
3368 let dctfield = Field::new_dict("dict", array.data_type().clone(), false, 0, false);
3369 let union_fields = std::iter::once((0, Arc::new(dctfield))).collect();
3370
3371 let types = [0, 0, 0].into_iter().collect::<ScalarBuffer<i8>>();
3372 let offsets = [0, 1, 2].into_iter().collect::<ScalarBuffer<i32>>();
3373
3374 let union = UnionArray::try_new(union_fields, types, Some(offsets), vec![array]).unwrap();
3375
3376 let schema = Arc::new(Schema::new(vec![Field::new(
3377 "union",
3378 union.data_type().clone(),
3379 false,
3380 )]));
3381
3382 let r#gen = IpcDataGenerator::default();
3383 let mut dict_tracker = DictionaryTracker::new(false);
3384 r#gen.schema_to_bytes_with_dictionary_tracker(
3385 &schema,
3386 &mut dict_tracker,
3387 &IpcWriteOptions::default(),
3388 );
3389
3390 let batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
3391
3392 r#gen
3393 .encode(
3394 &batch,
3395 &mut dict_tracker,
3396 &Default::default(),
3397 &mut Default::default(),
3398 )
3399 .unwrap();
3400
3401 assert!(dict_tracker.written.contains_key(&0));
3404 }
3405
3406 #[test]
3407 fn track_struct_nested_dict() {
3408 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3409
3410 let array = Arc::new(inner) as ArrayRef;
3411
3412 #[expect(deprecated)]
3414 let dctfield = Arc::new(Field::new_dict(
3415 "dict",
3416 array.data_type().clone(),
3417 false,
3418 2,
3419 false,
3420 ));
3421
3422 let s = StructArray::from(vec![(dctfield, array)]);
3423 let struct_array = Arc::new(s) as ArrayRef;
3424
3425 let schema = Arc::new(Schema::new(vec![Field::new(
3426 "struct",
3427 struct_array.data_type().clone(),
3428 false,
3429 )]));
3430
3431 let r#gen = IpcDataGenerator::default();
3432 let mut dict_tracker = DictionaryTracker::new(false);
3433 r#gen.schema_to_bytes_with_dictionary_tracker(
3434 &schema,
3435 &mut dict_tracker,
3436 &IpcWriteOptions::default(),
3437 );
3438
3439 let batch = RecordBatch::try_new(schema, vec![struct_array]).unwrap();
3440
3441 r#gen
3442 .encode(
3443 &batch,
3444 &mut dict_tracker,
3445 &Default::default(),
3446 &mut Default::default(),
3447 )
3448 .unwrap();
3449
3450 assert!(dict_tracker.written.contains_key(&0));
3451 }
3452
3453 fn write_union_file(options: IpcWriteOptions) {
3454 let schema = Schema::new(vec![Field::new_union(
3455 "union",
3456 vec![0, 1],
3457 vec![
3458 Field::new("a", DataType::Int32, false),
3459 Field::new("c", DataType::Float64, false),
3460 ],
3461 UnionMode::Sparse,
3462 )]);
3463 let mut builder = UnionBuilder::with_capacity_sparse(5);
3464 builder.append::<Int32Type>("a", 1).unwrap();
3465 builder.append_null::<Int32Type>("a").unwrap();
3466 builder.append::<Float64Type>("c", 3.0).unwrap();
3467 builder.append_null::<Float64Type>("c").unwrap();
3468 builder.append::<Int32Type>("a", 4).unwrap();
3469 let union = builder.build().unwrap();
3470
3471 let batch =
3472 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(union) as ArrayRef])
3473 .unwrap();
3474
3475 let mut file = tempfile::tempfile().unwrap();
3476 {
3477 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
3478
3479 writer.write(&batch).unwrap();
3480 writer.finish().unwrap();
3481 }
3482 file.rewind().unwrap();
3483
3484 {
3485 let reader = FileReader::try_new(file, None).unwrap();
3486 reader.for_each(|maybe_batch| {
3487 maybe_batch
3488 .unwrap()
3489 .columns()
3490 .iter()
3491 .zip(batch.columns())
3492 .for_each(|(a, b)| {
3493 assert_eq!(a.data_type(), b.data_type());
3494 assert_eq!(a.len(), b.len());
3495 assert_eq!(a.null_count(), b.null_count());
3496 });
3497 });
3498 }
3499 }
3500
3501 #[test]
3502 fn test_write_union_file_v4_v5() {
3503 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
3504 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
3505 }
3506
3507 #[test]
3508 fn test_write_view_types() {
3509 const LONG_TEST_STRING: &str =
3510 "This is a long string to make sure binary view array handles it";
3511 let schema = Schema::new(vec![
3512 Field::new("field1", DataType::BinaryView, true),
3513 Field::new("field2", DataType::Utf8View, true),
3514 ]);
3515 let values: Vec<Option<&[u8]>> = vec![
3516 Some(b"foo"),
3517 Some(b"bar"),
3518 Some(LONG_TEST_STRING.as_bytes()),
3519 ];
3520 let binary_array = BinaryViewArray::from_iter(values);
3521 let utf8_array =
3522 StringViewArray::from_iter(vec![Some("foo"), Some("bar"), Some(LONG_TEST_STRING)]);
3523 let record_batch = RecordBatch::try_new(
3524 Arc::new(schema.clone()),
3525 vec![Arc::new(binary_array), Arc::new(utf8_array)],
3526 )
3527 .unwrap();
3528
3529 let mut file = tempfile::tempfile().unwrap();
3530 {
3531 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
3532 writer.write(&record_batch).unwrap();
3533 writer.finish().unwrap();
3534 }
3535 file.rewind().unwrap();
3536 {
3537 let mut reader = FileReader::try_new(&file, None).unwrap();
3538 let read_batch = reader.next().unwrap().unwrap();
3539 read_batch
3540 .columns()
3541 .iter()
3542 .zip(record_batch.columns())
3543 .for_each(|(a, b)| {
3544 assert_eq!(a, b);
3545 });
3546 }
3547 file.rewind().unwrap();
3548 {
3549 let mut reader = FileReader::try_new(&file, Some(vec![0])).unwrap();
3550 let read_batch = reader.next().unwrap().unwrap();
3551 assert_eq!(read_batch.num_columns(), 1);
3552 let read_array = read_batch.column(0);
3553 let write_array = record_batch.column(0);
3554 assert_eq!(read_array, write_array);
3555 }
3556 }
3557
3558 #[test]
3559 #[cfg_attr(miri, ignore)] fn truncate_ipc_record_batch() {
3561 fn create_batch(rows: usize) -> RecordBatch {
3562 let schema = Schema::new(vec![
3563 Field::new("a", DataType::Int32, false),
3564 Field::new("b", DataType::Utf8, false),
3565 ]);
3566
3567 let a = Int32Array::from_iter_values(0..rows as i32);
3568 let b = StringArray::from_iter_values((0..rows).map(|i| i.to_string()));
3569
3570 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
3571 }
3572
3573 let big_record_batch = create_batch(65536);
3574
3575 let length = 5;
3576 let small_record_batch = create_batch(length);
3577
3578 let offset = 2;
3579 let record_batch_slice = big_record_batch.slice(offset, length);
3580 assert!(
3581 serialize_stream(&big_record_batch).len() > serialize_stream(&small_record_batch).len()
3582 );
3583 assert_eq!(
3584 serialize_stream(&small_record_batch).len(),
3585 serialize_stream(&record_batch_slice).len()
3586 );
3587
3588 assert_eq!(
3589 deserialize_stream(serialize_stream(&record_batch_slice)),
3590 record_batch_slice
3591 );
3592 }
3593
3594 #[test]
3595 fn truncate_ipc_record_batch_with_nulls() {
3596 fn create_batch() -> RecordBatch {
3597 let schema = Schema::new(vec![
3598 Field::new("a", DataType::Int32, true),
3599 Field::new("b", DataType::Utf8, true),
3600 ]);
3601
3602 let a = Int32Array::from(vec![Some(1), None, Some(1), None, Some(1)]);
3603 let b = StringArray::from(vec![None, Some("a"), Some("a"), None, Some("a")]);
3604
3605 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
3606 }
3607
3608 let record_batch = create_batch();
3609 let record_batch_slice = record_batch.slice(1, 2);
3610 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3611
3612 assert!(
3613 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3614 );
3615
3616 assert!(deserialized_batch.column(0).is_null(0));
3617 assert!(deserialized_batch.column(0).is_valid(1));
3618 assert!(deserialized_batch.column(1).is_valid(0));
3619 assert!(deserialized_batch.column(1).is_valid(1));
3620
3621 assert_eq!(record_batch_slice, deserialized_batch);
3622 }
3623
3624 #[test]
3625 fn test_write_empty_validity_buffer_without_nulls() {
3626 let batch = RecordBatch::try_from_iter([
3627 ("a", Arc::new(Int32Array::from(vec![1, 2])) as _),
3628 (
3630 "b",
3631 Arc::new(Int32Array::from(vec![None, Some(2), Some(3)]).slice(1, 2)) as _,
3632 ),
3633 ("c", Arc::new(Int32Array::from(vec![Some(1), None])) as _),
3634 ])
3635 .unwrap();
3636 assert_eq!(batch.column(1).nulls().map(|n| n.null_count()), Some(0));
3637
3638 let (_, encoded) = IpcDataGenerator::default()
3639 .encode(
3640 &batch,
3641 &mut DictionaryTracker::new(false),
3642 &Default::default(),
3643 &mut Default::default(),
3644 )
3645 .unwrap();
3646 let message = crate::root_as_message(&encoded.ipc_message).unwrap();
3647 let buffers = message.header_as_record_batch().unwrap().buffers().unwrap();
3648 let validity_lengths: Vec<_> = buffers.iter().step_by(2).map(|b| b.length()).collect();
3650 assert_eq!(validity_lengths, [0, 0, 1]);
3651
3652 assert_eq!(deserialize_stream(serialize_stream(&batch)), batch);
3653 }
3654
3655 #[test]
3656 fn truncate_ipc_dictionary_array() {
3657 fn create_batch() -> RecordBatch {
3658 let values: StringArray = [Some("foo"), Some("bar"), Some("baz")]
3659 .into_iter()
3660 .collect();
3661 let keys: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
3662
3663 let array = DictionaryArray::new(keys, Arc::new(values));
3664
3665 let schema = Schema::new(vec![Field::new("dict", array.data_type().clone(), true)]);
3666
3667 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(array)]).unwrap()
3668 }
3669
3670 let record_batch = create_batch();
3671 let record_batch_slice = record_batch.slice(1, 2);
3672 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3673
3674 assert!(
3675 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3676 );
3677
3678 assert!(deserialized_batch.column(0).is_valid(0));
3679 assert!(deserialized_batch.column(0).is_null(1));
3680
3681 assert_eq!(record_batch_slice, deserialized_batch);
3682 }
3683
3684 #[test]
3685 fn truncate_ipc_struct_array() {
3686 fn create_batch() -> RecordBatch {
3687 let strings: StringArray = [Some("foo"), None, Some("bar"), Some("baz")]
3688 .into_iter()
3689 .collect();
3690 let ints: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
3691
3692 let struct_array = StructArray::from(vec![
3693 (
3694 Arc::new(Field::new("s", DataType::Utf8, true)),
3695 Arc::new(strings) as ArrayRef,
3696 ),
3697 (
3698 Arc::new(Field::new("c", DataType::Int32, true)),
3699 Arc::new(ints) as ArrayRef,
3700 ),
3701 ]);
3702
3703 let schema = Schema::new(vec![Field::new(
3704 "struct_array",
3705 struct_array.data_type().clone(),
3706 true,
3707 )]);
3708
3709 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(struct_array)]).unwrap()
3710 }
3711
3712 let record_batch = create_batch();
3713 let record_batch_slice = record_batch.slice(1, 2);
3714 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3715
3716 assert!(
3717 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3718 );
3719
3720 let structs = deserialized_batch
3721 .column(0)
3722 .as_any()
3723 .downcast_ref::<StructArray>()
3724 .unwrap();
3725
3726 assert!(structs.column(0).is_null(0));
3727 assert!(structs.column(0).is_valid(1));
3728 assert!(structs.column(1).is_valid(0));
3729 assert!(structs.column(1).is_null(1));
3730 assert_eq!(record_batch_slice, deserialized_batch);
3731 }
3732
3733 #[test]
3734 fn truncate_ipc_string_array_with_all_empty_string() {
3735 fn create_batch() -> RecordBatch {
3736 let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
3737 let a = StringArray::from(vec![Some(""), Some(""), Some(""), Some(""), Some("")]);
3738 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap()
3739 }
3740
3741 let record_batch = create_batch();
3742 let record_batch_slice = record_batch.slice(0, 1);
3743 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3744
3745 assert!(
3746 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3747 );
3748 assert_eq!(record_batch_slice, deserialized_batch);
3749 }
3750
3751 #[test]
3752 fn test_stream_writer_writes_array_slice() {
3753 let array = UInt32Array::from(vec![Some(1), Some(2), Some(3)]);
3754 assert_eq!(
3755 vec![Some(1), Some(2), Some(3)],
3756 array.iter().collect::<Vec<_>>()
3757 );
3758
3759 let sliced = array.slice(1, 2);
3760 assert_eq!(vec![Some(2), Some(3)], sliced.iter().collect::<Vec<_>>());
3761
3762 let batch = RecordBatch::try_new(
3763 Arc::new(Schema::new(vec![Field::new("a", DataType::UInt32, true)])),
3764 vec![Arc::new(sliced)],
3765 )
3766 .expect("new batch");
3767
3768 let mut writer = StreamWriter::try_new(vec![], batch.schema_ref()).expect("new writer");
3769 writer.write(&batch).expect("write");
3770 let outbuf = writer.into_inner().expect("inner");
3771
3772 let mut reader = StreamReader::try_new(&outbuf[..], None).expect("new reader");
3773 let read_batch = reader.next().unwrap().expect("read batch");
3774
3775 let read_array: &UInt32Array = read_batch.column(0).as_primitive();
3776 assert_eq!(
3777 vec![Some(2), Some(3)],
3778 read_array.iter().collect::<Vec<_>>()
3779 );
3780 }
3781
3782 #[test]
3783 fn test_large_slice_uint32() {
3784 ensure_roundtrip(Arc::new(UInt32Array::from_iter(
3785 (0..8000).map(|i| if i % 2 == 0 { Some(i) } else { None }),
3786 )));
3787 }
3788
3789 #[test]
3790 #[cfg_attr(miri, ignore)] fn test_large_slice_string() {
3792 let strings: Vec<_> = (0..8000)
3793 .map(|i| {
3794 if i % 2 == 0 {
3795 Some(format!("value{i}"))
3796 } else {
3797 None
3798 }
3799 })
3800 .collect();
3801
3802 ensure_roundtrip(Arc::new(StringArray::from(strings)));
3803 }
3804
3805 #[test]
3806 #[cfg_attr(miri, ignore)] fn test_large_slice_string_list() {
3808 let mut ls = ListBuilder::new(StringBuilder::new());
3809
3810 let mut s = String::new();
3811 for row_number in 0..8000 {
3812 if row_number % 2 == 0 {
3813 for list_element in 0..1000 {
3814 s.clear();
3815 use std::fmt::Write;
3816 write!(&mut s, "value{row_number}-{list_element}").unwrap();
3817 ls.values().append_value(&s);
3818 }
3819 ls.append(true)
3820 } else {
3821 ls.append(false); }
3823 }
3824
3825 ensure_roundtrip(Arc::new(ls.finish()));
3826 }
3827
3828 #[test]
3829 #[cfg_attr(miri, ignore)] fn test_large_slice_string_list_of_lists() {
3831 let mut ls = ListBuilder::new(ListBuilder::new(StringBuilder::new()));
3835
3836 for _ in 0..4000 {
3837 ls.values().append(true);
3838 ls.append(true)
3839 }
3840
3841 let mut s = String::new();
3842 for row_number in 0..4000 {
3843 if row_number % 2 == 0 {
3844 for list_element in 0..1000 {
3845 s.clear();
3846 use std::fmt::Write;
3847 write!(&mut s, "value{row_number}-{list_element}").unwrap();
3848 ls.values().values().append_value(&s);
3849 }
3850 ls.values().append(true);
3851 ls.append(true)
3852 } else {
3853 ls.append(false); }
3855 }
3856
3857 ensure_roundtrip(Arc::new(ls.finish()));
3858 }
3859
3860 fn ensure_roundtrip(array: ArrayRef) {
3862 let num_rows = array.len();
3863 let orig_batch = RecordBatch::try_from_iter(vec![("a", array)]).unwrap();
3864 let sliced_batch = orig_batch.slice(1, num_rows - 1);
3866
3867 let schema = orig_batch.schema();
3868 let stream_data = {
3869 let mut writer = StreamWriter::try_new(vec![], &schema).unwrap();
3870 writer.write(&sliced_batch).unwrap();
3871 writer.into_inner().unwrap()
3872 };
3873 let read_batch = {
3874 let projection = None;
3875 let mut reader = StreamReader::try_new(Cursor::new(stream_data), projection).unwrap();
3876 reader
3877 .next()
3878 .expect("expect no errors reading batch")
3879 .expect("expect batch")
3880 };
3881 assert_eq!(sliced_batch, read_batch);
3882
3883 let file_data = {
3884 let mut writer = FileWriter::try_new_buffered(vec![], &schema).unwrap();
3885 writer.write(&sliced_batch).unwrap();
3886 writer.into_inner().unwrap().into_inner().unwrap()
3887 };
3888 let read_batch = {
3889 let projection = None;
3890 let mut reader = FileReader::try_new(Cursor::new(file_data), projection).unwrap();
3891 reader
3892 .next()
3893 .expect("expect no errors reading batch")
3894 .expect("expect batch")
3895 };
3896 assert_eq!(sliced_batch, read_batch);
3897
3898 }
3900
3901 #[test]
3902 fn encode_bools_slice() {
3903 assert_bool_roundtrip([true, false], 1, 1);
3905
3906 assert_bool_roundtrip(
3908 [
3909 true, false, true, true, false, false, true, true, true, false, false, false, true,
3910 true, true, true, false, false, false, false, true, true, true, true, true, false,
3911 false, false, false, false,
3912 ],
3913 13,
3914 17,
3915 );
3916
3917 assert_bool_roundtrip(
3919 [
3920 true, false, true, true, false, false, true, true, true, false, false, false,
3921 ],
3922 8,
3923 2,
3924 );
3925
3926 assert_bool_roundtrip(
3928 [
3929 true, false, true, true, false, false, true, true, true, false, false, false, true,
3930 true, true, true, true, false, false, false, false, false,
3931 ],
3932 8,
3933 8,
3934 );
3935 }
3936
3937 fn assert_bool_roundtrip<const N: usize>(bools: [bool; N], offset: usize, length: usize) {
3938 let val_bool_field = Field::new("val", DataType::Boolean, false);
3939
3940 let schema = Arc::new(Schema::new(vec![val_bool_field]));
3941
3942 let bools = BooleanArray::from(bools.to_vec());
3943
3944 let batch = RecordBatch::try_new(Arc::clone(&schema), vec![Arc::new(bools)]).unwrap();
3945 let batch = batch.slice(offset, length);
3946
3947 let data = serialize_stream(&batch);
3948 let batch2 = deserialize_stream(data);
3949 assert_eq!(batch, batch2);
3950 }
3951
3952 fn assert_empty_run_array_roundtrip<R: RunEndIndexType>() {
3953 let run_ends = PrimitiveArray::<R>::from_iter_values(
3954 [2, 5]
3955 .into_iter()
3956 .map(|v| R::Native::from_usize(v).unwrap()),
3957 );
3958 let values = Int32Array::from(vec![10, 20]);
3959 let array = RunArray::<R>::try_new(&run_ends, &values).unwrap();
3960 let empty = RunArray::<R>::from(ArrayData::new_empty(array.data_type()));
3961
3962 for source in [&array, &empty] {
3963 for offset in 0..=source.len() {
3964 let sliced = source.slice(offset, 0);
3965 let batch = RecordBatch::try_from_iter(vec![("run", Arc::new(sliced) as ArrayRef)])
3966 .unwrap();
3967 for decoded in [
3968 deserialize_stream(serialize_stream(&batch)),
3969 deserialize_file(serialize_file(&batch)),
3970 ] {
3971 assert_eq!(decoded, batch);
3972 let data = decoded.column(0).to_data();
3973 data.validate_full().unwrap();
3974 assert_eq!(data.offset(), 0);
3975 assert!(data.child_data().iter().all(ArrayData::is_empty));
3976 }
3977
3978 let normalized = into_zero_offset_run_array(source.slice(offset, 0)).unwrap();
3979 normalized.to_data().validate_full().unwrap();
3980 assert_eq!(normalized.offset(), 0);
3981 assert!(normalized.run_ends().values().is_empty());
3982 assert!(normalized.values().is_empty());
3983 }
3984 }
3985 }
3986
3987 #[test]
3988 fn test_empty_run_array_roundtrip_i16() {
3989 assert_empty_run_array_roundtrip::<Int16Type>();
3990 }
3991
3992 #[test]
3993 fn test_empty_run_array_roundtrip_i32() {
3994 assert_empty_run_array_roundtrip::<Int32Type>();
3995 }
3996
3997 #[test]
3998 fn test_empty_run_array_roundtrip_i64() {
3999 assert_empty_run_array_roundtrip::<Int64Type>();
4000 }
4001
4002 #[test]
4003 fn test_run_array_unslice() {
4004 let total_len = 80;
4005 let vals: Vec<Option<i32>> = vec![Some(1), None, Some(2), Some(3), Some(4), None, Some(5)];
4006 let repeats: Vec<usize> = vec![3, 4, 1, 2];
4007 let mut input_array: Vec<Option<i32>> = Vec::with_capacity(total_len);
4008 for ix in 0_usize..32 {
4009 let repeat: usize = repeats[ix % repeats.len()];
4010 let val: Option<i32> = vals[ix % vals.len()];
4011 input_array.resize(input_array.len() + repeat, val);
4012 }
4013
4014 let mut builder =
4016 PrimitiveRunBuilder::<Int16Type, Int32Type>::with_capacity(input_array.len());
4017 builder.extend(input_array.iter().copied());
4018 let run_array = builder.finish();
4019
4020 for slice_len in 1..=total_len {
4022 let sliced_run_array: RunArray<Int16Type> =
4024 run_array.slice(0, slice_len).into_data().into();
4025
4026 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
4028 let typed = unsliced_run_array
4029 .downcast::<PrimitiveArray<Int32Type>>()
4030 .unwrap();
4031 let expected: Vec<Option<i32>> = input_array.iter().take(slice_len).copied().collect();
4032 let actual: Vec<Option<i32>> = typed.into_iter().collect();
4033 assert_eq!(expected, actual);
4034
4035 let sliced_run_array: RunArray<Int16Type> = run_array
4037 .slice(total_len - slice_len, slice_len)
4038 .into_data()
4039 .into();
4040
4041 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
4043 let typed = unsliced_run_array
4044 .downcast::<PrimitiveArray<Int32Type>>()
4045 .unwrap();
4046 let expected: Vec<Option<i32>> = input_array
4047 .iter()
4048 .skip(total_len - slice_len)
4049 .copied()
4050 .collect();
4051 let actual: Vec<Option<i32>> = typed.into_iter().collect();
4052 assert_eq!(expected, actual);
4053 }
4054 }
4055
4056 fn generate_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
4057 let mut ls = GenericListBuilder::<O, _>::new(UInt32Builder::new());
4058
4059 for i in 0..100_000 {
4060 for value in [i, i, i] {
4061 ls.values().append_value(value);
4062 }
4063 ls.append(true)
4064 }
4065
4066 ls.finish()
4067 }
4068
4069 fn generate_utf8view_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
4070 let mut ls = GenericListBuilder::<O, _>::new(StringViewBuilder::new());
4071
4072 for i in 0..100_000 {
4073 for value in [
4074 format!("value{i}"),
4075 format!("value{i}"),
4076 format!("value{i}"),
4077 ] {
4078 ls.values().append_value(&value);
4079 }
4080 ls.append(true)
4081 }
4082
4083 ls.finish()
4084 }
4085
4086 fn generate_string_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
4087 let mut ls = GenericListBuilder::<O, _>::new(StringBuilder::new());
4088
4089 for i in 0..100_000 {
4090 for value in [
4091 format!("value{i}"),
4092 format!("value{i}"),
4093 format!("value{i}"),
4094 ] {
4095 ls.values().append_value(&value);
4096 }
4097 ls.append(true)
4098 }
4099
4100 ls.finish()
4101 }
4102
4103 fn generate_nested_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
4104 let mut ls =
4105 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
4106
4107 for _i in 0..10_000 {
4108 for j in 0..10 {
4109 for value in [j, j, j, j] {
4110 ls.values().values().append_value(value);
4111 }
4112 ls.values().append(true)
4113 }
4114 ls.append(true);
4115 }
4116
4117 ls.finish()
4118 }
4119
4120 fn generate_nested_list_data_starting_at_zero<O: OffsetSizeTrait>() -> GenericListArray<O> {
4121 let mut ls =
4122 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
4123
4124 for _i in 0..999 {
4125 ls.values().append(true);
4126 ls.append(true);
4127 }
4128
4129 for j in 0..10 {
4130 for value in [j, j, j, j] {
4131 ls.values().values().append_value(value);
4132 }
4133 ls.values().append(true)
4134 }
4135 ls.append(true);
4136
4137 for i in 0..9_000 {
4138 for j in 0..10 {
4139 for value in [i + j, i + j, i + j, i + j] {
4140 ls.values().values().append_value(value);
4141 }
4142 ls.values().append(true)
4143 }
4144 ls.append(true);
4145 }
4146
4147 ls.finish()
4148 }
4149
4150 fn generate_map_array_data() -> MapArray {
4151 let keys_builder = UInt32Builder::new();
4152 let values_builder = UInt32Builder::new();
4153
4154 let mut builder = MapBuilder::new(None, keys_builder, values_builder);
4155
4156 for i in 0..100_000 {
4157 for _j in 0..3 {
4158 builder.keys().append_value(i);
4159 builder.values().append_value(i * 2);
4160 }
4161 builder.append(true).unwrap();
4162 }
4163
4164 builder.finish()
4165 }
4166
4167 #[test]
4168 #[cfg_attr(miri, ignore)] fn reencode_offsets_when_first_offset_is_not_zero() {
4170 let original_list = generate_list_data::<i32>();
4171 let original_data = original_list.into_data();
4172 let slice_data = original_data.slice(75, 7);
4173 let (new_offsets, original_start, length) =
4174 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
4175 assert_eq!(
4176 vec![0, 3, 6, 9, 12, 15, 18, 21],
4177 new_offsets.typed_data::<i32>()
4178 );
4179 assert_eq!(225, original_start);
4180 assert_eq!(21, length);
4181 }
4182
4183 #[test]
4184 fn reencode_offsets_when_first_offset_is_zero() {
4185 let mut ls = GenericListBuilder::<i32, _>::new(UInt32Builder::new());
4186 ls.append(true);
4188 ls.values().append_value(35);
4189 ls.values().append_value(42);
4190 ls.append(true);
4191 let original_list = ls.finish();
4192 let original_data = original_list.into_data();
4193
4194 let slice_data = original_data.slice(1, 1);
4195 let (new_offsets, original_start, length) =
4196 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
4197 assert_eq!(vec![0, 2], new_offsets.typed_data::<i32>());
4198 assert_eq!(0, original_start);
4199 assert_eq!(2, length);
4200 }
4201
4202 fn roundtrip_ensure_sliced_smaller(in_batch: RecordBatch, expected_size_factor: usize) {
4205 let in_sliced = in_batch.slice(999, 1);
4207
4208 let bytes_batch = serialize_file(&in_batch);
4209 let bytes_sliced = serialize_file(&in_sliced);
4210
4211 assert!(bytes_sliced.len() < (bytes_batch.len() / expected_size_factor));
4213
4214 let out_batch = deserialize_file(bytes_batch);
4216 assert_eq!(in_batch, out_batch);
4217
4218 let out_sliced = deserialize_file(bytes_sliced);
4219 assert_eq!(in_sliced, out_sliced);
4220 }
4221
4222 #[test]
4223 #[cfg_attr(miri, ignore)] fn encode_lists() {
4225 let val_inner = Field::new_list_field(DataType::UInt32, true);
4226 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
4227 let schema = Arc::new(Schema::new(vec![val_list_field]));
4228
4229 let values = Arc::new(generate_list_data::<i32>());
4230
4231 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4232 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4233 }
4234
4235 #[test]
4236 #[cfg_attr(miri, ignore)] fn encode_empty_list() {
4238 let val_inner = Field::new_list_field(DataType::UInt32, true);
4239 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
4240 let schema = Arc::new(Schema::new(vec![val_list_field]));
4241
4242 let values = Arc::new(generate_list_data::<i32>());
4243
4244 let in_batch = RecordBatch::try_new(schema, vec![values])
4245 .unwrap()
4246 .slice(999, 0);
4247 let out_batch = deserialize_file(serialize_file(&in_batch));
4248 assert_eq!(in_batch, out_batch);
4249 }
4250
4251 #[test]
4252 #[cfg_attr(miri, ignore)] fn encode_large_lists() {
4254 let val_inner = Field::new_list_field(DataType::UInt32, true);
4255 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4256 let schema = Arc::new(Schema::new(vec![val_list_field]));
4257
4258 let values = Arc::new(generate_list_data::<i64>());
4259
4260 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4263 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4264 }
4265
4266 #[test]
4267 #[cfg_attr(miri, ignore)] fn encode_large_lists_non_zero_offset() {
4269 let val_inner = Field::new_list_field(DataType::UInt32, true);
4270 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4271 let schema = Arc::new(Schema::new(vec![val_list_field]));
4272
4273 let values = Arc::new(generate_list_data::<i64>());
4274
4275 check_sliced_list_array(schema, values);
4276 }
4277
4278 #[test]
4279 #[cfg_attr(miri, ignore)] fn encode_large_lists_string_non_zero_offset() {
4281 let val_inner = Field::new_list_field(DataType::Utf8, true);
4282 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4283 let schema = Arc::new(Schema::new(vec![val_list_field]));
4284
4285 let values = Arc::new(generate_string_list_data::<i64>());
4286
4287 check_sliced_list_array(schema, values);
4288 }
4289
4290 #[test]
4291 #[cfg_attr(miri, ignore)] fn encode_large_list_string_view_non_zero_offset() {
4293 let val_inner = Field::new_list_field(DataType::Utf8View, true);
4294 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4295 let schema = Arc::new(Schema::new(vec![val_list_field]));
4296
4297 let values = Arc::new(generate_utf8view_list_data::<i64>());
4298
4299 check_sliced_list_array(schema, values);
4300 }
4301
4302 fn check_sliced_list_array(schema: Arc<Schema>, values: Arc<GenericListArray<i64>>) {
4303 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4304 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4305 .unwrap()
4306 .slice(offset, len);
4307 let out_batch = deserialize_file(serialize_file(&in_batch));
4308 assert_eq!(in_batch, out_batch);
4309 }
4310 }
4311
4312 #[test]
4313 #[cfg_attr(miri, ignore)] fn encode_nested_lists() {
4315 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
4316 let inner_list_field = Arc::new(Field::new_list_field(DataType::List(inner_int), true));
4317 let list_field = Field::new("val", DataType::List(inner_list_field), true);
4318 let schema = Arc::new(Schema::new(vec![list_field]));
4319
4320 let values = Arc::new(generate_nested_list_data::<i32>());
4321
4322 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4323 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4324 }
4325
4326 #[test]
4327 #[cfg_attr(miri, ignore)] fn encode_nested_lists_starting_at_zero() {
4329 let inner_int = Arc::new(Field::new("item", DataType::UInt32, true));
4330 let inner_list_field = Arc::new(Field::new("item", DataType::List(inner_int), true));
4331 let list_field = Field::new("val", DataType::List(inner_list_field), true);
4332 let schema = Arc::new(Schema::new(vec![list_field]));
4333
4334 let values = Arc::new(generate_nested_list_data_starting_at_zero::<i32>());
4335
4336 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4337 roundtrip_ensure_sliced_smaller(in_batch, 1);
4338 }
4339
4340 #[test]
4341 #[cfg_attr(miri, ignore)] fn encode_map_array() {
4343 let keys = Arc::new(Field::new(
4344 Field::MAP_KEY_FIELD_DEFAULT_NAME,
4345 DataType::UInt32,
4346 false,
4347 ));
4348 let values = Arc::new(Field::new(
4349 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
4350 DataType::UInt32,
4351 true,
4352 ));
4353 let map_field = Field::new_map(
4354 "map",
4355 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
4356 keys,
4357 values,
4358 false,
4359 true,
4360 );
4361 let schema = Arc::new(Schema::new(vec![map_field]));
4362
4363 let values = Arc::new(generate_map_array_data());
4364
4365 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4366 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4367 }
4368
4369 fn generate_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
4370 let mut builder = GenericListViewBuilder::<O, _>::new(UInt32Builder::new());
4371
4372 for i in 0u32..100_000 {
4373 if i.is_multiple_of(10_000) {
4374 builder.append(false);
4375 continue;
4376 }
4377 for value in [i, i, i] {
4378 builder.values().append_value(value);
4379 }
4380 builder.append(true);
4381 }
4382
4383 builder.finish()
4384 }
4385
4386 #[test]
4387 #[cfg_attr(miri, ignore)] fn encode_list_view_arrays() {
4389 let val_inner = Field::new_list_field(DataType::UInt32, true);
4390 let val_field = Field::new("val", DataType::ListView(Arc::new(val_inner)), true);
4391 let schema = Arc::new(Schema::new(vec![val_field]));
4392
4393 let values = Arc::new(generate_list_view_data::<i32>());
4394
4395 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4396 let out_batch = deserialize_file(serialize_file(&in_batch));
4397 assert_eq!(in_batch, out_batch);
4398 }
4399
4400 #[test]
4401 #[cfg_attr(miri, ignore)] fn encode_large_list_view_arrays() {
4403 let val_inner = Field::new_list_field(DataType::UInt32, true);
4404 let val_field = Field::new("val", DataType::LargeListView(Arc::new(val_inner)), true);
4405 let schema = Arc::new(Schema::new(vec![val_field]));
4406
4407 let values = Arc::new(generate_list_view_data::<i64>());
4408
4409 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4410 let out_batch = deserialize_file(serialize_file(&in_batch));
4411 assert_eq!(in_batch, out_batch);
4412 }
4413
4414 #[test]
4415 #[cfg_attr(miri, ignore)] fn check_sliced_list_view_array() {
4417 let inner = Field::new_list_field(DataType::UInt32, true);
4418 let field = Field::new("val", DataType::ListView(Arc::new(inner)), true);
4419 let schema = Arc::new(Schema::new(vec![field]));
4420 let values = Arc::new(generate_list_view_data::<i32>());
4421
4422 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4423 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4424 .unwrap()
4425 .slice(offset, len);
4426 let out_batch = deserialize_file(serialize_file(&in_batch));
4427 assert_eq!(in_batch, out_batch);
4428 }
4429 }
4430
4431 #[test]
4432 #[cfg_attr(miri, ignore)] fn check_sliced_large_list_view_array() {
4434 let inner = Field::new_list_field(DataType::UInt32, true);
4435 let field = Field::new("val", DataType::LargeListView(Arc::new(inner)), true);
4436 let schema = Arc::new(Schema::new(vec![field]));
4437 let values = Arc::new(generate_list_view_data::<i64>());
4438
4439 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4440 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4441 .unwrap()
4442 .slice(offset, len);
4443 let out_batch = deserialize_file(serialize_file(&in_batch));
4444 assert_eq!(in_batch, out_batch);
4445 }
4446 }
4447
4448 fn generate_nested_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
4449 let inner_builder = UInt32Builder::new();
4450 let middle_builder = GenericListViewBuilder::<O, _>::new(inner_builder);
4451 let mut outer_builder = GenericListViewBuilder::<O, _>::new(middle_builder);
4452
4453 for i in 0u32..10_000 {
4454 if i.is_multiple_of(1_000) {
4455 outer_builder.append(false);
4456 continue;
4457 }
4458
4459 for _ in 0..3 {
4460 for value in [i, i + 1, i + 2] {
4461 outer_builder.values().values().append_value(value);
4462 }
4463 outer_builder.values().append(true);
4464 }
4465 outer_builder.append(true);
4466 }
4467
4468 outer_builder.finish()
4469 }
4470
4471 #[test]
4472 #[cfg_attr(miri, ignore)] fn encode_nested_list_views() {
4474 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
4475 let inner_list_field = Arc::new(Field::new_list_field(DataType::ListView(inner_int), true));
4476 let list_field = Field::new("val", DataType::ListView(inner_list_field), true);
4477 let schema = Arc::new(Schema::new(vec![list_field]));
4478
4479 let values = Arc::new(generate_nested_list_view_data::<i32>());
4480
4481 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4482 let out_batch = deserialize_file(serialize_file(&in_batch));
4483 assert_eq!(in_batch, out_batch);
4484 }
4485
4486 fn test_roundtrip_list_view_of_dict_impl<OffsetSize: OffsetSizeTrait, U: ArrowNativeType>(
4487 list_data_type: DataType,
4488 offsets: &[U; 5],
4489 sizes: &[U; 4],
4490 ) {
4491 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4492 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4493 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4494 let dict_data = dict_array.to_data();
4495
4496 let value_offsets = Buffer::from_slice_ref(offsets);
4497 let value_sizes = Buffer::from_slice_ref(sizes);
4498
4499 let list_data = ArrayData::builder(list_data_type)
4500 .len(4)
4501 .add_buffer(value_offsets)
4502 .add_buffer(value_sizes)
4503 .add_child_data(dict_data)
4504 .build()
4505 .unwrap();
4506 let list_view_array = GenericListViewArray::<OffsetSize>::from(list_data);
4507
4508 let schema = Arc::new(Schema::new(vec![Field::new(
4509 "f1",
4510 list_view_array.data_type().clone(),
4511 false,
4512 )]));
4513 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
4514
4515 let output_batch = deserialize_file(serialize_file(&input_batch));
4516 assert_eq!(input_batch, output_batch);
4517
4518 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4519 assert_eq!(input_batch, output_batch);
4520 }
4521
4522 #[test]
4523 fn test_roundtrip_list_view_of_dict() {
4524 #[expect(deprecated)]
4525 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
4526 "item",
4527 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4528 true,
4529 1,
4530 false,
4531 )));
4532 let offsets: &[i32; 5] = &[0, 2, 4, 4, 7];
4533 let sizes: &[i32; 4] = &[2, 2, 0, 3];
4534 test_roundtrip_list_view_of_dict_impl::<i32, i32>(list_data_type, offsets, sizes);
4535 }
4536
4537 #[test]
4538 fn test_roundtrip_large_list_view_of_dict() {
4539 #[expect(deprecated)]
4540 let list_data_type = DataType::LargeListView(Arc::new(Field::new_dict(
4541 "item",
4542 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4543 true,
4544 2,
4545 false,
4546 )));
4547 let offsets: &[i64; 5] = &[0, 2, 4, 4, 7];
4548 let sizes: &[i64; 4] = &[2, 2, 0, 3];
4549 test_roundtrip_list_view_of_dict_impl::<i64, i64>(list_data_type, offsets, sizes);
4550 }
4551
4552 #[test]
4553 fn test_roundtrip_sliced_list_view_of_dict() {
4554 #[expect(deprecated)]
4555 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
4556 "item",
4557 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4558 true,
4559 3,
4560 false,
4561 )));
4562
4563 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4564 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2, 1, 0, 3, 2, 1]);
4565 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4566 let dict_data = dict_array.to_data();
4567
4568 let offsets: &[i32; 7] = &[0, 2, 4, 4, 7, 9, 12];
4569 let sizes: &[i32; 6] = &[2, 2, 0, 3, 2, 3];
4570 let value_offsets = Buffer::from_slice_ref(offsets);
4571 let value_sizes = Buffer::from_slice_ref(sizes);
4572
4573 let list_data = ArrayData::builder(list_data_type)
4574 .len(6)
4575 .add_buffer(value_offsets)
4576 .add_buffer(value_sizes)
4577 .add_child_data(dict_data)
4578 .build()
4579 .unwrap();
4580 let list_view_array = GenericListViewArray::<i32>::from(list_data);
4581
4582 let schema = Arc::new(Schema::new(vec![Field::new(
4583 "f1",
4584 list_view_array.data_type().clone(),
4585 false,
4586 )]));
4587 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
4588
4589 let sliced_batch = input_batch.slice(1, 4);
4590
4591 let output_batch = deserialize_file(serialize_file(&sliced_batch));
4592 assert_eq!(sliced_batch, output_batch);
4593
4594 let output_batch = deserialize_stream(serialize_stream(&sliced_batch));
4595 assert_eq!(sliced_batch, output_batch);
4596 }
4597
4598 #[test]
4599 fn test_roundtrip_dense_union_of_dict() {
4600 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4601 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4602 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4603
4604 #[expect(deprecated)]
4605 let dict_field = Arc::new(Field::new_dict(
4606 "dict",
4607 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4608 true,
4609 1,
4610 false,
4611 ));
4612 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
4613 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
4614
4615 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
4616 let offsets = ScalarBuffer::from(vec![0i32, 1, 0, 2, 1, 3, 4]);
4617
4618 let int_array = Int32Array::from(vec![100, 200]);
4619
4620 let union = UnionArray::try_new(
4621 union_fields.clone(),
4622 types,
4623 Some(offsets),
4624 vec![Arc::new(dict_array), Arc::new(int_array)],
4625 )
4626 .unwrap();
4627
4628 let schema = Arc::new(Schema::new(vec![Field::new(
4629 "union",
4630 DataType::Union(union_fields, UnionMode::Dense),
4631 false,
4632 )]));
4633 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
4634
4635 let output_batch = deserialize_file(serialize_file(&input_batch));
4636 assert_eq!(input_batch, output_batch);
4637
4638 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4639 assert_eq!(input_batch, output_batch);
4640 }
4641
4642 #[test]
4643 fn test_roundtrip_sparse_union_of_dict() {
4644 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4645 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4646 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4647
4648 #[expect(deprecated)]
4649 let dict_field = Arc::new(Field::new_dict(
4650 "dict",
4651 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4652 true,
4653 2,
4654 false,
4655 ));
4656 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
4657 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
4658
4659 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
4660
4661 let int_array = Int32Array::from(vec![0, 0, 100, 0, 200, 0, 0]);
4662
4663 let union = UnionArray::try_new(
4664 union_fields.clone(),
4665 types,
4666 None,
4667 vec![Arc::new(dict_array), Arc::new(int_array)],
4668 )
4669 .unwrap();
4670
4671 let schema = Arc::new(Schema::new(vec![Field::new(
4672 "union",
4673 DataType::Union(union_fields, UnionMode::Sparse),
4674 false,
4675 )]));
4676 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
4677
4678 let output_batch = deserialize_file(serialize_file(&input_batch));
4679 assert_eq!(input_batch, output_batch);
4680
4681 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4682 assert_eq!(input_batch, output_batch);
4683 }
4684
4685 #[test]
4686 fn test_roundtrip_map_with_dict_keys() {
4687 let key_values = StringArray::from(vec!["key_a", "key_b", "key_c"]);
4690 let keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
4691 let dict_keys = DictionaryArray::new(keys, Arc::new(key_values));
4692
4693 let values = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
4694
4695 #[expect(deprecated)]
4696 let entries_field = Arc::new(Field::new(
4697 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
4698 DataType::Struct(
4699 vec![
4700 Field::new_dict(
4701 Field::MAP_KEY_FIELD_DEFAULT_NAME,
4702 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4703 false,
4704 1,
4705 false,
4706 ),
4707 Field::new(Field::MAP_VALUE_FIELD_DEFAULT_NAME, DataType::Int32, true),
4708 ]
4709 .into(),
4710 ),
4711 false,
4712 ));
4713
4714 let entries = StructArray::from(vec![
4715 (
4716 Arc::new(Field::new(
4717 Field::MAP_KEY_FIELD_DEFAULT_NAME,
4718 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4719 false,
4720 )),
4721 Arc::new(dict_keys) as ArrayRef,
4722 ),
4723 (
4724 Arc::new(Field::new(
4725 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
4726 DataType::Int32,
4727 true,
4728 )),
4729 Arc::new(values) as ArrayRef,
4730 ),
4731 ]);
4732
4733 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
4734
4735 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
4736 .len(3)
4737 .add_buffer(offsets)
4738 .add_child_data(entries.into_data())
4739 .build()
4740 .unwrap();
4741 let map_array = MapArray::from(map_data);
4742
4743 let schema = Arc::new(Schema::new(vec![Field::new(
4744 "map",
4745 map_array.data_type().clone(),
4746 false,
4747 )]));
4748 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
4749
4750 let output_batch = deserialize_file(serialize_file(&input_batch));
4751 assert_eq!(input_batch, output_batch);
4752
4753 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4754 assert_eq!(input_batch, output_batch);
4755 }
4756
4757 #[test]
4758 fn test_roundtrip_map_with_dict_values() {
4759 let keys = StringArray::from(vec!["a", "b", "c", "d", "e", "f"]);
4762
4763 let value_values = StringArray::from(vec!["val_x", "val_y", "val_z"]);
4764 let value_keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
4765 let dict_values = DictionaryArray::new(value_keys, Arc::new(value_values));
4766
4767 #[expect(deprecated)]
4768 let entries_field = Arc::new(Field::new(
4769 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
4770 DataType::Struct(
4771 vec![
4772 Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Utf8, false),
4773 Field::new_dict(
4774 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
4775 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4776 true,
4777 2,
4778 false,
4779 ),
4780 ]
4781 .into(),
4782 ),
4783 false,
4784 ));
4785
4786 let entries = StructArray::from(vec![
4787 (
4788 Arc::new(Field::new(
4789 Field::MAP_KEY_FIELD_DEFAULT_NAME,
4790 DataType::Utf8,
4791 false,
4792 )),
4793 Arc::new(keys) as ArrayRef,
4794 ),
4795 (
4796 Arc::new(Field::new(
4797 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
4798 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4799 true,
4800 )),
4801 Arc::new(dict_values) as ArrayRef,
4802 ),
4803 ]);
4804
4805 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
4806
4807 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
4808 .len(3)
4809 .add_buffer(offsets)
4810 .add_child_data(entries.into_data())
4811 .build()
4812 .unwrap();
4813 let map_array = MapArray::from(map_data);
4814
4815 let schema = Arc::new(Schema::new(vec![Field::new(
4816 "map",
4817 map_array.data_type().clone(),
4818 false,
4819 )]));
4820 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
4821
4822 let output_batch = deserialize_file(serialize_file(&input_batch));
4823 assert_eq!(input_batch, output_batch);
4824
4825 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4826 assert_eq!(input_batch, output_batch);
4827 }
4828
4829 #[test]
4830 fn test_decimal128_alignment16_is_sufficient() {
4831 const IPC_ALIGNMENT: usize = 16;
4832
4833 for num_cols in [1, 2, 3, 17, 50, 73, 99] {
4838 let num_rows = (num_cols * 7 + 11) % 100; let mut fields = Vec::new();
4841 let mut arrays = Vec::new();
4842 for i in 0..num_cols {
4843 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4844 let array = Decimal128Array::from(vec![num_cols as i128; num_rows]);
4845 fields.push(field);
4846 arrays.push(Arc::new(array) as Arc<dyn Array>);
4847 }
4848 let schema = Schema::new(fields);
4849 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
4850
4851 let mut writer = FileWriter::try_new_with_options(
4852 Vec::new(),
4853 batch.schema_ref(),
4854 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
4855 )
4856 .unwrap();
4857 writer.write(&batch).unwrap();
4858 writer.finish().unwrap();
4859
4860 let out: Vec<u8> = writer.into_inner().unwrap();
4861
4862 let buffer = Buffer::from_slice_ref(out);
4863 let trailer_start = buffer.len() - 10;
4864 let footer_len =
4865 read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
4866 let footer =
4867 root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
4868
4869 let schema = try_fb_to_schema(footer.schema().unwrap()).unwrap();
4870
4871 let decoder =
4874 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
4875
4876 let batches = footer.recordBatches().unwrap();
4877
4878 let block = batches.get(0);
4879 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
4880 let data = buffer.slice_with_length(block.offset() as _, block_len);
4881
4882 let batch2 = decoder.read_record_batch(block, &data).unwrap().unwrap();
4883
4884 assert_eq!(batch, batch2);
4885 }
4886 }
4887
4888 #[test]
4889 fn test_decimal128_alignment8_is_unaligned() {
4890 const IPC_ALIGNMENT: usize = 8;
4891
4892 let num_cols = 2;
4893
4894 let mut fields = Vec::new();
4895 let mut arrays = Vec::new();
4896 for i in 0..num_cols {
4897 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4898 let array = Decimal128Array::from(vec![Some(num_cols as i128), None]);
4902 fields.push(field);
4903 arrays.push(Arc::new(array) as Arc<dyn Array>);
4904 }
4905 let schema = Schema::new(fields);
4906 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
4907
4908 let mut writer = FileWriter::try_new_with_options(
4909 Vec::new(),
4910 batch.schema_ref(),
4911 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
4912 )
4913 .unwrap();
4914 writer.write(&batch).unwrap();
4915 writer.finish().unwrap();
4916
4917 let out: Vec<u8> = writer.into_inner().unwrap();
4918
4919 let buffer = Buffer::from_slice_ref(out);
4920 let trailer_start = buffer.len() - 10;
4921 let footer_len = read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
4922 let footer = root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
4923 let schema = try_fb_to_schema(footer.schema().unwrap()).unwrap();
4924
4925 let decoder =
4928 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
4929
4930 let batches = footer.recordBatches().unwrap();
4931
4932 let block = batches.get(0);
4933 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
4934 let data = buffer.slice_with_length(block.offset() as _, block_len);
4935
4936 let result = decoder.read_record_batch(block, &data);
4937
4938 let error = result.unwrap_err();
4939 assert_eq!(
4940 error.to_string(),
4941 "Invalid argument error: Misaligned buffers[0] in array of type Decimal128(38, 10), \
4942 offset from expected alignment of 16 by 8"
4943 );
4944 }
4945
4946 #[test]
4947 fn test_flush() {
4948 let num_cols = 2;
4951 let mut fields = Vec::new();
4952 let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap();
4953 for i in 0..num_cols {
4954 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4955 fields.push(field);
4956 }
4957 let schema = Schema::new(fields);
4958 let inner_stream_writer = BufWriter::with_capacity(1024, Vec::new());
4959 let inner_file_writer = BufWriter::with_capacity(1024, Vec::new());
4960 let mut stream_writer =
4961 StreamWriter::try_new_with_options(inner_stream_writer, &schema, options.clone())
4962 .unwrap();
4963 let mut file_writer =
4964 FileWriter::try_new_with_options(inner_file_writer, &schema, options).unwrap();
4965
4966 let stream_bytes_written_on_new = stream_writer.get_ref().get_ref().len();
4967 let file_bytes_written_on_new = file_writer.get_ref().get_ref().len();
4968 stream_writer.flush().unwrap();
4969 file_writer.flush().unwrap();
4970 let stream_bytes_written_on_flush = stream_writer.get_ref().get_ref().len();
4971 let file_bytes_written_on_flush = file_writer.get_ref().get_ref().len();
4972 let stream_out = stream_writer.into_inner().unwrap().into_inner().unwrap();
4973 let expected_stream_flushed_bytes = stream_out.len() - 8;
4977 let expected_file_flushed_bytes = expected_stream_flushed_bytes + 8;
4980
4981 assert!(
4982 stream_bytes_written_on_new < stream_bytes_written_on_flush,
4983 "this test makes no sense if flush is not actually required"
4984 );
4985 assert!(
4986 file_bytes_written_on_new < file_bytes_written_on_flush,
4987 "this test makes no sense if flush is not actually required"
4988 );
4989 assert_eq!(stream_bytes_written_on_flush, expected_stream_flushed_bytes);
4990 assert_eq!(file_bytes_written_on_flush, expected_file_flushed_bytes);
4991 }
4992
4993 #[test]
4994 fn test_roundtrip_list_of_fixed_list() -> Result<(), ArrowError> {
4995 let l1_type =
4996 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Float32, false)), 3);
4997 let l2_type = DataType::List(Arc::new(Field::new("item", l1_type.clone(), false)));
4998
4999 let l0_builder = Float32Builder::new();
5000 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3).with_field(Arc::new(Field::new(
5001 "item",
5002 DataType::Float32,
5003 false,
5004 )));
5005 let mut l2_builder =
5006 ListBuilder::new(l1_builder).with_field(Arc::new(Field::new("item", l1_type, false)));
5007
5008 for point in [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]] {
5009 l2_builder.values().values().append_value(point[0]);
5010 l2_builder.values().values().append_value(point[1]);
5011 l2_builder.values().values().append_value(point[2]);
5012
5013 l2_builder.values().append(true);
5014 }
5015 l2_builder.append(true);
5016
5017 let point = [10., 11., 12.];
5018 l2_builder.values().values().append_value(point[0]);
5019 l2_builder.values().values().append_value(point[1]);
5020 l2_builder.values().values().append_value(point[2]);
5021
5022 l2_builder.values().append(true);
5023 l2_builder.append(true);
5024
5025 let array = Arc::new(l2_builder.finish()) as ArrayRef;
5026
5027 let schema = Arc::new(Schema::new_with_metadata(
5028 vec![Field::new("points", l2_type, false)],
5029 HashMap::default(),
5030 ));
5031
5032 test_slices(&array, &schema, 0, 1)?;
5035 test_slices(&array, &schema, 0, 2)?;
5036 test_slices(&array, &schema, 1, 1)?;
5037
5038 Ok(())
5039 }
5040
5041 #[test]
5042 fn test_roundtrip_list_of_fixed_list_w_nulls() -> Result<(), ArrowError> {
5043 let l0_builder = Float32Builder::new();
5044 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3);
5045 let mut l2_builder = ListBuilder::new(l1_builder);
5046
5047 for point in [
5048 [Some(1.0), Some(2.0), None],
5049 [Some(4.0), Some(5.0), Some(6.0)],
5050 [None, Some(8.0), Some(9.0)],
5051 ] {
5052 for p in point {
5053 match p {
5054 Some(p) => l2_builder.values().values().append_value(p),
5055 None => l2_builder.values().values().append_null(),
5056 }
5057 }
5058
5059 l2_builder.values().append(true);
5060 }
5061 l2_builder.append(true);
5062
5063 let point = [Some(10.), None, None];
5064 for p in point {
5065 match p {
5066 Some(p) => l2_builder.values().values().append_value(p),
5067 None => l2_builder.values().values().append_null(),
5068 }
5069 }
5070
5071 l2_builder.values().append(true);
5072 l2_builder.append(true);
5073
5074 let array = Arc::new(l2_builder.finish()) as ArrayRef;
5075
5076 let schema = Arc::new(Schema::new_with_metadata(
5077 vec![Field::new(
5078 "points",
5079 DataType::List(Arc::new(Field::new(
5080 "item",
5081 DataType::FixedSizeList(
5082 Arc::new(Field::new("item", DataType::Float32, true)),
5083 3,
5084 ),
5085 true,
5086 ))),
5087 true,
5088 )],
5089 HashMap::default(),
5090 ));
5091
5092 test_slices(&array, &schema, 0, 1)?;
5095 test_slices(&array, &schema, 0, 2)?;
5096 test_slices(&array, &schema, 1, 1)?;
5097
5098 Ok(())
5099 }
5100
5101 fn test_slices(
5102 parent_array: &ArrayRef,
5103 schema: &SchemaRef,
5104 offset: usize,
5105 length: usize,
5106 ) -> Result<(), ArrowError> {
5107 let subarray = parent_array.slice(offset, length);
5108 let original_batch = RecordBatch::try_new(schema.clone(), vec![subarray])?;
5109
5110 let mut bytes = Vec::new();
5111 let mut writer = StreamWriter::try_new(&mut bytes, schema)?;
5112 writer.write(&original_batch)?;
5113 writer.finish()?;
5114
5115 let mut cursor = std::io::Cursor::new(bytes);
5116 let mut reader = StreamReader::try_new(&mut cursor, None)?;
5117 let returned_batch = reader.next().unwrap()?;
5118
5119 assert_eq!(original_batch, returned_batch);
5120
5121 Ok(())
5122 }
5123
5124 #[test]
5125 fn test_roundtrip_fixed_list() -> Result<(), ArrowError> {
5126 let int_builder = Int64Builder::new();
5127 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3)
5128 .with_field(Arc::new(Field::new("item", DataType::Int64, false)));
5129
5130 for point in [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] {
5131 fixed_list_builder.values().append_value(point[0]);
5132 fixed_list_builder.values().append_value(point[1]);
5133 fixed_list_builder.values().append_value(point[2]);
5134
5135 fixed_list_builder.append(true);
5136 }
5137
5138 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
5139
5140 let schema = Arc::new(Schema::new_with_metadata(
5141 vec![Field::new(
5142 "points",
5143 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, false)), 3),
5144 false,
5145 )],
5146 HashMap::default(),
5147 ));
5148
5149 test_slices(&array, &schema, 0, 4)?;
5152 test_slices(&array, &schema, 0, 2)?;
5153 test_slices(&array, &schema, 1, 3)?;
5154 test_slices(&array, &schema, 2, 1)?;
5155
5156 Ok(())
5157 }
5158
5159 #[test]
5160 fn test_roundtrip_fixed_list_w_nulls() -> Result<(), ArrowError> {
5161 let int_builder = Int64Builder::new();
5162 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3);
5163
5164 for point in [
5165 [Some(1), Some(2), None],
5166 [Some(4), Some(5), Some(6)],
5167 [None, Some(8), Some(9)],
5168 [Some(10), None, None],
5169 ] {
5170 for p in point {
5171 match p {
5172 Some(p) => fixed_list_builder.values().append_value(p),
5173 None => fixed_list_builder.values().append_null(),
5174 }
5175 }
5176
5177 fixed_list_builder.append(true);
5178 }
5179
5180 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
5181
5182 let schema = Arc::new(Schema::new_with_metadata(
5183 vec![Field::new(
5184 "points",
5185 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, true)), 3),
5186 true,
5187 )],
5188 HashMap::default(),
5189 ));
5190
5191 test_slices(&array, &schema, 0, 4)?;
5194 test_slices(&array, &schema, 0, 2)?;
5195 test_slices(&array, &schema, 1, 3)?;
5196 test_slices(&array, &schema, 2, 1)?;
5197
5198 Ok(())
5199 }
5200
5201 #[test]
5202 fn test_metadata_encoding_ordering() {
5203 fn create_hash() -> u64 {
5204 let metadata: HashMap<String, String> = [
5205 ("a", "1"), ("b", "2"), ("c", "3"), ("d", "4"), ("e", "5"), ]
5211 .into_iter()
5212 .map(|(k, v)| (k.to_owned(), v.to_owned()))
5213 .collect();
5214
5215 let schema = Arc::new(
5217 Schema::new(vec![
5218 Field::new("a", DataType::Int64, true).with_metadata(metadata.clone()),
5219 ])
5220 .with_metadata(metadata)
5221 .clone(),
5222 );
5223 let batch = RecordBatch::new_empty(schema.clone());
5224
5225 let mut bytes = Vec::new();
5226 let mut w = StreamWriter::try_new(&mut bytes, batch.schema_ref()).unwrap();
5227 w.write(&batch).unwrap();
5228 w.finish().unwrap();
5229
5230 let mut h = std::hash::DefaultHasher::new();
5231 h.write(&bytes);
5232 h.finish()
5233 }
5234
5235 let expected = create_hash();
5236
5237 let all_passed = (0..20).all(|_| create_hash() == expected);
5242 assert!(all_passed);
5243 }
5244
5245 #[test]
5246 fn test_dictionary_tracker_reset() {
5247 let data_gen = IpcDataGenerator::default();
5248 let mut dictionary_tracker = DictionaryTracker::new(false);
5249 let writer_options = IpcWriteOptions::default();
5250 let mut compression_ctx = IpcWriteContext::default();
5251
5252 let schema = Arc::new(Schema::new(vec![Field::new(
5253 "a",
5254 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
5255 false,
5256 )]));
5257
5258 let mut write_single_batch_stream =
5259 |batch: RecordBatch, dict_tracker: &mut DictionaryTracker| -> Vec<u8> {
5260 let mut buffer = Vec::new();
5261
5262 let stream_header = data_gen.schema_to_bytes_with_dictionary_tracker(
5264 &schema,
5265 dict_tracker,
5266 &writer_options,
5267 );
5268 _ = write_message(&mut buffer, stream_header, &writer_options).unwrap();
5269
5270 let (encoded_dicts, encoded_batch) = data_gen
5271 .encode(&batch, dict_tracker, &writer_options, &mut compression_ctx)
5272 .unwrap();
5273 for encoded_dict in encoded_dicts {
5274 _ = write_message(&mut buffer, encoded_dict, &writer_options).unwrap();
5275 }
5276 _ = write_message(&mut buffer, encoded_batch, &writer_options).unwrap();
5277
5278 buffer
5279 };
5280
5281 let batch1 = RecordBatch::try_new(
5282 schema.clone(),
5283 vec![Arc::new(DictionaryArray::new(
5284 UInt8Array::from_iter_values([0]),
5285 Arc::new(StringArray::from_iter_values(["a"])),
5286 ))],
5287 )
5288 .unwrap();
5289 let buffer = write_single_batch_stream(batch1.clone(), &mut dictionary_tracker);
5290
5291 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
5293 let read_batch = reader.next().unwrap().unwrap();
5294 assert_eq!(read_batch, batch1);
5295
5296 dictionary_tracker.clear();
5298
5299 let batch2 = RecordBatch::try_new(
5301 schema.clone(),
5302 vec![Arc::new(DictionaryArray::new(
5303 UInt8Array::from_iter_values([0]),
5304 Arc::new(StringArray::from_iter_values(["a"])),
5305 ))],
5306 )
5307 .unwrap();
5308 let buffer = write_single_batch_stream(batch2.clone(), &mut dictionary_tracker);
5309 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
5310 let read_batch = reader.next().unwrap().unwrap();
5311 assert_eq!(read_batch, batch2);
5312 }
5313}