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;
45pub use crate::compression::CompressionContext;
46use crate::convert::IpcSchemaEncoder;
47
48#[derive(Debug, Clone)]
50pub struct IpcWriteOptions {
51 alignment: u8,
54 write_legacy_ipc_format: bool,
56 metadata_version: crate::MetadataVersion,
65 batch_compression_type: Option<crate::CompressionType>,
68 dictionary_handling: DictionaryHandling,
70}
71
72impl IpcWriteOptions {
73 pub fn try_with_compression(
78 mut self,
79 batch_compression_type: Option<crate::CompressionType>,
80 ) -> Result<Self, ArrowError> {
81 self.batch_compression_type = batch_compression_type;
82
83 if self.batch_compression_type.is_some()
84 && self.metadata_version < crate::MetadataVersion::V5
85 {
86 return Err(ArrowError::InvalidArgumentError(
87 "Compression only supported in metadata v5 and above".to_string(),
88 ));
89 }
90 Ok(self)
91 }
92 pub fn try_new(
94 alignment: usize,
95 write_legacy_ipc_format: bool,
96 metadata_version: crate::MetadataVersion,
97 ) -> Result<Self, ArrowError> {
98 let is_alignment_valid =
99 alignment == 8 || alignment == 16 || alignment == 32 || alignment == 64;
100 if !is_alignment_valid {
101 return Err(ArrowError::InvalidArgumentError(
102 "Alignment should be 8, 16, 32, or 64.".to_string(),
103 ));
104 }
105 let alignment: u8 = u8::try_from(alignment).expect("range already checked");
106 match metadata_version {
107 crate::MetadataVersion::V1
108 | crate::MetadataVersion::V2
109 | crate::MetadataVersion::V3 => Err(ArrowError::InvalidArgumentError(
110 "Writing IPC metadata version 3 and lower not supported".to_string(),
111 )),
112 #[allow(deprecated)]
113 crate::MetadataVersion::V4 => Ok(Self {
114 alignment,
115 write_legacy_ipc_format,
116 metadata_version,
117 batch_compression_type: None,
118 dictionary_handling: DictionaryHandling::default(),
119 }),
120 crate::MetadataVersion::V5 => {
121 if write_legacy_ipc_format {
122 Err(ArrowError::InvalidArgumentError(
123 "Legacy IPC format only supported on metadata version 4".to_string(),
124 ))
125 } else {
126 Ok(Self {
127 alignment,
128 write_legacy_ipc_format,
129 metadata_version,
130 batch_compression_type: None,
131 dictionary_handling: DictionaryHandling::default(),
132 })
133 }
134 }
135 z => Err(ArrowError::InvalidArgumentError(format!(
136 "Unsupported crate::MetadataVersion {z:?}"
137 ))),
138 }
139 }
140
141 pub fn with_dictionary_handling(mut self, dictionary_handling: DictionaryHandling) -> Self {
143 self.dictionary_handling = dictionary_handling;
144 self
145 }
146}
147
148impl Default for IpcWriteOptions {
149 fn default() -> Self {
150 Self {
151 alignment: 64,
152 write_legacy_ipc_format: false,
153 metadata_version: crate::MetadataVersion::V5,
154 batch_compression_type: None,
155 dictionary_handling: DictionaryHandling::default(),
156 }
157 }
158}
159
160#[derive(Debug, Default)]
161pub struct IpcDataGenerator {}
195
196impl IpcDataGenerator {
197 pub fn schema_to_bytes_with_dictionary_tracker(
200 &self,
201 schema: &Schema,
202 dictionary_tracker: &mut DictionaryTracker,
203 write_options: &IpcWriteOptions,
204 ) -> EncodedData {
205 let mut fbb = FlatBufferBuilder::new();
206 let schema = {
207 let fb = IpcSchemaEncoder::new()
208 .with_dictionary_tracker(dictionary_tracker)
209 .schema_to_fb_offset(&mut fbb, schema);
210 fb.as_union_value()
211 };
212
213 let mut message = crate::MessageBuilder::new(&mut fbb);
214 message.add_version(write_options.metadata_version);
215 message.add_header_type(crate::MessageHeader::Schema);
216 message.add_bodyLength(0);
217 message.add_header(schema);
218 let data = message.finish();
220 fbb.finish(data, None);
221
222 let data = fbb.finished_data();
223 EncodedData {
224 ipc_message: data.to_vec(),
225 arrow_data: vec![],
226 }
227 }
228
229 fn _encode_dictionaries<I: Iterator<Item = i64>>(
230 &self,
231 column: &ArrayRef,
232 encoded_dictionaries: &mut Vec<EncodedData>,
233 dictionary_tracker: &mut DictionaryTracker,
234 write_options: &IpcWriteOptions,
235 dict_id: &mut I,
236 compression_context: &mut CompressionContext,
237 ) -> Result<(), ArrowError> {
238 match column.data_type() {
239 DataType::Struct(fields) => {
240 let s = as_struct_array(column);
241 for (field, column) in fields.iter().zip(s.columns()) {
242 self.encode_dictionaries(
243 field,
244 column,
245 encoded_dictionaries,
246 dictionary_tracker,
247 write_options,
248 dict_id,
249 compression_context,
250 )?;
251 }
252 }
253 DataType::RunEndEncoded(_, values) => {
254 let data = column.to_data();
255 if data.child_data().len() != 2 {
256 return Err(ArrowError::InvalidArgumentError(format!(
257 "The run encoded array should have exactly two child arrays. Found {}",
258 data.child_data().len()
259 )));
260 }
261 let values_array = make_array(data.child_data()[1].clone());
264 self.encode_dictionaries(
265 values,
266 &values_array,
267 encoded_dictionaries,
268 dictionary_tracker,
269 write_options,
270 dict_id,
271 compression_context,
272 )?;
273 }
274 DataType::List(field) => {
275 let list = as_list_array(column);
276 self.encode_dictionaries(
277 field,
278 list.values(),
279 encoded_dictionaries,
280 dictionary_tracker,
281 write_options,
282 dict_id,
283 compression_context,
284 )?;
285 }
286 DataType::LargeList(field) => {
287 let list = as_large_list_array(column);
288 self.encode_dictionaries(
289 field,
290 list.values(),
291 encoded_dictionaries,
292 dictionary_tracker,
293 write_options,
294 dict_id,
295 compression_context,
296 )?;
297 }
298 DataType::ListView(field) => {
299 let list = column.as_list_view::<i32>();
300 self.encode_dictionaries(
301 field,
302 list.values(),
303 encoded_dictionaries,
304 dictionary_tracker,
305 write_options,
306 dict_id,
307 compression_context,
308 )?;
309 }
310 DataType::LargeListView(field) => {
311 let list = column.as_list_view::<i64>();
312 self.encode_dictionaries(
313 field,
314 list.values(),
315 encoded_dictionaries,
316 dictionary_tracker,
317 write_options,
318 dict_id,
319 compression_context,
320 )?;
321 }
322 DataType::FixedSizeList(field, _) => {
323 let list = column
324 .as_any()
325 .downcast_ref::<FixedSizeListArray>()
326 .expect("Unable to downcast to fixed size list array");
327 self.encode_dictionaries(
328 field,
329 list.values(),
330 encoded_dictionaries,
331 dictionary_tracker,
332 write_options,
333 dict_id,
334 compression_context,
335 )?;
336 }
337 DataType::Map(field, _) => {
338 let map_array = as_map_array(column);
339
340 let (keys, values) = match field.data_type() {
341 DataType::Struct(fields) if fields.len() == 2 => (&fields[0], &fields[1]),
342 _ => panic!("Incorrect field data type {:?}", field.data_type()),
343 };
344
345 self.encode_dictionaries(
347 keys,
348 map_array.keys(),
349 encoded_dictionaries,
350 dictionary_tracker,
351 write_options,
352 dict_id,
353 compression_context,
354 )?;
355
356 self.encode_dictionaries(
358 values,
359 map_array.values(),
360 encoded_dictionaries,
361 dictionary_tracker,
362 write_options,
363 dict_id,
364 compression_context,
365 )?;
366 }
367 DataType::Union(fields, _) => {
368 let union = as_union_array(column);
369 for (type_id, field) in fields.iter() {
370 let column = union.child(type_id);
371 self.encode_dictionaries(
372 field,
373 column,
374 encoded_dictionaries,
375 dictionary_tracker,
376 write_options,
377 dict_id,
378 compression_context,
379 )?;
380 }
381 }
382 _ => (),
383 }
384
385 Ok(())
386 }
387
388 #[allow(clippy::too_many_arguments)]
389 fn encode_dictionaries<I: Iterator<Item = i64>>(
390 &self,
391 field: &Field,
392 column: &ArrayRef,
393 encoded_dictionaries: &mut Vec<EncodedData>,
394 dictionary_tracker: &mut DictionaryTracker,
395 write_options: &IpcWriteOptions,
396 dict_id_seq: &mut I,
397 compression_context: &mut CompressionContext,
398 ) -> Result<(), ArrowError> {
399 match column.data_type() {
400 DataType::Dictionary(_key_type, _value_type) => {
401 let dict_data = column.to_data();
402 let dict_values = &dict_data.child_data()[0];
403
404 let values = make_array(dict_data.child_data()[0].clone());
405
406 self._encode_dictionaries(
407 &values,
408 encoded_dictionaries,
409 dictionary_tracker,
410 write_options,
411 dict_id_seq,
412 compression_context,
413 )?;
414
415 let dict_id = dict_id_seq.next().ok_or_else(|| {
419 ArrowError::IpcError(format!(
420 "no dict id for field {:?}: field.data_type={:?}, column.data_type={:?}",
421 field.name(),
422 field.data_type(),
423 column.data_type()
424 ))
425 })?;
426
427 match dictionary_tracker.insert_column(
428 dict_id,
429 column,
430 write_options.dictionary_handling,
431 )? {
432 DictionaryUpdate::None => {}
433 DictionaryUpdate::New | DictionaryUpdate::Replaced => {
434 encoded_dictionaries.push(self.dictionary_batch_to_bytes(
435 dict_id,
436 dict_values,
437 write_options,
438 false,
439 compression_context,
440 )?);
441 }
442 DictionaryUpdate::Delta(data) => {
443 encoded_dictionaries.push(self.dictionary_batch_to_bytes(
444 dict_id,
445 &data,
446 write_options,
447 true,
448 compression_context,
449 )?);
450 }
451 }
452 }
453 _ => self._encode_dictionaries(
454 column,
455 encoded_dictionaries,
456 dictionary_tracker,
457 write_options,
458 dict_id_seq,
459 compression_context,
460 )?,
461 }
462
463 Ok(())
464 }
465
466 pub fn encode(
470 &self,
471 batch: &RecordBatch,
472 dictionary_tracker: &mut DictionaryTracker,
473 write_options: &IpcWriteOptions,
474 compression_context: &mut CompressionContext,
475 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
476 let schema = batch.schema();
477 let mut encoded_dictionaries = Vec::with_capacity(schema.flattened_fields().len());
478
479 let mut dict_id = dictionary_tracker.dict_ids.clone().into_iter();
480
481 for (i, field) in schema.fields().iter().enumerate() {
482 let column = batch.column(i);
483 self.encode_dictionaries(
484 field,
485 column,
486 &mut encoded_dictionaries,
487 dictionary_tracker,
488 write_options,
489 &mut dict_id,
490 compression_context,
491 )?;
492 }
493
494 let encoded_message =
495 self.record_batch_to_bytes(batch, write_options, compression_context)?;
496 Ok((encoded_dictionaries, encoded_message))
497 }
498
499 #[deprecated(since = "57.0.0", note = "Use `encode` instead")]
503 pub fn encoded_batch(
504 &self,
505 batch: &RecordBatch,
506 dictionary_tracker: &mut DictionaryTracker,
507 write_options: &IpcWriteOptions,
508 ) -> Result<(Vec<EncodedData>, EncodedData), ArrowError> {
509 self.encode(
510 batch,
511 dictionary_tracker,
512 write_options,
513 &mut Default::default(),
514 )
515 }
516
517 fn record_batch_to_bytes(
520 &self,
521 batch: &RecordBatch,
522 write_options: &IpcWriteOptions,
523 compression_context: &mut CompressionContext,
524 ) -> Result<EncodedData, ArrowError> {
525 let mut fbb = FlatBufferBuilder::new();
526
527 let mut nodes: Vec<crate::FieldNode> = vec![];
528 let mut buffers: Vec<crate::Buffer> = vec![];
529 let mut arrow_data: Vec<u8> = vec![];
530 let mut offset = 0;
531
532 let batch_compression_type = write_options.batch_compression_type;
534
535 let compression = batch_compression_type.map(|batch_compression_type| {
536 let mut c = crate::BodyCompressionBuilder::new(&mut fbb);
537 c.add_method(crate::BodyCompressionMethod::BUFFER);
538 c.add_codec(batch_compression_type);
539 c.finish()
540 });
541
542 let compression_codec: Option<CompressionCodec> =
543 batch_compression_type.map(TryInto::try_into).transpose()?;
544
545 let mut variadic_buffer_counts = vec![];
546
547 for array in batch.columns() {
548 let array_data = array.to_data();
549 offset = write_array_data(
550 &array_data,
551 &mut buffers,
552 &mut arrow_data,
553 &mut nodes,
554 offset,
555 array.len(),
556 array.null_count(),
557 compression_codec,
558 compression_context,
559 write_options,
560 )?;
561
562 append_variadic_buffer_counts(&mut variadic_buffer_counts, &array_data);
563 }
564 let len = arrow_data.len();
566 let pad_len = pad_to_alignment(write_options.alignment, len);
567 arrow_data.extend_from_slice(&PADDING[..pad_len]);
568
569 let buffers = fbb.create_vector(&buffers);
571 let nodes = fbb.create_vector(&nodes);
572 let variadic_buffer = if variadic_buffer_counts.is_empty() {
573 None
574 } else {
575 Some(fbb.create_vector(&variadic_buffer_counts))
576 };
577
578 let root = {
579 let mut batch_builder = crate::RecordBatchBuilder::new(&mut fbb);
580 batch_builder.add_length(batch.num_rows() as i64);
581 batch_builder.add_nodes(nodes);
582 batch_builder.add_buffers(buffers);
583 if let Some(c) = compression {
584 batch_builder.add_compression(c);
585 }
586
587 if let Some(v) = variadic_buffer {
588 batch_builder.add_variadicBufferCounts(v);
589 }
590 let b = batch_builder.finish();
591 b.as_union_value()
592 };
593 let mut message = crate::MessageBuilder::new(&mut fbb);
595 message.add_version(write_options.metadata_version);
596 message.add_header_type(crate::MessageHeader::RecordBatch);
597 message.add_bodyLength(arrow_data.len() as i64);
598 message.add_header(root);
599 let root = message.finish();
600 fbb.finish(root, None);
601 let finished_data = fbb.finished_data();
602
603 Ok(EncodedData {
604 ipc_message: finished_data.to_vec(),
605 arrow_data,
606 })
607 }
608
609 fn dictionary_batch_to_bytes(
612 &self,
613 dict_id: i64,
614 array_data: &ArrayData,
615 write_options: &IpcWriteOptions,
616 is_delta: bool,
617 compression_context: &mut CompressionContext,
618 ) -> Result<EncodedData, ArrowError> {
619 let mut fbb = FlatBufferBuilder::new();
620
621 let mut nodes: Vec<crate::FieldNode> = vec![];
622 let mut buffers: Vec<crate::Buffer> = vec![];
623 let mut arrow_data: Vec<u8> = vec![];
624
625 let batch_compression_type = write_options.batch_compression_type;
627
628 let compression = batch_compression_type.map(|batch_compression_type| {
629 let mut c = crate::BodyCompressionBuilder::new(&mut fbb);
630 c.add_method(crate::BodyCompressionMethod::BUFFER);
631 c.add_codec(batch_compression_type);
632 c.finish()
633 });
634
635 let compression_codec: Option<CompressionCodec> = batch_compression_type
636 .map(|batch_compression_type| batch_compression_type.try_into())
637 .transpose()?;
638
639 write_array_data(
640 array_data,
641 &mut buffers,
642 &mut arrow_data,
643 &mut nodes,
644 0,
645 array_data.len(),
646 array_data.null_count(),
647 compression_codec,
648 compression_context,
649 write_options,
650 )?;
651
652 let mut variadic_buffer_counts = vec![];
653 append_variadic_buffer_counts(&mut variadic_buffer_counts, array_data);
654
655 let len = arrow_data.len();
657 let pad_len = pad_to_alignment(write_options.alignment, len);
658 arrow_data.extend_from_slice(&PADDING[..pad_len]);
659
660 let buffers = fbb.create_vector(&buffers);
662 let nodes = fbb.create_vector(&nodes);
663 let variadic_buffer = if variadic_buffer_counts.is_empty() {
664 None
665 } else {
666 Some(fbb.create_vector(&variadic_buffer_counts))
667 };
668
669 let root = {
670 let mut batch_builder = crate::RecordBatchBuilder::new(&mut fbb);
671 batch_builder.add_length(array_data.len() as i64);
672 batch_builder.add_nodes(nodes);
673 batch_builder.add_buffers(buffers);
674 if let Some(c) = compression {
675 batch_builder.add_compression(c);
676 }
677 if let Some(v) = variadic_buffer {
678 batch_builder.add_variadicBufferCounts(v);
679 }
680 batch_builder.finish()
681 };
682
683 let root = {
684 let mut batch_builder = crate::DictionaryBatchBuilder::new(&mut fbb);
685 batch_builder.add_id(dict_id);
686 batch_builder.add_data(root);
687 batch_builder.add_isDelta(is_delta);
688 batch_builder.finish().as_union_value()
689 };
690
691 let root = {
692 let mut message_builder = crate::MessageBuilder::new(&mut fbb);
693 message_builder.add_version(write_options.metadata_version);
694 message_builder.add_header_type(crate::MessageHeader::DictionaryBatch);
695 message_builder.add_bodyLength(arrow_data.len() as i64);
696 message_builder.add_header(root);
697 message_builder.finish()
698 };
699
700 fbb.finish(root, None);
701 let finished_data = fbb.finished_data();
702
703 Ok(EncodedData {
704 ipc_message: finished_data.to_vec(),
705 arrow_data,
706 })
707 }
708}
709
710fn append_variadic_buffer_counts(counts: &mut Vec<i64>, array: &ArrayData) {
711 match array.data_type() {
712 DataType::BinaryView | DataType::Utf8View => {
713 counts.push(array.buffers().len() as i64 - 1);
716 }
717 DataType::Dictionary(_, _) => {
718 }
721 _ => {
722 for child in array.child_data() {
723 append_variadic_buffer_counts(counts, child)
724 }
725 }
726 }
727}
728
729pub(crate) fn unslice_run_array(arr: ArrayData) -> Result<ArrayData, ArrowError> {
730 match arr.data_type() {
731 DataType::RunEndEncoded(k, _) => match k.data_type() {
732 DataType::Int16 => {
733 Ok(into_zero_offset_run_array(RunArray::<Int16Type>::from(arr))?.into_data())
734 }
735 DataType::Int32 => {
736 Ok(into_zero_offset_run_array(RunArray::<Int32Type>::from(arr))?.into_data())
737 }
738 DataType::Int64 => {
739 Ok(into_zero_offset_run_array(RunArray::<Int64Type>::from(arr))?.into_data())
740 }
741 d => unreachable!("Unexpected data type {d}"),
742 },
743 d => Err(ArrowError::InvalidArgumentError(format!(
744 "The given array is not a run array. Data type of given array: {d}"
745 ))),
746 }
747}
748
749fn into_zero_offset_run_array<R: RunEndIndexType>(
752 run_array: RunArray<R>,
753) -> Result<RunArray<R>, ArrowError> {
754 let run_ends = run_array.run_ends();
755 if run_ends.offset() == 0 && run_ends.max_value() == run_ends.len() {
756 return Ok(run_array);
757 }
758
759 let start_physical_index = run_ends.get_start_physical_index();
761
762 let end_physical_index = run_ends.get_end_physical_index();
764
765 let physical_length = end_physical_index - start_physical_index + 1;
766
767 let offset = R::Native::usize_as(run_ends.offset());
769 let mut builder = BufferBuilder::<R::Native>::new(physical_length);
770 for run_end_value in &run_ends.values()[start_physical_index..end_physical_index] {
771 builder.append(run_end_value.sub_wrapping(offset));
772 }
773 builder.append(R::Native::from_usize(run_array.len()).unwrap());
774 let new_run_ends = unsafe {
775 ArrayDataBuilder::new(R::DATA_TYPE)
778 .len(physical_length)
779 .add_buffer(builder.finish())
780 .build_unchecked()
781 };
782
783 let new_values = run_array
785 .values()
786 .slice(start_physical_index, physical_length)
787 .into_data();
788
789 let builder = ArrayDataBuilder::new(run_array.data_type().clone())
790 .len(run_array.len())
791 .add_child_data(new_run_ends)
792 .add_child_data(new_values);
793 let array_data = unsafe {
794 builder.build_unchecked()
797 };
798 Ok(array_data.into())
799}
800
801#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
803pub enum DictionaryHandling {
804 #[default]
806 Resend,
807 Delta,
813}
814
815#[derive(Debug, Clone)]
817pub enum DictionaryUpdate {
818 None,
821 New,
823 Replaced,
825 Delta(ArrayData),
827}
828
829#[derive(Debug)]
835pub struct DictionaryTracker {
836 written: HashMap<i64, ArrayData>,
838 dict_ids: Vec<i64>,
839 error_on_replacement: bool,
840}
841
842impl DictionaryTracker {
843 pub fn new(error_on_replacement: bool) -> Self {
849 #[allow(deprecated)]
850 Self {
851 written: HashMap::new(),
852 dict_ids: Vec::new(),
853 error_on_replacement,
854 }
855 }
856
857 pub fn next_dict_id(&mut self) -> i64 {
859 let next = self
860 .dict_ids
861 .last()
862 .copied()
863 .map(|i| i + 1)
864 .unwrap_or_default();
865
866 self.dict_ids.push(next);
867 next
868 }
869
870 pub fn dict_id(&mut self) -> &[i64] {
873 &self.dict_ids
874 }
875
876 #[deprecated(since = "56.1.0", note = "Use `insert_column` instead")]
886 pub fn insert(&mut self, dict_id: i64, column: &ArrayRef) -> Result<bool, ArrowError> {
887 let dict_data = column.to_data();
888 let dict_values = &dict_data.child_data()[0];
889
890 if let Some(last) = self.written.get(&dict_id) {
892 if ArrayData::ptr_eq(&last.child_data()[0], dict_values) {
893 return Ok(false);
895 }
896 if self.error_on_replacement {
897 if last.child_data()[0] == *dict_values {
899 return Ok(false);
901 }
902 return Err(ArrowError::InvalidArgumentError(
903 "Dictionary replacement detected when writing IPC file format. \
904 Arrow IPC files only support a single dictionary for a given field \
905 across all batches."
906 .to_string(),
907 ));
908 }
909 }
910
911 self.written.insert(dict_id, dict_data);
912 Ok(true)
913 }
914
915 pub fn insert_column(
931 &mut self,
932 dict_id: i64,
933 column: &ArrayRef,
934 dict_handling: DictionaryHandling,
935 ) -> Result<DictionaryUpdate, ArrowError> {
936 let new_data = column.to_data();
937 let new_values = &new_data.child_data()[0];
938
939 let Some(old) = self.written.get(&dict_id) else {
941 self.written.insert(dict_id, new_data);
942 return Ok(DictionaryUpdate::New);
943 };
944
945 let old_values = &old.child_data()[0];
948 if ArrayData::ptr_eq(old_values, new_values) {
949 return Ok(DictionaryUpdate::None);
950 }
951
952 let comparison = compare_dictionaries(old_values, new_values);
954 if matches!(comparison, DictionaryComparison::Equal) {
955 return Ok(DictionaryUpdate::None);
956 }
957
958 const REPLACEMENT_ERROR: &str = "Dictionary replacement detected when writing IPC file format. \
959 Arrow IPC files only support a single dictionary for a given field \
960 across all batches.";
961
962 match comparison {
963 DictionaryComparison::NotEqual => {
964 if self.error_on_replacement {
965 return Err(ArrowError::InvalidArgumentError(
966 REPLACEMENT_ERROR.to_string(),
967 ));
968 }
969
970 self.written.insert(dict_id, new_data);
971 Ok(DictionaryUpdate::Replaced)
972 }
973 DictionaryComparison::Delta => match dict_handling {
974 DictionaryHandling::Resend => {
975 if self.error_on_replacement {
976 return Err(ArrowError::InvalidArgumentError(
977 REPLACEMENT_ERROR.to_string(),
978 ));
979 }
980
981 self.written.insert(dict_id, new_data);
982 Ok(DictionaryUpdate::Replaced)
983 }
984 DictionaryHandling::Delta => {
985 let delta =
986 new_values.slice(old_values.len(), new_values.len() - old_values.len());
987 self.written.insert(dict_id, new_data);
988 Ok(DictionaryUpdate::Delta(delta))
989 }
990 },
991 DictionaryComparison::Equal => unreachable!("Already checked equal case"),
992 }
993 }
994
995 pub fn clear(&mut self) {
1001 self.dict_ids.clear();
1002 self.written.clear();
1003 }
1004}
1005
1006#[derive(Debug, Clone)]
1008enum DictionaryComparison {
1009 NotEqual,
1011 Equal,
1013 Delta,
1016}
1017
1018fn compare_dictionaries(old: &ArrayData, new: &ArrayData) -> DictionaryComparison {
1020 let existing_len = old.len();
1022 let new_len = new.len();
1023 if existing_len == new_len {
1024 if *old == *new {
1025 return DictionaryComparison::Equal;
1026 } else {
1027 return DictionaryComparison::NotEqual;
1028 }
1029 }
1030
1031 if new_len < existing_len {
1033 return DictionaryComparison::NotEqual;
1034 }
1035
1036 if new.slice(0, existing_len) == *old {
1038 return DictionaryComparison::Delta;
1039 }
1040
1041 DictionaryComparison::NotEqual
1042}
1043
1044pub struct FileWriter<W> {
1067 writer: W,
1069 write_options: IpcWriteOptions,
1071 schema: SchemaRef,
1073 block_offsets: usize,
1075 dictionary_blocks: Vec<crate::Block>,
1077 record_blocks: Vec<crate::Block>,
1079 finished: bool,
1081 dictionary_tracker: DictionaryTracker,
1083 custom_metadata: HashMap<String, String>,
1085
1086 data_gen: IpcDataGenerator,
1087
1088 compression_context: CompressionContext,
1089}
1090
1091impl<W: Write> FileWriter<BufWriter<W>> {
1092 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1096 Self::try_new(BufWriter::new(writer), schema)
1097 }
1098}
1099
1100impl<W: Write> FileWriter<W> {
1101 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1109 let write_options = IpcWriteOptions::default();
1110 Self::try_new_with_options(writer, schema, write_options)
1111 }
1112
1113 pub fn try_new_with_options(
1121 mut writer: W,
1122 schema: &Schema,
1123 write_options: IpcWriteOptions,
1124 ) -> Result<Self, ArrowError> {
1125 let data_gen = IpcDataGenerator::default();
1126 let pad_len = pad_to_alignment(write_options.alignment, super::ARROW_MAGIC.len());
1128 let header_size = super::ARROW_MAGIC.len() + pad_len;
1129 writer.write_all(&super::ARROW_MAGIC)?;
1130 writer.write_all(&PADDING[..pad_len])?;
1131 let mut dictionary_tracker = DictionaryTracker::new(true);
1133 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
1134 schema,
1135 &mut dictionary_tracker,
1136 &write_options,
1137 );
1138 let (meta, data) = write_message(&mut writer, encoded_message, &write_options)?;
1139 Ok(Self {
1140 writer,
1141 write_options,
1142 schema: Arc::new(schema.clone()),
1143 block_offsets: meta + data + header_size,
1144 dictionary_blocks: vec![],
1145 record_blocks: vec![],
1146 finished: false,
1147 dictionary_tracker,
1148 custom_metadata: HashMap::new(),
1149 data_gen,
1150 compression_context: CompressionContext::default(),
1151 })
1152 }
1153
1154 pub fn write_metadata(&mut self, key: impl Into<String>, value: impl Into<String>) {
1156 self.custom_metadata.insert(key.into(), value.into());
1157 }
1158
1159 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1161 if self.finished {
1162 return Err(ArrowError::IpcError(
1163 "Cannot write record batch to file writer as it is closed".to_string(),
1164 ));
1165 }
1166
1167 let (encoded_dictionaries, encoded_message) = self.data_gen.encode(
1168 batch,
1169 &mut self.dictionary_tracker,
1170 &self.write_options,
1171 &mut self.compression_context,
1172 )?;
1173
1174 for encoded_dictionary in encoded_dictionaries {
1175 let (meta, data) =
1176 write_message(&mut self.writer, encoded_dictionary, &self.write_options)?;
1177
1178 let block = crate::Block::new(self.block_offsets as i64, meta as i32, data as i64);
1179 self.dictionary_blocks.push(block);
1180 self.block_offsets += meta + data;
1181 }
1182
1183 let (meta, data) = write_message(&mut self.writer, encoded_message, &self.write_options)?;
1184
1185 let block = crate::Block::new(
1187 self.block_offsets as i64,
1188 meta as i32, data as i64,
1190 );
1191 self.record_blocks.push(block);
1192 self.block_offsets += meta + data;
1193 Ok(())
1194 }
1195
1196 pub fn finish(&mut self) -> Result<(), ArrowError> {
1198 if self.finished {
1199 return Err(ArrowError::IpcError(
1200 "Cannot write footer to file writer as it is closed".to_string(),
1201 ));
1202 }
1203
1204 write_continuation(&mut self.writer, &self.write_options, 0)?;
1206
1207 let mut fbb = FlatBufferBuilder::new();
1208 let dictionaries = fbb.create_vector(&self.dictionary_blocks);
1209 let record_batches = fbb.create_vector(&self.record_blocks);
1210
1211 self.dictionary_tracker.clear();
1213 let schema = IpcSchemaEncoder::new()
1214 .with_dictionary_tracker(&mut self.dictionary_tracker)
1215 .schema_to_fb_offset(&mut fbb, &self.schema);
1216 let fb_custom_metadata = (!self.custom_metadata.is_empty())
1217 .then(|| crate::convert::metadata_to_fb(&mut fbb, &self.custom_metadata));
1218
1219 let root = {
1220 let mut footer_builder = crate::FooterBuilder::new(&mut fbb);
1221 footer_builder.add_version(self.write_options.metadata_version);
1222 footer_builder.add_schema(schema);
1223 footer_builder.add_dictionaries(dictionaries);
1224 footer_builder.add_recordBatches(record_batches);
1225 if let Some(fb_custom_metadata) = fb_custom_metadata {
1226 footer_builder.add_custom_metadata(fb_custom_metadata);
1227 }
1228 footer_builder.finish()
1229 };
1230 fbb.finish(root, None);
1231 let footer_data = fbb.finished_data();
1232 self.writer.write_all(footer_data)?;
1233 self.writer
1234 .write_all(&(footer_data.len() as i32).to_le_bytes())?;
1235 self.writer.write_all(&super::ARROW_MAGIC)?;
1236 self.writer.flush()?;
1237 self.finished = true;
1238
1239 Ok(())
1240 }
1241
1242 pub fn schema(&self) -> &SchemaRef {
1244 &self.schema
1245 }
1246
1247 pub fn get_ref(&self) -> &W {
1249 &self.writer
1250 }
1251
1252 pub fn get_mut(&mut self) -> &mut W {
1256 &mut self.writer
1257 }
1258
1259 pub fn flush(&mut self) -> Result<(), ArrowError> {
1263 self.writer.flush()?;
1264 Ok(())
1265 }
1266
1267 pub fn into_inner(mut self) -> Result<W, ArrowError> {
1276 if !self.finished {
1277 self.finish()?;
1279 }
1280 Ok(self.writer)
1281 }
1282}
1283
1284impl<W: Write> RecordBatchWriter for FileWriter<W> {
1285 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1286 self.write(batch)
1287 }
1288
1289 fn close(mut self) -> Result<(), ArrowError> {
1290 self.finish()
1291 }
1292}
1293
1294pub struct StreamWriter<W> {
1368 writer: W,
1370 write_options: IpcWriteOptions,
1372 finished: bool,
1374 dictionary_tracker: DictionaryTracker,
1376
1377 data_gen: IpcDataGenerator,
1378
1379 compression_context: CompressionContext,
1380}
1381
1382impl<W: Write> StreamWriter<BufWriter<W>> {
1383 pub fn try_new_buffered(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1387 Self::try_new(BufWriter::new(writer), schema)
1388 }
1389}
1390
1391impl<W: Write> StreamWriter<W> {
1392 pub fn try_new(writer: W, schema: &Schema) -> Result<Self, ArrowError> {
1400 let write_options = IpcWriteOptions::default();
1401 Self::try_new_with_options(writer, schema, write_options)
1402 }
1403
1404 pub fn try_new_with_options(
1410 mut writer: W,
1411 schema: &Schema,
1412 write_options: IpcWriteOptions,
1413 ) -> Result<Self, ArrowError> {
1414 let data_gen = IpcDataGenerator::default();
1415 let mut dictionary_tracker = DictionaryTracker::new(false);
1416
1417 let encoded_message = data_gen.schema_to_bytes_with_dictionary_tracker(
1419 schema,
1420 &mut dictionary_tracker,
1421 &write_options,
1422 );
1423 write_message(&mut writer, encoded_message, &write_options)?;
1424 Ok(Self {
1425 writer,
1426 write_options,
1427 finished: false,
1428 dictionary_tracker,
1429 data_gen,
1430 compression_context: CompressionContext::default(),
1431 })
1432 }
1433
1434 pub fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1436 if self.finished {
1437 return Err(ArrowError::IpcError(
1438 "Cannot write record batch to stream writer as it is closed".to_string(),
1439 ));
1440 }
1441
1442 let (encoded_dictionaries, encoded_message) = self
1443 .data_gen
1444 .encode(
1445 batch,
1446 &mut self.dictionary_tracker,
1447 &self.write_options,
1448 &mut self.compression_context,
1449 )
1450 .expect("StreamWriter is configured to not error on dictionary replacement");
1451
1452 for encoded_dictionary in encoded_dictionaries {
1453 write_message(&mut self.writer, encoded_dictionary, &self.write_options)?;
1454 }
1455
1456 write_message(&mut self.writer, encoded_message, &self.write_options)?;
1457 Ok(())
1458 }
1459
1460 pub fn finish(&mut self) -> Result<(), ArrowError> {
1462 if self.finished {
1463 return Err(ArrowError::IpcError(
1464 "Cannot write footer to stream writer as it is closed".to_string(),
1465 ));
1466 }
1467
1468 write_continuation(&mut self.writer, &self.write_options, 0)?;
1469 self.writer.flush()?;
1470
1471 self.finished = true;
1472
1473 Ok(())
1474 }
1475
1476 pub fn get_ref(&self) -> &W {
1478 &self.writer
1479 }
1480
1481 pub fn get_mut(&mut self) -> &mut W {
1485 &mut self.writer
1486 }
1487
1488 pub fn flush(&mut self) -> Result<(), ArrowError> {
1492 self.writer.flush()?;
1493 Ok(())
1494 }
1495
1496 pub fn into_inner(mut self) -> Result<W, ArrowError> {
1534 if !self.finished {
1535 self.finish()?;
1537 }
1538 Ok(self.writer)
1539 }
1540}
1541
1542impl<W: Write> RecordBatchWriter for StreamWriter<W> {
1543 fn write(&mut self, batch: &RecordBatch) -> Result<(), ArrowError> {
1544 self.write(batch)
1545 }
1546
1547 fn close(mut self) -> Result<(), ArrowError> {
1548 self.finish()
1549 }
1550}
1551
1552pub struct EncodedData {
1554 pub ipc_message: Vec<u8>,
1556 pub arrow_data: Vec<u8>,
1558}
1559pub fn write_message<W: Write>(
1561 mut writer: W,
1562 encoded: EncodedData,
1563 write_options: &IpcWriteOptions,
1564) -> Result<(usize, usize), ArrowError> {
1565 let arrow_data_len = encoded.arrow_data.len();
1566 if arrow_data_len % usize::from(write_options.alignment) != 0 {
1567 return Err(ArrowError::MemoryError(
1568 "Arrow data not aligned".to_string(),
1569 ));
1570 }
1571
1572 let a = usize::from(write_options.alignment - 1);
1573 let buffer = encoded.ipc_message;
1574 let flatbuf_size = buffer.len();
1575 let prefix_size = if write_options.write_legacy_ipc_format {
1576 4
1577 } else {
1578 8
1579 };
1580 let aligned_size = (flatbuf_size + prefix_size + a) & !a;
1581 let padding_bytes = aligned_size - flatbuf_size - prefix_size;
1582
1583 write_continuation(
1584 &mut writer,
1585 write_options,
1586 (aligned_size - prefix_size) as i32,
1587 )?;
1588
1589 if flatbuf_size > 0 {
1591 writer.write_all(&buffer)?;
1592 }
1593 writer.write_all(&PADDING[..padding_bytes])?;
1595
1596 let body_len = if arrow_data_len > 0 {
1598 write_body_buffers(&mut writer, &encoded.arrow_data, write_options.alignment)?
1599 } else {
1600 0
1601 };
1602
1603 Ok((aligned_size, body_len))
1604}
1605
1606fn write_body_buffers<W: Write>(
1607 mut writer: W,
1608 data: &[u8],
1609 alignment: u8,
1610) -> Result<usize, ArrowError> {
1611 let len = data.len();
1612 let pad_len = pad_to_alignment(alignment, len);
1613 let total_len = len + pad_len;
1614
1615 writer.write_all(data)?;
1617 if pad_len > 0 {
1618 writer.write_all(&PADDING[..pad_len])?;
1619 }
1620
1621 Ok(total_len)
1622}
1623
1624fn write_continuation<W: Write>(
1627 mut writer: W,
1628 write_options: &IpcWriteOptions,
1629 total_len: i32,
1630) -> Result<usize, ArrowError> {
1631 let mut written = 8;
1632
1633 match write_options.metadata_version {
1635 crate::MetadataVersion::V1 | crate::MetadataVersion::V2 | crate::MetadataVersion::V3 => {
1636 unreachable!("Options with the metadata version cannot be created")
1637 }
1638 crate::MetadataVersion::V4 => {
1639 if !write_options.write_legacy_ipc_format {
1640 writer.write_all(&CONTINUATION_MARKER)?;
1642 written = 4;
1643 }
1644 writer.write_all(&total_len.to_le_bytes()[..])?;
1645 }
1646 crate::MetadataVersion::V5 => {
1647 writer.write_all(&CONTINUATION_MARKER)?;
1649 writer.write_all(&total_len.to_le_bytes()[..])?;
1650 }
1651 z => panic!("Unsupported crate::MetadataVersion {z:?}"),
1652 };
1653
1654 Ok(written)
1655}
1656
1657fn has_validity_bitmap(data_type: &DataType, write_options: &IpcWriteOptions) -> bool {
1661 if write_options.metadata_version < crate::MetadataVersion::V5 {
1662 !matches!(data_type, DataType::Null)
1663 } else {
1664 !matches!(
1665 data_type,
1666 DataType::Null | DataType::Union(_, _) | DataType::RunEndEncoded(_, _)
1667 )
1668 }
1669}
1670
1671#[inline]
1673fn buffer_need_truncate(
1674 array_offset: usize,
1675 buffer: &Buffer,
1676 spec: &BufferSpec,
1677 min_length: usize,
1678) -> bool {
1679 spec != &BufferSpec::AlwaysNull && (array_offset != 0 || min_length < buffer.len())
1680}
1681
1682#[inline]
1684fn get_buffer_element_width(spec: &BufferSpec) -> usize {
1685 match spec {
1686 BufferSpec::FixedWidth { byte_width, .. } => *byte_width,
1687 _ => 0,
1688 }
1689}
1690
1691fn reencode_offsets<O: OffsetSizeTrait>(
1694 offsets: &Buffer,
1695 data: &ArrayData,
1696) -> (Buffer, usize, usize) {
1697 let offsets_slice: &[O] = offsets.typed_data::<O>();
1698 let offset_slice = &offsets_slice[data.offset()..data.offset() + data.len() + 1];
1699
1700 let start_offset = offset_slice.first().unwrap();
1701 let end_offset = offset_slice.last().unwrap();
1702
1703 let offsets = match start_offset.as_usize() {
1704 0 => {
1705 let size = size_of::<O>();
1706 offsets.slice_with_length(data.offset() * size, (data.len() + 1) * size)
1707 }
1708 _ => offset_slice.iter().map(|x| *x - *start_offset).collect(),
1709 };
1710
1711 let start_offset = start_offset.as_usize();
1712 let end_offset = end_offset.as_usize();
1713
1714 (offsets, start_offset, end_offset - start_offset)
1715}
1716
1717fn get_byte_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, Buffer) {
1723 if data.is_empty() {
1724 let mut offsets = MutableBuffer::new(size_of::<O>());
1727 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
1728 return (offsets.into(), MutableBuffer::new(0).into());
1729 }
1730
1731 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
1732 let values = data.buffers()[1].slice_with_length(original_start_offset, len);
1733 (offsets, values)
1734}
1735
1736fn get_list_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, ArrayData) {
1739 if data.is_empty() {
1740 let mut offsets = MutableBuffer::new(size_of::<O>());
1743 offsets.extend_from_slice(O::usize_as(0).to_byte_slice());
1744 return (offsets.into(), data.child_data()[0].slice(0, 0));
1745 }
1746
1747 let (offsets, original_start_offset, len) = reencode_offsets::<O>(&data.buffers()[0], data);
1748 let child_data = data.child_data()[0].slice(original_start_offset, len);
1749 (offsets, child_data)
1750}
1751
1752fn get_list_view_array_buffers<O: OffsetSizeTrait>(
1758 data: &ArrayData,
1759) -> (Buffer, Buffer, ArrayData) {
1760 if data.is_empty() {
1761 return (
1762 MutableBuffer::new(0).into(),
1763 MutableBuffer::new(0).into(),
1764 data.child_data()[0].slice(0, 0),
1765 );
1766 }
1767
1768 let offsets = &data.buffers()[0];
1769 let sizes = &data.buffers()[1];
1770
1771 let element_size = std::mem::size_of::<O>();
1772 let offsets_slice =
1773 offsets.slice_with_length(data.offset() * element_size, data.len() * element_size);
1774 let sizes_slice =
1775 sizes.slice_with_length(data.offset() * element_size, data.len() * element_size);
1776
1777 let child_data = data.child_data()[0].clone();
1778
1779 (offsets_slice, sizes_slice, child_data)
1780}
1781
1782fn get_or_truncate_buffer(array_data: &ArrayData) -> &[u8] {
1789 let buffer = &array_data.buffers()[0];
1790 let layout = layout(array_data.data_type());
1791 let spec = &layout.buffers[0];
1792
1793 let byte_width = get_buffer_element_width(spec);
1794 let min_length = array_data.len() * byte_width;
1795 if buffer_need_truncate(array_data.offset(), buffer, spec, min_length) {
1796 let byte_offset = array_data.offset() * byte_width;
1797 let buffer_length = min(min_length, buffer.len() - byte_offset);
1798 &buffer.as_slice()[byte_offset..(byte_offset + buffer_length)]
1799 } else {
1800 buffer.as_slice()
1801 }
1802}
1803
1804#[allow(clippy::too_many_arguments)]
1806fn write_array_data(
1807 array_data: &ArrayData,
1808 buffers: &mut Vec<crate::Buffer>,
1809 arrow_data: &mut Vec<u8>,
1810 nodes: &mut Vec<crate::FieldNode>,
1811 offset: i64,
1812 num_rows: usize,
1813 null_count: usize,
1814 compression_codec: Option<CompressionCodec>,
1815 compression_context: &mut CompressionContext,
1816 write_options: &IpcWriteOptions,
1817) -> Result<i64, ArrowError> {
1818 let mut offset = offset;
1819 if !matches!(array_data.data_type(), DataType::Null) {
1820 nodes.push(crate::FieldNode::new(num_rows as i64, null_count as i64));
1821 } else {
1822 nodes.push(crate::FieldNode::new(num_rows as i64, num_rows as i64));
1825 }
1826 if has_validity_bitmap(array_data.data_type(), write_options) {
1827 let null_buffer = match array_data.nulls() {
1830 Some(buffer) if null_count > 0 => buffer.inner().sliced(),
1831 _ => MutableBuffer::new(0).into(),
1832 };
1833
1834 offset = write_buffer(
1835 null_buffer.as_slice(),
1836 buffers,
1837 arrow_data,
1838 offset,
1839 compression_codec,
1840 compression_context,
1841 write_options.alignment,
1842 )?;
1843 }
1844
1845 let data_type = array_data.data_type();
1846 if matches!(data_type, DataType::Binary | DataType::Utf8) {
1847 let (offsets, values) = get_byte_array_buffers::<i32>(array_data);
1848 for buffer in [offsets, values] {
1849 offset = write_buffer(
1850 buffer.as_slice(),
1851 buffers,
1852 arrow_data,
1853 offset,
1854 compression_codec,
1855 compression_context,
1856 write_options.alignment,
1857 )?;
1858 }
1859 } else if matches!(data_type, DataType::BinaryView | DataType::Utf8View) {
1860 let views = get_or_truncate_buffer(array_data);
1867 offset = write_buffer(
1868 views,
1869 buffers,
1870 arrow_data,
1871 offset,
1872 compression_codec,
1873 compression_context,
1874 write_options.alignment,
1875 )?;
1876
1877 for buffer in array_data.buffers().iter().skip(1) {
1878 offset = write_buffer(
1879 buffer.as_slice(),
1880 buffers,
1881 arrow_data,
1882 offset,
1883 compression_codec,
1884 compression_context,
1885 write_options.alignment,
1886 )?;
1887 }
1888 } else if matches!(data_type, DataType::LargeBinary | DataType::LargeUtf8) {
1889 let (offsets, values) = get_byte_array_buffers::<i64>(array_data);
1890 for buffer in [offsets, values] {
1891 offset = write_buffer(
1892 buffer.as_slice(),
1893 buffers,
1894 arrow_data,
1895 offset,
1896 compression_codec,
1897 compression_context,
1898 write_options.alignment,
1899 )?;
1900 }
1901 } else if DataType::is_numeric(data_type)
1902 || DataType::is_temporal(data_type)
1903 || matches!(
1904 array_data.data_type(),
1905 DataType::FixedSizeBinary(_) | DataType::Dictionary(_, _)
1906 )
1907 {
1908 assert_eq!(array_data.buffers().len(), 1);
1910
1911 let buffer = get_or_truncate_buffer(array_data);
1912 offset = write_buffer(
1913 buffer,
1914 buffers,
1915 arrow_data,
1916 offset,
1917 compression_codec,
1918 compression_context,
1919 write_options.alignment,
1920 )?;
1921 } else if matches!(data_type, DataType::Boolean) {
1922 assert_eq!(array_data.buffers().len(), 1);
1925
1926 let buffer = &array_data.buffers()[0];
1927 let buffer = buffer.bit_slice(array_data.offset(), array_data.len());
1928 offset = write_buffer(
1929 &buffer,
1930 buffers,
1931 arrow_data,
1932 offset,
1933 compression_codec,
1934 compression_context,
1935 write_options.alignment,
1936 )?;
1937 } else if matches!(
1938 data_type,
1939 DataType::List(_) | DataType::LargeList(_) | DataType::Map(_, _)
1940 ) {
1941 assert_eq!(array_data.buffers().len(), 1);
1942 assert_eq!(array_data.child_data().len(), 1);
1943
1944 let (offsets, sliced_child_data) = match data_type {
1946 DataType::List(_) => get_list_array_buffers::<i32>(array_data),
1947 DataType::Map(_, _) => get_list_array_buffers::<i32>(array_data),
1948 DataType::LargeList(_) => get_list_array_buffers::<i64>(array_data),
1949 _ => unreachable!(),
1950 };
1951 offset = write_buffer(
1952 offsets.as_slice(),
1953 buffers,
1954 arrow_data,
1955 offset,
1956 compression_codec,
1957 compression_context,
1958 write_options.alignment,
1959 )?;
1960 offset = write_array_data(
1961 &sliced_child_data,
1962 buffers,
1963 arrow_data,
1964 nodes,
1965 offset,
1966 sliced_child_data.len(),
1967 sliced_child_data.null_count(),
1968 compression_codec,
1969 compression_context,
1970 write_options,
1971 )?;
1972 return Ok(offset);
1973 } else if matches!(
1974 data_type,
1975 DataType::ListView(_) | DataType::LargeListView(_)
1976 ) {
1977 assert_eq!(array_data.buffers().len(), 2); assert_eq!(array_data.child_data().len(), 1);
1979
1980 let (offsets, sizes, child_data) = match data_type {
1981 DataType::ListView(_) => get_list_view_array_buffers::<i32>(array_data),
1982 DataType::LargeListView(_) => get_list_view_array_buffers::<i64>(array_data),
1983 _ => unreachable!(),
1984 };
1985
1986 offset = write_buffer(
1987 offsets.as_slice(),
1988 buffers,
1989 arrow_data,
1990 offset,
1991 compression_codec,
1992 compression_context,
1993 write_options.alignment,
1994 )?;
1995
1996 offset = write_buffer(
1997 sizes.as_slice(),
1998 buffers,
1999 arrow_data,
2000 offset,
2001 compression_codec,
2002 compression_context,
2003 write_options.alignment,
2004 )?;
2005
2006 offset = write_array_data(
2007 &child_data,
2008 buffers,
2009 arrow_data,
2010 nodes,
2011 offset,
2012 child_data.len(),
2013 child_data.null_count(),
2014 compression_codec,
2015 compression_context,
2016 write_options,
2017 )?;
2018 return Ok(offset);
2019 } else if let DataType::FixedSizeList(_, fixed_size) = data_type {
2020 assert_eq!(array_data.child_data().len(), 1);
2021 let fixed_size = *fixed_size as usize;
2022
2023 let child_offset = array_data.offset() * fixed_size;
2024 let child_length = array_data.len() * fixed_size;
2025 let child_data = array_data.child_data()[0].slice(child_offset, child_length);
2026
2027 offset = write_array_data(
2028 &child_data,
2029 buffers,
2030 arrow_data,
2031 nodes,
2032 offset,
2033 child_data.len(),
2034 child_data.null_count(),
2035 compression_codec,
2036 compression_context,
2037 write_options,
2038 )?;
2039 return Ok(offset);
2040 } else {
2041 for buffer in array_data.buffers() {
2042 offset = write_buffer(
2043 buffer,
2044 buffers,
2045 arrow_data,
2046 offset,
2047 compression_codec,
2048 compression_context,
2049 write_options.alignment,
2050 )?;
2051 }
2052 }
2053
2054 match array_data.data_type() {
2055 DataType::Dictionary(_, _) => {}
2056 DataType::RunEndEncoded(_, _) => {
2057 let arr = unslice_run_array(array_data.clone())?;
2059 for data_ref in arr.child_data() {
2061 offset = write_array_data(
2063 data_ref,
2064 buffers,
2065 arrow_data,
2066 nodes,
2067 offset,
2068 data_ref.len(),
2069 data_ref.null_count(),
2070 compression_codec,
2071 compression_context,
2072 write_options,
2073 )?;
2074 }
2075 }
2076 _ => {
2077 for data_ref in array_data.child_data() {
2079 offset = write_array_data(
2081 data_ref,
2082 buffers,
2083 arrow_data,
2084 nodes,
2085 offset,
2086 data_ref.len(),
2087 data_ref.null_count(),
2088 compression_codec,
2089 compression_context,
2090 write_options,
2091 )?;
2092 }
2093 }
2094 }
2095 Ok(offset)
2096}
2097
2098fn write_buffer(
2111 buffer: &[u8], buffers: &mut Vec<crate::Buffer>, arrow_data: &mut Vec<u8>, offset: i64, compression_codec: Option<CompressionCodec>,
2116 compression_context: &mut CompressionContext,
2117 alignment: u8,
2118) -> Result<i64, ArrowError> {
2119 let len: i64 = match compression_codec {
2120 Some(compressor) => compressor.compress_to_vec(buffer, arrow_data, compression_context)?,
2121 None => {
2122 arrow_data.extend_from_slice(buffer);
2123 buffer.len()
2124 }
2125 }
2126 .try_into()
2127 .map_err(|e| {
2128 ArrowError::InvalidArgumentError(format!("Could not convert compressed size to i64: {e}"))
2129 })?;
2130
2131 buffers.push(crate::Buffer::new(offset, len));
2133 let pad_len = pad_to_alignment(alignment, len as usize);
2135 arrow_data.extend_from_slice(&PADDING[..pad_len]);
2136
2137 Ok(offset + len + (pad_len as i64))
2138}
2139
2140const PADDING: [u8; 64] = [0; 64];
2141
2142#[inline]
2144fn pad_to_alignment(alignment: u8, len: usize) -> usize {
2145 let a = usize::from(alignment - 1);
2146 ((len + a) & !a) - len
2147}
2148
2149#[cfg(test)]
2150mod tests {
2151 use std::hash::Hasher;
2152 use std::io::Cursor;
2153 use std::io::Seek;
2154
2155 use arrow_array::builder::FixedSizeListBuilder;
2156 use arrow_array::builder::Float32Builder;
2157 use arrow_array::builder::Int64Builder;
2158 use arrow_array::builder::MapBuilder;
2159 use arrow_array::builder::StringViewBuilder;
2160 use arrow_array::builder::UnionBuilder;
2161 use arrow_array::builder::{
2162 GenericListBuilder, GenericListViewBuilder, ListBuilder, StringBuilder,
2163 };
2164 use arrow_array::builder::{PrimitiveRunBuilder, UInt32Builder};
2165 use arrow_array::types::*;
2166 use arrow_buffer::ScalarBuffer;
2167
2168 use crate::MetadataVersion;
2169 use crate::convert::fb_to_schema;
2170 use crate::reader::*;
2171 use crate::root_as_footer;
2172
2173 use super::*;
2174
2175 fn serialize_file(rb: &RecordBatch) -> Vec<u8> {
2176 let mut writer = FileWriter::try_new(vec![], rb.schema_ref()).unwrap();
2177 writer.write(rb).unwrap();
2178 writer.finish().unwrap();
2179 writer.into_inner().unwrap()
2180 }
2181
2182 fn deserialize_file(bytes: Vec<u8>) -> RecordBatch {
2183 let mut reader = FileReader::try_new(Cursor::new(bytes), None).unwrap();
2184 reader.next().unwrap().unwrap()
2185 }
2186
2187 fn serialize_stream(record: &RecordBatch) -> Vec<u8> {
2188 const IPC_ALIGNMENT: usize = 8;
2192
2193 let mut stream_writer = StreamWriter::try_new_with_options(
2194 vec![],
2195 record.schema_ref(),
2196 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
2197 )
2198 .unwrap();
2199 stream_writer.write(record).unwrap();
2200 stream_writer.finish().unwrap();
2201 stream_writer.into_inner().unwrap()
2202 }
2203
2204 fn deserialize_stream(bytes: Vec<u8>) -> RecordBatch {
2205 let mut stream_reader = StreamReader::try_new(Cursor::new(bytes), None).unwrap();
2206 stream_reader.next().unwrap().unwrap()
2207 }
2208
2209 #[test]
2210 #[cfg(feature = "lz4")]
2211 fn test_write_empty_record_batch_lz4_compression() {
2212 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
2213 let values: Vec<Option<i32>> = vec![];
2214 let array = Int32Array::from(values);
2215 let record_batch =
2216 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
2217
2218 let mut file = tempfile::tempfile().unwrap();
2219
2220 {
2221 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
2222 .unwrap()
2223 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
2224 .unwrap();
2225
2226 let mut writer =
2227 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
2228 writer.write(&record_batch).unwrap();
2229 writer.finish().unwrap();
2230 }
2231 file.rewind().unwrap();
2232 {
2233 let reader = FileReader::try_new(file, None).unwrap();
2235 for read_batch in reader {
2236 read_batch
2237 .unwrap()
2238 .columns()
2239 .iter()
2240 .zip(record_batch.columns())
2241 .for_each(|(a, b)| {
2242 assert_eq!(a.data_type(), b.data_type());
2243 assert_eq!(a.len(), b.len());
2244 assert_eq!(a.null_count(), b.null_count());
2245 });
2246 }
2247 }
2248 }
2249
2250 #[test]
2251 #[cfg(feature = "lz4")]
2252 fn test_write_file_with_lz4_compression() {
2253 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
2254 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
2255 let array = Int32Array::from(values);
2256 let record_batch =
2257 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
2258
2259 let mut file = tempfile::tempfile().unwrap();
2260 {
2261 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
2262 .unwrap()
2263 .try_with_compression(Some(crate::CompressionType::LZ4_FRAME))
2264 .unwrap();
2265
2266 let mut writer =
2267 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
2268 writer.write(&record_batch).unwrap();
2269 writer.finish().unwrap();
2270 }
2271 file.rewind().unwrap();
2272 {
2273 let reader = FileReader::try_new(file, None).unwrap();
2275 for read_batch in reader {
2276 read_batch
2277 .unwrap()
2278 .columns()
2279 .iter()
2280 .zip(record_batch.columns())
2281 .for_each(|(a, b)| {
2282 assert_eq!(a.data_type(), b.data_type());
2283 assert_eq!(a.len(), b.len());
2284 assert_eq!(a.null_count(), b.null_count());
2285 });
2286 }
2287 }
2288 }
2289
2290 #[test]
2291 #[cfg(feature = "zstd")]
2292 fn test_write_file_with_zstd_compression() {
2293 let schema = Schema::new(vec![Field::new("field1", DataType::Int32, true)]);
2294 let values: Vec<Option<i32>> = vec![Some(12), Some(1)];
2295 let array = Int32Array::from(values);
2296 let record_batch =
2297 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array)]).unwrap();
2298 let mut file = tempfile::tempfile().unwrap();
2299 {
2300 let write_option = IpcWriteOptions::try_new(8, false, crate::MetadataVersion::V5)
2301 .unwrap()
2302 .try_with_compression(Some(crate::CompressionType::ZSTD))
2303 .unwrap();
2304
2305 let mut writer =
2306 FileWriter::try_new_with_options(&mut file, &schema, write_option).unwrap();
2307 writer.write(&record_batch).unwrap();
2308 writer.finish().unwrap();
2309 }
2310 file.rewind().unwrap();
2311 {
2312 let reader = FileReader::try_new(file, None).unwrap();
2314 for read_batch in reader {
2315 read_batch
2316 .unwrap()
2317 .columns()
2318 .iter()
2319 .zip(record_batch.columns())
2320 .for_each(|(a, b)| {
2321 assert_eq!(a.data_type(), b.data_type());
2322 assert_eq!(a.len(), b.len());
2323 assert_eq!(a.null_count(), b.null_count());
2324 });
2325 }
2326 }
2327 }
2328
2329 #[test]
2330 fn test_write_file() {
2331 let schema = Schema::new(vec![Field::new("field1", DataType::UInt32, true)]);
2332 let values: Vec<Option<u32>> = vec![
2333 Some(999),
2334 None,
2335 Some(235),
2336 Some(123),
2337 None,
2338 None,
2339 None,
2340 None,
2341 None,
2342 ];
2343 let array1 = UInt32Array::from(values);
2344 let batch =
2345 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(array1) as ArrayRef])
2346 .unwrap();
2347 let mut file = tempfile::tempfile().unwrap();
2348 {
2349 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
2350
2351 writer.write(&batch).unwrap();
2352 writer.finish().unwrap();
2353 }
2354 file.rewind().unwrap();
2355
2356 {
2357 let mut reader = FileReader::try_new(file, None).unwrap();
2358 while let Some(Ok(read_batch)) = reader.next() {
2359 read_batch
2360 .columns()
2361 .iter()
2362 .zip(batch.columns())
2363 .for_each(|(a, b)| {
2364 assert_eq!(a.data_type(), b.data_type());
2365 assert_eq!(a.len(), b.len());
2366 assert_eq!(a.null_count(), b.null_count());
2367 });
2368 }
2369 }
2370 }
2371
2372 #[test]
2373 fn test_empty_utf8_ipc_writes_nonempty_offsets_buffer() {
2374 let name = StringArray::from(Vec::<String>::new());
2375 let (offsets, values) = get_byte_array_buffers::<i32>(&name.to_data());
2376
2377 assert_eq!(name.len(), 0);
2378 assert_eq!(
2379 offsets.len(),
2380 std::mem::size_of::<i32>(),
2381 "offsets buffer should contain one zero i32 offset"
2382 );
2383 assert_eq!(values.len(), 0, "values buffer should remain empty");
2384 }
2385
2386 #[test]
2387 fn test_empty_large_utf8_ipc_writes_nonempty_offsets_buffer() {
2388 let name = LargeStringArray::from(Vec::<String>::new());
2389 let (offsets, values) = get_byte_array_buffers::<i64>(&name.to_data());
2390
2391 assert_eq!(name.len(), 0);
2392 assert_eq!(
2393 offsets.len(),
2394 std::mem::size_of::<i64>(),
2395 "offsets buffer should contain one zero i64 offset"
2396 );
2397 assert_eq!(values.len(), 0, "values buffer should remain empty");
2398 }
2399
2400 #[test]
2401 fn test_empty_list_ipc_writes_nonempty_offsets_buffer() {
2402 let list = GenericListBuilder::<i32, _>::new(UInt32Builder::new()).finish();
2403 let (offsets, child_data) = get_list_array_buffers::<i32>(&list.to_data());
2404
2405 assert_eq!(list.len(), 0);
2406 assert_eq!(
2407 offsets.len(),
2408 std::mem::size_of::<i32>(),
2409 "offsets buffer should contain one zero i32 offset"
2410 );
2411 assert_eq!(child_data.len(), 0, "child data should remain empty");
2412 }
2413
2414 #[test]
2415 fn test_empty_large_list_ipc_writes_nonempty_offsets_buffer() {
2416 let list = GenericListBuilder::<i64, _>::new(UInt32Builder::new()).finish();
2417 let (offsets, child_data) = get_list_array_buffers::<i64>(&list.to_data());
2418
2419 assert_eq!(list.len(), 0);
2420 assert_eq!(
2421 offsets.len(),
2422 std::mem::size_of::<i64>(),
2423 "offsets buffer should contain one zero i64 offset"
2424 );
2425 assert_eq!(child_data.len(), 0, "child data should remain empty");
2426 }
2427
2428 fn write_null_file(options: IpcWriteOptions) {
2429 let schema = Schema::new(vec![
2430 Field::new("nulls", DataType::Null, true),
2431 Field::new("int32s", DataType::Int32, false),
2432 Field::new("nulls2", DataType::Null, true),
2433 Field::new("f64s", DataType::Float64, false),
2434 ]);
2435 let array1 = NullArray::new(32);
2436 let array2 = Int32Array::from(vec![1; 32]);
2437 let array3 = NullArray::new(32);
2438 let array4 = Float64Array::from(vec![f64::NAN; 32]);
2439 let batch = RecordBatch::try_new(
2440 Arc::new(schema.clone()),
2441 vec![
2442 Arc::new(array1) as ArrayRef,
2443 Arc::new(array2) as ArrayRef,
2444 Arc::new(array3) as ArrayRef,
2445 Arc::new(array4) as ArrayRef,
2446 ],
2447 )
2448 .unwrap();
2449 let mut file = tempfile::tempfile().unwrap();
2450 {
2451 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
2452
2453 writer.write(&batch).unwrap();
2454 writer.finish().unwrap();
2455 }
2456
2457 file.rewind().unwrap();
2458
2459 {
2460 let reader = FileReader::try_new(file, None).unwrap();
2461 reader.for_each(|maybe_batch| {
2462 maybe_batch
2463 .unwrap()
2464 .columns()
2465 .iter()
2466 .zip(batch.columns())
2467 .for_each(|(a, b)| {
2468 assert_eq!(a.data_type(), b.data_type());
2469 assert_eq!(a.len(), b.len());
2470 assert_eq!(a.null_count(), b.null_count());
2471 });
2472 });
2473 }
2474 }
2475 #[test]
2476 fn test_write_null_file_v4() {
2477 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
2478 write_null_file(IpcWriteOptions::try_new(8, true, MetadataVersion::V4).unwrap());
2479 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V4).unwrap());
2480 write_null_file(IpcWriteOptions::try_new(64, true, MetadataVersion::V4).unwrap());
2481 }
2482
2483 #[test]
2484 fn test_write_null_file_v5() {
2485 write_null_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
2486 write_null_file(IpcWriteOptions::try_new(64, false, MetadataVersion::V5).unwrap());
2487 }
2488
2489 #[test]
2490 fn track_union_nested_dict() {
2491 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
2492
2493 let array = Arc::new(inner) as ArrayRef;
2494
2495 #[allow(deprecated)]
2497 let dctfield = Field::new_dict("dict", array.data_type().clone(), false, 0, false);
2498 let union_fields = [(0, Arc::new(dctfield))].into_iter().collect();
2499
2500 let types = [0, 0, 0].into_iter().collect::<ScalarBuffer<i8>>();
2501 let offsets = [0, 1, 2].into_iter().collect::<ScalarBuffer<i32>>();
2502
2503 let union = UnionArray::try_new(union_fields, types, Some(offsets), vec![array]).unwrap();
2504
2505 let schema = Arc::new(Schema::new(vec![Field::new(
2506 "union",
2507 union.data_type().clone(),
2508 false,
2509 )]));
2510
2511 let r#gen = IpcDataGenerator::default();
2512 let mut dict_tracker = DictionaryTracker::new(false);
2513 r#gen.schema_to_bytes_with_dictionary_tracker(
2514 &schema,
2515 &mut dict_tracker,
2516 &IpcWriteOptions::default(),
2517 );
2518
2519 let batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
2520
2521 r#gen
2522 .encode(
2523 &batch,
2524 &mut dict_tracker,
2525 &Default::default(),
2526 &mut Default::default(),
2527 )
2528 .unwrap();
2529
2530 assert!(dict_tracker.written.contains_key(&0));
2533 }
2534
2535 #[test]
2536 fn track_struct_nested_dict() {
2537 let inner: DictionaryArray<Int32Type> = vec!["a", "b", "a"].into_iter().collect();
2538
2539 let array = Arc::new(inner) as ArrayRef;
2540
2541 #[allow(deprecated)]
2543 let dctfield = Arc::new(Field::new_dict(
2544 "dict",
2545 array.data_type().clone(),
2546 false,
2547 2,
2548 false,
2549 ));
2550
2551 let s = StructArray::from(vec![(dctfield, array)]);
2552 let struct_array = Arc::new(s) as ArrayRef;
2553
2554 let schema = Arc::new(Schema::new(vec![Field::new(
2555 "struct",
2556 struct_array.data_type().clone(),
2557 false,
2558 )]));
2559
2560 let r#gen = IpcDataGenerator::default();
2561 let mut dict_tracker = DictionaryTracker::new(false);
2562 r#gen.schema_to_bytes_with_dictionary_tracker(
2563 &schema,
2564 &mut dict_tracker,
2565 &IpcWriteOptions::default(),
2566 );
2567
2568 let batch = RecordBatch::try_new(schema, vec![struct_array]).unwrap();
2569
2570 r#gen
2571 .encode(
2572 &batch,
2573 &mut dict_tracker,
2574 &Default::default(),
2575 &mut Default::default(),
2576 )
2577 .unwrap();
2578
2579 assert!(dict_tracker.written.contains_key(&0));
2580 }
2581
2582 fn write_union_file(options: IpcWriteOptions) {
2583 let schema = Schema::new(vec![Field::new_union(
2584 "union",
2585 vec![0, 1],
2586 vec![
2587 Field::new("a", DataType::Int32, false),
2588 Field::new("c", DataType::Float64, false),
2589 ],
2590 UnionMode::Sparse,
2591 )]);
2592 let mut builder = UnionBuilder::with_capacity_sparse(5);
2593 builder.append::<Int32Type>("a", 1).unwrap();
2594 builder.append_null::<Int32Type>("a").unwrap();
2595 builder.append::<Float64Type>("c", 3.0).unwrap();
2596 builder.append_null::<Float64Type>("c").unwrap();
2597 builder.append::<Int32Type>("a", 4).unwrap();
2598 let union = builder.build().unwrap();
2599
2600 let batch =
2601 RecordBatch::try_new(Arc::new(schema.clone()), vec![Arc::new(union) as ArrayRef])
2602 .unwrap();
2603
2604 let mut file = tempfile::tempfile().unwrap();
2605 {
2606 let mut writer = FileWriter::try_new_with_options(&mut file, &schema, options).unwrap();
2607
2608 writer.write(&batch).unwrap();
2609 writer.finish().unwrap();
2610 }
2611 file.rewind().unwrap();
2612
2613 {
2614 let reader = FileReader::try_new(file, None).unwrap();
2615 reader.for_each(|maybe_batch| {
2616 maybe_batch
2617 .unwrap()
2618 .columns()
2619 .iter()
2620 .zip(batch.columns())
2621 .for_each(|(a, b)| {
2622 assert_eq!(a.data_type(), b.data_type());
2623 assert_eq!(a.len(), b.len());
2624 assert_eq!(a.null_count(), b.null_count());
2625 });
2626 });
2627 }
2628 }
2629
2630 #[test]
2631 fn test_write_union_file_v4_v5() {
2632 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V4).unwrap());
2633 write_union_file(IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap());
2634 }
2635
2636 #[test]
2637 fn test_write_view_types() {
2638 const LONG_TEST_STRING: &str =
2639 "This is a long string to make sure binary view array handles it";
2640 let schema = Schema::new(vec![
2641 Field::new("field1", DataType::BinaryView, true),
2642 Field::new("field2", DataType::Utf8View, true),
2643 ]);
2644 let values: Vec<Option<&[u8]>> = vec![
2645 Some(b"foo"),
2646 Some(b"bar"),
2647 Some(LONG_TEST_STRING.as_bytes()),
2648 ];
2649 let binary_array = BinaryViewArray::from_iter(values);
2650 let utf8_array =
2651 StringViewArray::from_iter(vec![Some("foo"), Some("bar"), Some(LONG_TEST_STRING)]);
2652 let record_batch = RecordBatch::try_new(
2653 Arc::new(schema.clone()),
2654 vec![Arc::new(binary_array), Arc::new(utf8_array)],
2655 )
2656 .unwrap();
2657
2658 let mut file = tempfile::tempfile().unwrap();
2659 {
2660 let mut writer = FileWriter::try_new(&mut file, &schema).unwrap();
2661 writer.write(&record_batch).unwrap();
2662 writer.finish().unwrap();
2663 }
2664 file.rewind().unwrap();
2665 {
2666 let mut reader = FileReader::try_new(&file, None).unwrap();
2667 let read_batch = reader.next().unwrap().unwrap();
2668 read_batch
2669 .columns()
2670 .iter()
2671 .zip(record_batch.columns())
2672 .for_each(|(a, b)| {
2673 assert_eq!(a, b);
2674 });
2675 }
2676 file.rewind().unwrap();
2677 {
2678 let mut reader = FileReader::try_new(&file, Some(vec![0])).unwrap();
2679 let read_batch = reader.next().unwrap().unwrap();
2680 assert_eq!(read_batch.num_columns(), 1);
2681 let read_array = read_batch.column(0);
2682 let write_array = record_batch.column(0);
2683 assert_eq!(read_array, write_array);
2684 }
2685 }
2686
2687 #[test]
2688 fn truncate_ipc_record_batch() {
2689 fn create_batch(rows: usize) -> RecordBatch {
2690 let schema = Schema::new(vec![
2691 Field::new("a", DataType::Int32, false),
2692 Field::new("b", DataType::Utf8, false),
2693 ]);
2694
2695 let a = Int32Array::from_iter_values(0..rows as i32);
2696 let b = StringArray::from_iter_values((0..rows).map(|i| i.to_string()));
2697
2698 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
2699 }
2700
2701 let big_record_batch = create_batch(65536);
2702
2703 let length = 5;
2704 let small_record_batch = create_batch(length);
2705
2706 let offset = 2;
2707 let record_batch_slice = big_record_batch.slice(offset, length);
2708 assert!(
2709 serialize_stream(&big_record_batch).len() > serialize_stream(&small_record_batch).len()
2710 );
2711 assert_eq!(
2712 serialize_stream(&small_record_batch).len(),
2713 serialize_stream(&record_batch_slice).len()
2714 );
2715
2716 assert_eq!(
2717 deserialize_stream(serialize_stream(&record_batch_slice)),
2718 record_batch_slice
2719 );
2720 }
2721
2722 #[test]
2723 fn truncate_ipc_record_batch_with_nulls() {
2724 fn create_batch() -> RecordBatch {
2725 let schema = Schema::new(vec![
2726 Field::new("a", DataType::Int32, true),
2727 Field::new("b", DataType::Utf8, true),
2728 ]);
2729
2730 let a = Int32Array::from(vec![Some(1), None, Some(1), None, Some(1)]);
2731 let b = StringArray::from(vec![None, Some("a"), Some("a"), None, Some("a")]);
2732
2733 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a), Arc::new(b)]).unwrap()
2734 }
2735
2736 let record_batch = create_batch();
2737 let record_batch_slice = record_batch.slice(1, 2);
2738 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
2739
2740 assert!(
2741 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
2742 );
2743
2744 assert!(deserialized_batch.column(0).is_null(0));
2745 assert!(deserialized_batch.column(0).is_valid(1));
2746 assert!(deserialized_batch.column(1).is_valid(0));
2747 assert!(deserialized_batch.column(1).is_valid(1));
2748
2749 assert_eq!(record_batch_slice, deserialized_batch);
2750 }
2751
2752 #[test]
2753 fn test_write_empty_validity_buffer_without_nulls() {
2754 let batch = RecordBatch::try_from_iter([
2755 ("a", Arc::new(Int32Array::from(vec![1, 2])) as _),
2756 (
2758 "b",
2759 Arc::new(Int32Array::from(vec![None, Some(2), Some(3)]).slice(1, 2)) as _,
2760 ),
2761 ("c", Arc::new(Int32Array::from(vec![Some(1), None])) as _),
2762 ])
2763 .unwrap();
2764 assert_eq!(batch.column(1).nulls().map(|n| n.null_count()), Some(0));
2765
2766 let (_, encoded) = IpcDataGenerator::default()
2767 .encode(
2768 &batch,
2769 &mut DictionaryTracker::new(false),
2770 &Default::default(),
2771 &mut Default::default(),
2772 )
2773 .unwrap();
2774 let message = crate::root_as_message(&encoded.ipc_message).unwrap();
2775 let buffers = message.header_as_record_batch().unwrap().buffers().unwrap();
2776 let validity_lengths: Vec<_> = buffers.iter().step_by(2).map(|b| b.length()).collect();
2778 assert_eq!(validity_lengths, [0, 0, 1]);
2779
2780 assert_eq!(deserialize_stream(serialize_stream(&batch)), batch);
2781 }
2782
2783 #[test]
2784 fn truncate_ipc_dictionary_array() {
2785 fn create_batch() -> RecordBatch {
2786 let values: StringArray = [Some("foo"), Some("bar"), Some("baz")]
2787 .into_iter()
2788 .collect();
2789 let keys: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
2790
2791 let array = DictionaryArray::new(keys, Arc::new(values));
2792
2793 let schema = Schema::new(vec![Field::new("dict", array.data_type().clone(), true)]);
2794
2795 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(array)]).unwrap()
2796 }
2797
2798 let record_batch = create_batch();
2799 let record_batch_slice = record_batch.slice(1, 2);
2800 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
2801
2802 assert!(
2803 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
2804 );
2805
2806 assert!(deserialized_batch.column(0).is_valid(0));
2807 assert!(deserialized_batch.column(0).is_null(1));
2808
2809 assert_eq!(record_batch_slice, deserialized_batch);
2810 }
2811
2812 #[test]
2813 fn truncate_ipc_struct_array() {
2814 fn create_batch() -> RecordBatch {
2815 let strings: StringArray = [Some("foo"), None, Some("bar"), Some("baz")]
2816 .into_iter()
2817 .collect();
2818 let ints: Int32Array = [Some(0), Some(2), None, Some(1)].into_iter().collect();
2819
2820 let struct_array = StructArray::from(vec![
2821 (
2822 Arc::new(Field::new("s", DataType::Utf8, true)),
2823 Arc::new(strings) as ArrayRef,
2824 ),
2825 (
2826 Arc::new(Field::new("c", DataType::Int32, true)),
2827 Arc::new(ints) as ArrayRef,
2828 ),
2829 ]);
2830
2831 let schema = Schema::new(vec![Field::new(
2832 "struct_array",
2833 struct_array.data_type().clone(),
2834 true,
2835 )]);
2836
2837 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(struct_array)]).unwrap()
2838 }
2839
2840 let record_batch = create_batch();
2841 let record_batch_slice = record_batch.slice(1, 2);
2842 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
2843
2844 assert!(
2845 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
2846 );
2847
2848 let structs = deserialized_batch
2849 .column(0)
2850 .as_any()
2851 .downcast_ref::<StructArray>()
2852 .unwrap();
2853
2854 assert!(structs.column(0).is_null(0));
2855 assert!(structs.column(0).is_valid(1));
2856 assert!(structs.column(1).is_valid(0));
2857 assert!(structs.column(1).is_null(1));
2858 assert_eq!(record_batch_slice, deserialized_batch);
2859 }
2860
2861 #[test]
2862 fn truncate_ipc_string_array_with_all_empty_string() {
2863 fn create_batch() -> RecordBatch {
2864 let schema = Schema::new(vec![Field::new("a", DataType::Utf8, true)]);
2865 let a = StringArray::from(vec![Some(""), Some(""), Some(""), Some(""), Some("")]);
2866 RecordBatch::try_new(Arc::new(schema), vec![Arc::new(a)]).unwrap()
2867 }
2868
2869 let record_batch = create_batch();
2870 let record_batch_slice = record_batch.slice(0, 1);
2871 let deserialized_batch = deserialize_stream(serialize_stream(&record_batch_slice));
2872
2873 assert!(
2874 serialize_stream(&record_batch).len() > serialize_stream(&record_batch_slice).len()
2875 );
2876 assert_eq!(record_batch_slice, deserialized_batch);
2877 }
2878
2879 #[test]
2880 fn test_stream_writer_writes_array_slice() {
2881 let array = UInt32Array::from(vec![Some(1), Some(2), Some(3)]);
2882 assert_eq!(
2883 vec![Some(1), Some(2), Some(3)],
2884 array.iter().collect::<Vec<_>>()
2885 );
2886
2887 let sliced = array.slice(1, 2);
2888 assert_eq!(vec![Some(2), Some(3)], sliced.iter().collect::<Vec<_>>());
2889
2890 let batch = RecordBatch::try_new(
2891 Arc::new(Schema::new(vec![Field::new("a", DataType::UInt32, true)])),
2892 vec![Arc::new(sliced)],
2893 )
2894 .expect("new batch");
2895
2896 let mut writer = StreamWriter::try_new(vec![], batch.schema_ref()).expect("new writer");
2897 writer.write(&batch).expect("write");
2898 let outbuf = writer.into_inner().expect("inner");
2899
2900 let mut reader = StreamReader::try_new(&outbuf[..], None).expect("new reader");
2901 let read_batch = reader.next().unwrap().expect("read batch");
2902
2903 let read_array: &UInt32Array = read_batch.column(0).as_primitive();
2904 assert_eq!(
2905 vec![Some(2), Some(3)],
2906 read_array.iter().collect::<Vec<_>>()
2907 );
2908 }
2909
2910 #[test]
2911 fn test_large_slice_uint32() {
2912 ensure_roundtrip(Arc::new(UInt32Array::from_iter(
2913 (0..8000).map(|i| if i % 2 == 0 { Some(i) } else { None }),
2914 )));
2915 }
2916
2917 #[test]
2918 fn test_large_slice_string() {
2919 let strings: Vec<_> = (0..8000)
2920 .map(|i| {
2921 if i % 2 == 0 {
2922 Some(format!("value{i}"))
2923 } else {
2924 None
2925 }
2926 })
2927 .collect();
2928
2929 ensure_roundtrip(Arc::new(StringArray::from(strings)));
2930 }
2931
2932 #[test]
2933 fn test_large_slice_string_list() {
2934 let mut ls = ListBuilder::new(StringBuilder::new());
2935
2936 let mut s = String::new();
2937 for row_number in 0..8000 {
2938 if row_number % 2 == 0 {
2939 for list_element in 0..1000 {
2940 s.clear();
2941 use std::fmt::Write;
2942 write!(&mut s, "value{row_number}-{list_element}").unwrap();
2943 ls.values().append_value(&s);
2944 }
2945 ls.append(true)
2946 } else {
2947 ls.append(false); }
2949 }
2950
2951 ensure_roundtrip(Arc::new(ls.finish()));
2952 }
2953
2954 #[test]
2955 fn test_large_slice_string_list_of_lists() {
2956 let mut ls = ListBuilder::new(ListBuilder::new(StringBuilder::new()));
2960
2961 for _ in 0..4000 {
2962 ls.values().append(true);
2963 ls.append(true)
2964 }
2965
2966 let mut s = String::new();
2967 for row_number in 0..4000 {
2968 if row_number % 2 == 0 {
2969 for list_element in 0..1000 {
2970 s.clear();
2971 use std::fmt::Write;
2972 write!(&mut s, "value{row_number}-{list_element}").unwrap();
2973 ls.values().values().append_value(&s);
2974 }
2975 ls.values().append(true);
2976 ls.append(true)
2977 } else {
2978 ls.append(false); }
2980 }
2981
2982 ensure_roundtrip(Arc::new(ls.finish()));
2983 }
2984
2985 fn ensure_roundtrip(array: ArrayRef) {
2987 let num_rows = array.len();
2988 let orig_batch = RecordBatch::try_from_iter(vec![("a", array)]).unwrap();
2989 let sliced_batch = orig_batch.slice(1, num_rows - 1);
2991
2992 let schema = orig_batch.schema();
2993 let stream_data = {
2994 let mut writer = StreamWriter::try_new(vec![], &schema).unwrap();
2995 writer.write(&sliced_batch).unwrap();
2996 writer.into_inner().unwrap()
2997 };
2998 let read_batch = {
2999 let projection = None;
3000 let mut reader = StreamReader::try_new(Cursor::new(stream_data), projection).unwrap();
3001 reader
3002 .next()
3003 .expect("expect no errors reading batch")
3004 .expect("expect batch")
3005 };
3006 assert_eq!(sliced_batch, read_batch);
3007
3008 let file_data = {
3009 let mut writer = FileWriter::try_new_buffered(vec![], &schema).unwrap();
3010 writer.write(&sliced_batch).unwrap();
3011 writer.into_inner().unwrap().into_inner().unwrap()
3012 };
3013 let read_batch = {
3014 let projection = None;
3015 let mut reader = FileReader::try_new(Cursor::new(file_data), projection).unwrap();
3016 reader
3017 .next()
3018 .expect("expect no errors reading batch")
3019 .expect("expect batch")
3020 };
3021 assert_eq!(sliced_batch, read_batch);
3022
3023 }
3025
3026 #[test]
3027 fn encode_bools_slice() {
3028 assert_bool_roundtrip([true, false], 1, 1);
3030
3031 assert_bool_roundtrip(
3033 [
3034 true, false, true, true, false, false, true, true, true, false, false, false, true,
3035 true, true, true, false, false, false, false, true, true, true, true, true, false,
3036 false, false, false, false,
3037 ],
3038 13,
3039 17,
3040 );
3041
3042 assert_bool_roundtrip(
3044 [
3045 true, false, true, true, false, false, true, true, true, false, false, false,
3046 ],
3047 8,
3048 2,
3049 );
3050
3051 assert_bool_roundtrip(
3053 [
3054 true, false, true, true, false, false, true, true, true, false, false, false, true,
3055 true, true, true, true, false, false, false, false, false,
3056 ],
3057 8,
3058 8,
3059 );
3060 }
3061
3062 fn assert_bool_roundtrip<const N: usize>(bools: [bool; N], offset: usize, length: usize) {
3063 let val_bool_field = Field::new("val", DataType::Boolean, false);
3064
3065 let schema = Arc::new(Schema::new(vec![val_bool_field]));
3066
3067 let bools = BooleanArray::from(bools.to_vec());
3068
3069 let batch = RecordBatch::try_new(Arc::clone(&schema), vec![Arc::new(bools)]).unwrap();
3070 let batch = batch.slice(offset, length);
3071
3072 let data = serialize_stream(&batch);
3073 let batch2 = deserialize_stream(data);
3074 assert_eq!(batch, batch2);
3075 }
3076
3077 #[test]
3078 fn test_run_array_unslice() {
3079 let total_len = 80;
3080 let vals: Vec<Option<i32>> = vec![Some(1), None, Some(2), Some(3), Some(4), None, Some(5)];
3081 let repeats: Vec<usize> = vec![3, 4, 1, 2];
3082 let mut input_array: Vec<Option<i32>> = Vec::with_capacity(total_len);
3083 for ix in 0_usize..32 {
3084 let repeat: usize = repeats[ix % repeats.len()];
3085 let val: Option<i32> = vals[ix % vals.len()];
3086 input_array.resize(input_array.len() + repeat, val);
3087 }
3088
3089 let mut builder =
3091 PrimitiveRunBuilder::<Int16Type, Int32Type>::with_capacity(input_array.len());
3092 builder.extend(input_array.iter().copied());
3093 let run_array = builder.finish();
3094
3095 for slice_len in 1..=total_len {
3097 let sliced_run_array: RunArray<Int16Type> =
3099 run_array.slice(0, slice_len).into_data().into();
3100
3101 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
3103 let typed = unsliced_run_array
3104 .downcast::<PrimitiveArray<Int32Type>>()
3105 .unwrap();
3106 let expected: Vec<Option<i32>> = input_array.iter().take(slice_len).copied().collect();
3107 let actual: Vec<Option<i32>> = typed.into_iter().collect();
3108 assert_eq!(expected, actual);
3109
3110 let sliced_run_array: RunArray<Int16Type> = run_array
3112 .slice(total_len - slice_len, slice_len)
3113 .into_data()
3114 .into();
3115
3116 let unsliced_run_array = into_zero_offset_run_array(sliced_run_array).unwrap();
3118 let typed = unsliced_run_array
3119 .downcast::<PrimitiveArray<Int32Type>>()
3120 .unwrap();
3121 let expected: Vec<Option<i32>> = input_array
3122 .iter()
3123 .skip(total_len - slice_len)
3124 .copied()
3125 .collect();
3126 let actual: Vec<Option<i32>> = typed.into_iter().collect();
3127 assert_eq!(expected, actual);
3128 }
3129 }
3130
3131 fn generate_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3132 let mut ls = GenericListBuilder::<O, _>::new(UInt32Builder::new());
3133
3134 for i in 0..100_000 {
3135 for value in [i, i, i] {
3136 ls.values().append_value(value);
3137 }
3138 ls.append(true)
3139 }
3140
3141 ls.finish()
3142 }
3143
3144 fn generate_utf8view_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3145 let mut ls = GenericListBuilder::<O, _>::new(StringViewBuilder::new());
3146
3147 for i in 0..100_000 {
3148 for value in [
3149 format!("value{}", i),
3150 format!("value{}", i),
3151 format!("value{}", i),
3152 ] {
3153 ls.values().append_value(&value);
3154 }
3155 ls.append(true)
3156 }
3157
3158 ls.finish()
3159 }
3160
3161 fn generate_string_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3162 let mut ls = GenericListBuilder::<O, _>::new(StringBuilder::new());
3163
3164 for i in 0..100_000 {
3165 for value in [
3166 format!("value{}", i),
3167 format!("value{}", i),
3168 format!("value{}", i),
3169 ] {
3170 ls.values().append_value(&value);
3171 }
3172 ls.append(true)
3173 }
3174
3175 ls.finish()
3176 }
3177
3178 fn generate_nested_list_data<O: OffsetSizeTrait>() -> GenericListArray<O> {
3179 let mut ls =
3180 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
3181
3182 for _i in 0..10_000 {
3183 for j in 0..10 {
3184 for value in [j, j, j, j] {
3185 ls.values().values().append_value(value);
3186 }
3187 ls.values().append(true)
3188 }
3189 ls.append(true);
3190 }
3191
3192 ls.finish()
3193 }
3194
3195 fn generate_nested_list_data_starting_at_zero<O: OffsetSizeTrait>() -> GenericListArray<O> {
3196 let mut ls =
3197 GenericListBuilder::<O, _>::new(GenericListBuilder::<O, _>::new(UInt32Builder::new()));
3198
3199 for _i in 0..999 {
3200 ls.values().append(true);
3201 ls.append(true);
3202 }
3203
3204 for j in 0..10 {
3205 for value in [j, j, j, j] {
3206 ls.values().values().append_value(value);
3207 }
3208 ls.values().append(true)
3209 }
3210 ls.append(true);
3211
3212 for i in 0..9_000 {
3213 for j in 0..10 {
3214 for value in [i + j, i + j, i + j, i + j] {
3215 ls.values().values().append_value(value);
3216 }
3217 ls.values().append(true)
3218 }
3219 ls.append(true);
3220 }
3221
3222 ls.finish()
3223 }
3224
3225 fn generate_map_array_data() -> MapArray {
3226 let keys_builder = UInt32Builder::new();
3227 let values_builder = UInt32Builder::new();
3228
3229 let mut builder = MapBuilder::new(None, keys_builder, values_builder);
3230
3231 for i in 0..100_000 {
3232 for _j in 0..3 {
3233 builder.keys().append_value(i);
3234 builder.values().append_value(i * 2);
3235 }
3236 builder.append(true).unwrap();
3237 }
3238
3239 builder.finish()
3240 }
3241
3242 #[test]
3243 fn reencode_offsets_when_first_offset_is_not_zero() {
3244 let original_list = generate_list_data::<i32>();
3245 let original_data = original_list.into_data();
3246 let slice_data = original_data.slice(75, 7);
3247 let (new_offsets, original_start, length) =
3248 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
3249 assert_eq!(
3250 vec![0, 3, 6, 9, 12, 15, 18, 21],
3251 new_offsets.typed_data::<i32>()
3252 );
3253 assert_eq!(225, original_start);
3254 assert_eq!(21, length);
3255 }
3256
3257 #[test]
3258 fn reencode_offsets_when_first_offset_is_zero() {
3259 let mut ls = GenericListBuilder::<i32, _>::new(UInt32Builder::new());
3260 ls.append(true);
3262 ls.values().append_value(35);
3263 ls.values().append_value(42);
3264 ls.append(true);
3265 let original_list = ls.finish();
3266 let original_data = original_list.into_data();
3267
3268 let slice_data = original_data.slice(1, 1);
3269 let (new_offsets, original_start, length) =
3270 reencode_offsets::<i32>(&slice_data.buffers()[0], &slice_data);
3271 assert_eq!(vec![0, 2], new_offsets.typed_data::<i32>());
3272 assert_eq!(0, original_start);
3273 assert_eq!(2, length);
3274 }
3275
3276 fn roundtrip_ensure_sliced_smaller(in_batch: RecordBatch, expected_size_factor: usize) {
3279 let in_sliced = in_batch.slice(999, 1);
3281
3282 let bytes_batch = serialize_file(&in_batch);
3283 let bytes_sliced = serialize_file(&in_sliced);
3284
3285 assert!(bytes_sliced.len() < (bytes_batch.len() / expected_size_factor));
3287
3288 let out_batch = deserialize_file(bytes_batch);
3290 assert_eq!(in_batch, out_batch);
3291
3292 let out_sliced = deserialize_file(bytes_sliced);
3293 assert_eq!(in_sliced, out_sliced);
3294 }
3295
3296 #[test]
3297 fn encode_lists() {
3298 let val_inner = Field::new_list_field(DataType::UInt32, true);
3299 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
3300 let schema = Arc::new(Schema::new(vec![val_list_field]));
3301
3302 let values = Arc::new(generate_list_data::<i32>());
3303
3304 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3305 roundtrip_ensure_sliced_smaller(in_batch, 1000);
3306 }
3307
3308 #[test]
3309 fn encode_empty_list() {
3310 let val_inner = Field::new_list_field(DataType::UInt32, true);
3311 let val_list_field = Field::new("val", DataType::List(Arc::new(val_inner)), false);
3312 let schema = Arc::new(Schema::new(vec![val_list_field]));
3313
3314 let values = Arc::new(generate_list_data::<i32>());
3315
3316 let in_batch = RecordBatch::try_new(schema, vec![values])
3317 .unwrap()
3318 .slice(999, 0);
3319 let out_batch = deserialize_file(serialize_file(&in_batch));
3320 assert_eq!(in_batch, out_batch);
3321 }
3322
3323 #[test]
3324 fn encode_large_lists() {
3325 let val_inner = Field::new_list_field(DataType::UInt32, true);
3326 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
3327 let schema = Arc::new(Schema::new(vec![val_list_field]));
3328
3329 let values = Arc::new(generate_list_data::<i64>());
3330
3331 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3334 roundtrip_ensure_sliced_smaller(in_batch, 1000);
3335 }
3336
3337 #[test]
3338 fn encode_large_lists_non_zero_offset() {
3339 let val_inner = Field::new_list_field(DataType::UInt32, true);
3340 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
3341 let schema = Arc::new(Schema::new(vec![val_list_field]));
3342
3343 let values = Arc::new(generate_list_data::<i64>());
3344
3345 check_sliced_list_array(schema, values);
3346 }
3347
3348 #[test]
3349 fn encode_large_lists_string_non_zero_offset() {
3350 let val_inner = Field::new_list_field(DataType::Utf8, true);
3351 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
3352 let schema = Arc::new(Schema::new(vec![val_list_field]));
3353
3354 let values = Arc::new(generate_string_list_data::<i64>());
3355
3356 check_sliced_list_array(schema, values);
3357 }
3358
3359 #[test]
3360 fn encode_large_list_string_view_non_zero_offset() {
3361 let val_inner = Field::new_list_field(DataType::Utf8View, true);
3362 let val_list_field = Field::new("val", DataType::LargeList(Arc::new(val_inner)), false);
3363 let schema = Arc::new(Schema::new(vec![val_list_field]));
3364
3365 let values = Arc::new(generate_utf8view_list_data::<i64>());
3366
3367 check_sliced_list_array(schema, values);
3368 }
3369
3370 fn check_sliced_list_array(schema: Arc<Schema>, values: Arc<GenericListArray<i64>>) {
3371 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
3372 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
3373 .unwrap()
3374 .slice(offset, len);
3375 let out_batch = deserialize_file(serialize_file(&in_batch));
3376 assert_eq!(in_batch, out_batch);
3377 }
3378 }
3379
3380 #[test]
3381 fn encode_nested_lists() {
3382 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
3383 let inner_list_field = Arc::new(Field::new_list_field(DataType::List(inner_int), true));
3384 let list_field = Field::new("val", DataType::List(inner_list_field), true);
3385 let schema = Arc::new(Schema::new(vec![list_field]));
3386
3387 let values = Arc::new(generate_nested_list_data::<i32>());
3388
3389 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3390 roundtrip_ensure_sliced_smaller(in_batch, 1000);
3391 }
3392
3393 #[test]
3394 fn encode_nested_lists_starting_at_zero() {
3395 let inner_int = Arc::new(Field::new("item", DataType::UInt32, true));
3396 let inner_list_field = Arc::new(Field::new("item", DataType::List(inner_int), true));
3397 let list_field = Field::new("val", DataType::List(inner_list_field), true);
3398 let schema = Arc::new(Schema::new(vec![list_field]));
3399
3400 let values = Arc::new(generate_nested_list_data_starting_at_zero::<i32>());
3401
3402 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3403 roundtrip_ensure_sliced_smaller(in_batch, 1);
3404 }
3405
3406 #[test]
3407 fn encode_map_array() {
3408 let keys = Arc::new(Field::new("keys", DataType::UInt32, false));
3409 let values = Arc::new(Field::new("values", DataType::UInt32, true));
3410 let map_field = Field::new_map("map", "entries", keys, values, false, true);
3411 let schema = Arc::new(Schema::new(vec![map_field]));
3412
3413 let values = Arc::new(generate_map_array_data());
3414
3415 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3416 roundtrip_ensure_sliced_smaller(in_batch, 1000);
3417 }
3418
3419 fn generate_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
3420 let mut builder = GenericListViewBuilder::<O, _>::new(UInt32Builder::new());
3421
3422 for i in 0u32..100_000 {
3423 if i.is_multiple_of(10_000) {
3424 builder.append(false);
3425 continue;
3426 }
3427 for value in [i, i, i] {
3428 builder.values().append_value(value);
3429 }
3430 builder.append(true);
3431 }
3432
3433 builder.finish()
3434 }
3435
3436 #[test]
3437 fn encode_list_view_arrays() {
3438 let val_inner = Field::new_list_field(DataType::UInt32, true);
3439 let val_field = Field::new("val", DataType::ListView(Arc::new(val_inner)), true);
3440 let schema = Arc::new(Schema::new(vec![val_field]));
3441
3442 let values = Arc::new(generate_list_view_data::<i32>());
3443
3444 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3445 let out_batch = deserialize_file(serialize_file(&in_batch));
3446 assert_eq!(in_batch, out_batch);
3447 }
3448
3449 #[test]
3450 fn encode_large_list_view_arrays() {
3451 let val_inner = Field::new_list_field(DataType::UInt32, true);
3452 let val_field = Field::new("val", DataType::LargeListView(Arc::new(val_inner)), true);
3453 let schema = Arc::new(Schema::new(vec![val_field]));
3454
3455 let values = Arc::new(generate_list_view_data::<i64>());
3456
3457 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3458 let out_batch = deserialize_file(serialize_file(&in_batch));
3459 assert_eq!(in_batch, out_batch);
3460 }
3461
3462 #[test]
3463 fn check_sliced_list_view_array() {
3464 let inner = Field::new_list_field(DataType::UInt32, true);
3465 let field = Field::new("val", DataType::ListView(Arc::new(inner)), true);
3466 let schema = Arc::new(Schema::new(vec![field]));
3467 let values = Arc::new(generate_list_view_data::<i32>());
3468
3469 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
3470 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
3471 .unwrap()
3472 .slice(offset, len);
3473 let out_batch = deserialize_file(serialize_file(&in_batch));
3474 assert_eq!(in_batch, out_batch);
3475 }
3476 }
3477
3478 #[test]
3479 fn check_sliced_large_list_view_array() {
3480 let inner = Field::new_list_field(DataType::UInt32, true);
3481 let field = Field::new("val", DataType::LargeListView(Arc::new(inner)), true);
3482 let schema = Arc::new(Schema::new(vec![field]));
3483 let values = Arc::new(generate_list_view_data::<i64>());
3484
3485 for (offset, len) in [(999, 1), (0, 13), (47, 12), (values.len() - 13, 13)] {
3486 let in_batch = RecordBatch::try_new(schema.clone(), vec![values.clone()])
3487 .unwrap()
3488 .slice(offset, len);
3489 let out_batch = deserialize_file(serialize_file(&in_batch));
3490 assert_eq!(in_batch, out_batch);
3491 }
3492 }
3493
3494 fn generate_nested_list_view_data<O: OffsetSizeTrait>() -> GenericListViewArray<O> {
3495 let inner_builder = UInt32Builder::new();
3496 let middle_builder = GenericListViewBuilder::<O, _>::new(inner_builder);
3497 let mut outer_builder = GenericListViewBuilder::<O, _>::new(middle_builder);
3498
3499 for i in 0u32..10_000 {
3500 if i.is_multiple_of(1_000) {
3501 outer_builder.append(false);
3502 continue;
3503 }
3504
3505 for _ in 0..3 {
3506 for value in [i, i + 1, i + 2] {
3507 outer_builder.values().values().append_value(value);
3508 }
3509 outer_builder.values().append(true);
3510 }
3511 outer_builder.append(true);
3512 }
3513
3514 outer_builder.finish()
3515 }
3516
3517 #[test]
3518 fn encode_nested_list_views() {
3519 let inner_int = Arc::new(Field::new_list_field(DataType::UInt32, true));
3520 let inner_list_field = Arc::new(Field::new_list_field(DataType::ListView(inner_int), true));
3521 let list_field = Field::new("val", DataType::ListView(inner_list_field), true);
3522 let schema = Arc::new(Schema::new(vec![list_field]));
3523
3524 let values = Arc::new(generate_nested_list_view_data::<i32>());
3525
3526 let in_batch = RecordBatch::try_new(schema, vec![values]).unwrap();
3527 let out_batch = deserialize_file(serialize_file(&in_batch));
3528 assert_eq!(in_batch, out_batch);
3529 }
3530
3531 fn test_roundtrip_list_view_of_dict_impl<OffsetSize: OffsetSizeTrait, U: ArrowNativeType>(
3532 list_data_type: DataType,
3533 offsets: &[U; 5],
3534 sizes: &[U; 4],
3535 ) {
3536 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
3537 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
3538 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3539 let dict_data = dict_array.to_data();
3540
3541 let value_offsets = Buffer::from_slice_ref(offsets);
3542 let value_sizes = Buffer::from_slice_ref(sizes);
3543
3544 let list_data = ArrayData::builder(list_data_type)
3545 .len(4)
3546 .add_buffer(value_offsets)
3547 .add_buffer(value_sizes)
3548 .add_child_data(dict_data)
3549 .build()
3550 .unwrap();
3551 let list_view_array = GenericListViewArray::<OffsetSize>::from(list_data);
3552
3553 let schema = Arc::new(Schema::new(vec![Field::new(
3554 "f1",
3555 list_view_array.data_type().clone(),
3556 false,
3557 )]));
3558 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
3559
3560 let output_batch = deserialize_file(serialize_file(&input_batch));
3561 assert_eq!(input_batch, output_batch);
3562
3563 let output_batch = deserialize_stream(serialize_stream(&input_batch));
3564 assert_eq!(input_batch, output_batch);
3565 }
3566
3567 #[test]
3568 fn test_roundtrip_list_view_of_dict() {
3569 #[allow(deprecated)]
3570 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
3571 "item",
3572 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3573 true,
3574 1,
3575 false,
3576 )));
3577 let offsets: &[i32; 5] = &[0, 2, 4, 4, 7];
3578 let sizes: &[i32; 4] = &[2, 2, 0, 3];
3579 test_roundtrip_list_view_of_dict_impl::<i32, i32>(list_data_type, offsets, sizes);
3580 }
3581
3582 #[test]
3583 fn test_roundtrip_large_list_view_of_dict() {
3584 #[allow(deprecated)]
3585 let list_data_type = DataType::LargeListView(Arc::new(Field::new_dict(
3586 "item",
3587 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3588 true,
3589 2,
3590 false,
3591 )));
3592 let offsets: &[i64; 5] = &[0, 2, 4, 4, 7];
3593 let sizes: &[i64; 4] = &[2, 2, 0, 3];
3594 test_roundtrip_list_view_of_dict_impl::<i64, i64>(list_data_type, offsets, sizes);
3595 }
3596
3597 #[test]
3598 fn test_roundtrip_sliced_list_view_of_dict() {
3599 #[allow(deprecated)]
3600 let list_data_type = DataType::ListView(Arc::new(Field::new_dict(
3601 "item",
3602 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3603 true,
3604 3,
3605 false,
3606 )));
3607
3608 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
3609 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2, 1, 0, 3, 2, 1]);
3610 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3611 let dict_data = dict_array.to_data();
3612
3613 let offsets: &[i32; 7] = &[0, 2, 4, 4, 7, 9, 12];
3614 let sizes: &[i32; 6] = &[2, 2, 0, 3, 2, 3];
3615 let value_offsets = Buffer::from_slice_ref(offsets);
3616 let value_sizes = Buffer::from_slice_ref(sizes);
3617
3618 let list_data = ArrayData::builder(list_data_type)
3619 .len(6)
3620 .add_buffer(value_offsets)
3621 .add_buffer(value_sizes)
3622 .add_child_data(dict_data)
3623 .build()
3624 .unwrap();
3625 let list_view_array = GenericListViewArray::<i32>::from(list_data);
3626
3627 let schema = Arc::new(Schema::new(vec![Field::new(
3628 "f1",
3629 list_view_array.data_type().clone(),
3630 false,
3631 )]));
3632 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(list_view_array)]).unwrap();
3633
3634 let sliced_batch = input_batch.slice(1, 4);
3635
3636 let output_batch = deserialize_file(serialize_file(&sliced_batch));
3637 assert_eq!(sliced_batch, output_batch);
3638
3639 let output_batch = deserialize_stream(serialize_stream(&sliced_batch));
3640 assert_eq!(sliced_batch, output_batch);
3641 }
3642
3643 #[test]
3644 fn test_roundtrip_dense_union_of_dict() {
3645 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
3646 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
3647 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3648
3649 #[allow(deprecated)]
3650 let dict_field = Arc::new(Field::new_dict(
3651 "dict",
3652 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3653 true,
3654 1,
3655 false,
3656 ));
3657 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
3658 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
3659
3660 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
3661 let offsets = ScalarBuffer::from(vec![0i32, 1, 0, 2, 1, 3, 4]);
3662
3663 let int_array = Int32Array::from(vec![100, 200]);
3664
3665 let union = UnionArray::try_new(
3666 union_fields.clone(),
3667 types,
3668 Some(offsets),
3669 vec![Arc::new(dict_array), Arc::new(int_array)],
3670 )
3671 .unwrap();
3672
3673 let schema = Arc::new(Schema::new(vec![Field::new(
3674 "union",
3675 DataType::Union(union_fields, UnionMode::Dense),
3676 false,
3677 )]));
3678 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
3679
3680 let output_batch = deserialize_file(serialize_file(&input_batch));
3681 assert_eq!(input_batch, output_batch);
3682
3683 let output_batch = deserialize_stream(serialize_stream(&input_batch));
3684 assert_eq!(input_batch, output_batch);
3685 }
3686
3687 #[test]
3688 fn test_roundtrip_sparse_union_of_dict() {
3689 let values = StringArray::from(vec![Some("alpha"), None, Some("beta"), Some("gamma")]);
3690 let keys = Int32Array::from_iter_values([0, 0, 1, 2, 3, 0, 2]);
3691 let dict_array = DictionaryArray::new(keys, Arc::new(values));
3692
3693 #[allow(deprecated)]
3694 let dict_field = Arc::new(Field::new_dict(
3695 "dict",
3696 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3697 true,
3698 2,
3699 false,
3700 ));
3701 let int_field = Arc::new(Field::new("int", DataType::Int32, false));
3702 let union_fields = UnionFields::try_new(vec![0, 1], vec![dict_field, int_field]).unwrap();
3703
3704 let types = ScalarBuffer::from(vec![0i8, 0, 1, 0, 1, 0, 0]);
3705
3706 let int_array = Int32Array::from(vec![0, 0, 100, 0, 200, 0, 0]);
3707
3708 let union = UnionArray::try_new(
3709 union_fields.clone(),
3710 types,
3711 None,
3712 vec![Arc::new(dict_array), Arc::new(int_array)],
3713 )
3714 .unwrap();
3715
3716 let schema = Arc::new(Schema::new(vec![Field::new(
3717 "union",
3718 DataType::Union(union_fields, UnionMode::Sparse),
3719 false,
3720 )]));
3721 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(union)]).unwrap();
3722
3723 let output_batch = deserialize_file(serialize_file(&input_batch));
3724 assert_eq!(input_batch, output_batch);
3725
3726 let output_batch = deserialize_stream(serialize_stream(&input_batch));
3727 assert_eq!(input_batch, output_batch);
3728 }
3729
3730 #[test]
3731 fn test_roundtrip_map_with_dict_keys() {
3732 let key_values = StringArray::from(vec!["key_a", "key_b", "key_c"]);
3735 let keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
3736 let dict_keys = DictionaryArray::new(keys, Arc::new(key_values));
3737
3738 let values = Int32Array::from(vec![1, 2, 3, 4, 5, 6]);
3739
3740 #[allow(deprecated)]
3741 let entries_field = Arc::new(Field::new(
3742 "entries",
3743 DataType::Struct(
3744 vec![
3745 Field::new_dict(
3746 "key",
3747 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3748 false,
3749 1,
3750 false,
3751 ),
3752 Field::new("value", DataType::Int32, true),
3753 ]
3754 .into(),
3755 ),
3756 false,
3757 ));
3758
3759 let entries = StructArray::from(vec![
3760 (
3761 Arc::new(Field::new(
3762 "key",
3763 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3764 false,
3765 )),
3766 Arc::new(dict_keys) as ArrayRef,
3767 ),
3768 (
3769 Arc::new(Field::new("value", DataType::Int32, true)),
3770 Arc::new(values) as ArrayRef,
3771 ),
3772 ]);
3773
3774 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
3775
3776 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
3777 .len(3)
3778 .add_buffer(offsets)
3779 .add_child_data(entries.into_data())
3780 .build()
3781 .unwrap();
3782 let map_array = MapArray::from(map_data);
3783
3784 let schema = Arc::new(Schema::new(vec![Field::new(
3785 "map",
3786 map_array.data_type().clone(),
3787 false,
3788 )]));
3789 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
3790
3791 let output_batch = deserialize_file(serialize_file(&input_batch));
3792 assert_eq!(input_batch, output_batch);
3793
3794 let output_batch = deserialize_stream(serialize_stream(&input_batch));
3795 assert_eq!(input_batch, output_batch);
3796 }
3797
3798 #[test]
3799 fn test_roundtrip_map_with_dict_values() {
3800 let keys = StringArray::from(vec!["a", "b", "c", "d", "e", "f"]);
3803
3804 let value_values = StringArray::from(vec!["val_x", "val_y", "val_z"]);
3805 let value_keys = Int32Array::from_iter_values([0, 1, 2, 0, 1, 0]);
3806 let dict_values = DictionaryArray::new(value_keys, Arc::new(value_values));
3807
3808 #[allow(deprecated)]
3809 let entries_field = Arc::new(Field::new(
3810 "entries",
3811 DataType::Struct(
3812 vec![
3813 Field::new("key", DataType::Utf8, false),
3814 Field::new_dict(
3815 "value",
3816 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3817 true,
3818 2,
3819 false,
3820 ),
3821 ]
3822 .into(),
3823 ),
3824 false,
3825 ));
3826
3827 let entries = StructArray::from(vec![
3828 (
3829 Arc::new(Field::new("key", DataType::Utf8, false)),
3830 Arc::new(keys) as ArrayRef,
3831 ),
3832 (
3833 Arc::new(Field::new(
3834 "value",
3835 DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
3836 true,
3837 )),
3838 Arc::new(dict_values) as ArrayRef,
3839 ),
3840 ]);
3841
3842 let offsets = Buffer::from_slice_ref([0i32, 2, 4, 6]);
3843
3844 let map_data = ArrayData::builder(DataType::Map(entries_field, false))
3845 .len(3)
3846 .add_buffer(offsets)
3847 .add_child_data(entries.into_data())
3848 .build()
3849 .unwrap();
3850 let map_array = MapArray::from(map_data);
3851
3852 let schema = Arc::new(Schema::new(vec![Field::new(
3853 "map",
3854 map_array.data_type().clone(),
3855 false,
3856 )]));
3857 let input_batch = RecordBatch::try_new(schema, vec![Arc::new(map_array)]).unwrap();
3858
3859 let output_batch = deserialize_file(serialize_file(&input_batch));
3860 assert_eq!(input_batch, output_batch);
3861
3862 let output_batch = deserialize_stream(serialize_stream(&input_batch));
3863 assert_eq!(input_batch, output_batch);
3864 }
3865
3866 #[test]
3867 fn test_decimal128_alignment16_is_sufficient() {
3868 const IPC_ALIGNMENT: usize = 16;
3869
3870 for num_cols in [1, 2, 3, 17, 50, 73, 99] {
3875 let num_rows = (num_cols * 7 + 11) % 100; let mut fields = Vec::new();
3878 let mut arrays = Vec::new();
3879 for i in 0..num_cols {
3880 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
3881 let array = Decimal128Array::from(vec![num_cols as i128; num_rows]);
3882 fields.push(field);
3883 arrays.push(Arc::new(array) as Arc<dyn Array>);
3884 }
3885 let schema = Schema::new(fields);
3886 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
3887
3888 let mut writer = FileWriter::try_new_with_options(
3889 Vec::new(),
3890 batch.schema_ref(),
3891 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
3892 )
3893 .unwrap();
3894 writer.write(&batch).unwrap();
3895 writer.finish().unwrap();
3896
3897 let out: Vec<u8> = writer.into_inner().unwrap();
3898
3899 let buffer = Buffer::from_vec(out);
3900 let trailer_start = buffer.len() - 10;
3901 let footer_len =
3902 read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
3903 let footer =
3904 root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
3905
3906 let schema = fb_to_schema(footer.schema().unwrap());
3907
3908 let decoder =
3911 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
3912
3913 let batches = footer.recordBatches().unwrap();
3914
3915 let block = batches.get(0);
3916 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
3917 let data = buffer.slice_with_length(block.offset() as _, block_len);
3918
3919 let batch2 = decoder.read_record_batch(block, &data).unwrap().unwrap();
3920
3921 assert_eq!(batch, batch2);
3922 }
3923 }
3924
3925 #[test]
3926 fn test_decimal128_alignment8_is_unaligned() {
3927 const IPC_ALIGNMENT: usize = 8;
3928
3929 let num_cols = 2;
3930
3931 let mut fields = Vec::new();
3932 let mut arrays = Vec::new();
3933 for i in 0..num_cols {
3934 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
3935 let array = Decimal128Array::from(vec![Some(num_cols as i128), None]);
3939 fields.push(field);
3940 arrays.push(Arc::new(array) as Arc<dyn Array>);
3941 }
3942 let schema = Schema::new(fields);
3943 let batch = RecordBatch::try_new(Arc::new(schema), arrays).unwrap();
3944
3945 let mut writer = FileWriter::try_new_with_options(
3946 Vec::new(),
3947 batch.schema_ref(),
3948 IpcWriteOptions::try_new(IPC_ALIGNMENT, false, MetadataVersion::V5).unwrap(),
3949 )
3950 .unwrap();
3951 writer.write(&batch).unwrap();
3952 writer.finish().unwrap();
3953
3954 let out: Vec<u8> = writer.into_inner().unwrap();
3955
3956 let buffer = Buffer::from_vec(out);
3957 let trailer_start = buffer.len() - 10;
3958 let footer_len = read_footer_length(buffer[trailer_start..].try_into().unwrap()).unwrap();
3959 let footer = root_as_footer(&buffer[trailer_start - footer_len..trailer_start]).unwrap();
3960 let schema = fb_to_schema(footer.schema().unwrap());
3961
3962 let decoder =
3965 FileDecoder::new(Arc::new(schema), footer.version()).with_require_alignment(true);
3966
3967 let batches = footer.recordBatches().unwrap();
3968
3969 let block = batches.get(0);
3970 let block_len = block.bodyLength() as usize + block.metaDataLength() as usize;
3971 let data = buffer.slice_with_length(block.offset() as _, block_len);
3972
3973 let result = decoder.read_record_batch(block, &data);
3974
3975 let error = result.unwrap_err();
3976 assert_eq!(
3977 error.to_string(),
3978 "Invalid argument error: Misaligned buffers[0] in array of type Decimal128(38, 10), \
3979 offset from expected alignment of 16 by 8"
3980 );
3981 }
3982
3983 #[test]
3984 fn test_flush() {
3985 let num_cols = 2;
3988 let mut fields = Vec::new();
3989 let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V5).unwrap();
3990 for i in 0..num_cols {
3991 let field = Field::new(format!("col_{i}"), DataType::Decimal128(38, 10), true);
3992 fields.push(field);
3993 }
3994 let schema = Schema::new(fields);
3995 let inner_stream_writer = BufWriter::with_capacity(1024, Vec::new());
3996 let inner_file_writer = BufWriter::with_capacity(1024, Vec::new());
3997 let mut stream_writer =
3998 StreamWriter::try_new_with_options(inner_stream_writer, &schema, options.clone())
3999 .unwrap();
4000 let mut file_writer =
4001 FileWriter::try_new_with_options(inner_file_writer, &schema, options).unwrap();
4002
4003 let stream_bytes_written_on_new = stream_writer.get_ref().get_ref().len();
4004 let file_bytes_written_on_new = file_writer.get_ref().get_ref().len();
4005 stream_writer.flush().unwrap();
4006 file_writer.flush().unwrap();
4007 let stream_bytes_written_on_flush = stream_writer.get_ref().get_ref().len();
4008 let file_bytes_written_on_flush = file_writer.get_ref().get_ref().len();
4009 let stream_out = stream_writer.into_inner().unwrap().into_inner().unwrap();
4010 let expected_stream_flushed_bytes = stream_out.len() - 8;
4014 let expected_file_flushed_bytes = expected_stream_flushed_bytes + 8;
4017
4018 assert!(
4019 stream_bytes_written_on_new < stream_bytes_written_on_flush,
4020 "this test makes no sense if flush is not actually required"
4021 );
4022 assert!(
4023 file_bytes_written_on_new < file_bytes_written_on_flush,
4024 "this test makes no sense if flush is not actually required"
4025 );
4026 assert_eq!(stream_bytes_written_on_flush, expected_stream_flushed_bytes);
4027 assert_eq!(file_bytes_written_on_flush, expected_file_flushed_bytes);
4028 }
4029
4030 #[test]
4031 fn test_roundtrip_list_of_fixed_list() -> Result<(), ArrowError> {
4032 let l1_type =
4033 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Float32, false)), 3);
4034 let l2_type = DataType::List(Arc::new(Field::new("item", l1_type.clone(), false)));
4035
4036 let l0_builder = Float32Builder::new();
4037 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3).with_field(Arc::new(Field::new(
4038 "item",
4039 DataType::Float32,
4040 false,
4041 )));
4042 let mut l2_builder =
4043 ListBuilder::new(l1_builder).with_field(Arc::new(Field::new("item", l1_type, false)));
4044
4045 for point in [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]] {
4046 l2_builder.values().values().append_value(point[0]);
4047 l2_builder.values().values().append_value(point[1]);
4048 l2_builder.values().values().append_value(point[2]);
4049
4050 l2_builder.values().append(true);
4051 }
4052 l2_builder.append(true);
4053
4054 let point = [10., 11., 12.];
4055 l2_builder.values().values().append_value(point[0]);
4056 l2_builder.values().values().append_value(point[1]);
4057 l2_builder.values().values().append_value(point[2]);
4058
4059 l2_builder.values().append(true);
4060 l2_builder.append(true);
4061
4062 let array = Arc::new(l2_builder.finish()) as ArrayRef;
4063
4064 let schema = Arc::new(Schema::new_with_metadata(
4065 vec![Field::new("points", l2_type, false)],
4066 HashMap::default(),
4067 ));
4068
4069 test_slices(&array, &schema, 0, 1)?;
4072 test_slices(&array, &schema, 0, 2)?;
4073 test_slices(&array, &schema, 1, 1)?;
4074
4075 Ok(())
4076 }
4077
4078 #[test]
4079 fn test_roundtrip_list_of_fixed_list_w_nulls() -> Result<(), ArrowError> {
4080 let l0_builder = Float32Builder::new();
4081 let l1_builder = FixedSizeListBuilder::new(l0_builder, 3);
4082 let mut l2_builder = ListBuilder::new(l1_builder);
4083
4084 for point in [
4085 [Some(1.0), Some(2.0), None],
4086 [Some(4.0), Some(5.0), Some(6.0)],
4087 [None, Some(8.0), Some(9.0)],
4088 ] {
4089 for p in point {
4090 match p {
4091 Some(p) => l2_builder.values().values().append_value(p),
4092 None => l2_builder.values().values().append_null(),
4093 }
4094 }
4095
4096 l2_builder.values().append(true);
4097 }
4098 l2_builder.append(true);
4099
4100 let point = [Some(10.), None, None];
4101 for p in point {
4102 match p {
4103 Some(p) => l2_builder.values().values().append_value(p),
4104 None => l2_builder.values().values().append_null(),
4105 }
4106 }
4107
4108 l2_builder.values().append(true);
4109 l2_builder.append(true);
4110
4111 let array = Arc::new(l2_builder.finish()) as ArrayRef;
4112
4113 let schema = Arc::new(Schema::new_with_metadata(
4114 vec![Field::new(
4115 "points",
4116 DataType::List(Arc::new(Field::new(
4117 "item",
4118 DataType::FixedSizeList(
4119 Arc::new(Field::new("item", DataType::Float32, true)),
4120 3,
4121 ),
4122 true,
4123 ))),
4124 true,
4125 )],
4126 HashMap::default(),
4127 ));
4128
4129 test_slices(&array, &schema, 0, 1)?;
4132 test_slices(&array, &schema, 0, 2)?;
4133 test_slices(&array, &schema, 1, 1)?;
4134
4135 Ok(())
4136 }
4137
4138 fn test_slices(
4139 parent_array: &ArrayRef,
4140 schema: &SchemaRef,
4141 offset: usize,
4142 length: usize,
4143 ) -> Result<(), ArrowError> {
4144 let subarray = parent_array.slice(offset, length);
4145 let original_batch = RecordBatch::try_new(schema.clone(), vec![subarray])?;
4146
4147 let mut bytes = Vec::new();
4148 let mut writer = StreamWriter::try_new(&mut bytes, schema)?;
4149 writer.write(&original_batch)?;
4150 writer.finish()?;
4151
4152 let mut cursor = std::io::Cursor::new(bytes);
4153 let mut reader = StreamReader::try_new(&mut cursor, None)?;
4154 let returned_batch = reader.next().unwrap()?;
4155
4156 assert_eq!(original_batch, returned_batch);
4157
4158 Ok(())
4159 }
4160
4161 #[test]
4162 fn test_roundtrip_fixed_list() -> Result<(), ArrowError> {
4163 let int_builder = Int64Builder::new();
4164 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3)
4165 .with_field(Arc::new(Field::new("item", DataType::Int64, false)));
4166
4167 for point in [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]] {
4168 fixed_list_builder.values().append_value(point[0]);
4169 fixed_list_builder.values().append_value(point[1]);
4170 fixed_list_builder.values().append_value(point[2]);
4171
4172 fixed_list_builder.append(true);
4173 }
4174
4175 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
4176
4177 let schema = Arc::new(Schema::new_with_metadata(
4178 vec![Field::new(
4179 "points",
4180 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, false)), 3),
4181 false,
4182 )],
4183 HashMap::default(),
4184 ));
4185
4186 test_slices(&array, &schema, 0, 4)?;
4189 test_slices(&array, &schema, 0, 2)?;
4190 test_slices(&array, &schema, 1, 3)?;
4191 test_slices(&array, &schema, 2, 1)?;
4192
4193 Ok(())
4194 }
4195
4196 #[test]
4197 fn test_roundtrip_fixed_list_w_nulls() -> Result<(), ArrowError> {
4198 let int_builder = Int64Builder::new();
4199 let mut fixed_list_builder = FixedSizeListBuilder::new(int_builder, 3);
4200
4201 for point in [
4202 [Some(1), Some(2), None],
4203 [Some(4), Some(5), Some(6)],
4204 [None, Some(8), Some(9)],
4205 [Some(10), None, None],
4206 ] {
4207 for p in point {
4208 match p {
4209 Some(p) => fixed_list_builder.values().append_value(p),
4210 None => fixed_list_builder.values().append_null(),
4211 }
4212 }
4213
4214 fixed_list_builder.append(true);
4215 }
4216
4217 let array = Arc::new(fixed_list_builder.finish()) as ArrayRef;
4218
4219 let schema = Arc::new(Schema::new_with_metadata(
4220 vec![Field::new(
4221 "points",
4222 DataType::FixedSizeList(Arc::new(Field::new("item", DataType::Int64, true)), 3),
4223 true,
4224 )],
4225 HashMap::default(),
4226 ));
4227
4228 test_slices(&array, &schema, 0, 4)?;
4231 test_slices(&array, &schema, 0, 2)?;
4232 test_slices(&array, &schema, 1, 3)?;
4233 test_slices(&array, &schema, 2, 1)?;
4234
4235 Ok(())
4236 }
4237
4238 #[test]
4239 fn test_metadata_encoding_ordering() {
4240 fn create_hash() -> u64 {
4241 let metadata: HashMap<String, String> = [
4242 ("a", "1"), ("b", "2"), ("c", "3"), ("d", "4"), ("e", "5"), ]
4248 .into_iter()
4249 .map(|(k, v)| (k.to_owned(), v.to_owned()))
4250 .collect();
4251
4252 let schema = Arc::new(
4254 Schema::new(vec![
4255 Field::new("a", DataType::Int64, true).with_metadata(metadata.clone()),
4256 ])
4257 .with_metadata(metadata)
4258 .clone(),
4259 );
4260 let batch = RecordBatch::new_empty(schema.clone());
4261
4262 let mut bytes = Vec::new();
4263 let mut w = StreamWriter::try_new(&mut bytes, batch.schema_ref()).unwrap();
4264 w.write(&batch).unwrap();
4265 w.finish().unwrap();
4266
4267 let mut h = std::hash::DefaultHasher::new();
4268 h.write(&bytes);
4269 h.finish()
4270 }
4271
4272 let expected = create_hash();
4273
4274 let all_passed = (0..20).all(|_| create_hash() == expected);
4279 assert!(all_passed);
4280 }
4281
4282 #[test]
4283 fn test_dictionary_tracker_reset() {
4284 let data_gen = IpcDataGenerator::default();
4285 let mut dictionary_tracker = DictionaryTracker::new(false);
4286 let writer_options = IpcWriteOptions::default();
4287 let mut compression_ctx = CompressionContext::default();
4288
4289 let schema = Arc::new(Schema::new(vec![Field::new(
4290 "a",
4291 DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::Utf8)),
4292 false,
4293 )]));
4294
4295 let mut write_single_batch_stream =
4296 |batch: RecordBatch, dict_tracker: &mut DictionaryTracker| -> Vec<u8> {
4297 let mut buffer = Vec::new();
4298
4299 let stream_header = data_gen.schema_to_bytes_with_dictionary_tracker(
4301 &schema,
4302 dict_tracker,
4303 &writer_options,
4304 );
4305 _ = write_message(&mut buffer, stream_header, &writer_options).unwrap();
4306
4307 let (encoded_dicts, encoded_batch) = data_gen
4308 .encode(&batch, dict_tracker, &writer_options, &mut compression_ctx)
4309 .unwrap();
4310 for encoded_dict in encoded_dicts {
4311 _ = write_message(&mut buffer, encoded_dict, &writer_options).unwrap();
4312 }
4313 _ = write_message(&mut buffer, encoded_batch, &writer_options).unwrap();
4314
4315 buffer
4316 };
4317
4318 let batch1 = RecordBatch::try_new(
4319 schema.clone(),
4320 vec![Arc::new(DictionaryArray::new(
4321 UInt8Array::from_iter_values([0]),
4322 Arc::new(StringArray::from_iter_values(["a"])),
4323 ))],
4324 )
4325 .unwrap();
4326 let buffer = write_single_batch_stream(batch1.clone(), &mut dictionary_tracker);
4327
4328 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
4330 let read_batch = reader.next().unwrap().unwrap();
4331 assert_eq!(read_batch, batch1);
4332
4333 dictionary_tracker.clear();
4335
4336 let batch2 = RecordBatch::try_new(
4338 schema.clone(),
4339 vec![Arc::new(DictionaryArray::new(
4340 UInt8Array::from_iter_values([0]),
4341 Arc::new(StringArray::from_iter_values(["a"])),
4342 ))],
4343 )
4344 .unwrap();
4345 let buffer = write_single_batch_stream(batch2.clone(), &mut dictionary_tracker);
4346 let mut reader = StreamReader::try_new(Cursor::new(buffer), None).unwrap();
4347 let read_batch = reader.next().unwrap().unwrap();
4348 assert_eq!(read_batch, batch2);
4349 }
4350}