Skip to main content

arrow_ipc/
writer.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Arrow IPC File and Stream Writers
19//!
20//! # Notes
21//!
22//! [`FileWriter`] and [`StreamWriter`] have similar interfaces,
23//! however the [`FileWriter`] expects a reader that supports [`Seek`]ing
24//!
25//! [`Seek`]: std::io::Seek
26
27use 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/// IPC write options used to control the behaviour of the [`IpcDataGenerator`]
49#[derive(Debug, Clone)]
50pub struct IpcWriteOptions {
51    /// Write padding after memory buffers to this multiple of bytes.
52    /// Must be 8, 16, 32, or 64 - defaults to 64.
53    alignment: u8,
54    /// The legacy format is for releases before 0.15.0, and uses metadata V4
55    write_legacy_ipc_format: bool,
56    /// The metadata version to write. The Rust IPC writer supports V4+
57    ///
58    /// *Default versions per crate*
59    ///
60    /// When creating the default IpcWriteOptions, the following metadata versions are used:
61    ///
62    /// version 2.0.0: V4, with legacy format enabled
63    /// version 4.0.0: V5
64    metadata_version: crate::MetadataVersion,
65    /// Compression, if desired. Will result in a runtime error
66    /// if the corresponding feature is not enabled
67    batch_compression_type: Option<crate::CompressionType>,
68    /// How to handle updating dictionaries in IPC messages
69    dictionary_handling: DictionaryHandling,
70}
71
72impl IpcWriteOptions {
73    /// Configures compression when writing IPC files.
74    ///
75    /// Will result in a runtime error if the corresponding feature
76    /// is not enabled
77    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    /// Try to create IpcWriteOptions, checking for incompatible settings
93    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    /// Configure how dictionaries are handled in IPC messages
142    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)]
161/// Handles low level details of encoding [`Array`] and [`Schema`] into the
162/// [Arrow IPC Format].
163///
164/// # Example
165/// ```
166/// # fn run() {
167/// # use std::sync::Arc;
168/// # use arrow_array::UInt64Array;
169/// # use arrow_array::RecordBatch;
170/// # use arrow_ipc::writer::{CompressionContext, DictionaryTracker, IpcDataGenerator, IpcWriteOptions};
171///
172/// // Create a record batch
173/// let batch = RecordBatch::try_from_iter(vec![
174///  ("col2", Arc::new(UInt64Array::from_iter([10, 23, 33])) as _)
175/// ]).unwrap();
176///
177/// // Error of dictionary ids are replaced.
178/// let error_on_replacement = true;
179/// let options = IpcWriteOptions::default();
180/// let mut dictionary_tracker = DictionaryTracker::new(error_on_replacement);
181///
182/// let mut compression_context = CompressionContext::default();
183///
184/// // encode the batch into zero or more encoded dictionaries
185/// // and the data for the actual array.
186/// let data_gen = IpcDataGenerator::default();
187/// let (encoded_dictionaries, encoded_message) = data_gen
188///   .encode(&batch, &mut dictionary_tracker, &options, &mut compression_context)
189///   .unwrap();
190/// # }
191/// ```
192///
193/// [Arrow IPC Format]: https://arrow.apache.org/docs/format/Columnar.html#serialization-and-interprocess-communication-ipc
194pub struct IpcDataGenerator {}
195
196impl IpcDataGenerator {
197    /// Converts a schema to an IPC message along with `dictionary_tracker`
198    /// and returns it encoded inside [EncodedData] as a flatbuffer.
199    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        // TODO: custom metadata
219        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                // The run_ends array is not expected to be dictionary encoded. Hence encode dictionaries
262                // only for values array.
263                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                // keys
346                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                // values
357                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                // It's important to only take the dict_id at this point, because the dict ID
416                // sequence is assigned depth-first, so we need to first encode children and have
417                // them take their assigned dict IDs before we take the dict ID for this field.
418                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    /// Encodes a batch to a number of [EncodedData] items (dictionary batches + the record batch).
467    /// The [DictionaryTracker] keeps track of dictionaries with new `dict_id`s  (so they are only sent once)
468    /// Make sure the [DictionaryTracker] is initialized at the start of the stream.
469    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    /// Encodes a batch to a number of [EncodedData] items (dictionary batches + the record batch).
500    /// The [DictionaryTracker] keeps track of dictionaries with new `dict_id`s  (so they are only sent once)
501    /// Make sure the [DictionaryTracker] is initialized at the start of the stream.
502    #[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    /// Write a `RecordBatch` into two sets of bytes, one for the header (crate::Message) and the
518    /// other for the batch's data
519    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        // get the type of compression
533        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        // pad the tail of body data
565        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        // write data
570        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        // create an crate::Message
594        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    /// Write dictionary values into two sets of bytes, one for the header (crate::Message) and the
610    /// other for the data
611    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        // get the type of compression
626        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        // pad the tail of body data
656        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        // write data
661        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            // The spec documents the counts only includes the variadic buffers, not the view/null buffers.
714            // https://arrow.apache.org/docs/format/Columnar.html#variadic-buffers
715            counts.push(array.buffers().len() as i64 - 1);
716        }
717        DataType::Dictionary(_, _) => {
718            // Do nothing
719            // Dictionary types are handled in `encode_dictionaries`.
720        }
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
749// Returns a `RunArray` with zero offset and length matching the last value
750// in run_ends array.
751fn 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    // The physical index of original run_ends array from which the `ArrayData`is sliced.
760    let start_physical_index = run_ends.get_start_physical_index();
761
762    // The physical index of original run_ends array until which the `ArrayData`is sliced.
763    let end_physical_index = run_ends.get_end_physical_index();
764
765    let physical_length = end_physical_index - start_physical_index + 1;
766
767    // build new run_ends array by subtracting offset from run ends.
768    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        // Safety:
776        // The function builds a valid run_ends array and hence need not be validated.
777        ArrayDataBuilder::new(R::DATA_TYPE)
778            .len(physical_length)
779            .add_buffer(builder.finish())
780            .build_unchecked()
781    };
782
783    // build new values by slicing physical indices.
784    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        // Safety:
795        //  This function builds a valid run array and hence can skip validation.
796        builder.build_unchecked()
797    };
798    Ok(array_data.into())
799}
800
801/// Controls how dictionaries are handled in Arrow IPC messages
802#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
803pub enum DictionaryHandling {
804    /// Send the entire dictionary every time it is encountered (default)
805    #[default]
806    Resend,
807    /// Send only new dictionary values since the last batch (delta encoding)
808    ///
809    /// When a dictionary is first encountered, the entire dictionary is sent.
810    /// For subsequent batches, only values that are new (not previously sent)
811    /// are transmitted with the `isDelta` flag set to true.
812    Delta,
813}
814
815/// Describes what kind of update took place after a call to [`DictionaryTracker::insert`].
816#[derive(Debug, Clone)]
817pub enum DictionaryUpdate {
818    /// No dictionary was written, the dictionary was identical to what was already
819    /// in the tracker.
820    None,
821    /// No dictionary was present in the tracker
822    New,
823    /// Dictionary was replaced with the new data
824    Replaced,
825    /// Dictionary was updated, ArrayData is the delta between old and new
826    Delta(ArrayData),
827}
828
829/// Keeps track of dictionaries that have been written, to avoid emitting the same dictionary
830/// multiple times.
831///
832/// Can optionally error if an update to an existing dictionary is attempted, which
833/// isn't allowed in the `FileWriter`.
834#[derive(Debug)]
835pub struct DictionaryTracker {
836    // NOTE: When adding fields, update the clear() method accordingly.
837    written: HashMap<i64, ArrayData>,
838    dict_ids: Vec<i64>,
839    error_on_replacement: bool,
840}
841
842impl DictionaryTracker {
843    /// Create a new [`DictionaryTracker`].
844    ///
845    /// If `error_on_replacement`
846    /// is true, an error will be generated if an update to an
847    /// existing dictionary is attempted.
848    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    /// Record and return the next dictionary ID.
858    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    /// Return the sequence of dictionary IDs in the order they should be observed while
871    /// traversing the schema
872    pub fn dict_id(&mut self) -> &[i64] {
873        &self.dict_ids
874    }
875
876    /// Keep track of the dictionary with the given ID and values. Behavior:
877    ///
878    /// * If this ID has been written already and has the same data, return `Ok(false)` to indicate
879    ///   that the dictionary was not actually inserted (because it's already been seen).
880    /// * If this ID has been written already but with different data, and this tracker is
881    ///   configured to return an error, return an error.
882    /// * If the tracker has not been configured to error on replacement or this dictionary
883    ///   has never been seen before, return `Ok(true)` to indicate that the dictionary was just
884    ///   inserted.
885    #[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 a dictionary with this id was already emitted, check if it was the same.
891        if let Some(last) = self.written.get(&dict_id) {
892            if ArrayData::ptr_eq(&last.child_data()[0], dict_values) {
893                // Same dictionary values => no need to emit it again
894                return Ok(false);
895            }
896            if self.error_on_replacement {
897                // If error on replacement perform a logical comparison
898                if last.child_data()[0] == *dict_values {
899                    // Same dictionary values => no need to emit it again
900                    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    /// Keep track of the dictionary with the given ID and values. The return
916    /// value indicates what, if any, update to the internal map took place
917    /// and how it should be interpreted based on the `dict_handling` parameter.
918    ///
919    /// # Returns
920    ///
921    /// * `Ok(Dictionary::New)` - If the dictionary was not previously written
922    /// * `Ok(Dictionary::Replaced)` - If the dictionary was previously written
923    ///   with completely different data, or if the data is a delta of the existing,
924    ///   but with `dict_handling` set to `DictionaryHandling::Resend`
925    /// * `Ok(Dictionary::Delta)` - If the dictionary was previously written, but
926    ///   the new data is a delta of the old and the `dict_handling` is set to
927    ///   `DictionaryHandling::Delta`
928    /// * `Err(e)` - If the dictionary was previously written with different data,
929    ///   and `error_on_replacement` is set to `true`.
930    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        // If there is no existing dictionary with this ID, we always insert
940        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        // Fast path - If the array data points to the same buffer as the
946        // existing then they're the same.
947        let old_values = &old.child_data()[0];
948        if ArrayData::ptr_eq(old_values, new_values) {
949            return Ok(DictionaryUpdate::None);
950        }
951
952        // Slow path - Compare the dictionaries value by value
953        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    /// Clears the state of the dictionary tracker.
996    ///
997    /// This allows the dictionary tracker to be reused for a new IPC stream while avoiding the
998    /// allocation cost of creating a new instance. This method should not be called if
999    /// the dictionary tracker will be used to continue writing to an existing IPC stream.
1000    pub fn clear(&mut self) {
1001        self.dict_ids.clear();
1002        self.written.clear();
1003    }
1004}
1005
1006/// Describes how two dictionary arrays compare to each other.
1007#[derive(Debug, Clone)]
1008enum DictionaryComparison {
1009    /// Neither a delta, nor an exact match
1010    NotEqual,
1011    /// Exact element-wise match
1012    Equal,
1013    /// The two arrays are dictionary deltas of each other, meaning the first
1014    /// is a prefix of the second.
1015    Delta,
1016}
1017
1018// Compares two dictionaries and returns a [`DictionaryComparison`].
1019fn compare_dictionaries(old: &ArrayData, new: &ArrayData) -> DictionaryComparison {
1020    // Check for exact match
1021    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    // Can't be a delta if the new is shorter than the existing
1032    if new_len < existing_len {
1033        return DictionaryComparison::NotEqual;
1034    }
1035
1036    // Check for delta
1037    if new.slice(0, existing_len) == *old {
1038        return DictionaryComparison::Delta;
1039    }
1040
1041    DictionaryComparison::NotEqual
1042}
1043
1044/// Arrow File Writer
1045///
1046/// Writes Arrow [`RecordBatch`]es in the [IPC File Format].
1047///
1048/// # See Also
1049///
1050/// * [`StreamWriter`] for writing IPC Streams
1051///
1052/// # Example
1053/// ```
1054/// # use arrow_array::record_batch;
1055/// # use arrow_ipc::writer::FileWriter;
1056/// # let mut file = vec![]; // mimic a file for the example
1057/// let batch = record_batch!(("a", Int32, [1, 2, 3])).unwrap();
1058/// // create a new writer, the schema must be known in advance
1059/// let mut writer = FileWriter::try_new(&mut file, &batch.schema()).unwrap();
1060/// // write each batch to the underlying writer
1061/// writer.write(&batch).unwrap();
1062/// // When all batches are written, call finish to flush all buffers
1063/// writer.finish().unwrap();
1064/// ```
1065/// [IPC File Format]: https://arrow.apache.org/docs/format/Columnar.html#ipc-file-format
1066pub struct FileWriter<W> {
1067    /// The object to write to
1068    writer: W,
1069    /// IPC write options
1070    write_options: IpcWriteOptions,
1071    /// A reference to the schema, used in validating record batches
1072    schema: SchemaRef,
1073    /// The number of bytes between each block of bytes, as an offset for random access
1074    block_offsets: usize,
1075    /// Dictionary blocks that will be written as part of the IPC footer
1076    dictionary_blocks: Vec<crate::Block>,
1077    /// Record blocks that will be written as part of the IPC footer
1078    record_blocks: Vec<crate::Block>,
1079    /// Whether the writer footer has been written, and the writer is finished
1080    finished: bool,
1081    /// Keeps track of dictionaries that have been written
1082    dictionary_tracker: DictionaryTracker,
1083    /// User level customized metadata
1084    custom_metadata: HashMap<String, String>,
1085
1086    data_gen: IpcDataGenerator,
1087
1088    compression_context: CompressionContext,
1089}
1090
1091impl<W: Write> FileWriter<BufWriter<W>> {
1092    /// Try to create a new file writer with the writer wrapped in a BufWriter.
1093    ///
1094    /// See [`FileWriter::try_new`] for an unbuffered version.
1095    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    /// Try to create a new writer, with the schema written as part of the header
1102    ///
1103    /// Note the created writer is not buffered. See [`FileWriter::try_new_buffered`] for details.
1104    ///
1105    /// # Errors
1106    ///
1107    /// An ['Err'](Result::Err) may be returned if writing the header to the writer fails.
1108    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    /// Try to create a new writer with IpcWriteOptions
1114    ///
1115    /// Note the created writer is not buffered. See [`FileWriter::try_new_buffered`] for details.
1116    ///
1117    /// # Errors
1118    ///
1119    /// An ['Err'](Result::Err) may be returned if writing the header to the writer fails.
1120    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        // write magic to header aligned on alignment boundary
1127        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        // write the schema, set the written bytes to the schema + header
1132        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    /// Adds a key-value pair to the [FileWriter]'s custom metadata
1155    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    /// Write a record batch to the file
1160    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        // add a record block for the footer
1186        let block = crate::Block::new(
1187            self.block_offsets as i64,
1188            meta as i32, // TODO: is this still applicable?
1189            data as i64,
1190        );
1191        self.record_blocks.push(block);
1192        self.block_offsets += meta + data;
1193        Ok(())
1194    }
1195
1196    /// Write footer and closing tag, then mark the writer as done
1197    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 EOS
1205        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        // dictionaries are already written, so we can reset dictionary tracker to reuse for schema
1212        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    /// Returns the arrow [`SchemaRef`] for this arrow file.
1243    pub fn schema(&self) -> &SchemaRef {
1244        &self.schema
1245    }
1246
1247    /// Gets a reference to the underlying writer.
1248    pub fn get_ref(&self) -> &W {
1249        &self.writer
1250    }
1251
1252    /// Gets a mutable reference to the underlying writer.
1253    ///
1254    /// It is inadvisable to directly write to the underlying writer.
1255    pub fn get_mut(&mut self) -> &mut W {
1256        &mut self.writer
1257    }
1258
1259    /// Flush the underlying writer.
1260    ///
1261    /// Both the BufWriter and the underlying writer are flushed.
1262    pub fn flush(&mut self) -> Result<(), ArrowError> {
1263        self.writer.flush()?;
1264        Ok(())
1265    }
1266
1267    /// Unwraps the underlying writer.
1268    ///
1269    /// The writer is flushed and the FileWriter is finished before returning.
1270    ///
1271    /// # Errors
1272    ///
1273    /// An ['Err'](Result::Err) may be returned if an error occurs while finishing the StreamWriter
1274    /// or while flushing the writer.
1275    pub fn into_inner(mut self) -> Result<W, ArrowError> {
1276        if !self.finished {
1277            // `finish` flushes the writer.
1278            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
1294/// Arrow Stream Writer
1295///
1296/// Writes Arrow [`RecordBatch`]es to bytes using the [IPC Streaming Format].
1297///
1298/// # See Also
1299///
1300/// * [`FileWriter`] for writing IPC Files
1301///
1302/// # Example - Basic usage
1303/// ```
1304/// # use arrow_array::record_batch;
1305/// # use arrow_ipc::writer::StreamWriter;
1306/// # let mut stream = vec![]; // mimic a stream for the example
1307/// let batch = record_batch!(("a", Int32, [1, 2, 3])).unwrap();
1308/// // create a new writer, the schema must be known in advance
1309/// let mut writer = StreamWriter::try_new(&mut stream, &batch.schema()).unwrap();
1310/// // write each batch to the underlying stream
1311/// writer.write(&batch).unwrap();
1312/// // When all batches are written, call finish to flush all buffers
1313/// writer.finish().unwrap();
1314/// ```
1315/// # Example - Efficient delta dictionaries
1316/// ```
1317/// # use arrow_array::record_batch;
1318/// # use arrow_ipc::writer::{StreamWriter, IpcWriteOptions};
1319/// # use arrow_ipc::writer::DictionaryHandling;
1320/// # use arrow_schema::{DataType, Field, Schema, SchemaRef};
1321/// # use arrow_array::{
1322/// #    builder::StringDictionaryBuilder, types::Int32Type, Array, ArrayRef, DictionaryArray,
1323/// #    RecordBatch, StringArray,
1324/// # };
1325/// # use std::sync::Arc;
1326///
1327/// let schema = Arc::new(Schema::new(vec![Field::new(
1328///    "col1",
1329///    DataType::Dictionary(Box::from(DataType::Int32), Box::from(DataType::Utf8)),
1330///    true,
1331/// )]));
1332///
1333/// let mut builder = StringDictionaryBuilder::<arrow_array::types::Int32Type>::new();
1334///
1335/// // `finish_preserve_values` will keep the dictionary values along with their
1336/// // key assignments so that they can be re-used in the next batch.
1337/// builder.append("a").unwrap();
1338/// builder.append("b").unwrap();
1339/// let array1 = builder.finish_preserve_values();
1340/// let batch1 = RecordBatch::try_new(schema.clone(), vec![Arc::new(array1) as ArrayRef]).unwrap();
1341///
1342/// // In this batch, 'a' will have the same dictionary key as 'a' in the previous batch,
1343/// // and 'd' will take the next available key.
1344/// builder.append("a").unwrap();
1345/// builder.append("d").unwrap();
1346/// let array2 = builder.finish_preserve_values();
1347/// let batch2 = RecordBatch::try_new(schema.clone(), vec![Arc::new(array2) as ArrayRef]).unwrap();
1348///
1349/// let mut stream = vec![];
1350/// // You must set `.with_dictionary_handling(DictionaryHandling::Delta)` to
1351/// // enable delta dictionaries in the writer
1352/// let options = IpcWriteOptions::default().with_dictionary_handling(DictionaryHandling::Delta);
1353/// let mut writer = StreamWriter::try_new_with_options(&mut stream, &schema, options).unwrap();
1354///
1355/// // When writing the first batch, a dictionary message with 'a' and 'b' will be written
1356/// // prior to the record batch.
1357/// writer.write(&batch1).unwrap();
1358/// // With the second batch only a delta dictionary with 'd' will be written
1359/// // prior to the record batch. This is only possible with `finish_preserve_values`.
1360/// // Without it, 'a' and 'd' in this batch would have different keys than the
1361/// // first batch and so we'd have to send a replacement dictionary with new keys
1362/// // for both.
1363/// writer.write(&batch2).unwrap();
1364/// writer.finish().unwrap();
1365/// ```
1366/// [IPC Streaming Format]: https://arrow.apache.org/docs/format/Columnar.html#ipc-streaming-format
1367pub struct StreamWriter<W> {
1368    /// The object to write to
1369    writer: W,
1370    /// IPC write options
1371    write_options: IpcWriteOptions,
1372    /// Whether the writer footer has been written, and the writer is finished
1373    finished: bool,
1374    /// Keeps track of dictionaries that have been written
1375    dictionary_tracker: DictionaryTracker,
1376
1377    data_gen: IpcDataGenerator,
1378
1379    compression_context: CompressionContext,
1380}
1381
1382impl<W: Write> StreamWriter<BufWriter<W>> {
1383    /// Try to create a new stream writer with the writer wrapped in a BufWriter.
1384    ///
1385    /// See [`StreamWriter::try_new`] for an unbuffered version.
1386    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    /// Try to create a new writer, with the schema written as part of the header.
1393    ///
1394    /// Note that there is no internal buffering. See also [`StreamWriter::try_new_buffered`].
1395    ///
1396    /// # Errors
1397    ///
1398    /// An ['Err'](Result::Err) may be returned if writing the header to the writer fails.
1399    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    /// Try to create a new writer with [`IpcWriteOptions`].
1405    ///
1406    /// # Errors
1407    ///
1408    /// An ['Err'](Result::Err) may be returned if writing the header to the writer fails.
1409    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        // write the schema, set the written bytes to the schema
1418        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    /// Write a record batch to the stream
1435    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    /// Write continuation bytes, and mark the stream as done
1461    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    /// Gets a reference to the underlying writer.
1477    pub fn get_ref(&self) -> &W {
1478        &self.writer
1479    }
1480
1481    /// Gets a mutable reference to the underlying writer.
1482    ///
1483    /// It is inadvisable to directly write to the underlying writer.
1484    pub fn get_mut(&mut self) -> &mut W {
1485        &mut self.writer
1486    }
1487
1488    /// Flush the underlying writer.
1489    ///
1490    /// Both the BufWriter and the underlying writer are flushed.
1491    pub fn flush(&mut self) -> Result<(), ArrowError> {
1492        self.writer.flush()?;
1493        Ok(())
1494    }
1495
1496    /// Unwraps the the underlying writer.
1497    ///
1498    /// The writer is flushed and the StreamWriter is finished before returning.
1499    ///
1500    /// # Errors
1501    ///
1502    /// An ['Err'](Result::Err) may be returned if an error occurs while finishing the StreamWriter
1503    /// or while flushing the writer.
1504    ///
1505    /// # Example
1506    ///
1507    /// ```
1508    /// # use arrow_ipc::writer::{StreamWriter, IpcWriteOptions};
1509    /// # use arrow_ipc::MetadataVersion;
1510    /// # use arrow_schema::{ArrowError, Schema};
1511    /// # fn main() -> Result<(), ArrowError> {
1512    /// // The result we expect from an empty schema
1513    /// let expected = vec![
1514    ///     255, 255, 255, 255,  48,   0,   0,   0,
1515    ///      16,   0,   0,   0,   0,   0,  10,   0,
1516    ///      12,   0,  10,   0,   9,   0,   4,   0,
1517    ///      10,   0,   0,   0,  16,   0,   0,   0,
1518    ///       0,   1,   4,   0,   8,   0,   8,   0,
1519    ///       0,   0,   4,   0,   8,   0,   0,   0,
1520    ///       4,   0,   0,   0,   0,   0,   0,   0,
1521    ///     255, 255, 255, 255,   0,   0,   0,   0
1522    /// ];
1523    ///
1524    /// let schema = Schema::empty();
1525    /// let buffer: Vec<u8> = Vec::new();
1526    /// let options = IpcWriteOptions::try_new(8, false, MetadataVersion::V5)?;
1527    /// let stream_writer = StreamWriter::try_new_with_options(buffer, &schema, options)?;
1528    ///
1529    /// assert_eq!(stream_writer.into_inner()?, expected);
1530    /// # Ok(())
1531    /// # }
1532    /// ```
1533    pub fn into_inner(mut self) -> Result<W, ArrowError> {
1534        if !self.finished {
1535            // `finish` flushes.
1536            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
1552/// Stores the encoded data, which is an crate::Message, and optional Arrow data
1553pub struct EncodedData {
1554    /// An encoded crate::Message
1555    pub ipc_message: Vec<u8>,
1556    /// Arrow buffers to be written, should be an empty vec for schema messages
1557    pub arrow_data: Vec<u8>,
1558}
1559/// Write a message's IPC data and buffers, returning metadata and buffer data lengths written
1560pub 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    // write the flatbuf
1590    if flatbuf_size > 0 {
1591        writer.write_all(&buffer)?;
1592    }
1593    // write padding
1594    writer.write_all(&PADDING[..padding_bytes])?;
1595
1596    // write arrow data
1597    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    // write body buffer
1616    writer.write_all(data)?;
1617    if pad_len > 0 {
1618        writer.write_all(&PADDING[..pad_len])?;
1619    }
1620
1621    Ok(total_len)
1622}
1623
1624/// Write a record batch to the writer, writing the message size before the message
1625/// if the record batch is being written to a stream
1626fn 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    // the version of the writer determines whether continuation markers should be added
1634    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                // v0.15.0 format
1641                writer.write_all(&CONTINUATION_MARKER)?;
1642                written = 4;
1643            }
1644            writer.write_all(&total_len.to_le_bytes()[..])?;
1645        }
1646        crate::MetadataVersion::V5 => {
1647            // write continuation marker and message length
1648            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
1657/// In V4, null types have no validity bitmap
1658/// In V5 and later, null and union types have no validity bitmap
1659/// Run end encoded type has no validity bitmap.
1660fn 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/// Whether to truncate the buffer
1672#[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/// Returns byte width for a buffer spec. Only for `BufferSpec::FixedWidth`.
1683#[inline]
1684fn get_buffer_element_width(spec: &BufferSpec) -> usize {
1685    match spec {
1686        BufferSpec::FixedWidth { byte_width, .. } => *byte_width,
1687        _ => 0,
1688    }
1689}
1690
1691/// Common functionality for re-encoding offsets. Returns the new offsets as well as
1692/// original start offset and length for use in slicing child data.
1693fn 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
1717/// Returns the values and offsets [`Buffer`] for a ByteArray with offset type `O`
1718///
1719/// In particular, this handles re-encoding the offsets if they don't start at `0`,
1720/// slicing the values buffer as appropriate. This helps reduce the encoded
1721/// size of sliced arrays, as values that have been sliced away are not encoded
1722fn get_byte_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, Buffer) {
1723    if data.is_empty() {
1724        // As per specification, offsets buffer has N+1 elements.
1725        // So an empty array should still be encoded with a single 0 offset.
1726        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
1736/// Similar logic as [`get_byte_array_buffers()`] but slices the child array instead
1737/// of a values buffer.
1738fn get_list_array_buffers<O: OffsetSizeTrait>(data: &ArrayData) -> (Buffer, ArrayData) {
1739    if data.is_empty() {
1740        // As per specification, offsets buffer has N+1 elements.
1741        // So an empty array should still be encoded with a single 0 offset.
1742        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
1752/// Returns the offsets, sizes, and child data buffers for a ListView array.
1753///
1754/// Unlike List arrays, ListView arrays store both offsets and sizes explicitly,
1755/// and offsets can be non-monotonic. When slicing, we simply pass through the
1756/// offsets and sizes without re-encoding, and do not slice the child data.
1757fn 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
1782/// Returns the sliced views [`Buffer`] for a BinaryView/Utf8View array.
1783///
1784/// The views buffer is sliced to only include views in the valid range based on
1785/// the array's offset and length. This helps reduce the encoded size of sliced
1786/// arrays
1787///
1788fn 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/// Write array data to a vector of bytes
1805#[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        // NullArray's null_count equals to len, but the `null_count` passed in is from ArrayData
1823        // where null_count is always 0.
1824        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        // Fields with null_count == 0 may write a 0-length validity buffer
1828        // instead of a materialized bitmap (see FieldNode in Message.fbs)
1829        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        // Slicing the views buffer is safe and easy,
1861        // but pruning unneeded data buffers is much more nuanced since it's complicated to prove that no views reference the pruned buffers
1862        //
1863        // Current implementation just serialize the raw arrays as given and not try to optimize anything.
1864        // If users wants to "compact" the arrays prior to sending them over IPC,
1865        // they should consider the gc API suggested in #5513
1866        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        // Truncate values
1909        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        // Bools are special because the payload (= 1 bit) is smaller than the physical container elements (= bytes).
1923        // The array data may not start at the physical boundary of the underlying buffer, so we need to shift bits around.
1924        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        // Truncate offsets and the child data to avoid writing unnecessary data
1945        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); // offsets + sizes
1978        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            // unslice the run encoded array.
2058            let arr = unslice_run_array(array_data.clone())?;
2059            // recursively write out nested structures
2060            for data_ref in arr.child_data() {
2061                // write the nested data (e.g list data)
2062                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            // recursively write out nested structures
2078            for data_ref in array_data.child_data() {
2079                // write the nested data (e.g list data)
2080                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
2098/// Write a buffer into `arrow_data`, a vector of bytes, and adds its
2099/// [`crate::Buffer`] to `buffers`. Returns the new offset in `arrow_data`
2100///
2101///
2102/// From <https://github.com/apache/arrow/blob/6a936c4ff5007045e86f65f1a6b6c3c955ad5103/format/Message.fbs#L58>
2103/// Each constituent buffer is first compressed with the indicated
2104/// compressor, and then written with the uncompressed length in the first 8
2105/// bytes as a 64-bit little-endian signed integer followed by the compressed
2106/// buffer bytes (and then padding as required by the protocol). The
2107/// uncompressed length may be set to -1 to indicate that the data that
2108/// follows is not compressed, which can be useful for cases where
2109/// compression does not yield appreciable savings.
2110fn write_buffer(
2111    buffer: &[u8],                    // input
2112    buffers: &mut Vec<crate::Buffer>, // output buffer descriptors
2113    arrow_data: &mut Vec<u8>,         // output stream
2114    offset: i64,                      // current output stream offset
2115    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    // make new index entry
2132    buffers.push(crate::Buffer::new(offset, len));
2133    // padding and make offset aligned
2134    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/// Calculate an alignment boundary and return the number of bytes needed to pad to the alignment boundary
2143#[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        // Use 8-byte alignment so that the various `truncate_*` tests can be compactly written,
2189        // without needing to construct a giant array to spill over the 64-byte default alignment
2190        // boundary.
2191        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            // read file
2234            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            // read file
2274            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            // read file
2313            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        // Dict field with id 2
2496        #[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        // The encoder will assign dict IDs itself to ensure uniqueness and ignore the dict ID in the schema
2531        // so we expect the dict will be keyed to 0
2532        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        // Dict field with id 2
2542        #[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            // Keeps a null buffer, but the slice has no nulls
2757            (
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        // Each column writes a validity buffer, then a values buffer
2777        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); // null
2948            }
2949        }
2950
2951        ensure_roundtrip(Arc::new(ls.finish()));
2952    }
2953
2954    #[test]
2955    fn test_large_slice_string_list_of_lists() {
2956        // The reason for the special test is to verify reencode_offsets which looks both at
2957        // the starting offset and the data offset.  So need a dataset where the starting_offset
2958        // is zero but the data offset is not.
2959        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); // null
2979            }
2980        }
2981
2982        ensure_roundtrip(Arc::new(ls.finish()));
2983    }
2984
2985    /// Read/write a record batch to a File and Stream and ensure it is the same at the outout
2986    fn ensure_roundtrip(array: ArrayRef) {
2987        let num_rows = array.len();
2988        let orig_batch = RecordBatch::try_from_iter(vec![("a", array)]).unwrap();
2989        // take off the first element
2990        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        // TODO test file writer/reader
3024    }
3025
3026    #[test]
3027    fn encode_bools_slice() {
3028        // Test case for https://github.com/apache/arrow-rs/issues/3496
3029        assert_bool_roundtrip([true, false], 1, 1);
3030
3031        // slice somewhere in the middle
3032        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        // start at byte boundary, end in the middle
3043        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        // start and stop and byte boundary
3052        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        // Encode the input_array to run array
3090        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        // test for all slice lengths.
3096        for slice_len in 1..=total_len {
3097            // test for offset = 0, slice length = slice_len
3098            let sliced_run_array: RunArray<Int16Type> =
3099                run_array.slice(0, slice_len).into_data().into();
3100
3101            // Create unsliced run array.
3102            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            // test for offset = total_len - slice_len, length = slice_len
3111            let sliced_run_array: RunArray<Int16Type> = run_array
3112                .slice(total_len - slice_len, slice_len)
3113                .into_data()
3114                .into();
3115
3116            // Create unsliced run array.
3117            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 = [[], [35, 42]
3261        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    /// Ensure when serde full & sliced versions they are equal to original input.
3277    /// Also ensure serialized sliced version is significantly smaller than serialized full.
3278    fn roundtrip_ensure_sliced_smaller(in_batch: RecordBatch, expected_size_factor: usize) {
3279        // test both full and sliced versions
3280        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        // serializing 1 row should be significantly smaller than serializing 100,000
3286        assert!(bytes_sliced.len() < (bytes_batch.len() / expected_size_factor));
3287
3288        // ensure both are still valid and equal to originals
3289        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        // ensure when serde full & sliced versions they are equal to original input
3332        // also ensure serialized sliced version is significantly smaller than serialized full
3333        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        // Building a map array is a bit involved. We first build a struct arary that has a key and
3733        // value field and then use that to build the actual map array.
3734        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        // Building a map array is a bit involved. We first build a struct arary that has a key and
3801        // value field and then use that to build the actual map array.
3802        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        // Test a bunch of different dimensions to ensure alignment is never an issue.
3871        // For example, if we only test `num_cols = 1` then even with alignment 8 this
3872        // test would _happen_ to pass, even though for different dimensions like
3873        // `num_cols = 2` it would fail.
3874        for num_cols in [1, 2, 3, 17, 50, 73, 99] {
3875            let num_rows = (num_cols * 7 + 11) % 100; // Deterministic swizzle
3876
3877            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            // Importantly we set `require_alignment`, checking that 16-byte alignment is sufficient
3909            // for `read_record_batch` later on to read the data in a zero-copy manner.
3910            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            // The null makes the writer emit a 1-byte validity buffer. With IPC_ALIGNMENT = 8,
3936            // the writer pads that buffer to 8 bytes, so the decimal values that follow start
3937            // at offset 8 of the body, which is not a multiple of 16.
3938            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        // Importantly we set `require_alignment`, otherwise the error later is suppressed due to copying
3963        // to an aligned buffer in `ArrayDataBuilder.build_aligned`.
3964        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        // We write a schema which is small enough to fit into a buffer and not get flushed,
3986        // and then force the write with .flush().
3987        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        // Finishing a stream writes the continuation bytes in MetadataVersion::V5 (4 bytes)
4011        // and then a length of 0 (4 bytes) for a total of 8 bytes.
4012        // Everything before that should have been flushed in the .flush() call.
4013        let expected_stream_flushed_bytes = stream_out.len() - 8;
4014        // A file write is the same as the stream write except for the leading magic string
4015        // ARROW1 plus padding, which is 8 bytes.
4016        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 a variety of combinations that include 0 and non-zero offsets
4070        // and also portions or the rest of the array
4071        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 a variety of combinations that include 0 and non-zero offsets
4130        // and also portions or the rest of the array
4131        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 a variety of combinations that include 0 and non-zero offsets
4187        // and also portions or the rest of the array
4188        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 a variety of combinations that include 0 and non-zero offsets
4229        // and also portions or the rest of the array
4230        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"), //
4243                ("b", "2"), //
4244                ("c", "3"), //
4245                ("d", "4"), //
4246                ("e", "5"), //
4247            ]
4248            .into_iter()
4249            .map(|(k, v)| (k.to_owned(), v.to_owned()))
4250            .collect();
4251
4252            // Set metadata on both the schema and a field within it.
4253            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        // Since there is randomness in the HashMap and we cannot specify our
4275        // own Hasher for the implementation used for metadata, run the above
4276        // code 20x and verify it does not change. This is not perfect but it
4277        // should be good enough.
4278        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                // create a new IPC stream:
4300                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        // ensure we can read the stream back
4329        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        // reset the dictionary tracker so it can be used for next stream
4334        dictionary_tracker.clear();
4335
4336        // now write a 2nd stream and ensure we can also read it:
4337        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}