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::builder::BufferBuilder;
36use arrow_array::cast::*;
37use arrow_array::types::{Int16Type, Int32Type, Int64Type, RunEndIndexType};
38use arrow_array::*;
39use arrow_buffer::{ArrowNativeType, Buffer, MutableBuffer, ToByteSlice};
40use arrow_data::{ArrayData, ArrayDataBuilder, BufferSpec, layout};
41use arrow_schema::*;
42
43use crate::CONTINUATION_MARKER;
44use crate::compression::CompressionCodec;
45#[expect(deprecated)]
46pub use crate::compression::{CompressionContext, IpcWriteContext};
47use crate::convert::IpcSchemaEncoder;
48
49#[derive(Debug, Clone)]
51pub struct IpcWriteOptions {
52 alignment: u8,
55 write_legacy_ipc_format: bool,
57 metadata_version: crate::MetadataVersion,
66 batch_compression_type: Option<crate::CompressionType>,
69 batch_compression_level: Option<i32>,
71 dictionary_handling: DictionaryHandling,
73}
74
75enum EncodedBuffer {
81 Raw(Buffer),
83 Compressed(Vec<u8>),
85}
86
87impl EncodedBuffer {
88 fn as_slice(&self) -> &[u8] {
89 match self {
90 EncodedBuffer::Raw(b) => b.as_slice(),
91 EncodedBuffer::Compressed(v) => v.as_slice(),
92 }
93 }
94
95 fn len(&self) -> usize {
96 match self {
97 EncodedBuffer::Raw(b) => b.len(),
98 EncodedBuffer::Compressed(v) => v.len(),
99 }
100 }
101}
102#[derive(Default)]
107struct IpcMetadataBuilder {
108 nodes: Vec<crate::FieldNode>,
109 buffers: Vec<crate::Buffer>,
110}
111
112enum IpcBodySink<'a> {
117 Write(&'a mut Vec<u8>),
119 Collect(&'a mut Vec<EncodedBuffer>),
121}
122impl<'a> IpcBodySink<'a> {
123 pub fn write(&mut self, pad_len: usize, buffer: EncodedBuffer) {
125 match self {
126 IpcBodySink::Write(vec) => {
127 vec.extend_from_slice(buffer.as_slice());
128 vec.extend_from_slice(&PADDING[..pad_len]);
129 }
130 IpcBodySink::Collect(vec) => {
131 vec.push(buffer);
132 }
133 }
134 }
135}
136
137struct MetadataLayout {
138 padded_header_len: usize,
139 padded_metadata_len: usize,
140 metadata_padding: usize,
141}
142
143impl MetadataLayout {
144 fn new(metadata_len: usize, write_options: &IpcWriteOptions) -> MetadataLayout {
145 let prefix_size = if write_options.write_legacy_ipc_format {
146 4
147 } else {
148 8
149 };
150 let alignment_mask = usize::from(write_options.alignment - 1);
151 let padded_header_len = (metadata_len + prefix_size + alignment_mask) & !alignment_mask;
152 let padded_metadata_len = padded_header_len - prefix_size;
153 let metadata_padding = padded_metadata_len - metadata_len;
154
155 MetadataLayout {
156 padded_header_len,
157 padded_metadata_len,
158 metadata_padding,
159 }
160 }
161}
162
163trait IpcMessageSink {
169 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError>;
170
171 fn write_vec(&mut self, bytes: Vec<u8>) -> Result<(), ArrowError> {
172 self.write_slice(&bytes)
173 }
174
175 fn write_encoded_buffer(&mut self, buffer: EncodedBuffer) -> Result<(), ArrowError> {
176 self.write_slice(buffer.as_slice())
177 }
178}
179
180trait IpcMessageSinkExt: IpcMessageSink {
182 fn write_padding(&mut self, len: usize) -> Result<(), ArrowError> {
183 self.write_slice(&PADDING[..len])
184 }
185
186 fn write_continuation(
188 &mut self,
189 write_options: &IpcWriteOptions,
190 metadata_len: i32,
191 ) -> Result<(), ArrowError> {
192 let mut buffer = [0; 8];
193 let len = match write_options.metadata_version {
194 crate::MetadataVersion::V1
195 | crate::MetadataVersion::V2
196 | crate::MetadataVersion::V3 => {
197 unreachable!("Options with the metadata version cannot be created")
198 }
199 crate::MetadataVersion::V4 => {
200 let metadata_len_bytes = metadata_len.to_le_bytes();
201 if !write_options.write_legacy_ipc_format {
202 buffer[..4].copy_from_slice(&CONTINUATION_MARKER);
204 buffer[4..].copy_from_slice(&metadata_len_bytes);
205 8
206 } else {
207 buffer[..4].copy_from_slice(&metadata_len_bytes);
208 4
209 }
210 }
211 crate::MetadataVersion::V5 => {
212 buffer[..4].copy_from_slice(&CONTINUATION_MARKER);
213 buffer[4..].copy_from_slice(&metadata_len.to_le_bytes());
214 8
215 }
216 z => panic!("Unsupported crate::MetadataVersion {z:?}"),
217 };
218 self.write_slice(&buffer[..len])
219 }
220
221 fn write_body_data(&mut self, data: Vec<u8>, alignment: u8) -> Result<usize, ArrowError> {
222 let len = data.len();
223 let pad_len = pad_to_alignment(alignment, len);
224 self.write_vec(data)?;
225 self.write_padding(pad_len)?;
226 Ok(len + pad_len)
227 }
228
229 fn write_encoded_data(
234 &mut self,
235 encoded: EncodedData,
236 write_options: &IpcWriteOptions,
237 ) -> Result<(usize, usize), ArrowError> {
238 let arrow_data_len = encoded.arrow_data.len();
239 if arrow_data_len % usize::from(write_options.alignment) != 0 {
240 return Err(ArrowError::MemoryError(
241 "Arrow data not aligned".to_string(),
242 ));
243 }
244
245 let metadata = encoded.ipc_message;
246 let metadata_len = metadata.len();
247 let layout = MetadataLayout::new(metadata_len, write_options);
248
249 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
250 self.write_vec(metadata)?;
251 self.write_padding(layout.metadata_padding)?;
252
253 let body_len = if arrow_data_len > 0 {
254 self.write_body_data(encoded.arrow_data, write_options.alignment)?
255 } else {
256 0
257 };
258
259 Ok((layout.padded_header_len, body_len))
260 }
261
262 fn write_eos(&mut self, write_options: &IpcWriteOptions) -> Result<(), ArrowError> {
264 self.write_continuation(write_options, 0)?;
265 Ok(())
266 }
267}
268
269impl<T: IpcMessageSink + ?Sized> IpcMessageSinkExt for T {}
270
271trait IpcRecordBatchSink: IpcMessageSinkExt {
273 fn write_record_batch(
279 &mut self,
280 metadata: Vec<u8>,
281 encoded_buffers: Vec<EncodedBuffer>,
282 body_len: usize,
283 tail_pad: usize,
284 write_options: &IpcWriteOptions,
285 ) -> Result<(usize, usize), ArrowError> {
286 let alignment = write_options.alignment;
287 let layout = MetadataLayout::new(metadata.len(), write_options);
288
289 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
290 self.write_vec(metadata)?;
291 self.write_padding(layout.metadata_padding)?;
292 for enc in encoded_buffers {
293 let len = enc.len();
294 self.write_encoded_buffer(enc)?;
295 self.write_padding(pad_to_alignment(alignment, len))?;
296 }
297 self.write_padding(tail_pad)?;
298
299 Ok((layout.padded_header_len, body_len))
300 }
301}
302
303impl<W> IpcMessageSink for W
304where
305 W: Write,
306{
307 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError> {
308 if !bytes.is_empty() {
309 self.write_all(bytes)?;
310 }
311 Ok(())
312 }
313}
314
315impl<W> IpcRecordBatchSink for W
316where
317 W: Write,
318{
319 fn write_record_batch(
320 &mut self,
321 metadata: Vec<u8>,
322 encoded_buffers: Vec<EncodedBuffer>,
323 body_len: usize,
324 tail_pad: usize,
325 write_options: &IpcWriteOptions,
326 ) -> Result<(usize, usize), ArrowError> {
327 let alignment = write_options.alignment;
328 let layout = MetadataLayout::new(metadata.len(), write_options);
329
330 self.write_continuation(write_options, layout.padded_metadata_len as i32)?;
331 self.write_all(&metadata)?;
332 self.write_all(&PADDING[..layout.metadata_padding])?;
333 for enc in &encoded_buffers {
334 self.write_all(enc.as_slice())?;
335 self.write_all(&PADDING[..pad_to_alignment(alignment, enc.len())])?;
336 }
337 self.write_all(&PADDING[..tail_pad])?;
338
339 Ok((layout.padded_header_len, body_len))
340 }
341}
342
343struct Buffers<'a> {
345 out: &'a mut Vec<Buffer>,
346}
347
348impl IpcMessageSink for Buffers<'_> {
349 fn write_slice(&mut self, bytes: &[u8]) -> Result<(), ArrowError> {
350 if !bytes.is_empty() {
351 self.out.push(Buffer::from(bytes));
352 }
353 Ok(())
354 }
355
356 fn write_vec(&mut self, bytes: Vec<u8>) -> Result<(), ArrowError> {
357 if !bytes.is_empty() {
358 self.out.push(Buffer::from(bytes));
359 }
360 Ok(())
361 }
362
363 fn write_encoded_buffer(&mut self, buffer: EncodedBuffer) -> Result<(), ArrowError> {
364 match buffer {
365 EncodedBuffer::Raw(buffer) => self.out.push(buffer),
366 EncodedBuffer::Compressed(bytes) => self.out.push(Buffer::from(bytes)),
367 }
368 Ok(())
369 }
370}
371
372impl IpcRecordBatchSink for Buffers<'_> {}
373
374struct IpcWriteMetadata {
379 dictionary_block_sizes: Vec<(usize, usize)>,
382 padded_header_len: usize,
384 body_len: usize,
386}
387
388impl IpcWriteOptions {
389 pub fn try_with_compression(
394 mut self,
395 batch_compression_type: Option<crate::CompressionType>,
396 ) -> Result<Self, ArrowError> {
397 self.batch_compression_type = batch_compression_type;
398
399 if self.batch_compression_type.is_some()
400 && self.metadata_version < crate::MetadataVersion::V5
401 {
402 return Err(ArrowError::InvalidArgumentError(
403 "Compression only supported in metadata v5 and above".to_string(),
404 ));
405 }
406 Ok(self)
407 }
408
409 pub fn try_with_compression_level(
414 mut self,
415 batch_compression_level: Option<i32>,
416 ) -> Result<Self, ArrowError> {
417 self.batch_compression_level = batch_compression_level;
418
419 if self.batch_compression_level.is_some()
420 && self.metadata_version < crate::MetadataVersion::V5
421 {
422 return Err(ArrowError::InvalidArgumentError(
423 "Compression only supported in metadata v5 and above".to_string(),
424 ));
425 }
426
427 match (self.batch_compression_type, self.batch_compression_level) {
428 (Some(crate::CompressionType::ZSTD), Some(level)) => {
429 return self.check_zstd_level(level);
430 }
431 (Some(crate::CompressionType::LZ4_FRAME), Some(_)) => {
432 return Err(ArrowError::InvalidArgumentError(
433 "LZ4 Frame compression does not support configurable compression levels"
434 .to_string(),
435 ));
436 }
437 _ => {}
438 }
439
440 Ok(self)
441 }
442
443 #[cfg(not(feature = "zstd"))]
444 fn check_zstd_level(self, _level: i32) -> Result<Self, ArrowError> {
445 Err(ArrowError::InvalidArgumentError(
446 "zstd IPC compression requires the zstd feature".to_string(),
447 ))
448 }
449
450 #[cfg(feature = "zstd")]
451 fn check_zstd_level(self, level: i32) -> Result<Self, ArrowError> {
452 let range = zstd::compression_level_range();
453 if !range.contains(&(level as zstd::zstd_safe::CompressionLevel)) {
454 return Err(ArrowError::InvalidArgumentError(format!(
455 "ZSTD compression level must be between {} and {}, got {}",
456 range.start(),
457 range.end(),
458 level,
459 )));
460 }
461
462 Ok(self)
463 }
464
465 pub fn try_new(
467 alignment: usize,
468 write_legacy_ipc_format: bool,
469 metadata_version: crate::MetadataVersion,
470 ) -> Result<Self, ArrowError> {
471 let is_alignment_valid =
472 alignment == 8 || alignment == 16 || alignment == 32 || alignment == 64;
473 if !is_alignment_valid {
474 return Err(ArrowError::InvalidArgumentError(
475 "Alignment should be 8, 16, 32, or 64.".to_string(),
476 ));
477 }
478 let alignment: u8 = u8::try_from(alignment).expect("range already checked");
479 match metadata_version {
480 crate::MetadataVersion::V1
481 | crate::MetadataVersion::V2
482 | crate::MetadataVersion::V3 => Err(ArrowError::InvalidArgumentError(
483 "Writing IPC metadata version 3 and lower not supported".to_string(),
484 )),
485 #[allow(deprecated)]
486 crate::MetadataVersion::V4 => Ok(Self {
487 alignment,
488 write_legacy_ipc_format,
489 metadata_version,
490 batch_compression_type: None,
491 batch_compression_level: None,
492 dictionary_handling: DictionaryHandling::default(),
493 }),
494 crate::MetadataVersion::V5 => {
495 if write_legacy_ipc_format {
496 Err(ArrowError::InvalidArgumentError(
497 "Legacy IPC format only supported on metadata version 4".to_string(),
498 ))
499 } else {
500 Ok(Self {
501 alignment,
502 write_legacy_ipc_format,
503 metadata_version,
504 batch_compression_type: None,
505 batch_compression_level: None,
506 dictionary_handling: DictionaryHandling::default(),
507 })
508 }
509 }
510 z => Err(ArrowError::InvalidArgumentError(format!(
511 "Unsupported crate::MetadataVersion {z:?}"
512 ))),
513 }
514 }
515
516 pub fn with_dictionary_handling(mut self, dictionary_handling: DictionaryHandling) -> Self {
518 self.dictionary_handling = dictionary_handling;
519 self
520 }
521}
522
523impl Default for IpcWriteOptions {
524 fn default() -> Self {
525 Self {
526 alignment: 64,
527 write_legacy_ipc_format: false,
528 metadata_version: crate::MetadataVersion::V5,
529 batch_compression_type: None,
530 batch_compression_level: None,
531 dictionary_handling: DictionaryHandling::default(),
532 }
533 }
534}
535
536#[derive(Debug, Default)]
537pub struct IpcDataGenerator {}
571
572impl IpcDataGenerator {
573 pub fn schema_to_bytes_with_dictionary_tracker(
576 &self,
577 schema: &Schema,
578 dictionary_tracker: &mut DictionaryTracker,
579 write_options: &IpcWriteOptions,
580 ) -> EncodedData {
581 let mut fbb = FlatBufferBuilder::new();
582 let schema = {
583 let fb = IpcSchemaEncoder::new()
584 .with_dictionary_tracker(dictionary_tracker)
585 .schema_to_fb_offset(&mut fbb, schema);
586 fb.as_union_value()
587 };
588
589 let mut message = crate::MessageBuilder::new(&mut fbb);
590 message.add_version(write_options.metadata_version);
591 message.add_header_type(crate::MessageHeader::Schema);
592 message.add_bodyLength(0);
593 message.add_header(schema);
594 let root = message.finish();
596 fbb.finish(root, None);
597
598 let metadata = fbb.finished_data();
599 EncodedData {
600 ipc_message: metadata.to_vec(),
601 arrow_data: vec![],
602 }
603 }
604
605 fn _encode_dictionaries<I: Iterator<Item = i64>>(
606 &self,
607 column: &ArrayRef,
608 encoded_dictionaries: &mut Vec<EncodedData>,
609 dictionary_tracker: &mut DictionaryTracker,
610 write_options: &IpcWriteOptions,
611 dict_id: &mut I,
612 ipc_write_context: &mut IpcWriteContext,
613 ) -> Result<(), ArrowError> {
614 match column.data_type() {
615 DataType::Struct(fields) => {
616 let s = as_struct_array(column);
617 for (field, column) in fields.iter().zip(s.columns()) {
618 self.encode_dictionaries(
619 field,
620 column,
621 encoded_dictionaries,
622 dictionary_tracker,
623 write_options,
624 dict_id,
625 ipc_write_context,
626 )?;
627 }
628 }
629 DataType::RunEndEncoded(_, values) => {
630 let data = column.to_data();
631 if data.child_data().len() != 2 {
632 return Err(ArrowError::InvalidArgumentError(format!(
633 "The run encoded array should have exactly two child arrays. Found {}",
634 data.child_data().len()
635 )));
636 }
637 let values_array = make_array(data.child_data()[1].clone());
640 self.encode_dictionaries(
641 values,
642 &values_array,
643 encoded_dictionaries,
644 dictionary_tracker,
645 write_options,
646 dict_id,
647 ipc_write_context,
648 )?;
649 }
650 DataType::List(field) => {
651 let list = as_list_array(column);
652 self.encode_dictionaries(
653 field,
654 list.values(),
655 encoded_dictionaries,
656 dictionary_tracker,
657 write_options,
658 dict_id,
659 ipc_write_context,
660 )?;
661 }
662 DataType::LargeList(field) => {
663 let list = as_large_list_array(column);
664 self.encode_dictionaries(
665 field,
666 list.values(),
667 encoded_dictionaries,
668 dictionary_tracker,
669 write_options,
670 dict_id,
671 ipc_write_context,
672 )?;
673 }
674 DataType::ListView(field) => {
675 let list = column.as_list_view::<i32>();
676 self.encode_dictionaries(
677 field,
678 list.values(),
679 encoded_dictionaries,
680 dictionary_tracker,
681 write_options,
682 dict_id,
683 ipc_write_context,
684 )?;
685 }
686 DataType::LargeListView(field) => {
687 let list = column.as_list_view::<i64>();
688 self.encode_dictionaries(
689 field,
690 list.values(),
691 encoded_dictionaries,
692 dictionary_tracker,
693 write_options,
694 dict_id,
695 ipc_write_context,
696 )?;
697 }
698 DataType::FixedSizeList(field, _) => {
699 let list = column
700 .as_any()
701 .downcast_ref::<FixedSizeListArray>()
702 .expect("Unable to downcast to fixed size list array");
703 self.encode_dictionaries(
704 field,
705 list.values(),
706 encoded_dictionaries,
707 dictionary_tracker,
708 write_options,
709 dict_id,
710 ipc_write_context,
711 )?;
712 }
713 DataType::Map(field, _) => {
714 let map_array = as_map_array(column);
715
716 let (keys, values) = match field.data_type() {
717 DataType::Struct(fields) if fields.len() == 2 => (&fields[0], &fields[1]),
718 _ => panic!("Incorrect field data type {:?}", field.data_type()),
719 };
720
721 self.encode_dictionaries(
723 keys,
724 map_array.keys(),
725 encoded_dictionaries,
726 dictionary_tracker,
727 write_options,
728 dict_id,
729 ipc_write_context,
730 )?;
731
732 self.encode_dictionaries(
734 values,
735 map_array.values(),
736 encoded_dictionaries,
737 dictionary_tracker,
738 write_options,
739 dict_id,
740 ipc_write_context,
741 )?;
742 }
743 DataType::Union(fields, _) => {
744 let union = as_union_array(column);
745 for (type_id, field) in fields.iter() {
746 let column = union.child(type_id);
747 self.encode_dictionaries(
748 field,
749 column,
750 encoded_dictionaries,
751 dictionary_tracker,
752 write_options,
753 dict_id,
754 ipc_write_context,
755 )?;
756 }
757 }
758 _ => (),
759 }
760
761 Ok(())
762 }
763
764 #[allow(clippy::too_many_arguments)]
765 fn encode_dictionaries<I: Iterator<Item = i64>>(
766 &self,
767 field: &Field,
768 column: &ArrayRef,
769 encoded_dictionaries: &mut Vec<EncodedData>,
770 dictionary_tracker: &mut DictionaryTracker,
771 write_options: &IpcWriteOptions,
772 dict_id_seq: &mut I,
773 ipc_write_context: &mut IpcWriteContext,
774 ) -> Result<(), ArrowError> {
775 match column.data_type() {
776 DataType::Dictionary(_key_type, value_type) => {
777 if matches!(value_type.as_ref(), DataType::Dictionary(_, _)) {
778 return Err(ArrowError::InvalidArgumentError(format!(
779 "Arrow IPC field metadata cannot encode direct dictionary-of-dictionary values for field {:?}",
780 field.name()
781 )));
782 }
783
784 let dict_data = column.to_data();
785 let dict_values = &dict_data.child_data()[0];
786
787 let values = make_array(dict_data.child_data()[0].clone());
788
789 self._encode_dictionaries(
790 &values,
791 encoded_dictionaries,
792 dictionary_tracker,
793 write_options,
794 dict_id_seq,
795 ipc_write_context,
796 )?;
797
798 let dict_id = dict_id_seq.next().ok_or_else(|| {
802 ArrowError::IpcError(format!(
803 "no dict id for field {:?}: field.data_type={:?}, column.data_type={:?}",
804 field.name(),
805 field.data_type(),
806 column.data_type()
807 ))
808 })?;
809
810 match dictionary_tracker.insert_column(
811 dict_id,
812 column,
813 write_options.dictionary_handling,
814 )? {
815 DictionaryUpdate::None => {}
816 DictionaryUpdate::New | DictionaryUpdate::Replaced => {
817 encoded_dictionaries.push(self.dictionary_batch_to_bytes(
818 dict_id,
819 dict_values,
820 write_options,
821 false,
822 ipc_write_context,
823 )?);
824 }
825 DictionaryUpdate::Delta(data) => {
826 encoded_dictionaries.push(self.dictionary_batch_to_bytes(
827 dict_id,
828 &data,
829 write_options,
830 true,
831 ipc_write_context,
832 )?);
833 }
834 }
835 }
836 _ => self._encode_dictionaries(
837 column,
838 encoded_dictionaries,
839 dictionary_tracker,
840 write_options,
841 dict_id_seq,
842 ipc_write_context,
843 )?,
844 }
845
846 Ok(())
847 }
848
849 pub fn encode(
853 &self,
854 batch: &RecordBatch,
855 dictionary_tracker: &mut DictionaryTracker,
856 write_options: &IpcWriteOptions,
857 ipc_write_context: &mut IpcWriteContext,
858 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
859 let encoded_dictionaries =
860 self.encode_all_dicts(batch, dictionary_tracker, write_options, ipc_write_context)?;
861 let mut arrow_data = ipc_write_context.scratch();
862 let (metadata, _, tail_pad) = self.record_batch_to_bytes(
863 batch,
864 write_options,
865 ipc_write_context,
866 &mut IpcBodySink::Write(&mut arrow_data),
867 )?;
868 arrow_data.extend_from_slice(&PADDING[..tail_pad]);
869 ipc_write_context.reserve_scratch_with_capacity(arrow_data.capacity());
870 Ok((
871 encoded_dictionaries,
872 EncodedData {
873 ipc_message: metadata,
874 arrow_data,
875 },
876 ))
877 }
878
879 fn encode_all_dicts(
881 &self,
882 batch: &RecordBatch,
883 dictionary_tracker: &mut DictionaryTracker,
884 write_options: &IpcWriteOptions,
885 ipc_write_context: &mut IpcWriteContext,
886 ) -> Result<Vec<EncodedData>, ArrowError> {
887 let schema = batch.schema();
888 let mut encoded_dictionaries = Vec::with_capacity(schema.flattened_fields().len());
889 let mut dict_id = dictionary_tracker.dict_ids.clone().into_iter();
890 for (i, field) in schema.fields().iter().enumerate() {
891 self.encode_dictionaries(
892 field,
893 batch.column(i),
894 &mut encoded_dictionaries,
895 dictionary_tracker,
896 write_options,
897 &mut dict_id,
898 ipc_write_context,
899 )?;
900 }
901 Ok(encoded_dictionaries)
902 }
903
904 fn write<W: Write>(
908 &self,
909 batch: &RecordBatch,
910 dictionary_tracker: &mut DictionaryTracker,
911 write_options: &IpcWriteOptions,
912 ipc_write_context: &mut IpcWriteContext,
913 writer: &mut W,
914 ) -> Result<IpcWriteMetadata, ArrowError> {
915 self.write_to_sink(
916 batch,
917 dictionary_tracker,
918 write_options,
919 ipc_write_context,
920 writer,
921 )
922 }
923
924 fn encode_to_buffers(
927 &self,
928 batch: &RecordBatch,
929 dictionary_tracker: &mut DictionaryTracker,
930 write_options: &IpcWriteOptions,
931 ipc_write_context: &mut IpcWriteContext,
932 out: &mut Vec<Buffer>,
933 ) -> Result<IpcWriteMetadata, ArrowError> {
934 let mut sink = Buffers { out };
935 self.write_to_sink(
936 batch,
937 dictionary_tracker,
938 write_options,
939 ipc_write_context,
940 &mut sink,
941 )
942 }
943
944 fn write_to_sink<S: IpcRecordBatchSink>(
945 &self,
946 batch: &RecordBatch,
947 dictionary_tracker: &mut DictionaryTracker,
948 write_options: &IpcWriteOptions,
949 ipc_write_context: &mut IpcWriteContext,
950 sink: &mut S,
951 ) -> Result<IpcWriteMetadata, ArrowError> {
952 let encoded_dictionaries =
953 self.encode_all_dicts(batch, dictionary_tracker, write_options, ipc_write_context)?;
954
955 let mut dictionary_block_sizes = Vec::with_capacity(encoded_dictionaries.len());
956 for dict in encoded_dictionaries {
957 dictionary_block_sizes.push(sink.write_encoded_data(dict, write_options)?);
958 }
959
960 let capacity = batch
961 .columns()
962 .iter()
963 .map(|a| estimate_encoded_buffer_count(a.data_type()))
964 .sum();
965 let mut encoded_buffers: Vec<EncodedBuffer> = Vec::with_capacity(capacity);
966 let (metadata, body_len, tail_pad) = self.record_batch_to_bytes(
967 batch,
968 write_options,
969 ipc_write_context,
970 &mut IpcBodySink::Collect(&mut encoded_buffers),
971 )?;
972
973 let (padded_header_len, body_len) =
974 sink.write_record_batch(metadata, encoded_buffers, body_len, tail_pad, write_options)?;
975
976 Ok(IpcWriteMetadata {
977 dictionary_block_sizes,
978 padded_header_len,
979 body_len,
980 })
981 }
982
983 #[deprecated(since = "57.0.0", note = "Use `encode` instead")]
987 pub fn encoded_batch(
988 &self,
989 batch: &RecordBatch,
990 dictionary_tracker: &mut DictionaryTracker,
991 write_options: &IpcWriteOptions,
992 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
993 self.encode(
994 batch,
995 dictionary_tracker,
996 write_options,
997 &mut Default::default(),
998 )
999 }
1000
1001 fn record_batch_to_bytes(
1007 &self,
1008 batch: &RecordBatch,
1009 write_options: &IpcWriteOptions,
1010 ipc_write_context: &mut IpcWriteContext,
1011 sink: &mut IpcBodySink<'_>,
1012 ) -> Result<(Vec<u8>, usize, usize), ArrowError> {
1013 let batch_compression_type = write_options.batch_compression_type;
1014
1015 let compression = batch_compression_type.map(|batch_compression_type| {
1016 let fbb = ipc_write_context.mut_fbb();
1017 let mut c = crate::BodyCompressionBuilder::new(fbb);
1018 c.add_method(crate::BodyCompressionMethod::BUFFER);
1019 c.add_codec(batch_compression_type);
1020 c.finish()
1021 });
1022
1023 let batch_compression_level = write_options.batch_compression_level;
1024 let compression_codec: Option<CompressionCodec> = batch_compression_type
1025 .map(|compression_type| match batch_compression_level {
1026 Some(level) => {
1027 CompressionCodec::try_new_with_compression_level(compression_type, level)
1028 }
1029 None => compression_type.try_into(),
1030 })
1031 .transpose()?;
1032
1033 let alignment = write_options.alignment;
1034 let mut variadic_buffer_counts = vec![];
1035 let mut meta = IpcMetadataBuilder::default();
1036 let mut offset = 0i64;
1037
1038 for array in batch.columns() {
1039 let array_data = array.to_data();
1040 offset = write_array_data(
1041 &array_data,
1042 &mut meta,
1043 sink,
1044 offset,
1045 compression_codec,
1046 ipc_write_context,
1047 write_options,
1048 )?;
1049 append_variadic_buffer_counts(&mut variadic_buffer_counts, &array_data);
1050 }
1051
1052 let tail_pad = pad_to_alignment(alignment, offset as usize);
1053 let body_len = offset as usize + tail_pad;
1054
1055 let fbb = ipc_write_context.mut_fbb();
1056 let buffers = fbb.create_vector(&meta.buffers);
1057 let nodes = fbb.create_vector(&meta.nodes);
1058 let variadic_buffer = if variadic_buffer_counts.is_empty() {
1059 None
1060 } else {
1061 Some(fbb.create_vector(&variadic_buffer_counts))
1062 };
1063
1064 let root = {
1065 let mut batch_builder = crate::RecordBatchBuilder::new(fbb);
1066 batch_builder.add_length(batch.num_rows() as i64);
1067 batch_builder.add_nodes(nodes);
1068 batch_builder.add_buffers(buffers);
1069 if let Some(c) = compression {
1070 batch_builder.add_compression(c);
1071 }
1072 if let Some(v) = variadic_buffer {
1073 batch_builder.add_variadicBufferCounts(v);
1074 }
1075 batch_builder.finish().as_union_value()
1076 };
1077 let mut message = crate::MessageBuilder::new(fbb);
1078 message.add_version(write_options.metadata_version);
1079 message.add_header_type(crate::MessageHeader::RecordBatch);
1080 message.add_bodyLength(body_len as i64);
1081 message.add_header(root);
1082 let root = message.finish();
1083 fbb.finish(root, None);
1084
1085 let metadata = fbb.finished_data().to_vec();
1086 fbb.reset();
1087 Ok((metadata, body_len, tail_pad))
1088 }
1089
1090 fn dictionary_batch_to_bytes(
1093 &self,
1094 dict_id: i64,
1095 array_data: &ArrayData,
1096 write_options: &IpcWriteOptions,
1097 is_delta: bool,
1098 ipc_write_context: &mut IpcWriteContext,
1099 ) -> Result<EncodedData, ArrowError> {
1100 let mut arrow_data: Vec<u8> = vec![];
1101
1102 let batch_compression_type = write_options.batch_compression_type;
1104
1105 let compression = batch_compression_type.map(|batch_compression_type| {
1106 let fbb = ipc_write_context.mut_fbb();
1107 let mut c = crate::BodyCompressionBuilder::new(fbb);
1108 c.add_method(crate::BodyCompressionMethod::BUFFER);
1109 c.add_codec(batch_compression_type);
1110 c.finish()
1111 });
1112
1113 let batch_compression_level = write_options.batch_compression_level;
1114 let compression_codec: Option<CompressionCodec> = batch_compression_type
1115 .map(|batch_compression_type| match batch_compression_level {
1116 Some(level) => {
1117 CompressionCodec::try_new_with_compression_level(batch_compression_type, level)
1118 }
1119 None => batch_compression_type.try_into(),
1120 })
1121 .transpose()?;
1122
1123 let alignment = write_options.alignment;
1124 let mut meta = IpcMetadataBuilder::default();
1125 let mut sink = IpcBodySink::Write(&mut arrow_data);
1126 let offset = write_array_data(
1127 array_data,
1128 &mut meta,
1129 &mut sink,
1130 0,
1131 compression_codec,
1132 ipc_write_context,
1133 write_options,
1134 )?;
1135
1136 let mut variadic_buffer_counts = vec![];
1137 append_variadic_buffer_counts(&mut variadic_buffer_counts, array_data);
1138
1139 let tail_pad = pad_to_alignment(alignment, offset as usize);
1141 let body_len = offset as usize + tail_pad;
1142 arrow_data.extend_from_slice(&PADDING[..tail_pad]);
1143
1144 let fbb = ipc_write_context.mut_fbb();
1145 let buffers = fbb.create_vector(&meta.buffers);
1146 let nodes = fbb.create_vector(&meta.nodes);
1147 let variadic_buffer = if variadic_buffer_counts.is_empty() {
1148 None
1149 } else {
1150 Some(fbb.create_vector(&variadic_buffer_counts))
1151 };
1152
1153 let root = {
1154 let mut batch_builder = crate::RecordBatchBuilder::new(fbb);
1155 batch_builder.add_length(array_data.len() as i64);
1156 batch_builder.add_nodes(nodes);
1157 batch_builder.add_buffers(buffers);
1158 if let Some(c) = compression {
1159 batch_builder.add_compression(c);
1160 }
1161 if let Some(v) = variadic_buffer {
1162 batch_builder.add_variadicBufferCounts(v);
1163 }
1164 batch_builder.finish()
1165 };
1166
1167 let root = {
1168 let mut batch_builder = crate::DictionaryBatchBuilder::new(fbb);
1169 batch_builder.add_id(dict_id);
1170 batch_builder.add_data(root);
1171 batch_builder.add_isDelta(is_delta);
1172 batch_builder.finish().as_union_value()
1173 };
1174
1175 let root = {
1176 let mut message_builder = crate::MessageBuilder::new(fbb);
1177 message_builder.add_version(write_options.metadata_version);
1178 message_builder.add_header_type(crate::MessageHeader::DictionaryBatch);
1179 message_builder.add_bodyLength(body_len as i64);
1180 message_builder.add_header(root);
1181 message_builder.finish()
1182 };
1183
1184 fbb.finish(root, None);
1185 let metadata = fbb.finished_data().to_vec();
1186 fbb.reset();
1187
1188 Ok(EncodedData {
1189 ipc_message: metadata,
1190 arrow_data,
1191 })
1192 }
1193}
1194
1195fn ensure_supported_ipc_schema(schema: &Schema) -> Result<(), ArrowError> {
1196 schema
1197 .fields()
1198 .iter()
1199 .try_for_each(|field| ensure_supported_ipc_data_type(field.name(), field.data_type()))
1200}
1201
1202fn ensure_supported_ipc_data_type(
1203 field_name: &str,
1204 data_type: &DataType,
1205) -> Result<(), ArrowError> {
1206 match data_type {
1207 DataType::Dictionary(_, value_type)
1208 if matches!(value_type.as_ref(), DataType::Dictionary(_, _)) =>
1209 {
1210 Err(ArrowError::InvalidArgumentError(format!(
1211 "Arrow IPC field metadata cannot encode direct dictionary-of-dictionary values for field {field_name:?}"
1212 )))
1213 }
1214 DataType::Dictionary(_, value_type) => {
1215 ensure_supported_ipc_data_type(field_name, value_type)
1216 }
1217 DataType::Struct(fields) => fields
1218 .iter()
1219 .try_for_each(|field| ensure_supported_ipc_data_type(field.name(), field.data_type())),
1220 DataType::RunEndEncoded(_, field)
1221 | DataType::List(field)
1222 | DataType::LargeList(field)
1223 | DataType::ListView(field)
1224 | DataType::LargeListView(field)
1225 | DataType::FixedSizeList(field, _)
1226 | DataType::Map(field, _) => {
1227 ensure_supported_ipc_data_type(field.name(), field.data_type())
1228 }
1229 DataType::Union(fields, _) => fields.iter().try_for_each(|(_, field)| {
1230 ensure_supported_ipc_data_type(field.name(), field.data_type())
1231 }),
1232 _ => Ok(()),
1233 }
1234}
1235
1236fn append_variadic_buffer_counts(counts: &mut Vec<i64>, array: &ArrayData) {
1237 match array.data_type() {
1238 DataType::BinaryView | DataType::Utf8View => {
1239 counts.push(array.buffers().len() as i64 - 1);
1242 }
1243 DataType::Dictionary(_, _) => {
1244 }
1247 _ => {
1248 for child in array.child_data() {
1249 append_variadic_buffer_counts(counts, child)
1250 }
1251 }
1252 }
1253}
1254
1255pub(crate) fn unslice_run_array(arr: ArrayData) -> Result<ArrayData, ArrowError> {
1256 match arr.data_type() {
1257 DataType::RunEndEncoded(k, _) => match k.data_type() {
1258 DataType::Int16 => {
1259 Ok(into_zero_offset_run_array(RunArray::<Int16Type>::from(arr))?.into_data())
1260 }
1261 DataType::Int32 => {
1262 Ok(into_zero_offset_run_array(RunArray::<Int32Type>::from(arr))?.into_data())
1263 }
1264 DataType::Int64 => {
1265 Ok(into_zero_offset_run_array(RunArray::<Int64Type>::from(arr))?.into_data())
1266 }
1267 d => unreachable!("Unexpected data type {d}"),
1268 },
1269 d => Err(ArrowError::InvalidArgumentError(format!(
1270 "The given array is not a run array. Data type of given array: {d}"
1271 ))),
1272 }
1273}
1274
1275fn into_zero_offset_run_array<R: RunEndIndexType>(
1278 run_array: RunArray<R>,
1279) -> Result<RunArray<R>, ArrowError> {
1280 let run_ends = run_array.run_ends();
1281 if run_ends.offset() == 0 && run_ends.max_value() == run_ends.len() {
1282 return Ok(run_array);
1283 }
1284
1285 let start_physical_index = run_ends.get_start_physical_index();
1287
1288 let end_physical_index = run_ends.get_end_physical_index();
1290
1291 let physical_length = end_physical_index - start_physical_index + 1;
1292
1293 let offset = R::Native::usize_as(run_ends.offset());
1295 let mut builder = BufferBuilder::<R::Native>::new(physical_length);
1296 for run_end_value in &run_ends.values()[start_physical_index..end_physical_index] {
1297 builder.append(run_end_value.sub_wrapping(offset));
1298 }
1299 builder.append(R::Native::from_usize(run_array.len()).unwrap());
1300 let new_run_ends = unsafe {
1301 ArrayDataBuilder::new(R::DATA_TYPE)
1304 .len(physical_length)
1305 .add_buffer(builder.finish())
1306 .build_unchecked()
1307 };
1308
1309 let new_values = run_array
1311 .values()
1312 .slice(start_physical_index, physical_length)
1313 .into_data();
1314
1315 let builder = ArrayDataBuilder::new(run_array.data_type().clone())
1316 .len(run_array.len())
1317 .add_child_data(new_run_ends)
1318 .add_child_data(new_values);
1319 let array_data = unsafe {
1320 builder.build_unchecked()
1323 };
1324 Ok(array_data.into())
1325}
1326
1327#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1329pub enum DictionaryHandling {
1330 #[default]
1332 Resend,
1333 Delta,
1339}
1340
1341#[derive(Debug, Clone)]
1343pub enum DictionaryUpdate {
1344 None,
1347 New,
1349 Replaced,
1351 Delta(ArrayData),
1353}
1354
1355#[derive(Debug)]
1361pub struct DictionaryTracker {
1362 written: HashMap<i64, ArrayData>,
1364 dict_ids: Vec<i64>,
1365 error_on_replacement: bool,
1366}
1367
1368impl DictionaryTracker {
1369 pub fn new(error_on_replacement: bool) -> Self {
1375 #[allow(deprecated)]
1376 Self {
1377 written: HashMap::new(),
1378 dict_ids: Vec::new(),
1379 error_on_replacement,
1380 }
1381 }
1382
1383 pub fn next_dict_id(&mut self) -> i64 {
1385 let next = self
1386 .dict_ids
1387 .last()
1388 .copied()
1389 .map(|i| i + 1)
1390 .unwrap_or_default();
1391
1392 self.dict_ids.push(next);
1393 next
1394 }
1395
1396 pub fn dict_id(&mut self) -> &[i64] {
1399 &self.dict_ids
1400 }
1401
1402 #[deprecated(since = "56.1.0", note = "Use `insert_column` instead")]
1412 pub fn insert(&mut self, dict_id: i64, column: &ArrayRef) -> Result<bool, ArrowError> {
1413 let dict_data = column.to_data();
1414 let dict_values = &dict_data.child_data()[0];
1415
1416 if let Some(last) = self.written.get(&dict_id) {
1418 if ArrayData::ptr_eq(&last.child_data()[0], dict_values) {
1419 return Ok(false);
1421 }
1422 if self.error_on_replacement {
1423 if last.child_data()[0] == *dict_values {
1425 return Ok(false);
1427 }
1428 return Err(ArrowError::InvalidArgumentError(
1429 "Dictionary replacement detected when writing IPC file format. \
1430 Arrow IPC files only support a single dictionary for a given field \
1431 across all batches."
1432 .to_string(),
1433 ));
1434 }
1435 }
1436
1437 self.written.insert(dict_id, dict_data);
1438 Ok(true)
1439 }
1440
1441 pub fn insert_column(
1457 &mut self,
1458 dict_id: i64,
1459 column: &ArrayRef,
1460 dict_handling: DictionaryHandling,
1461 ) -> Result<DictionaryUpdate, ArrowError> {
1462 let new_data = column.to_data();
1463 let new_values = &new_data.child_data()[0];
1464
1465 let Some(old) = self.written.get(&dict_id) else {
1467 self.written.insert(dict_id, new_data);
1468 return Ok(DictionaryUpdate::New);
1469 };
1470
1471 let old_values = &old.child_data()[0];
1474 if ArrayData::ptr_eq(old_values, new_values) {
1475 return Ok(DictionaryUpdate::None);
1476 }
1477
1478 let comparison = compare_dictionaries(old_values, new_values);
1480 if matches!(comparison, DictionaryComparison::Equal) {
1481 return Ok(DictionaryUpdate::None);
1482 }
1483
1484 const REPLACEMENT_ERROR: &str = "Dictionary replacement detected when writing IPC file format. \
1485 Arrow IPC files only support a single dictionary for a given field \
1486 across all batches.";
1487
1488 match comparison {
1489 DictionaryComparison::NotEqual => {
1490 if self.error_on_replacement {
1491 return Err(ArrowError::InvalidArgumentError(
1492 REPLACEMENT_ERROR.to_string(),
1493 ));
1494 }
1495
1496 self.written.insert(dict_id, new_data);
1497 Ok(DictionaryUpdate::Replaced)
1498 }
1499 DictionaryComparison::Delta => match dict_handling {
1500 DictionaryHandling::Resend => {
1501 if self.error_on_replacement {
1502 return Err(ArrowError::InvalidArgumentError(
1503 REPLACEMENT_ERROR.to_string(),
1504 ));
1505 }
1506
1507 self.written.insert(dict_id, new_data);
1508 Ok(DictionaryUpdate::Replaced)
1509 }
1510 DictionaryHandling::Delta => {
1511 let delta =
1512 new_values.slice(old_values.len(), new_values.len() - old_values.len());
1513 self.written.insert(dict_id, new_data);
1514 Ok(DictionaryUpdate::Delta(delta))
1515 }
1516 },
1517 DictionaryComparison::Equal => unreachable!("Already checked equal case"),
1518 }
1519 }
1520
1521 pub fn clear(&mut self) {
1527 self.dict_ids.clear();
1528 self.written.clear();
1529 }
1530}
1531
1532#[derive(Debug, Clone)]
1534enum DictionaryComparison {
1535 NotEqual,
1537 Equal,
1539 Delta,
1542}
1543
1544fn compare_dictionaries(old: &ArrayData, new: &ArrayData) -> DictionaryComparison {
1546 let existing_len = old.len();
1548 let new_len = new.len();
1549 if existing_len == new_len {
1550 if *old == *new {
1551 return DictionaryComparison::Equal;
1552 } else {
1553 return DictionaryComparison::NotEqual;
1554 }
1555 }
1556
1557 if new_len < existing_len {
1559 return DictionaryComparison::NotEqual;
1560 }
1561
1562 if new.slice(0, existing_len) == *old {
1564 return DictionaryComparison::Delta;
1565 }
1566
1567 DictionaryComparison::NotEqual
1568}
1569
1570pub struct FileWriter<W> {
1593 writer: W,
1595 write_options: IpcWriteOptions,
1597 schema: SchemaRef,
1599 block_offsets: usize,
1601 dictionary_blocks: Vec<crate::Block>,
1603 record_blocks: Vec<crate::Block>,
1605 finished: bool,
1607 dictionary_tracker: DictionaryTracker,
1609 custom_metadata: HashMap<String, String>,
1611
1612 data_gen: IpcDataGenerator,
1613
1614 ipc_write_context: IpcWriteContext,
1615}
1616
1617impl<W: Write> FileWriter<BufWriter<W>> {
1618 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1622 Self::try_new(BufWriter::new(writer), schema)
1623 }
1624}
1625
1626impl<W: Write> FileWriter<W> {
1627 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1635 let write_options = IpcWriteOptions::default();
1636 Self::try_new_with_options(writer, schema, write_options)
1637 }
1638
1639 pub fn try_new_with_options(
1647 mut writer: W,
1648 schema: &Schema,
1649 write_options: IpcWriteOptions,
1650 ) -> Result<Self, ArrowError> {
1651 ensure_supported_ipc_schema(schema)?;
1652
1653 let data_gen = IpcDataGenerator::default();
1654 let pad_len = pad_to_alignment(write_options.alignment, super::ARROW_MAGIC.len());
1656 let header_size = super::ARROW_MAGIC.len() + pad_len;
1657 writer.write_all(&super::ARROW_MAGIC)?;
1658 writer.write_all(&PADDING[..pad_len])?;
1659 let mut dictionary_tracker = DictionaryTracker::new(true);
1661 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
1662 schema,
1663 &mut dictionary_tracker,
1664 &write_options,
1665 );
1666 let (meta, data) = write_message(&mut writer, encoded_message, &write_options)?;
1667 Ok(Self {
1668 writer,
1669 write_options,
1670 schema: Arc::new(schema.clone()),
1671 block_offsets: meta + data + header_size,
1672 dictionary_blocks: vec![],
1673 record_blocks: vec![],
1674 finished: false,
1675 dictionary_tracker,
1676 custom_metadata: HashMap::new(),
1677 data_gen,
1678 ipc_write_context: IpcWriteContext::default(),
1679 })
1680 }
1681
1682 pub fn write_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
1684 self.custom_metadata.insert(key.into(), value.into());
1685 }
1686
1687 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1689 if self.finished {
1690 return Err(ArrowError::IpcError(
1691 "Cannot write record batch to file writer as it is closed".to_string(),
1692 ));
1693 }
1694
1695 let meta = self.data_gen.write(
1696 batch,
1697 &mut self.dictionary_tracker,
1698 &self.write_options,
1699 &mut self.ipc_write_context,
1700 &mut self.writer,
1701 )?;
1702
1703 for (header_len, body_len) in meta.dictionary_block_sizes {
1704 let block = crate::Block::new(
1705 self.block_offsets as i64,
1706 header_len as i32,
1707 body_len as i64,
1708 );
1709 self.dictionary_blocks.push(block);
1710 self.block_offsets += header_len + body_len;
1711 }
1712
1713 let block = crate::Block::new(
1715 self.block_offsets as i64,
1716 meta.padded_header_len as i32,
1717 meta.body_len as i64,
1718 );
1719 self.record_blocks.push(block);
1720 self.block_offsets += meta.padded_header_len + meta.body_len;
1721 Ok(())
1722 }
1723
1724 pub fn finish(&mut self) -> Result<(), ArrowError> {
1726 if self.finished {
1727 return Err(ArrowError::IpcError(
1728 "Cannot write footer to file writer as it is closed".to_string(),
1729 ));
1730 }
1731
1732 {
1734 self.writer.write_eos(&self.write_options)?;
1735 }
1736
1737 let mut fbb = FlatBufferBuilder::new();
1738 let dictionaries = fbb.create_vector(&self.dictionary_blocks);
1739 let record_batches = fbb.create_vector(&self.record_blocks);
1740
1741 self.dictionary_tracker.clear();
1743 let schema = IpcSchemaEncoder::new()
1744 .with_dictionary_tracker(&mut self.dictionary_tracker)
1745 .schema_to_fb_offset(&mut fbb, &self.schema);
1746 let fb_custom_metadata = (!self.custom_metadata.is_empty())
1747 .then(|| crate::convert::metadata_to_fb(&mut fbb, &self.custom_metadata));
1748
1749 let root = {
1750 let mut footer_builder = crate::FooterBuilder::new(&mut fbb);
1751 footer_builder.add_version(self.write_options.metadata_version);
1752 footer_builder.add_schema(schema);
1753 footer_builder.add_dictionaries(dictionaries);
1754 footer_builder.add_recordBatches(record_batches);
1755 if let Some(fb_custom_metadata) = fb_custom_metadata {
1756 footer_builder.add_custom_metadata(fb_custom_metadata);
1757 }
1758 footer_builder.finish()
1759 };
1760 fbb.finish(root, None);
1761 let footer_data = fbb.finished_data();
1762 self.writer.write_all(footer_data)?;
1763 self.writer
1764 .write_all(&(footer_data.len() as i32).to_le_bytes())?;
1765 self.writer.write_all(&super::ARROW_MAGIC)?;
1766 self.writer.flush()?;
1767 self.finished = true;
1768
1769 Ok(())
1770 }
1771
1772 pub fn schema(&self) -> &SchemaRef {
1774 &self.schema
1775 }
1776
1777 pub fn get_ref(&self) -> &W {
1779 &self.writer
1780 }
1781
1782 pub fn get_mut(&mut self) -> &mut W {
1786 &mut self.writer
1787 }
1788
1789 pub fn flush(&mut self) -> Result<(), ArrowError> {
1793 self.writer.flush()?;
1794 Ok(())
1795 }
1796
1797 pub fn into_inner(mut self) -> Result<W, ArrowError> {
1806 if !self.finished {
1807 self.finish()?;
1809 }
1810 Ok(self.writer)
1811 }
1812}
1813
1814impl<W: Write> RecordBatchWriter for FileWriter<W> {
1815 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1816 self.write(batch)
1817 }
1818
1819 fn close(mut self) -> Result<(), ArrowError> {
1820 self.finish()
1821 }
1822}
1823
1824pub struct StreamEncoder {
1853 schema: Schema,
1854 write_options: IpcWriteOptions,
1856 schema_encoded: bool,
1858 dictionary_tracker: DictionaryTracker,
1860 data_gen: IpcDataGenerator,
1861 ipc_write_context: IpcWriteContext,
1862}
1863
1864impl StreamEncoder {
1865 pub fn try_new(schema: &Schema) -> Result<Self, ArrowError> {
1867 let write_options = IpcWriteOptions::default();
1868 Self::try_new_with_options(schema, write_options)
1869 }
1870
1871 pub fn try_new_with_options(
1873 schema: &Schema,
1874 write_options: IpcWriteOptions,
1875 ) -> Result<Self, ArrowError> {
1876 ensure_supported_ipc_schema(schema)?;
1877
1878 Ok(Self {
1879 schema: schema.clone(),
1880 write_options,
1881 schema_encoded: false,
1882 dictionary_tracker: DictionaryTracker::new(false),
1883 data_gen: IpcDataGenerator::default(),
1884 ipc_write_context: IpcWriteContext::default(),
1885 })
1886 }
1887
1888 pub fn encode(&mut self, batch: &RecordBatch) -> Result<Vec<Buffer>, ArrowError> {
1898 let mut out = vec![];
1899 self.encode_schema(&mut out)?;
1900 self.data_gen.encode_to_buffers(
1901 batch,
1902 &mut self.dictionary_tracker,
1903 &self.write_options,
1904 &mut self.ipc_write_context,
1905 &mut out,
1906 )?;
1907 Ok(out)
1908 }
1909
1910 pub fn finish(mut self) -> Result<Vec<Buffer>, ArrowError> {
1919 let mut out = vec![];
1920 self.encode_schema(&mut out)?;
1921 let mut sink = Buffers { out: &mut out };
1922 sink.write_eos(&self.write_options)?;
1923 Ok(out)
1924 }
1925
1926 fn encode_schema(&mut self, out: &mut Vec<Buffer>) -> Result<(), ArrowError> {
1927 if !self.schema_encoded {
1928 let encoded_message = self.data_gen.schema_to_bytes_with_dictionary_tracker(
1929 &self.schema,
1930 &mut self.dictionary_tracker,
1931 &self.write_options,
1932 );
1933 let mut sink = Buffers { out };
1934 sink.write_encoded_data(encoded_message, &self.write_options)?;
1935 self.schema_encoded = true;
1936 }
1937 Ok(())
1938 }
1939}
1940
1941pub struct StreamWriter<W> {
2015 writer: W,
2017 write_options: IpcWriteOptions,
2019 finished: bool,
2021 dictionary_tracker: DictionaryTracker,
2023
2024 data_gen: IpcDataGenerator,
2025
2026 ipc_write_context: IpcWriteContext,
2027}
2028
2029impl<W: Write> StreamWriter<BufWriter<W>> {
2030 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
2034 Self::try_new(BufWriter::new(writer), schema)
2035 }
2036}
2037
2038impl<W: Write> StreamWriter<W> {
2039 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
2047 let write_options = IpcWriteOptions::default();
2048 Self::try_new_with_options(writer, schema, write_options)
2049 }
2050
2051 pub fn try_new_with_options(
2057 mut writer: W,
2058 schema: &Schema,
2059 write_options: IpcWriteOptions,
2060 ) -> Result<Self, ArrowError> {
2061 ensure_supported_ipc_schema(schema)?;
2062
2063 let data_gen = IpcDataGenerator::default();
2064 let mut dictionary_tracker = DictionaryTracker::new(false);
2065
2066 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
2068 schema,
2069 &mut dictionary_tracker,
2070 &write_options,
2071 );
2072 write_message(&mut writer, encoded_message, &write_options)?;
2073 Ok(Self {
2074 writer,
2075 write_options,
2076 finished: false,
2077 dictionary_tracker,
2078 data_gen,
2079 ipc_write_context: IpcWriteContext::default(),
2080 })
2081 }
2082
2083 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
2085 if self.finished {
2086 return Err(ArrowError::IpcError(
2087 "Cannot write record batch to stream writer as it is closed".to_string(),
2088 ));
2089 }
2090
2091 self.data_gen.write(
2092 batch,
2093 &mut self.dictionary_tracker,
2094 &self.write_options,
2095 &mut self.ipc_write_context,
2096 &mut self.writer,
2097 )?;
2098 Ok(())
2099 }
2100
2101 pub fn finish(&mut self) -> Result<(), ArrowError> {
2103 if self.finished {
2104 return Err(ArrowError::IpcError(
2105 "Cannot write footer to stream writer as it is closed".to_string(),
2106 ));
2107 }
2108
2109 {
2110 self.writer.write_eos(&self.write_options)?;
2111 }
2112 self.writer.flush()?;
2113
2114 self.finished = true;
2115
2116 Ok(())
2117 }
2118
2119 pub fn get_ref(&self) -> &W {
2121 &self.writer
2122 }
2123
2124 pub fn get_mut(&mut self) -> &mut W {
2128 &mut self.writer
2129 }
2130
2131 pub fn flush(&mut self) -> Result<(), ArrowError> {
2135 self.writer.flush()?;
2136 Ok(())
2137 }
2138
2139 pub fn into_inner(mut self) -> Result<W, ArrowError> {
2177 if !self.finished {
2178 self.finish()?;
2180 }
2181 Ok(self.writer)
2182 }
2183}
2184
2185impl<W: Write> RecordBatchWriter for StreamWriter<W> {
2186 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
2187 self.write(batch)
2188 }
2189
2190 fn close(mut self) -> Result<(), ArrowError> {
2191 self.finish()
2192 }
2193}
2194
2195pub struct EncodedData {
2197 pub ipc_message: Vec<u8>,
2199 pub arrow_data: Vec<u8>,
2201}
2202
2203pub fn write_message<W: Write>(
2205 mut writer: W,
2206 encoded: EncodedData,
2207 write_options: &IpcWriteOptions,
2208) -> Result<(usize, usize), ArrowError> {
2209 writer.write_encoded_data(encoded, write_options)
2210}
2211
2212fn has_validity_bitmap(data_type: &DataType, write_options: &IpcWriteOptions) -> bool {
2216 if write_options.metadata_version < crate::MetadataVersion::V5 {
2217 !matches!(data_type, DataType::Null)
2218 } else {
2219 !matches!(
2220 data_type,
2221 DataType::Null | DataType::Union(_, _) | DataType::RunEndEncoded(_, _)
2222 )
2223 }
2224}
2225
2226#[inline]
2228fn buffer_need_truncate(
2229 array_offset: usize,
2230 buffer: &Buffer,
2231 spec: &BufferSpec,
2232 min_length: usize,
2233) -> bool {
2234 spec != &BufferSpec::AlwaysNull && (array_offset != 0 || min_length < buffer.len())
2235}
2236
2237#[inline]
2239fn get_buffer_element_width(spec: &BufferSpec) -> usize {
2240 match spec {
2241 BufferSpec::FixedWidth { byte_width, .. } => *byte_width,
2242 _ => 0,
2243 }
2244}
2245
2246fn reencode_offsets<O: OffsetSizeTrait>(
2249 offsets: &Buffer,
2250 data: &ArrayData,
2251) -> (Buffer, usize, usize) {
2252 let offsets_slice: &[O] = offsets.typed_data::<O>();
2253 let offset_slice = &offsets_slice[data.offset()..data.offset() + data.len() + 1];
2254
2255 let start_offset = offset_slice.first().unwrap();
2256 let end_offset = offset_slice.last().unwrap();
2257
2258 let offsets = match start_offset.as_usize() {
2259 0 => {
2260 let size = size_of::<O>();
2261 offsets.slice_with_length(data.offset() * size, (data.len() + 1) * size)
2262 }
2263 _ => offset_slice.iter().map(|x| *x - *start_offset).collect(),
2264 };
2265
2266 let start_offset = start_offset.as_usize();
2267 let end_offset = end_offset.as_usize();
2268
2269 (offsets, start_offset, end_offset - start_offset)
2270}
2271
2272fn get_byte_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, Buffer) {
2278 if data.is_empty() {
2279 let mut offsets = MutableBuffer::new(size_of::<O>());
2282 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
2283 return (offsets.into(), MutableBuffer::new(0).into());
2284 }
2285
2286 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
2287 let values = data.buffers()[1].slice_with_length(original_start_offset, len);
2288 (offsets, values)
2289}
2290
2291fn get_list_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, ArrayData) {
2294 if data.is_empty() {
2295 let mut offsets = MutableBuffer::new(size_of::<O>());
2298 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
2299 return (offsets.into(), data.child_data()[0].slice(0, 0));
2300 }
2301
2302 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
2303 let child_data = data.child_data()[0].slice(original_start_offset, len);
2304 (offsets, child_data)
2305}
2306
2307fn get_list_view_array_buffers<O: OffsetSizeTrait>(
2313 data: &ArrayData,
2314) -> (Buffer, Buffer, ArrayData) {
2315 if data.is_empty() {
2316 return (
2317 MutableBuffer::new(0).into(),
2318 MutableBuffer::new(0).into(),
2319 data.child_data()[0].slice(0, 0),
2320 );
2321 }
2322
2323 let offsets = &data.buffers()[0];
2324 let sizes = &data.buffers()[1];
2325
2326 let element_size = std::mem::size_of::<O>();
2327 let offsets_slice =
2328 offsets.slice_with_length(data.offset() * element_size, data.len() * element_size);
2329 let sizes_slice =
2330 sizes.slice_with_length(data.offset() * element_size, data.len() * element_size);
2331
2332 let child_data = data.child_data()[0].clone();
2333
2334 (offsets_slice, sizes_slice, child_data)
2335}
2336
2337fn get_or_truncate_buffer(array_data: &ArrayData) -> Buffer {
2344 let buffer = &array_data.buffers()[0];
2345 let layout = layout(array_data.data_type());
2346 let spec = &layout.buffers[0];
2347
2348 let byte_width = get_buffer_element_width(spec);
2349 let min_length = array_data.len() * byte_width;
2350 if buffer_need_truncate(array_data.offset(), buffer, spec, min_length) {
2351 let byte_offset = array_data.offset() * byte_width;
2352 let buffer_length = min(min_length, buffer.len() - byte_offset);
2353 buffer.slice_with_length(byte_offset, buffer_length)
2354 } else {
2355 buffer.clone()
2356 }
2357}
2358
2359fn write_array_data(
2365 array_data: &ArrayData,
2366 meta: &mut IpcMetadataBuilder,
2367 sink: &mut IpcBodySink<'_>,
2368 offset: i64,
2369 compression_codec: Option<CompressionCodec>,
2370 ipc_write_context: &mut IpcWriteContext,
2371 write_options: &IpcWriteOptions,
2372) -> Result<i64, ArrowError> {
2373 let mut offset = offset;
2374 let num_rows = array_data.len();
2375 let null_count = array_data.null_count();
2376 if !matches!(array_data.data_type(), DataType::Null) {
2377 meta.nodes
2378 .push(crate::FieldNode::new(num_rows as i64, null_count as i64));
2379 } else {
2380 meta.nodes
2382 .push(crate::FieldNode::new(num_rows as i64, num_rows as i64));
2383 }
2384 if has_validity_bitmap(array_data.data_type(), write_options) {
2385 let null_buffer = match array_data.nulls() {
2388 Some(buffer) if null_count > 0 => buffer.inner().sliced(),
2389 _ => MutableBuffer::new(0).into(),
2390 };
2391
2392 offset = encode_sink_buffer(
2393 null_buffer,
2394 meta,
2395 sink,
2396 offset,
2397 compression_codec,
2398 ipc_write_context,
2399 write_options.alignment,
2400 )?;
2401 }
2402
2403 let data_type = array_data.data_type();
2404 if matches!(data_type, DataType::Binary | DataType::Utf8) {
2405 let (offsets, values) = get_byte_array_buffers::<i32>(array_data);
2406 for buffer in [offsets, values] {
2407 offset = encode_sink_buffer(
2408 buffer,
2409 meta,
2410 sink,
2411 offset,
2412 compression_codec,
2413 ipc_write_context,
2414 write_options.alignment,
2415 )?;
2416 }
2417 } else if matches!(data_type, DataType::BinaryView | DataType::Utf8View) {
2418 let views = get_or_truncate_buffer(array_data);
2425 offset = encode_sink_buffer(
2426 views,
2427 meta,
2428 sink,
2429 offset,
2430 compression_codec,
2431 ipc_write_context,
2432 write_options.alignment,
2433 )?;
2434
2435 for buffer in array_data.buffers().iter().skip(1) {
2436 offset = encode_sink_buffer(
2437 buffer.clone(),
2438 meta,
2439 sink,
2440 offset,
2441 compression_codec,
2442 ipc_write_context,
2443 write_options.alignment,
2444 )?;
2445 }
2446 } else if matches!(data_type, DataType::LargeBinary | DataType::LargeUtf8) {
2447 let (offsets, values) = get_byte_array_buffers::<i64>(array_data);
2448 for buffer in [offsets, values] {
2449 offset = encode_sink_buffer(
2450 buffer,
2451 meta,
2452 sink,
2453 offset,
2454 compression_codec,
2455 ipc_write_context,
2456 write_options.alignment,
2457 )?;
2458 }
2459 } else if DataType::is_numeric(data_type)
2460 || DataType::is_temporal(data_type)
2461 || matches!(
2462 array_data.data_type(),
2463 DataType::FixedSizeBinary(_) | DataType::Dictionary(_, _)
2464 )
2465 {
2466 assert_eq!(array_data.buffers().len(), 1);
2468
2469 let buffer = get_or_truncate_buffer(array_data);
2470 offset = encode_sink_buffer(
2471 buffer,
2472 meta,
2473 sink,
2474 offset,
2475 compression_codec,
2476 ipc_write_context,
2477 write_options.alignment,
2478 )?;
2479 } else if matches!(data_type, DataType::Boolean) {
2480 assert_eq!(array_data.buffers().len(), 1);
2483
2484 let buffer = &array_data.buffers()[0];
2485 let buffer = buffer.bit_slice(array_data.offset(), array_data.len());
2486 offset = encode_sink_buffer(
2487 buffer,
2488 meta,
2489 sink,
2490 offset,
2491 compression_codec,
2492 ipc_write_context,
2493 write_options.alignment,
2494 )?;
2495 } else if matches!(
2496 data_type,
2497 DataType::List(_) | DataType::LargeList(_) | DataType::Map(_, _)
2498 ) {
2499 assert_eq!(array_data.buffers().len(), 1);
2500 assert_eq!(array_data.child_data().len(), 1);
2501
2502 let (offsets, sliced_child_data) = match data_type {
2504 DataType::List(_) => get_list_array_buffers::<i32>(array_data),
2505 DataType::Map(_, _) => get_list_array_buffers::<i32>(array_data),
2506 DataType::LargeList(_) => get_list_array_buffers::<i64>(array_data),
2507 _ => unreachable!(),
2508 };
2509 offset = encode_sink_buffer(
2510 offsets,
2511 meta,
2512 sink,
2513 offset,
2514 compression_codec,
2515 ipc_write_context,
2516 write_options.alignment,
2517 )?;
2518 offset = write_array_data(
2519 &sliced_child_data,
2520 meta,
2521 sink,
2522 offset,
2523 compression_codec,
2524 ipc_write_context,
2525 write_options,
2526 )?;
2527 return Ok(offset);
2528 } else if matches!(
2529 data_type,
2530 DataType::ListView(_) | DataType::LargeListView(_)
2531 ) {
2532 assert_eq!(array_data.buffers().len(), 2); assert_eq!(array_data.child_data().len(), 1);
2534
2535 let (offsets, sizes, child_data) = match data_type {
2536 DataType::ListView(_) => get_list_view_array_buffers::<i32>(array_data),
2537 DataType::LargeListView(_) => get_list_view_array_buffers::<i64>(array_data),
2538 _ => unreachable!(),
2539 };
2540
2541 offset = encode_sink_buffer(
2542 offsets,
2543 meta,
2544 sink,
2545 offset,
2546 compression_codec,
2547 ipc_write_context,
2548 write_options.alignment,
2549 )?;
2550 offset = encode_sink_buffer(
2551 sizes,
2552 meta,
2553 sink,
2554 offset,
2555 compression_codec,
2556 ipc_write_context,
2557 write_options.alignment,
2558 )?;
2559
2560 offset = write_array_data(
2561 &child_data,
2562 meta,
2563 sink,
2564 offset,
2565 compression_codec,
2566 ipc_write_context,
2567 write_options,
2568 )?;
2569 return Ok(offset);
2570 } else if let DataType::FixedSizeList(_, fixed_size) = data_type {
2571 assert_eq!(array_data.child_data().len(), 1);
2572 let fixed_size = *fixed_size as usize;
2573
2574 let child_offset = array_data.offset() * fixed_size;
2575 let child_length = array_data.len() * fixed_size;
2576 let child_data = array_data.child_data()[0].slice(child_offset, child_length);
2577
2578 offset = write_array_data(
2579 &child_data,
2580 meta,
2581 sink,
2582 offset,
2583 compression_codec,
2584 ipc_write_context,
2585 write_options,
2586 )?;
2587 return Ok(offset);
2588 } else {
2589 for buffer in array_data.buffers() {
2590 offset = encode_sink_buffer(
2591 buffer.clone(),
2592 meta,
2593 sink,
2594 offset,
2595 compression_codec,
2596 ipc_write_context,
2597 write_options.alignment,
2598 )?;
2599 }
2600 }
2601
2602 match array_data.data_type() {
2603 DataType::Dictionary(_, _) => {}
2604 DataType::RunEndEncoded(_, _) => {
2605 let arr = unslice_run_array(array_data.clone())?;
2607 for data_ref in arr.child_data() {
2609 offset = write_array_data(
2611 data_ref,
2612 meta,
2613 sink,
2614 offset,
2615 compression_codec,
2616 ipc_write_context,
2617 write_options,
2618 )?;
2619 }
2620 }
2621 _ => {
2622 for data_ref in array_data.child_data() {
2624 offset = write_array_data(
2626 data_ref,
2627 meta,
2628 sink,
2629 offset,
2630 compression_codec,
2631 ipc_write_context,
2632 write_options,
2633 )?;
2634 }
2635 }
2636 }
2637 Ok(offset)
2638}
2639
2640fn encode_sink_buffer(
2654 buffer: Buffer,
2655 ipc_meta_data: &mut IpcMetadataBuilder,
2656 sink: &mut IpcBodySink<'_>,
2657 offset: i64,
2658 compression_codec: Option<CompressionCodec>,
2659 ipc_write_context: &mut IpcWriteContext,
2660 alignment: u8,
2661) -> Result<i64, ArrowError> {
2662 let (encoded, len) = match compression_codec {
2663 None => {
2664 let len = buffer.len() as i64;
2665 (EncodedBuffer::Raw(buffer), len)
2666 }
2667 Some(codec) => {
2668 let mut scratch = Vec::new();
2669 let written =
2670 codec.compress_to_vec(buffer.as_slice(), &mut scratch, ipc_write_context)?;
2671 let len = i64::try_from(written)
2672 .map_err(|e| ArrowError::InvalidArgumentError(format!("{e}")))?;
2673 (EncodedBuffer::Compressed(scratch), len)
2674 }
2675 };
2676
2677 let pad_len = pad_to_alignment(alignment, len as usize);
2678 sink.write(pad_len, encoded);
2679 ipc_meta_data.buffers.push(crate::Buffer::new(offset, len));
2680 Ok(offset + len + pad_len as i64)
2681}
2682
2683const PADDING: [u8; 64] = [0; 64];
2684
2685#[inline]
2691fn estimate_encoded_buffer_count(dt: &DataType) -> usize {
2692 match dt {
2693 DataType::Null => 0,
2694
2695 DataType::Binary | DataType::Utf8 | DataType::LargeBinary | DataType::LargeUtf8 => 3,
2696
2697 DataType::BinaryView | DataType::Utf8View => 3,
2698
2699 DataType::List(f) | DataType::LargeList(f) | DataType::Map(f, _) => {
2700 2 + estimate_encoded_buffer_count(f.data_type())
2701 }
2702
2703 DataType::ListView(f) | DataType::LargeListView(f) => {
2704 3 + estimate_encoded_buffer_count(f.data_type())
2705 }
2706
2707 DataType::FixedSizeList(f, _) => 1 + estimate_encoded_buffer_count(f.data_type()),
2708
2709 DataType::Struct(fields) => {
2710 1 + fields
2711 .iter()
2712 .map(|f| estimate_encoded_buffer_count(f.data_type()))
2713 .sum::<usize>()
2714 }
2715
2716 DataType::Dictionary(_, _) => 2,
2718
2719 DataType::Union(fields, UnionMode::Sparse) => {
2720 1 + fields
2721 .iter()
2722 .map(|(_, f)| estimate_encoded_buffer_count(f.data_type()))
2723 .sum::<usize>()
2724 }
2725 DataType::Union(fields, UnionMode::Dense) => {
2726 2 + fields
2727 .iter()
2728 .map(|(_, f)| estimate_encoded_buffer_count(f.data_type()))
2729 .sum::<usize>()
2730 }
2731
2732 DataType::RunEndEncoded(run_ends, values) => {
2733 estimate_encoded_buffer_count(run_ends.data_type())
2734 + estimate_encoded_buffer_count(values.data_type())
2735 }
2736 _ => 2,
2738 }
2739}
2740
2741#[inline]
2743fn pad_to_alignment(alignment: u8, len: usize) -> usize {
2744 let a = usize::from(alignment - 1);
2745 ((len + a) & !a) - len
2746}
2747
2748#[cfg(test)]
2749mod tests {
2750 use std::hash::Hasher;
2751 use std::io::Cursor;
2752 use std::io::Seek;
2753
2754 use arrow_array::builder::FixedSizeListBuilder;
2755 use arrow_array::builder::Float32Builder;
2756 use arrow_array::builder::Int64Builder;
2757 use arrow_array::builder::MapBuilder;
2758 use arrow_array::builder::StringViewBuilder;
2759 use arrow_array::builder::UnionBuilder;
2760 use arrow_array::builder::{
2761 GenericListBuilder, GenericListViewBuilder, ListBuilder, StringBuilder,
2762 };
2763 use arrow_array::builder::{PrimitiveRunBuilder, UInt32Builder};
2764 use arrow_array::types::*;
2765 use arrow_buffer::ScalarBuffer;
2766
2767 use crate::MetadataVersion;
2768 use crate::convert::fb_to_schema;
2769 use crate::reader::*;
2770 use crate::root_as_footer;
2771
2772 use super::*;
2773
2774 fn serialize_file(rb: &RecordBatch) -> Vec<u8> {
2775 let mut writer = FileWriter::try_new(vec![], rb.schema_ref()).unwrap();
2776 writer.write(rb).unwrap();
2777 writer.finish().unwrap();
2778 writer.into_inner().unwrap()
2779 }
2780
2781 fn deserialize_file(bytes: Vec<u8>) -> RecordBatch {
2782 let mut reader = FileReader::try_new(Cursor::new(bytes), None).unwrap();
2783 reader.next().unwrap().unwrap()
2784 }
2785
2786 fn serialize_stream(record: &RecordBatch) -> Vec<u8> {
2787 const IPC_ALIGNMENT: usize = 8;
2791
2792 let mut stream_writer = StreamWriter::try_new_with_options(
2793 vec![],
2794 record.schema_ref(),
2795 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
2796 )
2797 .unwrap();
2798 stream_writer.write(record).unwrap();
2799 stream_writer.finish().unwrap();
2800 stream_writer.into_inner().unwrap()
2801 }
2802
2803 fn deserialize_stream(bytes: Vec<u8>) -> RecordBatch {
2804 let mut stream_reader = StreamReader::try_new(Cursor::new(bytes), None).unwrap();
2805 stream_reader.next().unwrap().unwrap()
2806 }
2807
2808 fn encode_stream(
2813 schema: &Schema,
2814 batches: &[RecordBatch],
2815 options: IpcWriteOptions,
2816 ) -> Vec<u8> {
2817 let mut encoder = StreamEncoder::try_new_with_options(schema, options).unwrap();
2818 let mut bytes = Vec::new();
2819 for batch in batches {
2820 for buffer in encoder.encode(batch).unwrap() {
2821 bytes.write_all(buffer.as_slice()).unwrap();
2822 }
2823 }
2824 for buffer in encoder.finish().unwrap() {
2825 bytes.write_all(buffer.as_slice()).unwrap();
2826 }
2827 bytes
2828 }
2829
2830 fn write_stream(schema: &Schema, batches: &[RecordBatch], options: IpcWriteOptions) -> Vec<u8> {
2831 let mut bytes = Vec::new();
2832 let mut writer = StreamWriter::try_new_with_options(&mut bytes, schema, options).unwrap();
2833 for batch in batches {
2834 writer.write(batch).unwrap();
2835 }
2836 writer.finish().unwrap();
2837 bytes
2838 }
2839
2840 #[tokio::test]
2843 async fn test_stream_encoder_async_writer_matches_stream_writer() {
2844 use tokio::io::{AsyncReadExt, AsyncWriteExt};
2845
2846 let batch = record_batch!(("a", Int32, [1, 2, 3]), ("b", Utf8, ["x", "y", "z"])).unwrap();
2847 let options = IpcWriteOptions::default();
2848 let expected = write_stream(
2849 batch.schema_ref(),
2850 std::slice::from_ref(&batch),
2851 options.clone(),
2852 );
2853
2854 let (mut sink, mut source) = tokio::io::duplex(64);
2855 let read = tokio::spawn(async move {
2856 let mut bytes = Vec::new();
2857 source.read_to_end(&mut bytes).await.unwrap();
2858 bytes
2859 });
2860
2861 let mut encoder = StreamEncoder::try_new_with_options(batch.schema_ref(), options).unwrap();
2862 for buffer in encoder.encode(&batch).unwrap() {
2863 sink.write_all(buffer.as_slice()).await.unwrap();
2864 }
2865 for buffer in encoder.finish().unwrap() {
2866 sink.write_all(buffer.as_slice()).await.unwrap();
2867 }
2868 sink.shutdown().await.unwrap();
2869
2870 let encoded = read.await.unwrap();
2871 assert_eq!(encoded, expected);
2872
2873 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
2874 assert_eq!(reader.next().unwrap().unwrap(), batch);
2875 assert!(reader.next().is_none());
2876 }
2877
2878 #[test]
2879 fn test_stream_encoder_empty_stream_matches_stream_writer() {
2880 let schema = Schema::new(vec![Field::new("a", DataType::Int32, true)]);
2881 let options = IpcWriteOptions::default();
2882 let encoded = encode_stream(&schema, &[], options.clone());
2883 let written = write_stream(&schema, &[], options);
2884
2885 assert_eq!(encoded, written);
2886
2887 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
2888 assert!(reader.next().is_none());
2889 }
2890
2891 #[test]
2892 fn test_stream_encoder_dictionary_batches_match_stream_writer() {
2893 let schema = Arc::new(Schema::new(vec![Field::new(
2894 "a",
2895 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
2896 false,
2897 )]));
2898 let batch = RecordBatch::try_new(
2899 schema.clone(),
2900 vec![Arc::new(DictionaryArray::new(
2901 UInt8Array::from_iter_values([0, 1, 0]),
2902 Arc::new(StringArray::from_iter_values(["a", "b"])),
2903 ))],
2904 )
2905 .unwrap();
2906 let options = IpcWriteOptions::default();
2907 let encoded = encode_stream(&schema, std::slice::from_ref(&batch), options.clone());
2908 let written = write_stream(&schema, std::slice::from_ref(&batch), options);
2909
2910 assert_eq!(encoded, written);
2911
2912 let mut reader = StreamReader::try_new(Cursor::new(encoded), None).unwrap();
2913 assert_eq!(reader.next().unwrap().unwrap(), batch);
2914 assert!(reader.next().is_none());
2915 }
2916
2917 #[test]
2918 #[cfg(feature = "lz4")]
2919 fn test_write_empty_record_batch_lz4_compression() {
2920 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
2921 let values: Vec<Option<i32>> = vec![];
2922 let array = Int32Array::from(values);
2923 let record_batch =
2924 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
2925
2926 let mut file = tempfile::tempfile().unwrap();
2927
2928 {
2929 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
2930 .unwrap()
2931 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
2932 .unwrap();
2933
2934 let mut writer =
2935 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
2936 writer.write(&record_batch).unwrap();
2937 writer.finish().unwrap();
2938 }
2939 file.rewind().unwrap();
2940 {
2941 let reader = FileReader::try_new(file, None).unwrap();
2943 for read_batch in reader {
2944 read_batch
2945 .unwrap()
2946 .columns()
2947 .iter()
2948 .zip(record_batch.columns())
2949 .for_each(|(a, b)| {
2950 assert_eq!(a.data_type(), b.data_type());
2951 assert_eq!(a.len(), b.len());
2952 assert_eq!(a.null_count(), b.null_count());
2953 });
2954 }
2955 }
2956 }
2957
2958 #[test]
2959 #[cfg(feature = "lz4")]
2960 fn test_write_file_with_lz4_compression() {
2961 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
2962 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
2963 let array = Int32Array::from(values);
2964 let record_batch =
2965 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
2966
2967 let mut file = tempfile::tempfile().unwrap();
2968 {
2969 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
2970 .unwrap()
2971 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
2972 .unwrap();
2973
2974 let mut writer =
2975 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
2976 writer.write(&record_batch).unwrap();
2977 writer.finish().unwrap();
2978 }
2979 file.rewind().unwrap();
2980 {
2981 let reader = FileReader::try_new(file, None).unwrap();
2983 for read_batch in reader {
2984 read_batch
2985 .unwrap()
2986 .columns()
2987 .iter()
2988 .zip(record_batch.columns())
2989 .for_each(|(a, b)| {
2990 assert_eq!(a.data_type(), b.data_type());
2991 assert_eq!(a.len(), b.len());
2992 assert_eq!(a.null_count(), b.null_count());
2993 });
2994 }
2995 }
2996 }
2997
2998 #[test]
2999 #[cfg(feature = "zstd")]
3000 fn test_write_file_with_zstd_compression() {
3001 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
3002 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
3003 let array = Int32Array::from(values);
3004 let record_batch =
3005 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
3006 let mut file = tempfile::tempfile().unwrap();
3007 {
3008 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
3009 .unwrap()
3010 .try_with_compression(Some(crate::CompressionType::ZSTD))
3011 .unwrap()
3012 .try_with_compression_level(Some(1))
3013 .unwrap();
3014
3015 let mut writer =
3016 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
3017 writer.write(&record_batch).unwrap();
3018 writer.finish().unwrap();
3019 }
3020 file.rewind().unwrap();
3021 {
3022 let reader = FileReader::try_new(file, None).unwrap();
3024 for read_batch in reader {
3025 read_batch
3026 .unwrap()
3027 .columns()
3028 .iter()
3029 .zip(record_batch.columns())
3030 .for_each(|(a, b)| {
3031 assert_eq!(a.data_type(), b.data_type());
3032 assert_eq!(a.len(), b.len());
3033 assert_eq!(a.null_count(), b.null_count());
3034 });
3035 }
3036 }
3037 }
3038
3039 #[test]
3040 fn test_write_file() {
3041 let schema = Schema::new(vec![Field::new("field1", DataType::UInt32, true)]);
3042 let values: Vec<Option<u32>> = vec![
3043 Some(999),
3044 None,
3045 Some(235),
3046 Some(123),
3047 None,
3048 None,
3049 None,
3050 None,
3051 None,
3052 ];
3053 let array1 = UInt32Array::from(values);
3054 let batch =
3055 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array1) as ArrayRef])
3056 .unwrap();
3057 let mut file = tempfile::tempfile().unwrap();
3058 {
3059 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
3060
3061 writer.write(&batch).unwrap();
3062 writer.finish().unwrap();
3063 }
3064 file.rewind().unwrap();
3065
3066 {
3067 let mut reader = FileReader::try_new(file, None).unwrap();
3068 while let Some(Ok(read_batch)) = reader.next() {
3069 read_batch
3070 .columns()
3071 .iter()
3072 .zip(batch.columns())
3073 .for_each(|(a, b)| {
3074 assert_eq!(a.data_type(), b.data_type());
3075 assert_eq!(a.len(), b.len());
3076 assert_eq!(a.null_count(), b.null_count());
3077 });
3078 }
3079 }
3080 }
3081
3082 #[test]
3083 fn test_empty_utf8_ipc_writes_nonempty_offsets_buffer() {
3084 let name = StringArray::from(Vec::<String>::new());
3085 let (offsets, values) = get_byte_array_buffers::<i32>(&name.to_data());
3086
3087 assert_eq!(name.len(), 0);
3088 assert_eq!(
3089 offsets.len(),
3090 std::mem::size_of::<i32>(),
3091 "offsets buffer should contain one zero i32 offset"
3092 );
3093 assert_eq!(values.len(), 0, "values buffer should remain empty");
3094 }
3095
3096 #[test]
3097 fn test_empty_large_utf8_ipc_writes_nonempty_offsets_buffer() {
3098 let name = LargeStringArray::from(Vec::<String>::new());
3099 let (offsets, values) = get_byte_array_buffers::<i64>(&name.to_data());
3100
3101 assert_eq!(name.len(), 0);
3102 assert_eq!(
3103 offsets.len(),
3104 std::mem::size_of::<i64>(),
3105 "offsets buffer should contain one zero i64 offset"
3106 );
3107 assert_eq!(values.len(), 0, "values buffer should remain empty");
3108 }
3109
3110 #[test]
3111 fn test_empty_list_ipc_writes_nonempty_offsets_buffer() {
3112 let list = GenericListBuilder::<i32, _>::new(UInt32Builder::new()).finish();
3113 let (offsets, child_data) = get_list_array_buffers::<i32>(&list.to_data());
3114
3115 assert_eq!(list.len(), 0);
3116 assert_eq!(
3117 offsets.len(),
3118 std::mem::size_of::<i32>(),
3119 "offsets buffer should contain one zero i32 offset"
3120 );
3121 assert_eq!(child_data.len(), 0, "child data should remain empty");
3122 }
3123
3124 #[test]
3125 fn test_empty_large_list_ipc_writes_nonempty_offsets_buffer() {
3126 let list = GenericListBuilder::<i64, _>::new(UInt32Builder::new()).finish();
3127 let (offsets, child_data) = get_list_array_buffers::<i64>(&list.to_data());
3128
3129 assert_eq!(list.len(), 0);
3130 assert_eq!(
3131 offsets.len(),
3132 std::mem::size_of::<i64>(),
3133 "offsets buffer should contain one zero i64 offset"
3134 );
3135 assert_eq!(child_data.len(), 0, "child data should remain empty");
3136 }
3137
3138 fn write_null_file(options: IpcWriteOptions) {
3139 let schema = Schema::new(vec![
3140 Field::new("nulls", DataType::Null, true),
3141 Field::new("int32s", DataType::Int32, false),
3142 Field::new("nulls2", DataType::Null, true),
3143 Field::new("f64s", DataType::Float64, false),
3144 ]);
3145 let array1 = NullArray::new(32);
3146 let array2 = Int32Array::from(vec![1; 32]);
3147 let array3 = NullArray::new(32);
3148 let array4 = Float64Array::from(vec![f64::NAN; 32]);
3149 let batch = RecordBatch::try_new(
3150 Arc::new(schema.clone()),
3151 vec![
3152 Arc::new(array1) as ArrayRef,
3153 Arc::new(array2) as ArrayRef,
3154 Arc::new(array3) as ArrayRef,
3155 Arc::new(array4) as ArrayRef,
3156 ],
3157 )
3158 .unwrap();
3159 let mut file = tempfile::tempfile().unwrap();
3160 {
3161 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
3162
3163 writer.write(&batch).unwrap();
3164 writer.finish().unwrap();
3165 }
3166
3167 file.rewind().unwrap();
3168
3169 {
3170 let reader = FileReader::try_new(file, None).unwrap();
3171 reader.for_each(|maybe_batch| {
3172 maybe_batch
3173 .unwrap()
3174 .columns()
3175 .iter()
3176 .zip(batch.columns())
3177 .for_each(|(a, b)| {
3178 assert_eq!(a.data_type(), b.data_type());
3179 assert_eq!(a.len(), b.len());
3180 assert_eq!(a.null_count(), b.null_count());
3181 });
3182 });
3183 }
3184 }
3185 #[test]
3186 fn test_write_null_file_v4() {
3187 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
3188 write_null_file(IpcWriteOptions::try_new(8, true, MetadataVersion::V4).unwrap());
3189 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V4).unwrap());
3190 write_null_file(IpcWriteOptions::try_new(64, true, MetadataVersion::V4).unwrap());
3191 }
3192
3193 #[test]
3194 fn test_write_null_file_v5() {
3195 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
3196 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V5).unwrap());
3197 }
3198
3199 #[test]
3200 fn track_union_nested_dict() {
3201 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3202
3203 let array = Arc::new(inner) as ArrayRef;
3204
3205 #[allow(deprecated)]
3207 let dctfield = Field::new_dict("dict", array.data_type().clone(), false, 0, false);
3208 let union_fields = [(0, Arc::new(dctfield))].into_iter().collect();
3209
3210 let types = [0, 0, 0].into_iter().collect::<ScalarBuffer<i8>>();
3211 let offsets = [0, 1, 2].into_iter().collect::<ScalarBuffer<i32>>();
3212
3213 let union = UnionArray::try_new(union_fields, types, Some(offsets), vec![array]).unwrap();
3214
3215 let schema = Arc::new(Schema::new(vec![Field::new(
3216 "union",
3217 union.data_type().clone(),
3218 false,
3219 )]));
3220
3221 let r#gen = IpcDataGenerator::default();
3222 let mut dict_tracker = DictionaryTracker::new(false);
3223 r#gen.schema_to_bytes_with_dictionary_tracker(
3224 &schema,
3225 &mut dict_tracker,
3226 &IpcWriteOptions::default(),
3227 );
3228
3229 let batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
3230
3231 r#gen
3232 .encode(
3233 &batch,
3234 &mut dict_tracker,
3235 &Default::default(),
3236 &mut Default::default(),
3237 )
3238 .unwrap();
3239
3240 assert!(dict_tracker.written.contains_key(&0));
3243 }
3244
3245 #[test]
3246 fn track_struct_nested_dict() {
3247 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
3248
3249 let array = Arc::new(inner) as ArrayRef;
3250
3251 #[allow(deprecated)]
3253 let dctfield = Arc::new(Field::new_dict(
3254 "dict",
3255 array.data_type().clone(),
3256 false,
3257 2,
3258 false,
3259 ));
3260
3261 let s = StructArray::from(vec![(dctfield, array)]);
3262 let struct_array = Arc::new(s) as ArrayRef;
3263
3264 let schema = Arc::new(Schema::new(vec![Field::new(
3265 "struct",
3266 struct_array.data_type().clone(),
3267 false,
3268 )]));
3269
3270 let r#gen = IpcDataGenerator::default();
3271 let mut dict_tracker = DictionaryTracker::new(false);
3272 r#gen.schema_to_bytes_with_dictionary_tracker(
3273 &schema,
3274 &mut dict_tracker,
3275 &IpcWriteOptions::default(),
3276 );
3277
3278 let batch = RecordBatch::try_new(schema, vec![struct_array]).unwrap();
3279
3280 r#gen
3281 .encode(
3282 &batch,
3283 &mut dict_tracker,
3284 &Default::default(),
3285 &mut Default::default(),
3286 )
3287 .unwrap();
3288
3289 assert!(dict_tracker.written.contains_key(&0));
3290 }
3291
3292 fn write_union_file(options: IpcWriteOptions) {
3293 let schema = Schema::new(vec![Field::new_union(
3294 "union",
3295 vec![0, 1],
3296 vec![
3297 Field::new("a", DataType::Int32, false),
3298 Field::new("c", DataType::Float64, false),
3299 ],
3300 UnionMode::Sparse,
3301 )]);
3302 let mut builder = UnionBuilder::with_capacity_sparse(5);
3303 builder.append::<Int32Type>("a", 1).unwrap();
3304 builder.append_null::<Int32Type>("a").unwrap();
3305 builder.append::<Float64Type>("c", 3.0).unwrap();
3306 builder.append_null::<Float64Type>("c").unwrap();
3307 builder.append::<Int32Type>("a", 4).unwrap();
3308 let union = builder.build().unwrap();
3309
3310 let batch =
3311 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(union) as ArrayRef])
3312 .unwrap();
3313
3314 let mut file = tempfile::tempfile().unwrap();
3315 {
3316 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
3317
3318 writer.write(&batch).unwrap();
3319 writer.finish().unwrap();
3320 }
3321 file.rewind().unwrap();
3322
3323 {
3324 let reader = FileReader::try_new(file, None).unwrap();
3325 reader.for_each(|maybe_batch| {
3326 maybe_batch
3327 .unwrap()
3328 .columns()
3329 .iter()
3330 .zip(batch.columns())
3331 .for_each(|(a, b)| {
3332 assert_eq!(a.data_type(), b.data_type());
3333 assert_eq!(a.len(), b.len());
3334 assert_eq!(a.null_count(), b.null_count());
3335 });
3336 });
3337 }
3338 }
3339
3340 #[test]
3341 fn test_write_union_file_v4_v5() {
3342 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
3343 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
3344 }
3345
3346 #[test]
3347 fn test_write_view_types() {
3348 const LONG_TEST_STRING: &str =
3349 "This is a long string to make sure binary view array handles it";
3350 let schema = Schema::new(vec![
3351 Field::new("field1", DataType::BinaryView, true),
3352 Field::new("field2", DataType::Utf8View, true),
3353 ]);
3354 let values: Vec<Option<&[u8]>> = vec![
3355 Some(b"foo"),
3356 Some(b"bar"),
3357 Some(LONG_TEST_STRING.as_bytes()),
3358 ];
3359 let binary_array = BinaryViewArray::from_iter(values);
3360 let utf8_array =
3361 StringViewArray::from_iter(vec![Some("foo"), Some("bar"), Some(LONG_TEST_STRING)]);
3362 let record_batch = RecordBatch::try_new(
3363 Arc::new(schema.clone()),
3364 vec![Arc::new(binary_array), Arc::new(utf8_array)],
3365 )
3366 .unwrap();
3367
3368 let mut file = tempfile::tempfile().unwrap();
3369 {
3370 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
3371 writer.write(&record_batch).unwrap();
3372 writer.finish().unwrap();
3373 }
3374 file.rewind().unwrap();
3375 {
3376 let mut reader = FileReader::try_new(&file, None).unwrap();
3377 let read_batch = reader.next().unwrap().unwrap();
3378 read_batch
3379 .columns()
3380 .iter()
3381 .zip(record_batch.columns())
3382 .for_each(|(a, b)| {
3383 assert_eq!(a, b);
3384 });
3385 }
3386 file.rewind().unwrap();
3387 {
3388 let mut reader = FileReader::try_new(&file, Some(vec![0])).unwrap();
3389 let read_batch = reader.next().unwrap().unwrap();
3390 assert_eq!(read_batch.num_columns(), 1);
3391 let read_array = read_batch.column(0);
3392 let write_array = record_batch.column(0);
3393 assert_eq!(read_array, write_array);
3394 }
3395 }
3396
3397 #[test]
3398 fn truncate_ipc_record_batch() {
3399 fn create_batch(rows: usize) -> RecordBatch {
3400 let schema = Schema::new(vec![
3401 Field::new("a", DataType::Int32, false),
3402 Field::new("b", DataType::Utf8, false),
3403 ]);
3404
3405 let a = Int32Array::from_iter_values(0..rows as i32);
3406 let b = StringArray::from_iter_values((0..rows).map(|i| i.to_string()));
3407
3408 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
3409 }
3410
3411 let big_record_batch = create_batch(65536);
3412
3413 let length = 5;
3414 let small_record_batch = create_batch(length);
3415
3416 let offset = 2;
3417 let record_batch_slice = big_record_batch.slice(offset, length);
3418 assert!(
3419 serialize_stream(&big_record_batch).len() > serialize_stream(&small_record_batch).len()
3420 );
3421 assert_eq!(
3422 serialize_stream(&small_record_batch).len(),
3423 serialize_stream(&record_batch_slice).len()
3424 );
3425
3426 assert_eq!(
3427 deserialize_stream(serialize_stream(&record_batch_slice)),
3428 record_batch_slice
3429 );
3430 }
3431
3432 #[test]
3433 fn truncate_ipc_record_batch_with_nulls() {
3434 fn create_batch() -> RecordBatch {
3435 let schema = Schema::new(vec![
3436 Field::new("a", DataType::Int32, true),
3437 Field::new("b", DataType::Utf8, true),
3438 ]);
3439
3440 let a = Int32Array::from(vec![Some(1), None, Some(1), None, Some(1)]);
3441 let b = StringArray::from(vec![None, Some("a"), Some("a"), None, Some("a")]);
3442
3443 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
3444 }
3445
3446 let record_batch = create_batch();
3447 let record_batch_slice = record_batch.slice(1, 2);
3448 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3449
3450 assert!(
3451 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3452 );
3453
3454 assert!(deserialized_batch.column(0).is_null(0));
3455 assert!(deserialized_batch.column(0).is_valid(1));
3456 assert!(deserialized_batch.column(1).is_valid(0));
3457 assert!(deserialized_batch.column(1).is_valid(1));
3458
3459 assert_eq!(record_batch_slice, deserialized_batch);
3460 }
3461
3462 #[test]
3463 fn test_write_empty_validity_buffer_without_nulls() {
3464 let batch = RecordBatch::try_from_iter([
3465 ("a", Arc::new(Int32Array::from(vec![1, 2])) as _),
3466 (
3468 "b",
3469 Arc::new(Int32Array::from(vec![None, Some(2), Some(3)]).slice(1, 2)) as _,
3470 ),
3471 ("c", Arc::new(Int32Array::from(vec![Some(1), None])) as _),
3472 ])
3473 .unwrap();
3474 assert_eq!(batch.column(1).nulls().map(|n| n.null_count()), Some(0));
3475
3476 let (_, encoded) = IpcDataGenerator::default()
3477 .encode(
3478 &batch,
3479 &mut DictionaryTracker::new(false),
3480 &Default::default(),
3481 &mut Default::default(),
3482 )
3483 .unwrap();
3484 let message = crate::root_as_message(&encoded.ipc_message).unwrap();
3485 let buffers = message.header_as_record_batch().unwrap().buffers().unwrap();
3486 let validity_lengths: Vec<_> = buffers.iter().step_by(2).map(|b| b.length()).collect();
3488 assert_eq!(validity_lengths, [0, 0, 1]);
3489
3490 assert_eq!(deserialize_stream(serialize_stream(&batch)), batch);
3491 }
3492
3493 #[test]
3494 fn truncate_ipc_dictionary_array() {
3495 fn create_batch() -> RecordBatch {
3496 let values: StringArray = [Some("foo"), Some("bar"), Some("baz")]
3497 .into_iter()
3498 .collect();
3499 let keys: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
3500
3501 let array = DictionaryArray::new(keys, Arc::new(values));
3502
3503 let schema = Schema::new(vec![Field::new("dict", array.data_type().clone(), true)]);
3504
3505 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(array)]).unwrap()
3506 }
3507
3508 let record_batch = create_batch();
3509 let record_batch_slice = record_batch.slice(1, 2);
3510 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3511
3512 assert!(
3513 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3514 );
3515
3516 assert!(deserialized_batch.column(0).is_valid(0));
3517 assert!(deserialized_batch.column(0).is_null(1));
3518
3519 assert_eq!(record_batch_slice, deserialized_batch);
3520 }
3521
3522 #[test]
3523 fn truncate_ipc_struct_array() {
3524 fn create_batch() -> RecordBatch {
3525 let strings: StringArray = [Some("foo"), None, Some("bar"), Some("baz")]
3526 .into_iter()
3527 .collect();
3528 let ints: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
3529
3530 let struct_array = StructArray::from(vec![
3531 (
3532 Arc::new(Field::new("s", DataType::Utf8, true)),
3533 Arc::new(strings) as ArrayRef,
3534 ),
3535 (
3536 Arc::new(Field::new("c", DataType::Int32, true)),
3537 Arc::new(ints) as ArrayRef,
3538 ),
3539 ]);
3540
3541 let schema = Schema::new(vec![Field::new(
3542 "struct_array",
3543 struct_array.data_type().clone(),
3544 true,
3545 )]);
3546
3547 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(struct_array)]).unwrap()
3548 }
3549
3550 let record_batch = create_batch();
3551 let record_batch_slice = record_batch.slice(1, 2);
3552 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3553
3554 assert!(
3555 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3556 );
3557
3558 let structs = deserialized_batch
3559 .column(0)
3560 .as_any()
3561 .downcast_ref::<StructArray>()
3562 .unwrap();
3563
3564 assert!(structs.column(0).is_null(0));
3565 assert!(structs.column(0).is_valid(1));
3566 assert!(structs.column(1).is_valid(0));
3567 assert!(structs.column(1).is_null(1));
3568 assert_eq!(record_batch_slice, deserialized_batch);
3569 }
3570
3571 #[test]
3572 fn truncate_ipc_string_array_with_all_empty_string() {
3573 fn create_batch() -> RecordBatch {
3574 let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
3575 let a = StringArray::from(vec![Some(""), Some(""), Some(""), Some(""), Some("")]);
3576 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap()
3577 }
3578
3579 let record_batch = create_batch();
3580 let record_batch_slice = record_batch.slice(0, 1);
3581 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
3582
3583 assert!(
3584 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
3585 );
3586 assert_eq!(record_batch_slice, deserialized_batch);
3587 }
3588
3589 #[test]
3590 fn test_stream_writer_writes_array_slice() {
3591 let array = UInt32Array::from(vec![Some(1), Some(2), Some(3)]);
3592 assert_eq!(
3593 vec![Some(1), Some(2), Some(3)],
3594 array.iter().collect::<Vec<_>>()
3595 );
3596
3597 let sliced = array.slice(1, 2);
3598 assert_eq!(vec![Some(2), Some(3)], sliced.iter().collect::<Vec<_>>());
3599
3600 let batch = RecordBatch::try_new(
3601 Arc::new(Schema::new(vec![Field::new("a", DataType::UInt32, true)])),
3602 vec![Arc::new(sliced)],
3603 )
3604 .expect("new batch");
3605
3606 let mut writer = StreamWriter::try_new(vec![], batch.schema_ref()).expect("new writer");
3607 writer.write(&batch).expect("write");
3608 let outbuf = writer.into_inner().expect("inner");
3609
3610 let mut reader = StreamReader::try_new(&outbuf[..], None).expect("new reader");
3611 let read_batch = reader.next().unwrap().expect("read batch");
3612
3613 let read_array: &UInt32Array = read_batch.column(0).as_primitive();
3614 assert_eq!(
3615 vec![Some(2), Some(3)],
3616 read_array.iter().collect::<Vec<_>>()
3617 );
3618 }
3619
3620 #[test]
3621 fn test_large_slice_uint32() {
3622 ensure_roundtrip(Arc::new(UInt32Array::from_iter(
3623 (0..8000).map(|i| if i % 2 == 0 { Some(i) } else { None }),
3624 )));
3625 }
3626
3627 #[test]
3628 fn test_large_slice_string() {
3629 let strings: Vec<_> = (0..8000)
3630 .map(|i| {
3631 if i % 2 == 0 {
3632 Some(format!("value{i}"))
3633 } else {
3634 None
3635 }
3636 })
3637 .collect();
3638
3639 ensure_roundtrip(Arc::new(StringArray::from(strings)));
3640 }
3641
3642 #[test]
3643 fn test_large_slice_string_list() {
3644 let mut ls = ListBuilder::new(StringBuilder::new());
3645
3646 let mut s = String::new();
3647 for row_number in 0..8000 {
3648 if row_number % 2 == 0 {
3649 for list_element in 0..1000 {
3650 s.clear();
3651 use std::fmt::Write;
3652 write!(&mut s, "value{row_number}-{list_element}").unwrap();
3653 ls.values().append_value(&s);
3654 }
3655 ls.append(true)
3656 } else {
3657 ls.append(false); }
3659 }
3660
3661 ensure_roundtrip(Arc::new(ls.finish()));
3662 }
3663
3664 #[test]
3665 fn test_large_slice_string_list_of_lists() {
3666 let mut ls = ListBuilder::new(ListBuilder::new(StringBuilder::new()));
3670
3671 for _ in 0..4000 {
3672 ls.values().append(true);
3673 ls.append(true)
3674 }
3675
3676 let mut s = String::new();
3677 for row_number in 0..4000 {
3678 if row_number % 2 == 0 {
3679 for list_element in 0..1000 {
3680 s.clear();
3681 use std::fmt::Write;
3682 write!(&mut s, "value{row_number}-{list_element}").unwrap();
3683 ls.values().values().append_value(&s);
3684 }
3685 ls.values().append(true);
3686 ls.append(true)
3687 } else {
3688 ls.append(false); }
3690 }
3691
3692 ensure_roundtrip(Arc::new(ls.finish()));
3693 }
3694
3695 fn ensure_roundtrip(array: ArrayRef) {
3697 let num_rows = array.len();
3698 let orig_batch = RecordBatch::try_from_iter(vec![("a", array)]).unwrap();
3699 let sliced_batch = orig_batch.slice(1, num_rows - 1);
3701
3702 let schema = orig_batch.schema();
3703 let stream_data = {
3704 let mut writer = StreamWriter::try_new(vec![], &schema).unwrap();
3705 writer.write(&sliced_batch).unwrap();
3706 writer.into_inner().unwrap()
3707 };
3708 let read_batch = {
3709 let projection = None;
3710 let mut reader = StreamReader::try_new(Cursor::new(stream_data), projection).unwrap();
3711 reader
3712 .next()
3713 .expect("expect no errors reading batch")
3714 .expect("expect batch")
3715 };
3716 assert_eq!(sliced_batch, read_batch);
3717
3718 let file_data = {
3719 let mut writer = FileWriter::try_new_buffered(vec![], &schema).unwrap();
3720 writer.write(&sliced_batch).unwrap();
3721 writer.into_inner().unwrap().into_inner().unwrap()
3722 };
3723 let read_batch = {
3724 let projection = None;
3725 let mut reader = FileReader::try_new(Cursor::new(file_data), projection).unwrap();
3726 reader
3727 .next()
3728 .expect("expect no errors reading batch")
3729 .expect("expect batch")
3730 };
3731 assert_eq!(sliced_batch, read_batch);
3732
3733 }
3735
3736 #[test]
3737 fn encode_bools_slice() {
3738 assert_bool_roundtrip([true, false], 1, 1);
3740
3741 assert_bool_roundtrip(
3743 [
3744 true, false, true, true, false, false, true, true, true, false, false, false, true,
3745 true, true, true, false, false, false, false, true, true, true, true, true, false,
3746 false, false, false, false,
3747 ],
3748 13,
3749 17,
3750 );
3751
3752 assert_bool_roundtrip(
3754 [
3755 true, false, true, true, false, false, true, true, true, false, false, false,
3756 ],
3757 8,
3758 2,
3759 );
3760
3761 assert_bool_roundtrip(
3763 [
3764 true, false, true, true, false, false, true, true, true, false, false, false, true,
3765 true, true, true, true, false, false, false, false, false,
3766 ],
3767 8,
3768 8,
3769 );
3770 }
3771
3772 fn assert_bool_roundtrip<const N: usize>(bools: [bool; N], offset: usize, length: usize) {
3773 let val_bool_field = Field::new("val", DataType::Boolean, false);
3774
3775 let schema = Arc::new(Schema::new(vec![val_bool_field]));
3776
3777 let bools = BooleanArray::from(bools.to_vec());
3778
3779 let batch = RecordBatch::try_new(Arc::clone(&schema), vec![Arc::new(bools)]).unwrap();
3780 let batch = batch.slice(offset, length);
3781
3782 let data = serialize_stream(&batch);
3783 let batch2 = deserialize_stream(data);
3784 assert_eq!(batch, batch2);
3785 }
3786
3787 #[test]
3788 fn test_run_array_unslice() {
3789 let total_len = 80;
3790 let vals: Vec<Option<i32>> = vec![Some(1), None, Some(2), Some(3), Some(4), None, Some(5)];
3791 let repeats: Vec<usize> = vec![3, 4, 1, 2];
3792 let mut input_array: Vec<Option<i32>> = Vec::with_capacity(total_len);
3793 for ix in 0_usize..32 {
3794 let repeat: usize = repeats[ix % repeats.len()];
3795 let val: Option<i32> = vals[ix % vals.len()];
3796 input_array.resize(input_array.len() + repeat, val);
3797 }
3798
3799 let mut builder =
3801 PrimitiveRunBuilder::<Int16Type, Int32Type>::with_capacity(input_array.len());
3802 builder.extend(input_array.iter().copied());
3803 let run_array = builder.finish();
3804
3805 for slice_len in 1..=total_len {
3807 let sliced_run_array: RunArray<Int16Type> =
3809 run_array.slice(0, slice_len).into_data().into();
3810
3811 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
3813 let typed = unsliced_run_array
3814 .downcast::<PrimitiveArray<Int32Type>>()
3815 .unwrap();
3816 let expected: Vec<Option<i32>> = input_array.iter().take(slice_len).copied().collect();
3817 let actual: Vec<Option<i32>> = typed.into_iter().collect();
3818 assert_eq!(expected, actual);
3819
3820 let sliced_run_array: RunArray<Int16Type> = run_array
3822 .slice(total_len - slice_len, slice_len)
3823 .into_data()
3824 .into();
3825
3826 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
3828 let typed = unsliced_run_array
3829 .downcast::<PrimitiveArray<Int32Type>>()
3830 .unwrap();
3831 let expected: Vec<Option<i32>> = input_array
3832 .iter()
3833 .skip(total_len - slice_len)
3834 .copied()
3835 .collect();
3836 let actual: Vec<Option<i32>> = typed.into_iter().collect();
3837 assert_eq!(expected, actual);
3838 }
3839 }
3840
3841 fn generate_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3842 let mut ls = GenericListBuilder::<O, _>::new(UInt32Builder::new());
3843
3844 for i in 0..100_000 {
3845 for value in [i, i, i] {
3846 ls.values().append_value(value);
3847 }
3848 ls.append(true)
3849 }
3850
3851 ls.finish()
3852 }
3853
3854 fn generate_utf8view_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3855 let mut ls = GenericListBuilder::<O, _>::new(StringViewBuilder::new());
3856
3857 for i in 0..100_000 {
3858 for value in [
3859 format!("value{}", i),
3860 format!("value{}", i),
3861 format!("value{}", i),
3862 ] {
3863 ls.values().append_value(&value);
3864 }
3865 ls.append(true)
3866 }
3867
3868 ls.finish()
3869 }
3870
3871 fn generate_string_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3872 let mut ls = GenericListBuilder::<O, _>::new(StringBuilder::new());
3873
3874 for i in 0..100_000 {
3875 for value in [
3876 format!("value{}", i),
3877 format!("value{}", i),
3878 format!("value{}", i),
3879 ] {
3880 ls.values().append_value(&value);
3881 }
3882 ls.append(true)
3883 }
3884
3885 ls.finish()
3886 }
3887
3888 fn generate_nested_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3889 let mut ls =
3890 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
3891
3892 for _i in 0..10_000 {
3893 for j in 0..10 {
3894 for value in [j, j, j, j] {
3895 ls.values().values().append_value(value);
3896 }
3897 ls.values().append(true)
3898 }
3899 ls.append(true);
3900 }
3901
3902 ls.finish()
3903 }
3904
3905 fn generate_nested_list_data_starting_at_zero<O: OffsetSizeTrait>() -> GenericListArray<O> {
3906 let mut ls =
3907 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
3908
3909 for _i in 0..999 {
3910 ls.values().append(true);
3911 ls.append(true);
3912 }
3913
3914 for j in 0..10 {
3915 for value in [j, j, j, j] {
3916 ls.values().values().append_value(value);
3917 }
3918 ls.values().append(true)
3919 }
3920 ls.append(true);
3921
3922 for i in 0..9_000 {
3923 for j in 0..10 {
3924 for value in [i + j, i + j, i + j, i + j] {
3925 ls.values().values().append_value(value);
3926 }
3927 ls.values().append(true)
3928 }
3929 ls.append(true);
3930 }
3931
3932 ls.finish()
3933 }
3934
3935 fn generate_map_array_data() -> MapArray {
3936 let keys_builder = UInt32Builder::new();
3937 let values_builder = UInt32Builder::new();
3938
3939 let mut builder = MapBuilder::new(None, keys_builder, values_builder);
3940
3941 for i in 0..100_000 {
3942 for _j in 0..3 {
3943 builder.keys().append_value(i);
3944 builder.values().append_value(i * 2);
3945 }
3946 builder.append(true).unwrap();
3947 }
3948
3949 builder.finish()
3950 }
3951
3952 #[test]
3953 fn reencode_offsets_when_first_offset_is_not_zero() {
3954 let original_list = generate_list_data::<i32>();
3955 let original_data = original_list.into_data();
3956 let slice_data = original_data.slice(75, 7);
3957 let (new_offsets, original_start, length) =
3958 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
3959 assert_eq!(
3960 vec![0, 3, 6, 9, 12, 15, 18, 21],
3961 new_offsets.typed_data::<i32>()
3962 );
3963 assert_eq!(225, original_start);
3964 assert_eq!(21, length);
3965 }
3966
3967 #[test]
3968 fn reencode_offsets_when_first_offset_is_zero() {
3969 let mut ls = GenericListBuilder::<i32, _>::new(UInt32Builder::new());
3970 ls.append(true);
3972 ls.values().append_value(35);
3973 ls.values().append_value(42);
3974 ls.append(true);
3975 let original_list = ls.finish();
3976 let original_data = original_list.into_data();
3977
3978 let slice_data = original_data.slice(1, 1);
3979 let (new_offsets, original_start, length) =
3980 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
3981 assert_eq!(vec![0, 2], new_offsets.typed_data::<i32>());
3982 assert_eq!(0, original_start);
3983 assert_eq!(2, length);
3984 }
3985
3986 fn roundtrip_ensure_sliced_smaller(in_batch: RecordBatch, expected_size_factor: usize) {
3989 let in_sliced = in_batch.slice(999, 1);
3991
3992 let bytes_batch = serialize_file(&in_batch);
3993 let bytes_sliced = serialize_file(&in_sliced);
3994
3995 assert!(bytes_sliced.len() < (bytes_batch.len() / expected_size_factor));
3997
3998 let out_batch = deserialize_file(bytes_batch);
4000 assert_eq!(in_batch, out_batch);
4001
4002 let out_sliced = deserialize_file(bytes_sliced);
4003 assert_eq!(in_sliced, out_sliced);
4004 }
4005
4006 #[test]
4007 fn encode_lists() {
4008 let val_inner = Field::new_list_field(DataType::UInt32, true);
4009 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
4010 let schema = Arc::new(Schema::new(vec![val_list_field]));
4011
4012 let values = Arc::new(generate_list_data::<i32>());
4013
4014 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4015 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4016 }
4017
4018 #[test]
4019 fn encode_empty_list() {
4020 let val_inner = Field::new_list_field(DataType::UInt32, true);
4021 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
4022 let schema = Arc::new(Schema::new(vec![val_list_field]));
4023
4024 let values = Arc::new(generate_list_data::<i32>());
4025
4026 let in_batch = RecordBatch::try_new(schema, vec![values])
4027 .unwrap()
4028 .slice(999, 0);
4029 let out_batch = deserialize_file(serialize_file(&in_batch));
4030 assert_eq!(in_batch, out_batch);
4031 }
4032
4033 #[test]
4034 fn encode_large_lists() {
4035 let val_inner = Field::new_list_field(DataType::UInt32, true);
4036 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4037 let schema = Arc::new(Schema::new(vec![val_list_field]));
4038
4039 let values = Arc::new(generate_list_data::<i64>());
4040
4041 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4044 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4045 }
4046
4047 #[test]
4048 fn encode_large_lists_non_zero_offset() {
4049 let val_inner = Field::new_list_field(DataType::UInt32, true);
4050 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4051 let schema = Arc::new(Schema::new(vec![val_list_field]));
4052
4053 let values = Arc::new(generate_list_data::<i64>());
4054
4055 check_sliced_list_array(schema, values);
4056 }
4057
4058 #[test]
4059 fn encode_large_lists_string_non_zero_offset() {
4060 let val_inner = Field::new_list_field(DataType::Utf8, true);
4061 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4062 let schema = Arc::new(Schema::new(vec![val_list_field]));
4063
4064 let values = Arc::new(generate_string_list_data::<i64>());
4065
4066 check_sliced_list_array(schema, values);
4067 }
4068
4069 #[test]
4070 fn encode_large_list_string_view_non_zero_offset() {
4071 let val_inner = Field::new_list_field(DataType::Utf8View, true);
4072 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
4073 let schema = Arc::new(Schema::new(vec![val_list_field]));
4074
4075 let values = Arc::new(generate_utf8view_list_data::<i64>());
4076
4077 check_sliced_list_array(schema, values);
4078 }
4079
4080 fn check_sliced_list_array(schema: Arc<Schema>, values: Arc<GenericListArray<i64>>) {
4081 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4082 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4083 .unwrap()
4084 .slice(offset, len);
4085 let out_batch = deserialize_file(serialize_file(&in_batch));
4086 assert_eq!(in_batch, out_batch);
4087 }
4088 }
4089
4090 #[test]
4091 fn encode_nested_lists() {
4092 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
4093 let inner_list_field = Arc::new(Field::new_list_field(DataType::List(inner_int), true));
4094 let list_field = Field::new("val", DataType::List(inner_list_field), true);
4095 let schema = Arc::new(Schema::new(vec![list_field]));
4096
4097 let values = Arc::new(generate_nested_list_data::<i32>());
4098
4099 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4100 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4101 }
4102
4103 #[test]
4104 fn encode_nested_lists_starting_at_zero() {
4105 let inner_int = Arc::new(Field::new("item", DataType::UInt32, true));
4106 let inner_list_field = Arc::new(Field::new("item", DataType::List(inner_int), true));
4107 let list_field = Field::new("val", DataType::List(inner_list_field), true);
4108 let schema = Arc::new(Schema::new(vec![list_field]));
4109
4110 let values = Arc::new(generate_nested_list_data_starting_at_zero::<i32>());
4111
4112 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4113 roundtrip_ensure_sliced_smaller(in_batch, 1);
4114 }
4115
4116 #[test]
4117 fn encode_map_array() {
4118 let keys = Arc::new(Field::new("keys", DataType::UInt32, false));
4119 let values = Arc::new(Field::new("values", DataType::UInt32, true));
4120 let map_field = Field::new_map("map", "entries", keys, values, false, true);
4121 let schema = Arc::new(Schema::new(vec![map_field]));
4122
4123 let values = Arc::new(generate_map_array_data());
4124
4125 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4126 roundtrip_ensure_sliced_smaller(in_batch, 1000);
4127 }
4128
4129 fn generate_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
4130 let mut builder = GenericListViewBuilder::<O, _>::new(UInt32Builder::new());
4131
4132 for i in 0u32..100_000 {
4133 if i.is_multiple_of(10_000) {
4134 builder.append(false);
4135 continue;
4136 }
4137 for value in [i, i, i] {
4138 builder.values().append_value(value);
4139 }
4140 builder.append(true);
4141 }
4142
4143 builder.finish()
4144 }
4145
4146 #[test]
4147 fn encode_list_view_arrays() {
4148 let val_inner = Field::new_list_field(DataType::UInt32, true);
4149 let val_field = Field::new("val", DataType::ListView(Arc::new(val_inner)), true);
4150 let schema = Arc::new(Schema::new(vec![val_field]));
4151
4152 let values = Arc::new(generate_list_view_data::<i32>());
4153
4154 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4155 let out_batch = deserialize_file(serialize_file(&in_batch));
4156 assert_eq!(in_batch, out_batch);
4157 }
4158
4159 #[test]
4160 fn encode_large_list_view_arrays() {
4161 let val_inner = Field::new_list_field(DataType::UInt32, true);
4162 let val_field = Field::new("val", DataType::LargeListView(Arc::new(val_inner)), true);
4163 let schema = Arc::new(Schema::new(vec![val_field]));
4164
4165 let values = Arc::new(generate_list_view_data::<i64>());
4166
4167 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4168 let out_batch = deserialize_file(serialize_file(&in_batch));
4169 assert_eq!(in_batch, out_batch);
4170 }
4171
4172 #[test]
4173 fn check_sliced_list_view_array() {
4174 let inner = Field::new_list_field(DataType::UInt32, true);
4175 let field = Field::new("val", DataType::ListView(Arc::new(inner)), true);
4176 let schema = Arc::new(Schema::new(vec![field]));
4177 let values = Arc::new(generate_list_view_data::<i32>());
4178
4179 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4180 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4181 .unwrap()
4182 .slice(offset, len);
4183 let out_batch = deserialize_file(serialize_file(&in_batch));
4184 assert_eq!(in_batch, out_batch);
4185 }
4186 }
4187
4188 #[test]
4189 fn check_sliced_large_list_view_array() {
4190 let inner = Field::new_list_field(DataType::UInt32, true);
4191 let field = Field::new("val", DataType::LargeListView(Arc::new(inner)), true);
4192 let schema = Arc::new(Schema::new(vec![field]));
4193 let values = Arc::new(generate_list_view_data::<i64>());
4194
4195 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
4196 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
4197 .unwrap()
4198 .slice(offset, len);
4199 let out_batch = deserialize_file(serialize_file(&in_batch));
4200 assert_eq!(in_batch, out_batch);
4201 }
4202 }
4203
4204 fn generate_nested_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
4205 let inner_builder = UInt32Builder::new();
4206 let middle_builder = GenericListViewBuilder::<O, _>::new(inner_builder);
4207 let mut outer_builder = GenericListViewBuilder::<O, _>::new(middle_builder);
4208
4209 for i in 0u32..10_000 {
4210 if i.is_multiple_of(1_000) {
4211 outer_builder.append(false);
4212 continue;
4213 }
4214
4215 for _ in 0..3 {
4216 for value in [i, i + 1, i + 2] {
4217 outer_builder.values().values().append_value(value);
4218 }
4219 outer_builder.values().append(true);
4220 }
4221 outer_builder.append(true);
4222 }
4223
4224 outer_builder.finish()
4225 }
4226
4227 #[test]
4228 fn encode_nested_list_views() {
4229 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
4230 let inner_list_field = Arc::new(Field::new_list_field(DataType::ListView(inner_int), true));
4231 let list_field = Field::new("val", DataType::ListView(inner_list_field), true);
4232 let schema = Arc::new(Schema::new(vec![list_field]));
4233
4234 let values = Arc::new(generate_nested_list_view_data::<i32>());
4235
4236 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
4237 let out_batch = deserialize_file(serialize_file(&in_batch));
4238 assert_eq!(in_batch, out_batch);
4239 }
4240
4241 fn test_roundtrip_list_view_of_dict_impl<OffsetSize: OffsetSizeTrait, U: ArrowNativeType>(
4242 list_data_type: DataType,
4243 offsets: &[U; 5],
4244 sizes: &[U; 4],
4245 ) {
4246 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4247 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4248 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4249 let dict_data = dict_array.to_data();
4250
4251 let value_offsets = Buffer::from_slice_ref(offsets);
4252 let value_sizes = Buffer::from_slice_ref(sizes);
4253
4254 let list_data = ArrayData::builder(list_data_type)
4255 .len(4)
4256 .add_buffer(value_offsets)
4257 .add_buffer(value_sizes)
4258 .add_child_data(dict_data)
4259 .build()
4260 .unwrap();
4261 let list_view_array = GenericListViewArray::<OffsetSize>::from(list_data);
4262
4263 let schema = Arc::new(Schema::new(vec![Field::new(
4264 "f1",
4265 list_view_array.data_type().clone(),
4266 false,
4267 )]));
4268 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
4269
4270 let output_batch = deserialize_file(serialize_file(&input_batch));
4271 assert_eq!(input_batch, output_batch);
4272
4273 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4274 assert_eq!(input_batch, output_batch);
4275 }
4276
4277 #[test]
4278 fn test_roundtrip_list_view_of_dict() {
4279 #[allow(deprecated)]
4280 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
4281 "item",
4282 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4283 true,
4284 1,
4285 false,
4286 )));
4287 let offsets: &[i32; 5] = &[0, 2, 4, 4, 7];
4288 let sizes: &[i32; 4] = &[2, 2, 0, 3];
4289 test_roundtrip_list_view_of_dict_impl::<i32, i32>(list_data_type, offsets, sizes);
4290 }
4291
4292 #[test]
4293 fn test_roundtrip_large_list_view_of_dict() {
4294 #[allow(deprecated)]
4295 let list_data_type = DataType::LargeListView(Arc::new(Field::new_dict(
4296 "item",
4297 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4298 true,
4299 2,
4300 false,
4301 )));
4302 let offsets: &[i64; 5] = &[0, 2, 4, 4, 7];
4303 let sizes: &[i64; 4] = &[2, 2, 0, 3];
4304 test_roundtrip_list_view_of_dict_impl::<i64, i64>(list_data_type, offsets, sizes);
4305 }
4306
4307 #[test]
4308 fn test_roundtrip_sliced_list_view_of_dict() {
4309 #[allow(deprecated)]
4310 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
4311 "item",
4312 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4313 true,
4314 3,
4315 false,
4316 )));
4317
4318 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4319 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2, 1, 0, 3, 2, 1]);
4320 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4321 let dict_data = dict_array.to_data();
4322
4323 let offsets: &[i32; 7] = &[0, 2, 4, 4, 7, 9, 12];
4324 let sizes: &[i32; 6] = &[2, 2, 0, 3, 2, 3];
4325 let value_offsets = Buffer::from_slice_ref(offsets);
4326 let value_sizes = Buffer::from_slice_ref(sizes);
4327
4328 let list_data = ArrayData::builder(list_data_type)
4329 .len(6)
4330 .add_buffer(value_offsets)
4331 .add_buffer(value_sizes)
4332 .add_child_data(dict_data)
4333 .build()
4334 .unwrap();
4335 let list_view_array = GenericListViewArray::<i32>::from(list_data);
4336
4337 let schema = Arc::new(Schema::new(vec![Field::new(
4338 "f1",
4339 list_view_array.data_type().clone(),
4340 false,
4341 )]));
4342 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
4343
4344 let sliced_batch = input_batch.slice(1, 4);
4345
4346 let output_batch = deserialize_file(serialize_file(&sliced_batch));
4347 assert_eq!(sliced_batch, output_batch);
4348
4349 let output_batch = deserialize_stream(serialize_stream(&sliced_batch));
4350 assert_eq!(sliced_batch, output_batch);
4351 }
4352
4353 #[test]
4354 fn test_roundtrip_dense_union_of_dict() {
4355 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4356 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4357 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4358
4359 #[allow(deprecated)]
4360 let dict_field = Arc::new(Field::new_dict(
4361 "dict",
4362 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4363 true,
4364 1,
4365 false,
4366 ));
4367 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
4368 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
4369
4370 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
4371 let offsets = ScalarBuffer::from(vec![0i32, 1, 0, 2, 1, 3, 4]);
4372
4373 let int_array = Int32Array::from(vec![100, 200]);
4374
4375 let union = UnionArray::try_new(
4376 union_fields.clone(),
4377 types,
4378 Some(offsets),
4379 vec![Arc::new(dict_array), Arc::new(int_array)],
4380 )
4381 .unwrap();
4382
4383 let schema = Arc::new(Schema::new(vec![Field::new(
4384 "union",
4385 DataType::Union(union_fields, UnionMode::Dense),
4386 false,
4387 )]));
4388 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
4389
4390 let output_batch = deserialize_file(serialize_file(&input_batch));
4391 assert_eq!(input_batch, output_batch);
4392
4393 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4394 assert_eq!(input_batch, output_batch);
4395 }
4396
4397 #[test]
4398 fn test_roundtrip_sparse_union_of_dict() {
4399 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
4400 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
4401 let dict_array = DictionaryArray::new(keys, Arc::new(values));
4402
4403 #[allow(deprecated)]
4404 let dict_field = Arc::new(Field::new_dict(
4405 "dict",
4406 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4407 true,
4408 2,
4409 false,
4410 ));
4411 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
4412 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
4413
4414 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
4415
4416 let int_array = Int32Array::from(vec![0, 0, 100, 0, 200, 0, 0]);
4417
4418 let union = UnionArray::try_new(
4419 union_fields.clone(),
4420 types,
4421 None,
4422 vec![Arc::new(dict_array), Arc::new(int_array)],
4423 )
4424 .unwrap();
4425
4426 let schema = Arc::new(Schema::new(vec![Field::new(
4427 "union",
4428 DataType::Union(union_fields, UnionMode::Sparse),
4429 false,
4430 )]));
4431 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
4432
4433 let output_batch = deserialize_file(serialize_file(&input_batch));
4434 assert_eq!(input_batch, output_batch);
4435
4436 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4437 assert_eq!(input_batch, output_batch);
4438 }
4439
4440 #[test]
4441 fn test_roundtrip_map_with_dict_keys() {
4442 let key_values = StringArray::from(vec!["key_a", "key_b", "key_c"]);
4445 let keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
4446 let dict_keys = DictionaryArray::new(keys, Arc::new(key_values));
4447
4448 let values = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
4449
4450 #[allow(deprecated)]
4451 let entries_field = Arc::new(Field::new(
4452 "entries",
4453 DataType::Struct(
4454 vec![
4455 Field::new_dict(
4456 "key",
4457 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4458 false,
4459 1,
4460 false,
4461 ),
4462 Field::new("value", DataType::Int32, true),
4463 ]
4464 .into(),
4465 ),
4466 false,
4467 ));
4468
4469 let entries = StructArray::from(vec![
4470 (
4471 Arc::new(Field::new(
4472 "key",
4473 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4474 false,
4475 )),
4476 Arc::new(dict_keys) as ArrayRef,
4477 ),
4478 (
4479 Arc::new(Field::new("value", DataType::Int32, true)),
4480 Arc::new(values) as ArrayRef,
4481 ),
4482 ]);
4483
4484 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
4485
4486 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
4487 .len(3)
4488 .add_buffer(offsets)
4489 .add_child_data(entries.into_data())
4490 .build()
4491 .unwrap();
4492 let map_array = MapArray::from(map_data);
4493
4494 let schema = Arc::new(Schema::new(vec![Field::new(
4495 "map",
4496 map_array.data_type().clone(),
4497 false,
4498 )]));
4499 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
4500
4501 let output_batch = deserialize_file(serialize_file(&input_batch));
4502 assert_eq!(input_batch, output_batch);
4503
4504 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4505 assert_eq!(input_batch, output_batch);
4506 }
4507
4508 #[test]
4509 fn test_roundtrip_map_with_dict_values() {
4510 let keys = StringArray::from(vec!["a", "b", "c", "d", "e", "f"]);
4513
4514 let value_values = StringArray::from(vec!["val_x", "val_y", "val_z"]);
4515 let value_keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
4516 let dict_values = DictionaryArray::new(value_keys, Arc::new(value_values));
4517
4518 #[allow(deprecated)]
4519 let entries_field = Arc::new(Field::new(
4520 "entries",
4521 DataType::Struct(
4522 vec![
4523 Field::new("key", DataType::Utf8, false),
4524 Field::new_dict(
4525 "value",
4526 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4527 true,
4528 2,
4529 false,
4530 ),
4531 ]
4532 .into(),
4533 ),
4534 false,
4535 ));
4536
4537 let entries = StructArray::from(vec![
4538 (
4539 Arc::new(Field::new("key", DataType::Utf8, false)),
4540 Arc::new(keys) as ArrayRef,
4541 ),
4542 (
4543 Arc::new(Field::new(
4544 "value",
4545 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
4546 true,
4547 )),
4548 Arc::new(dict_values) as ArrayRef,
4549 ),
4550 ]);
4551
4552 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
4553
4554 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
4555 .len(3)
4556 .add_buffer(offsets)
4557 .add_child_data(entries.into_data())
4558 .build()
4559 .unwrap();
4560 let map_array = MapArray::from(map_data);
4561
4562 let schema = Arc::new(Schema::new(vec![Field::new(
4563 "map",
4564 map_array.data_type().clone(),
4565 false,
4566 )]));
4567 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
4568
4569 let output_batch = deserialize_file(serialize_file(&input_batch));
4570 assert_eq!(input_batch, output_batch);
4571
4572 let output_batch = deserialize_stream(serialize_stream(&input_batch));
4573 assert_eq!(input_batch, output_batch);
4574 }
4575
4576 #[test]
4577 fn test_decimal128_alignment16_is_sufficient() {
4578 const IPC_ALIGNMENT: usize = 16;
4579
4580 for num_cols in [1, 2, 3, 17, 50, 73, 99] {
4585 let num_rows = (num_cols * 7 + 11) % 100; let mut fields = Vec::new();
4588 let mut arrays = Vec::new();
4589 for i in 0..num_cols {
4590 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4591 let array = Decimal128Array::from(vec![num_cols as i128; num_rows]);
4592 fields.push(field);
4593 arrays.push(Arc::new(array) as Arc<dyn Array>);
4594 }
4595 let schema = Schema::new(fields);
4596 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
4597
4598 let mut writer = FileWriter::try_new_with_options(
4599 Vec::new(),
4600 batch.schema_ref(),
4601 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
4602 )
4603 .unwrap();
4604 writer.write(&batch).unwrap();
4605 writer.finish().unwrap();
4606
4607 let out: Vec<u8> = writer.into_inner().unwrap();
4608
4609 let buffer = Buffer::from_vec(out);
4610 let trailer_start = buffer.len() - 10;
4611 let footer_len =
4612 read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
4613 let footer =
4614 root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
4615
4616 let schema = fb_to_schema(footer.schema().unwrap());
4617
4618 let decoder =
4621 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
4622
4623 let batches = footer.recordBatches().unwrap();
4624
4625 let block = batches.get(0);
4626 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
4627 let data = buffer.slice_with_length(block.offset() as _, block_len);
4628
4629 let batch2 = decoder.read_record_batch(block, &data).unwrap().unwrap();
4630
4631 assert_eq!(batch, batch2);
4632 }
4633 }
4634
4635 #[test]
4636 fn test_decimal128_alignment8_is_unaligned() {
4637 const IPC_ALIGNMENT: usize = 8;
4638
4639 let num_cols = 2;
4640
4641 let mut fields = Vec::new();
4642 let mut arrays = Vec::new();
4643 for i in 0..num_cols {
4644 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4645 let array = Decimal128Array::from(vec![Some(num_cols as i128), None]);
4649 fields.push(field);
4650 arrays.push(Arc::new(array) as Arc<dyn Array>);
4651 }
4652 let schema = Schema::new(fields);
4653 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
4654
4655 let mut writer = FileWriter::try_new_with_options(
4656 Vec::new(),
4657 batch.schema_ref(),
4658 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
4659 )
4660 .unwrap();
4661 writer.write(&batch).unwrap();
4662 writer.finish().unwrap();
4663
4664 let out: Vec<u8> = writer.into_inner().unwrap();
4665
4666 let buffer = Buffer::from_vec(out);
4667 let trailer_start = buffer.len() - 10;
4668 let footer_len = read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
4669 let footer = root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
4670 let schema = fb_to_schema(footer.schema().unwrap());
4671
4672 let decoder =
4675 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
4676
4677 let batches = footer.recordBatches().unwrap();
4678
4679 let block = batches.get(0);
4680 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
4681 let data = buffer.slice_with_length(block.offset() as _, block_len);
4682
4683 let result = decoder.read_record_batch(block, &data);
4684
4685 let error = result.unwrap_err();
4686 assert_eq!(
4687 error.to_string(),
4688 "Invalid argument error: Misaligned buffers[0] in array of type Decimal128(38, 10), \
4689 offset from expected alignment of 16 by 8"
4690 );
4691 }
4692
4693 #[test]
4694 fn test_flush() {
4695 let num_cols = 2;
4698 let mut fields = Vec::new();
4699 let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap();
4700 for i in 0..num_cols {
4701 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
4702 fields.push(field);
4703 }
4704 let schema = Schema::new(fields);
4705 let inner_stream_writer = BufWriter::with_capacity(1024, Vec::new());
4706 let inner_file_writer = BufWriter::with_capacity(1024, Vec::new());
4707 let mut stream_writer =
4708 StreamWriter::try_new_with_options(inner_stream_writer, &schema, options.clone())
4709 .unwrap();
4710 let mut file_writer =
4711 FileWriter::try_new_with_options(inner_file_writer, &schema, options).unwrap();
4712
4713 let stream_bytes_written_on_new = stream_writer.get_ref().get_ref().len();
4714 let file_bytes_written_on_new = file_writer.get_ref().get_ref().len();
4715 stream_writer.flush().unwrap();
4716 file_writer.flush().unwrap();
4717 let stream_bytes_written_on_flush = stream_writer.get_ref().get_ref().len();
4718 let file_bytes_written_on_flush = file_writer.get_ref().get_ref().len();
4719 let stream_out = stream_writer.into_inner().unwrap().into_inner().unwrap();
4720 let expected_stream_flushed_bytes = stream_out.len() - 8;
4724 let expected_file_flushed_bytes = expected_stream_flushed_bytes + 8;
4727
4728 assert!(
4729 stream_bytes_written_on_new < stream_bytes_written_on_flush,
4730 "this test makes no sense if flush is not actually required"
4731 );
4732 assert!(
4733 file_bytes_written_on_new < file_bytes_written_on_flush,
4734 "this test makes no sense if flush is not actually required"
4735 );
4736 assert_eq!(stream_bytes_written_on_flush, expected_stream_flushed_bytes);
4737 assert_eq!(file_bytes_written_on_flush, expected_file_flushed_bytes);
4738 }
4739
4740 #[test]
4741 fn test_roundtrip_list_of_fixed_list() -> Result<(), ArrowError> {
4742 let l1_type =
4743 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Float32, false)), 3);
4744 let l2_type = DataType::List(Arc::new(Field::new("item", l1_type.clone(), false)));
4745
4746 let l0_builder = Float32Builder::new();
4747 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3).with_field(Arc::new(Field::new(
4748 "item",
4749 DataType::Float32,
4750 false,
4751 )));
4752 let mut l2_builder =
4753 ListBuilder::new(l1_builder).with_field(Arc::new(Field::new("item", l1_type, false)));
4754
4755 for point in [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]] {
4756 l2_builder.values().values().append_value(point[0]);
4757 l2_builder.values().values().append_value(point[1]);
4758 l2_builder.values().values().append_value(point[2]);
4759
4760 l2_builder.values().append(true);
4761 }
4762 l2_builder.append(true);
4763
4764 let point = [10., 11., 12.];
4765 l2_builder.values().values().append_value(point[0]);
4766 l2_builder.values().values().append_value(point[1]);
4767 l2_builder.values().values().append_value(point[2]);
4768
4769 l2_builder.values().append(true);
4770 l2_builder.append(true);
4771
4772 let array = Arc::new(l2_builder.finish()) as ArrayRef;
4773
4774 let schema = Arc::new(Schema::new_with_metadata(
4775 vec![Field::new("points", l2_type, false)],
4776 HashMap::default(),
4777 ));
4778
4779 test_slices(&array, &schema, 0, 1)?;
4782 test_slices(&array, &schema, 0, 2)?;
4783 test_slices(&array, &schema, 1, 1)?;
4784
4785 Ok(())
4786 }
4787
4788 #[test]
4789 fn test_roundtrip_list_of_fixed_list_w_nulls() -> Result<(), ArrowError> {
4790 let l0_builder = Float32Builder::new();
4791 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3);
4792 let mut l2_builder = ListBuilder::new(l1_builder);
4793
4794 for point in [
4795 [Some(1.0), Some(2.0), None],
4796 [Some(4.0), Some(5.0), Some(6.0)],
4797 [None, Some(8.0), Some(9.0)],
4798 ] {
4799 for p in point {
4800 match p {
4801 Some(p) => l2_builder.values().values().append_value(p),
4802 None => l2_builder.values().values().append_null(),
4803 }
4804 }
4805
4806 l2_builder.values().append(true);
4807 }
4808 l2_builder.append(true);
4809
4810 let point = [Some(10.), None, None];
4811 for p in point {
4812 match p {
4813 Some(p) => l2_builder.values().values().append_value(p),
4814 None => l2_builder.values().values().append_null(),
4815 }
4816 }
4817
4818 l2_builder.values().append(true);
4819 l2_builder.append(true);
4820
4821 let array = Arc::new(l2_builder.finish()) as ArrayRef;
4822
4823 let schema = Arc::new(Schema::new_with_metadata(
4824 vec![Field::new(
4825 "points",
4826 DataType::List(Arc::new(Field::new(
4827 "item",
4828 DataType::FixedSizeList(
4829 Arc::new(Field::new("item", DataType::Float32, true)),
4830 3,
4831 ),
4832 true,
4833 ))),
4834 true,
4835 )],
4836 HashMap::default(),
4837 ));
4838
4839 test_slices(&array, &schema, 0, 1)?;
4842 test_slices(&array, &schema, 0, 2)?;
4843 test_slices(&array, &schema, 1, 1)?;
4844
4845 Ok(())
4846 }
4847
4848 fn test_slices(
4849 parent_array: &ArrayRef,
4850 schema: &SchemaRef,
4851 offset: usize,
4852 length: usize,
4853 ) -> Result<(), ArrowError> {
4854 let subarray = parent_array.slice(offset, length);
4855 let original_batch = RecordBatch::try_new(schema.clone(), vec![subarray])?;
4856
4857 let mut bytes = Vec::new();
4858 let mut writer = StreamWriter::try_new(&mut bytes, schema)?;
4859 writer.write(&original_batch)?;
4860 writer.finish()?;
4861
4862 let mut cursor = std::io::Cursor::new(bytes);
4863 let mut reader = StreamReader::try_new(&mut cursor, None)?;
4864 let returned_batch = reader.next().unwrap()?;
4865
4866 assert_eq!(original_batch, returned_batch);
4867
4868 Ok(())
4869 }
4870
4871 #[test]
4872 fn test_roundtrip_fixed_list() -> Result<(), ArrowError> {
4873 let int_builder = Int64Builder::new();
4874 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3)
4875 .with_field(Arc::new(Field::new("item", DataType::Int64, false)));
4876
4877 for point in [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] {
4878 fixed_list_builder.values().append_value(point[0]);
4879 fixed_list_builder.values().append_value(point[1]);
4880 fixed_list_builder.values().append_value(point[2]);
4881
4882 fixed_list_builder.append(true);
4883 }
4884
4885 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
4886
4887 let schema = Arc::new(Schema::new_with_metadata(
4888 vec![Field::new(
4889 "points",
4890 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, false)), 3),
4891 false,
4892 )],
4893 HashMap::default(),
4894 ));
4895
4896 test_slices(&array, &schema, 0, 4)?;
4899 test_slices(&array, &schema, 0, 2)?;
4900 test_slices(&array, &schema, 1, 3)?;
4901 test_slices(&array, &schema, 2, 1)?;
4902
4903 Ok(())
4904 }
4905
4906 #[test]
4907 fn test_roundtrip_fixed_list_w_nulls() -> Result<(), ArrowError> {
4908 let int_builder = Int64Builder::new();
4909 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3);
4910
4911 for point in [
4912 [Some(1), Some(2), None],
4913 [Some(4), Some(5), Some(6)],
4914 [None, Some(8), Some(9)],
4915 [Some(10), None, None],
4916 ] {
4917 for p in point {
4918 match p {
4919 Some(p) => fixed_list_builder.values().append_value(p),
4920 None => fixed_list_builder.values().append_null(),
4921 }
4922 }
4923
4924 fixed_list_builder.append(true);
4925 }
4926
4927 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
4928
4929 let schema = Arc::new(Schema::new_with_metadata(
4930 vec![Field::new(
4931 "points",
4932 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, true)), 3),
4933 true,
4934 )],
4935 HashMap::default(),
4936 ));
4937
4938 test_slices(&array, &schema, 0, 4)?;
4941 test_slices(&array, &schema, 0, 2)?;
4942 test_slices(&array, &schema, 1, 3)?;
4943 test_slices(&array, &schema, 2, 1)?;
4944
4945 Ok(())
4946 }
4947
4948 #[test]
4949 fn test_metadata_encoding_ordering() {
4950 fn create_hash() -> u64 {
4951 let metadata: HashMap<String, String> = [
4952 ("a", "1"), ("b", "2"), ("c", "3"), ("d", "4"), ("e", "5"), ]
4958 .into_iter()
4959 .map(|(k, v)| (k.to_owned(), v.to_owned()))
4960 .collect();
4961
4962 let schema = Arc::new(
4964 Schema::new(vec![
4965 Field::new("a", DataType::Int64, true).with_metadata(metadata.clone()),
4966 ])
4967 .with_metadata(metadata)
4968 .clone(),
4969 );
4970 let batch = RecordBatch::new_empty(schema.clone());
4971
4972 let mut bytes = Vec::new();
4973 let mut w = StreamWriter::try_new(&mut bytes, batch.schema_ref()).unwrap();
4974 w.write(&batch).unwrap();
4975 w.finish().unwrap();
4976
4977 let mut h = std::hash::DefaultHasher::new();
4978 h.write(&bytes);
4979 h.finish()
4980 }
4981
4982 let expected = create_hash();
4983
4984 let all_passed = (0..20).all(|_| create_hash() == expected);
4989 assert!(all_passed);
4990 }
4991
4992 #[test]
4993 fn test_dictionary_tracker_reset() {
4994 let data_gen = IpcDataGenerator::default();
4995 let mut dictionary_tracker = DictionaryTracker::new(false);
4996 let writer_options = IpcWriteOptions::default();
4997 let mut compression_ctx = IpcWriteContext::default();
4998
4999 let schema = Arc::new(Schema::new(vec![Field::new(
5000 "a",
5001 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
5002 false,
5003 )]));
5004
5005 let mut write_single_batch_stream =
5006 |batch: RecordBatch, dict_tracker: &mut DictionaryTracker| -> Vec<u8> {
5007 let mut buffer = Vec::new();
5008
5009 let stream_header = data_gen.schema_to_bytes_with_dictionary_tracker(
5011 &schema,
5012 dict_tracker,
5013 &writer_options,
5014 );
5015 _ = write_message(&mut buffer, stream_header, &writer_options).unwrap();
5016
5017 let (encoded_dicts, encoded_batch) = data_gen
5018 .encode(&batch, dict_tracker, &writer_options, &mut compression_ctx)
5019 .unwrap();
5020 for encoded_dict in encoded_dicts {
5021 _ = write_message(&mut buffer, encoded_dict, &writer_options).unwrap();
5022 }
5023 _ = write_message(&mut buffer, encoded_batch, &writer_options).unwrap();
5024
5025 buffer
5026 };
5027
5028 let batch1 = RecordBatch::try_new(
5029 schema.clone(),
5030 vec![Arc::new(DictionaryArray::new(
5031 UInt8Array::from_iter_values([0]),
5032 Arc::new(StringArray::from_iter_values(["a"])),
5033 ))],
5034 )
5035 .unwrap();
5036 let buffer = write_single_batch_stream(batch1.clone(), &mut dictionary_tracker);
5037
5038 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
5040 let read_batch = reader.next().unwrap().unwrap();
5041 assert_eq!(read_batch, batch1);
5042
5043 dictionary_tracker.clear();
5045
5046 let batch2 = RecordBatch::try_new(
5048 schema.clone(),
5049 vec![Arc::new(DictionaryArray::new(
5050 UInt8Array::from_iter_values([0]),
5051 Arc::new(StringArray::from_iter_values(["a"])),
5052 ))],
5053 )
5054 .unwrap();
5055 let buffer = write_single_batch_stream(batch2.clone(), &mut dictionary_tracker);
5056 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
5057 let read_batch = reader.next().unwrap().unwrap();
5058 assert_eq!(read_batch, batch2);
5059 }
5060}