Skip to main content

arrow_ipc/
convert.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//! Utilities for converting between IPC types and native Arrow types
19
20use arrow_buffer::Buffer;
21use arrow_schema::*;
22use core::panic;
23use flatbuffers::{
24    FlatBufferBuilder, ForwardsUOffset, UnionWIPOffset, Vector, Verifiable, Verifier,
25    VerifierOptions, WIPOffset,
26};
27use std::collections::HashMap;
28use std::fmt::{Debug, Formatter};
29use std::sync::Arc;
30
31use crate::writer::DictionaryTracker;
32use crate::{CONTINUATION_MARKER, KeyValue, Message};
33use DataType::*;
34
35/// Low level Arrow [Schema] to IPC bytes converter
36///
37/// See also [`try_fb_to_schema`] for the reverse operation
38///
39/// # Example
40/// ```
41/// # use arrow_ipc::convert::{try_fb_to_schema, IpcSchemaEncoder};
42/// # use arrow_ipc::root_as_schema;
43/// # use arrow_ipc::writer::DictionaryTracker;
44/// # use arrow_schema::{DataType, Field, Schema};
45/// // given an arrow schema to serialize
46/// let schema = Schema::new(vec![
47///    Field::new("a", DataType::Int32, false),
48/// ]);
49///
50/// // Use a dictionary tracker to track dictionary id if needed
51///  let mut dictionary_tracker = DictionaryTracker::new(true);
52/// // create a FlatBuffersBuilder that contains the encoded bytes
53///  let fb = IpcSchemaEncoder::new()
54///    .with_dictionary_tracker(&mut dictionary_tracker)
55///    .schema_to_fb(&schema);
56///
57/// // the bytes are in `fb.finished_data()`
58/// let ipc_bytes = fb.finished_data();
59///
60///  // convert the IPC bytes back to an Arrow schema
61///  let ipc_schema = root_as_schema(ipc_bytes).unwrap();
62///  let schema2 = try_fb_to_schema(ipc_schema).unwrap();
63/// assert_eq!(schema, schema2);
64/// ```
65#[derive(Debug)]
66pub struct IpcSchemaEncoder<'a> {
67    dictionary_tracker: Option<&'a mut DictionaryTracker>,
68}
69
70impl Default for IpcSchemaEncoder<'_> {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl<'a> IpcSchemaEncoder<'a> {
77    /// Create a new schema encoder
78    pub fn new() -> IpcSchemaEncoder<'a> {
79        IpcSchemaEncoder {
80            dictionary_tracker: None,
81        }
82    }
83
84    /// Specify a dictionary tracker to use
85    pub fn with_dictionary_tracker(
86        mut self,
87        dictionary_tracker: &'a mut DictionaryTracker,
88    ) -> Self {
89        self.dictionary_tracker = Some(dictionary_tracker);
90        self
91    }
92
93    /// Serialize a schema in IPC format, returning a completed [`FlatBufferBuilder`]
94    ///
95    /// Note: Call [`FlatBufferBuilder::finished_data`] to get the serialized bytes
96    pub fn schema_to_fb<'b>(&mut self, schema: &Schema) -> FlatBufferBuilder<'b> {
97        let mut fbb = FlatBufferBuilder::new();
98
99        let root = self.schema_to_fb_offset(&mut fbb, schema);
100
101        fbb.finish(root, None);
102
103        fbb
104    }
105
106    /// Serialize a schema to an in progress [`FlatBufferBuilder`], returning the in progress offset.
107    pub fn schema_to_fb_offset<'b>(
108        &mut self,
109        fbb: &mut FlatBufferBuilder<'b>,
110        schema: &Schema,
111    ) -> WIPOffset<crate::Schema<'b>> {
112        let fields = schema
113            .fields()
114            .iter()
115            .map(|field| build_field(fbb, &mut self.dictionary_tracker, field))
116            .collect::<Vec<_>>();
117        let fb_field_list = fbb.create_vector(&fields);
118
119        let fb_metadata_list =
120            (!schema.metadata().is_empty()).then(|| metadata_to_fb(fbb, schema.metadata()));
121
122        let mut builder = crate::SchemaBuilder::new(fbb);
123        builder.add_fields(fb_field_list);
124        if let Some(fb_metadata_list) = fb_metadata_list {
125            builder.add_custom_metadata(fb_metadata_list);
126        }
127        builder.finish()
128    }
129}
130
131/// Push a key-value metadata into a FlatBufferBuilder and return [WIPOffset]
132pub fn metadata_to_fb<'a>(
133    fbb: &mut FlatBufferBuilder<'a>,
134    metadata: &Metadata,
135) -> WIPOffset<Vector<'a, ForwardsUOffset<KeyValue<'a>>>> {
136    // `Metadata` iterates in deterministic (sorted) key order
137    let custom_metadata = metadata
138        .iter()
139        .map(|(k, v)| {
140            let fb_key_name = fbb.create_string(k);
141            let fb_val_name = fbb.create_string(v);
142
143            let mut kv_builder = crate::KeyValueBuilder::new(fbb);
144            kv_builder.add_key(fb_key_name);
145            kv_builder.add_value(fb_val_name);
146            kv_builder.finish()
147        })
148        .collect::<Vec<_>>();
149    fbb.create_vector(&custom_metadata)
150}
151
152/// Adds a [Schema] to a flatbuffer and returns the offset
153pub fn schema_to_fb_offset<'a>(
154    fbb: &mut FlatBufferBuilder<'a>,
155    schema: &Schema,
156) -> WIPOffset<crate::Schema<'a>> {
157    IpcSchemaEncoder::new().schema_to_fb_offset(fbb, schema)
158}
159
160/// Convert an IPC Field to Arrow Field
161///
162/// This panics on malformed input; every reader path uses the fallible
163/// conversion instead. kept for backwards compatibility only.
164impl From<crate::Field<'_>> for Field {
165    fn from(field: crate::Field) -> Field {
166        try_field_from(field).expect("invalid IPC field")
167    }
168}
169
170/// Convert an IPC Field to Arrow Field
171fn try_field_from(field: crate::Field) -> Result<Field, ArrowError> {
172    let arrow_field = if let Some(dictionary) = field.dictionary() {
173        #[expect(deprecated)]
174        Field::new_dict(
175            field.name().unwrap_or_default(),
176            get_data_type(field, true)?,
177            field.nullable(),
178            dictionary.id(),
179            dictionary.isOrdered(),
180        )
181    } else {
182        Field::new(
183            field.name().unwrap_or_default(),
184            get_data_type(field, true)?,
185            field.nullable(),
186        )
187    };
188
189    let mut metadata_map = HashMap::default();
190    if let Some(list) = field.custom_metadata() {
191        for kv in list {
192            if let (Some(k), Some(v)) = (kv.key(), kv.value()) {
193                metadata_map.insert(k.to_string(), v.to_string());
194            }
195        }
196    }
197
198    Ok(arrow_field.with_metadata(metadata_map))
199}
200
201/// Deserialize an ipc [`crate::Schema`] from flat buffers to an arrow [Schema].
202#[deprecated(since = "60.0.0", note = "Use `try_fb_to_schema` instead")]
203pub fn fb_to_schema(fb: crate::Schema) -> Schema {
204    try_fb_to_schema(fb).expect("invalid IPC schema")
205}
206
207/// Deserialize an ipc [`crate::Schema`] from flat buffers to an arrow [Schema].
208///
209/// Returns an error on schema messages that the flatbuffer
210/// verifier accepts but that are not valid Arrow.
211pub fn try_fb_to_schema(fb: crate::Schema) -> Result<Schema, ArrowError> {
212    let mut fields: Vec<Field> = vec![];
213    let c_fields = fb
214        .fields()
215        .ok_or_else(|| ArrowError::ParseError("IPC schema has no fields".to_string()))?;
216    let len = c_fields.len();
217    for i in 0..len {
218        let c_field: crate::Field = c_fields.get(i);
219        match c_field.type_type() {
220            crate::Type::Decimal if fb.endianness() == crate::Endianness::Big => {
221                return Err(ArrowError::ParseError(
222                    "Big Endian is not supported for Decimal!".to_string(),
223                ));
224            }
225            _ => (),
226        }
227        fields.push(try_field_from(c_field)?);
228    }
229
230    let mut metadata: HashMap<String, String> = HashMap::default();
231    if let Some(md_fields) = fb.custom_metadata() {
232        let len = md_fields.len();
233        for i in 0..len {
234            let kv = md_fields.get(i);
235            let k_str = kv.key();
236            let v_str = kv.value();
237            if let Some(k) = k_str
238                && let Some(v) = v_str
239            {
240                metadata.insert(k.to_string(), v.to_string());
241            }
242        }
243    }
244    Ok(Schema::new_with_metadata(fields, metadata))
245}
246
247/// Try deserialize flat buffer format bytes into a schema
248pub fn try_schema_from_flatbuffer_bytes(bytes: &[u8]) -> Result<Schema, ArrowError> {
249    if let Ok(ipc) = crate::root_as_message(bytes) {
250        if let Some(schema) = ipc.header_as_schema() {
251            try_fb_to_schema(schema)
252        } else {
253            Err(ArrowError::ParseError(
254                "Unable to get head as schema".to_string(),
255            ))
256        }
257    } else {
258        Err(ArrowError::ParseError(
259            "Unable to get root as message".to_string(),
260        ))
261    }
262}
263
264/// Try deserialize the IPC format bytes into a schema
265pub fn try_schema_from_ipc_buffer(buffer: &[u8]) -> Result<Schema, ArrowError> {
266    // There are two protocol types: https://issues.apache.org/jira/browse/ARROW-6313
267    // The original protocol is:
268    //   4 bytes - the byte length of the payload
269    //   a flatbuffer Message whose header is the Schema
270    // The latest version of protocol is:
271    // The schema of the dataset in its IPC form:
272    //   4 bytes - an optional IPC_CONTINUATION_TOKEN prefix
273    //   4 bytes - the byte length of the payload
274    //   a flatbuffer Message whose header is the Schema
275    if buffer.len() < 4 {
276        return Err(ArrowError::ParseError(
277            "The buffer length is less than 4 and missing the continuation marker or length of buffer".to_string()
278        ));
279    }
280
281    let (len, buffer) = if buffer[..4] == CONTINUATION_MARKER {
282        if buffer.len() < 8 {
283            return Err(ArrowError::ParseError(
284                "The buffer length is less than 8 and missing the length of buffer".to_string(),
285            ));
286        }
287        buffer[4..].split_at(4)
288    } else {
289        buffer.split_at(4)
290    };
291
292    let len = <i32>::from_le_bytes(len.try_into().unwrap());
293    if len < 0 {
294        return Err(ArrowError::ParseError(format!(
295            "The encapsulated message's reported length is negative ({len})"
296        )));
297    }
298
299    if buffer.len() < len as usize {
300        let actual_len = buffer.len();
301        return Err(ArrowError::ParseError(format!(
302            "The buffer length ({actual_len}) is less than the encapsulated message's reported length ({len})"
303        )));
304    }
305
306    let msg = crate::root_as_message(buffer)
307        .map_err(|err| ArrowError::ParseError(format!("Unable to get root as message: {err:?}")))?;
308    let ipc_schema = msg.header_as_schema().ok_or_else(|| {
309        ArrowError::ParseError("Unable to convert flight info to a schema".to_string())
310    })?;
311    try_fb_to_schema(ipc_schema)
312}
313
314/// Get the Arrow data type from the flatbuffer Field table
315pub(crate) fn get_data_type(
316    field: crate::Field,
317    may_be_dictionary: bool,
318) -> Result<DataType, ArrowError> {
319    // helper: the flatbuffer verifier accepts a Field whose `type_type` tag does
320    // not match the union value present, so every `type_as_*` accessor can return
321    // None. surface that as an error instead of unwrapping.
322    fn type_err(what: &str) -> ArrowError {
323        ArrowError::ParseError(format!("IPC schema field is missing its {what} type"))
324    }
325
326    if let Some(dictionary) = field.dictionary()
327        && may_be_dictionary
328    {
329        let int = dictionary
330            .indexType()
331            .ok_or_else(|| type_err("dictionary index"))?;
332        let index_type = match (int.bitWidth(), int.is_signed()) {
333            (8, true) => DataType::Int8,
334            (8, false) => DataType::UInt8,
335            (16, true) => DataType::Int16,
336            (16, false) => DataType::UInt16,
337            (32, true) => DataType::Int32,
338            (32, false) => DataType::UInt32,
339            (64, true) => DataType::Int64,
340            (64, false) => DataType::UInt64,
341            _ => {
342                return Err(ArrowError::ParseError(format!(
343                    "Index type with bit width of {} and signed of {} not supported",
344                    int.bitWidth(),
345                    int.is_signed()
346                )));
347            }
348        };
349        return Ok(DataType::Dictionary(
350            Box::new(index_type),
351            Box::new(get_data_type(field, false)?),
352        ));
353    }
354
355    let data_type = match field.type_type() {
356        crate::Type::Null => DataType::Null,
357        crate::Type::Bool => DataType::Boolean,
358        crate::Type::Int => {
359            let int = field.type_as_int().ok_or_else(|| type_err("int"))?;
360            match (int.bitWidth(), int.is_signed()) {
361                (8, true) => DataType::Int8,
362                (8, false) => DataType::UInt8,
363                (16, true) => DataType::Int16,
364                (16, false) => DataType::UInt16,
365                (32, true) => DataType::Int32,
366                (32, false) => DataType::UInt32,
367                (64, true) => DataType::Int64,
368                (64, false) => DataType::UInt64,
369                z => {
370                    return Err(ArrowError::ParseError(format!(
371                        "Int type with bit width of {} and signed of {} not supported",
372                        z.0, z.1
373                    )));
374                }
375            }
376        }
377        crate::Type::Binary => DataType::Binary,
378        crate::Type::BinaryView => DataType::BinaryView,
379        crate::Type::LargeBinary => DataType::LargeBinary,
380        crate::Type::Utf8 => DataType::Utf8,
381        crate::Type::Utf8View => DataType::Utf8View,
382        crate::Type::LargeUtf8 => DataType::LargeUtf8,
383        crate::Type::FixedSizeBinary => {
384            let fsb = field
385                .type_as_fixed_size_binary()
386                .ok_or_else(|| type_err("fixed size binary"))?;
387            DataType::FixedSizeBinary(fsb.byteWidth())
388        }
389        crate::Type::FloatingPoint => {
390            let float = field
391                .type_as_floating_point()
392                .ok_or_else(|| type_err("floating point"))?;
393            match float.precision() {
394                crate::Precision::HALF => DataType::Float16,
395                crate::Precision::SINGLE => DataType::Float32,
396                crate::Precision::DOUBLE => DataType::Float64,
397                z => {
398                    return Err(ArrowError::ParseError(format!(
399                        "FloatingPoint type with precision of {z:?} not supported"
400                    )));
401                }
402            }
403        }
404        crate::Type::Date => {
405            let date = field.type_as_date().ok_or_else(|| type_err("date"))?;
406            match date.unit() {
407                crate::DateUnit::DAY => DataType::Date32,
408                crate::DateUnit::MILLISECOND => DataType::Date64,
409                z => {
410                    return Err(ArrowError::ParseError(format!(
411                        "Date type with unit of {z:?} not supported"
412                    )));
413                }
414            }
415        }
416        crate::Type::Time => {
417            let time = field.type_as_time().ok_or_else(|| type_err("time"))?;
418            match (time.bitWidth(), time.unit()) {
419                (32, crate::TimeUnit::SECOND) => DataType::Time32(TimeUnit::Second),
420                (32, crate::TimeUnit::MILLISECOND) => DataType::Time32(TimeUnit::Millisecond),
421                (64, crate::TimeUnit::MICROSECOND) => DataType::Time64(TimeUnit::Microsecond),
422                (64, crate::TimeUnit::NANOSECOND) => DataType::Time64(TimeUnit::Nanosecond),
423                z => {
424                    return Err(ArrowError::ParseError(format!(
425                        "Time type with bit width of {} and unit of {:?} not supported",
426                        z.0, z.1
427                    )));
428                }
429            }
430        }
431        crate::Type::Timestamp => {
432            let timestamp = field
433                .type_as_timestamp()
434                .ok_or_else(|| type_err("timestamp"))?;
435            let timezone: Option<_> = timestamp.timezone().map(|tz| tz.into());
436            match timestamp.unit() {
437                crate::TimeUnit::SECOND => DataType::Timestamp(TimeUnit::Second, timezone),
438                crate::TimeUnit::MILLISECOND => {
439                    DataType::Timestamp(TimeUnit::Millisecond, timezone)
440                }
441                crate::TimeUnit::MICROSECOND => {
442                    DataType::Timestamp(TimeUnit::Microsecond, timezone)
443                }
444                crate::TimeUnit::NANOSECOND => DataType::Timestamp(TimeUnit::Nanosecond, timezone),
445                z => {
446                    return Err(ArrowError::ParseError(format!(
447                        "Timestamp type with unit of {z:?} not supported"
448                    )));
449                }
450            }
451        }
452        crate::Type::Interval => {
453            let interval = field
454                .type_as_interval()
455                .ok_or_else(|| type_err("interval"))?;
456            match interval.unit() {
457                crate::IntervalUnit::YEAR_MONTH => DataType::Interval(IntervalUnit::YearMonth),
458                crate::IntervalUnit::DAY_TIME => DataType::Interval(IntervalUnit::DayTime),
459                crate::IntervalUnit::MONTH_DAY_NANO => {
460                    DataType::Interval(IntervalUnit::MonthDayNano)
461                }
462                z => {
463                    return Err(ArrowError::ParseError(format!(
464                        "Interval type with unit of {z:?} unsupported"
465                    )));
466                }
467            }
468        }
469        crate::Type::Duration => {
470            let duration = field
471                .type_as_duration()
472                .ok_or_else(|| type_err("duration"))?;
473            match duration.unit() {
474                crate::TimeUnit::SECOND => DataType::Duration(TimeUnit::Second),
475                crate::TimeUnit::MILLISECOND => DataType::Duration(TimeUnit::Millisecond),
476                crate::TimeUnit::MICROSECOND => DataType::Duration(TimeUnit::Microsecond),
477                crate::TimeUnit::NANOSECOND => DataType::Duration(TimeUnit::Nanosecond),
478                z => {
479                    return Err(ArrowError::ParseError(format!(
480                        "Duration type with unit of {z:?} unsupported"
481                    )));
482                }
483            }
484        }
485        crate::Type::List => {
486            let child = single_child(&field, "list")?;
487            DataType::List(Arc::new(try_field_from(child)?))
488        }
489        crate::Type::LargeList => {
490            let child = single_child(&field, "large list")?;
491            DataType::LargeList(Arc::new(try_field_from(child)?))
492        }
493        crate::Type::ListView => {
494            let child = single_child(&field, "listview")?;
495            DataType::ListView(Arc::new(try_field_from(child)?))
496        }
497        crate::Type::LargeListView => {
498            let child = single_child(&field, "large listview")?;
499            DataType::LargeListView(Arc::new(try_field_from(child)?))
500        }
501        crate::Type::FixedSizeList => {
502            let child = single_child(&field, "list")?;
503            let fsl = field
504                .type_as_fixed_size_list()
505                .ok_or_else(|| type_err("fixed size list"))?;
506            DataType::FixedSizeList(Arc::new(try_field_from(child)?), fsl.listSize())
507        }
508        crate::Type::Struct_ => {
509            let fields = match field.children() {
510                Some(children) => children
511                    .iter()
512                    .map(try_field_from)
513                    .collect::<Result<Vec<_>, _>>()?
514                    .into(),
515                None => Fields::empty(),
516            };
517            DataType::Struct(fields)
518        }
519        crate::Type::RunEndEncoded => {
520            let children = field
521                .children()
522                .ok_or_else(|| ArrowError::ParseError("IPC list has no children".to_string()))?;
523            if children.len() != 2 {
524                return Err(ArrowError::ParseError(format!(
525                    "RunEndEncoded type should have exactly two children. Found {}",
526                    children.len()
527                )));
528            }
529            let run_ends_field = try_field_from(children.get(0))?;
530            let values_field = try_field_from(children.get(1))?;
531            DataType::RunEndEncoded(Arc::new(run_ends_field), Arc::new(values_field))
532        }
533        crate::Type::Map => {
534            let map = field.type_as_map().ok_or_else(|| type_err("map"))?;
535            let child = single_child(&field, "map")?;
536            DataType::Map(Arc::new(try_field_from(child)?), map.keysSorted())
537        }
538        crate::Type::Decimal => {
539            let fsb = field.type_as_decimal().ok_or_else(|| type_err("decimal"))?;
540            let bit_width = fsb.bitWidth();
541            let precision: u8 = fsb.precision().try_into().map_err(|_| {
542                ArrowError::ParseError(format!("Invalid decimal precision {}", fsb.precision()))
543            })?;
544            let scale: i8 = fsb.scale().try_into().map_err(|_| {
545                ArrowError::ParseError(format!("Invalid decimal scale {}", fsb.scale()))
546            })?;
547            match bit_width {
548                32 => DataType::Decimal32(precision, scale),
549                64 => DataType::Decimal64(precision, scale),
550                128 => DataType::Decimal128(precision, scale),
551                256 => DataType::Decimal256(precision, scale),
552                _ => {
553                    return Err(ArrowError::ParseError(format!(
554                        "Unexpected decimal bit width {bit_width}"
555                    )));
556                }
557            }
558        }
559        crate::Type::Union => {
560            let union = field.type_as_union().ok_or_else(|| type_err("union"))?;
561
562            let union_mode = match union.mode() {
563                crate::UnionMode::Dense => UnionMode::Dense,
564                crate::UnionMode::Sparse => UnionMode::Sparse,
565                mode => {
566                    return Err(ArrowError::ParseError(format!(
567                        "Unexpected union mode: {mode:?}"
568                    )));
569                }
570            };
571
572            let mut fields = vec![];
573            if let Some(children) = field.children() {
574                for i in 0..children.len() {
575                    fields.push(try_field_from(children.get(i))?);
576                }
577            }
578
579            let fields = match union.typeIds() {
580                None => UnionFields::from_fields(fields),
581                Some(ids) => UnionFields::try_new(ids.iter().map(|i| i as i8), fields)?,
582            };
583
584            DataType::Union(fields, union_mode)
585        }
586        t => return Err(ArrowError::ParseError(format!("Type {t:?} not supported"))),
587    };
588    Ok(data_type)
589}
590
591/// Return the single child of a flatbuffer Field, erroring if it does not have
592/// exactly one.
593fn single_child<'a>(field: &crate::Field<'a>, kind: &str) -> Result<crate::Field<'a>, ArrowError> {
594    let children = field
595        .children()
596        .ok_or_else(|| ArrowError::ParseError(format!("IPC {kind} has no children")))?;
597    if children.len() != 1 {
598        return Err(ArrowError::ParseError(format!(
599            "expect a {kind} to have one child"
600        )));
601    }
602    Ok(children.get(0))
603}
604
605pub(crate) struct FBFieldType<'b> {
606    pub(crate) type_type: crate::Type,
607    pub(crate) type_: WIPOffset<UnionWIPOffset>,
608    pub(crate) children: Option<WIPOffset<Vector<'b, ForwardsUOffset<crate::Field<'b>>>>>,
609}
610
611/// Create an IPC Field from an Arrow Field
612pub(crate) fn build_field<'a>(
613    fbb: &mut FlatBufferBuilder<'a>,
614    dictionary_tracker: &mut Option<&mut DictionaryTracker>,
615    field: &Field,
616) -> WIPOffset<crate::Field<'a>> {
617    // Optional custom metadata.
618    let fb_metadata = if field.metadata().is_empty() {
619        None
620    } else {
621        Some(metadata_to_fb(fbb, field.metadata()))
622    };
623
624    let fb_field_name = fbb.create_string(field.name().as_str());
625    let field_type = get_fb_field_type(field.data_type(), dictionary_tracker, fbb);
626
627    let fb_dictionary = if let Dictionary(index_type, _) = field.data_type() {
628        match dictionary_tracker {
629            Some(tracker) => Some(get_fb_dictionary(
630                index_type,
631                tracker.next_dict_id(),
632                field
633                    .dict_is_ordered()
634                    .expect("All Dictionary types have `dict_is_ordered`"),
635                fbb,
636            )),
637            None => panic!("IPC must no longer be used without dictionary tracker"),
638        }
639    } else {
640        None
641    };
642
643    let mut field_builder = crate::FieldBuilder::new(fbb);
644    field_builder.add_name(fb_field_name);
645    if let Some(dictionary) = fb_dictionary {
646        field_builder.add_dictionary(dictionary)
647    }
648    field_builder.add_type_type(field_type.type_type);
649    field_builder.add_nullable(field.is_nullable());
650    match field_type.children {
651        None => {}
652        Some(children) => field_builder.add_children(children),
653    }
654    field_builder.add_type_(field_type.type_);
655
656    if let Some(fb_metadata) = fb_metadata {
657        field_builder.add_custom_metadata(fb_metadata);
658    }
659
660    field_builder.finish()
661}
662
663/// Get the IPC type of a data type
664pub(crate) fn get_fb_field_type<'a>(
665    data_type: &DataType,
666    dictionary_tracker: &mut Option<&mut DictionaryTracker>,
667    fbb: &mut FlatBufferBuilder<'a>,
668) -> FBFieldType<'a> {
669    // some IPC implementations expect an empty list for child data, instead of a null value.
670    // An empty field list is thus returned for primitive types
671    let empty_fields: Vec<WIPOffset<crate::Field>> = vec![];
672    match data_type {
673        Null => FBFieldType {
674            type_type: crate::Type::Null,
675            type_: crate::NullBuilder::new(fbb).finish().as_union_value(),
676            children: Some(fbb.create_vector(&empty_fields[..])),
677        },
678        Boolean => FBFieldType {
679            type_type: crate::Type::Bool,
680            type_: crate::BoolBuilder::new(fbb).finish().as_union_value(),
681            children: Some(fbb.create_vector(&empty_fields[..])),
682        },
683        UInt8 | UInt16 | UInt32 | UInt64 => {
684            let children = fbb.create_vector(&empty_fields[..]);
685            let mut builder = crate::IntBuilder::new(fbb);
686            builder.add_is_signed(false);
687            match data_type {
688                UInt8 => builder.add_bitWidth(8),
689                UInt16 => builder.add_bitWidth(16),
690                UInt32 => builder.add_bitWidth(32),
691                UInt64 => builder.add_bitWidth(64),
692                _ => {}
693            }
694            FBFieldType {
695                type_type: crate::Type::Int,
696                type_: builder.finish().as_union_value(),
697                children: Some(children),
698            }
699        }
700        Int8 | Int16 | Int32 | Int64 => {
701            let children = fbb.create_vector(&empty_fields[..]);
702            let mut builder = crate::IntBuilder::new(fbb);
703            builder.add_is_signed(true);
704            match data_type {
705                Int8 => builder.add_bitWidth(8),
706                Int16 => builder.add_bitWidth(16),
707                Int32 => builder.add_bitWidth(32),
708                Int64 => builder.add_bitWidth(64),
709                _ => {}
710            }
711            FBFieldType {
712                type_type: crate::Type::Int,
713                type_: builder.finish().as_union_value(),
714                children: Some(children),
715            }
716        }
717        Float16 | Float32 | Float64 => {
718            let children = fbb.create_vector(&empty_fields[..]);
719            let mut builder = crate::FloatingPointBuilder::new(fbb);
720            match data_type {
721                Float16 => builder.add_precision(crate::Precision::HALF),
722                Float32 => builder.add_precision(crate::Precision::SINGLE),
723                Float64 => builder.add_precision(crate::Precision::DOUBLE),
724                _ => {}
725            }
726            FBFieldType {
727                type_type: crate::Type::FloatingPoint,
728                type_: builder.finish().as_union_value(),
729                children: Some(children),
730            }
731        }
732        Binary => FBFieldType {
733            type_type: crate::Type::Binary,
734            type_: crate::BinaryBuilder::new(fbb).finish().as_union_value(),
735            children: Some(fbb.create_vector(&empty_fields[..])),
736        },
737        LargeBinary => FBFieldType {
738            type_type: crate::Type::LargeBinary,
739            type_: crate::LargeBinaryBuilder::new(fbb)
740                .finish()
741                .as_union_value(),
742            children: Some(fbb.create_vector(&empty_fields[..])),
743        },
744        BinaryView => FBFieldType {
745            type_type: crate::Type::BinaryView,
746            type_: crate::BinaryViewBuilder::new(fbb).finish().as_union_value(),
747            children: Some(fbb.create_vector(&empty_fields[..])),
748        },
749        Utf8View => FBFieldType {
750            type_type: crate::Type::Utf8View,
751            type_: crate::Utf8ViewBuilder::new(fbb).finish().as_union_value(),
752            children: Some(fbb.create_vector(&empty_fields[..])),
753        },
754        Utf8 => FBFieldType {
755            type_type: crate::Type::Utf8,
756            type_: crate::Utf8Builder::new(fbb).finish().as_union_value(),
757            children: Some(fbb.create_vector(&empty_fields[..])),
758        },
759        LargeUtf8 => FBFieldType {
760            type_type: crate::Type::LargeUtf8,
761            type_: crate::LargeUtf8Builder::new(fbb).finish().as_union_value(),
762            children: Some(fbb.create_vector(&empty_fields[..])),
763        },
764        FixedSizeBinary(len) => {
765            let mut builder = crate::FixedSizeBinaryBuilder::new(fbb);
766            builder.add_byteWidth(*len);
767            FBFieldType {
768                type_type: crate::Type::FixedSizeBinary,
769                type_: builder.finish().as_union_value(),
770                children: Some(fbb.create_vector(&empty_fields[..])),
771            }
772        }
773        Date32 => {
774            let mut builder = crate::DateBuilder::new(fbb);
775            builder.add_unit(crate::DateUnit::DAY);
776            FBFieldType {
777                type_type: crate::Type::Date,
778                type_: builder.finish().as_union_value(),
779                children: Some(fbb.create_vector(&empty_fields[..])),
780            }
781        }
782        Date64 => {
783            let mut builder = crate::DateBuilder::new(fbb);
784            builder.add_unit(crate::DateUnit::MILLISECOND);
785            FBFieldType {
786                type_type: crate::Type::Date,
787                type_: builder.finish().as_union_value(),
788                children: Some(fbb.create_vector(&empty_fields[..])),
789            }
790        }
791        Time32(unit) | Time64(unit) => {
792            let mut builder = crate::TimeBuilder::new(fbb);
793            match unit {
794                TimeUnit::Second => {
795                    builder.add_bitWidth(32);
796                    builder.add_unit(crate::TimeUnit::SECOND);
797                }
798                TimeUnit::Millisecond => {
799                    builder.add_bitWidth(32);
800                    builder.add_unit(crate::TimeUnit::MILLISECOND);
801                }
802                TimeUnit::Microsecond => {
803                    builder.add_bitWidth(64);
804                    builder.add_unit(crate::TimeUnit::MICROSECOND);
805                }
806                TimeUnit::Nanosecond => {
807                    builder.add_bitWidth(64);
808                    builder.add_unit(crate::TimeUnit::NANOSECOND);
809                }
810            }
811            FBFieldType {
812                type_type: crate::Type::Time,
813                type_: builder.finish().as_union_value(),
814                children: Some(fbb.create_vector(&empty_fields[..])),
815            }
816        }
817        Timestamp(unit, tz) => {
818            let tz = tz.as_deref().unwrap_or_default();
819            let tz_str = fbb.create_string(tz);
820            let mut builder = crate::TimestampBuilder::new(fbb);
821            let time_unit = match unit {
822                TimeUnit::Second => crate::TimeUnit::SECOND,
823                TimeUnit::Millisecond => crate::TimeUnit::MILLISECOND,
824                TimeUnit::Microsecond => crate::TimeUnit::MICROSECOND,
825                TimeUnit::Nanosecond => crate::TimeUnit::NANOSECOND,
826            };
827            builder.add_unit(time_unit);
828            if !tz.is_empty() {
829                builder.add_timezone(tz_str);
830            }
831            FBFieldType {
832                type_type: crate::Type::Timestamp,
833                type_: builder.finish().as_union_value(),
834                children: Some(fbb.create_vector(&empty_fields[..])),
835            }
836        }
837        Interval(unit) => {
838            let mut builder = crate::IntervalBuilder::new(fbb);
839            let interval_unit = match unit {
840                IntervalUnit::YearMonth => crate::IntervalUnit::YEAR_MONTH,
841                IntervalUnit::DayTime => crate::IntervalUnit::DAY_TIME,
842                IntervalUnit::MonthDayNano => crate::IntervalUnit::MONTH_DAY_NANO,
843            };
844            builder.add_unit(interval_unit);
845            FBFieldType {
846                type_type: crate::Type::Interval,
847                type_: builder.finish().as_union_value(),
848                children: Some(fbb.create_vector(&empty_fields[..])),
849            }
850        }
851        Duration(unit) => {
852            let mut builder = crate::DurationBuilder::new(fbb);
853            let time_unit = match unit {
854                TimeUnit::Second => crate::TimeUnit::SECOND,
855                TimeUnit::Millisecond => crate::TimeUnit::MILLISECOND,
856                TimeUnit::Microsecond => crate::TimeUnit::MICROSECOND,
857                TimeUnit::Nanosecond => crate::TimeUnit::NANOSECOND,
858            };
859            builder.add_unit(time_unit);
860            FBFieldType {
861                type_type: crate::Type::Duration,
862                type_: builder.finish().as_union_value(),
863                children: Some(fbb.create_vector(&empty_fields[..])),
864            }
865        }
866        List(list_type) => {
867            let child = build_field(fbb, dictionary_tracker, list_type);
868            FBFieldType {
869                type_type: crate::Type::List,
870                type_: crate::ListBuilder::new(fbb).finish().as_union_value(),
871                children: Some(fbb.create_vector(&[child])),
872            }
873        }
874        ListView(list_type) => {
875            let child = build_field(fbb, dictionary_tracker, list_type);
876            FBFieldType {
877                type_type: crate::Type::ListView,
878                type_: crate::ListViewBuilder::new(fbb).finish().as_union_value(),
879                children: Some(fbb.create_vector(&[child])),
880            }
881        }
882        LargeListView(list_type) => {
883            let child = build_field(fbb, dictionary_tracker, list_type);
884            FBFieldType {
885                type_type: crate::Type::LargeListView,
886                type_: crate::LargeListViewBuilder::new(fbb)
887                    .finish()
888                    .as_union_value(),
889                children: Some(fbb.create_vector(&[child])),
890            }
891        }
892        LargeList(list_type) => {
893            let child = build_field(fbb, dictionary_tracker, list_type);
894            FBFieldType {
895                type_type: crate::Type::LargeList,
896                type_: crate::LargeListBuilder::new(fbb).finish().as_union_value(),
897                children: Some(fbb.create_vector(&[child])),
898            }
899        }
900        FixedSizeList(list_type, len) => {
901            let child = build_field(fbb, dictionary_tracker, list_type);
902            let mut builder = crate::FixedSizeListBuilder::new(fbb);
903            builder.add_listSize(*len);
904            FBFieldType {
905                type_type: crate::Type::FixedSizeList,
906                type_: builder.finish().as_union_value(),
907                children: Some(fbb.create_vector(&[child])),
908            }
909        }
910        Struct(fields) => {
911            // struct's fields are children
912            let mut children = vec![];
913            for field in fields {
914                children.push(build_field(fbb, dictionary_tracker, field));
915            }
916            FBFieldType {
917                type_type: crate::Type::Struct_,
918                type_: crate::Struct_Builder::new(fbb).finish().as_union_value(),
919                children: Some(fbb.create_vector(&children[..])),
920            }
921        }
922        RunEndEncoded(run_ends, values) => {
923            let run_ends_field = build_field(fbb, dictionary_tracker, run_ends);
924            let values_field = build_field(fbb, dictionary_tracker, values);
925            let children = [run_ends_field, values_field];
926            FBFieldType {
927                type_type: crate::Type::RunEndEncoded,
928                type_: crate::RunEndEncodedBuilder::new(fbb)
929                    .finish()
930                    .as_union_value(),
931                children: Some(fbb.create_vector(&children[..])),
932            }
933        }
934        Map(map_field, keys_sorted) => {
935            let child = build_field(fbb, dictionary_tracker, map_field);
936            let mut field_type = crate::MapBuilder::new(fbb);
937            field_type.add_keysSorted(*keys_sorted);
938            FBFieldType {
939                type_type: crate::Type::Map,
940                type_: field_type.finish().as_union_value(),
941                children: Some(fbb.create_vector(&[child])),
942            }
943        }
944        Dictionary(_, value_type) => {
945            // In this library, the dictionary "type" is a logical construct. Here we
946            // pass through to the value type, as we've already captured the index
947            // type in the DictionaryEncoding metadata in the parent field
948            get_fb_field_type(value_type, dictionary_tracker, fbb)
949        }
950        Decimal32(precision, scale) => {
951            let mut builder = crate::DecimalBuilder::new(fbb);
952            builder.add_precision(*precision as i32);
953            builder.add_scale(*scale as i32);
954            builder.add_bitWidth(32);
955            FBFieldType {
956                type_type: crate::Type::Decimal,
957                type_: builder.finish().as_union_value(),
958                children: Some(fbb.create_vector(&empty_fields[..])),
959            }
960        }
961        Decimal64(precision, scale) => {
962            let mut builder = crate::DecimalBuilder::new(fbb);
963            builder.add_precision(*precision as i32);
964            builder.add_scale(*scale as i32);
965            builder.add_bitWidth(64);
966            FBFieldType {
967                type_type: crate::Type::Decimal,
968                type_: builder.finish().as_union_value(),
969                children: Some(fbb.create_vector(&empty_fields[..])),
970            }
971        }
972        Decimal128(precision, scale) => {
973            let mut builder = crate::DecimalBuilder::new(fbb);
974            builder.add_precision(*precision as i32);
975            builder.add_scale(*scale as i32);
976            builder.add_bitWidth(128);
977            FBFieldType {
978                type_type: crate::Type::Decimal,
979                type_: builder.finish().as_union_value(),
980                children: Some(fbb.create_vector(&empty_fields[..])),
981            }
982        }
983        Decimal256(precision, scale) => {
984            let mut builder = crate::DecimalBuilder::new(fbb);
985            builder.add_precision(*precision as i32);
986            builder.add_scale(*scale as i32);
987            builder.add_bitWidth(256);
988            FBFieldType {
989                type_type: crate::Type::Decimal,
990                type_: builder.finish().as_union_value(),
991                children: Some(fbb.create_vector(&empty_fields[..])),
992            }
993        }
994        Union(fields, mode) => {
995            let mut children = vec![];
996            for (_, field) in fields.iter() {
997                children.push(build_field(fbb, dictionary_tracker, field));
998            }
999
1000            let union_mode = match mode {
1001                UnionMode::Sparse => crate::UnionMode::Sparse,
1002                UnionMode::Dense => crate::UnionMode::Dense,
1003            };
1004
1005            let fbb_type_ids =
1006                fbb.create_vector(&fields.iter().map(|(t, _)| t as i32).collect::<Vec<_>>());
1007            let mut builder = crate::UnionBuilder::new(fbb);
1008            builder.add_mode(union_mode);
1009            builder.add_typeIds(fbb_type_ids);
1010
1011            FBFieldType {
1012                type_type: crate::Type::Union,
1013                type_: builder.finish().as_union_value(),
1014                children: Some(fbb.create_vector(&children[..])),
1015            }
1016        }
1017    }
1018}
1019
1020/// Create an IPC dictionary encoding
1021pub(crate) fn get_fb_dictionary<'a>(
1022    index_type: &DataType,
1023    dict_id: i64,
1024    dict_is_ordered: bool,
1025    fbb: &mut FlatBufferBuilder<'a>,
1026) -> WIPOffset<crate::DictionaryEncoding<'a>> {
1027    // We assume that the dictionary index type (as an integer) has already been
1028    // validated elsewhere, and can safely assume we are dealing with integers
1029    let mut index_builder = crate::IntBuilder::new(fbb);
1030
1031    match *index_type {
1032        Int8 | Int16 | Int32 | Int64 => index_builder.add_is_signed(true),
1033        UInt8 | UInt16 | UInt32 | UInt64 => index_builder.add_is_signed(false),
1034        _ => {}
1035    }
1036
1037    match *index_type {
1038        Int8 | UInt8 => index_builder.add_bitWidth(8),
1039        Int16 | UInt16 => index_builder.add_bitWidth(16),
1040        Int32 | UInt32 => index_builder.add_bitWidth(32),
1041        Int64 | UInt64 => index_builder.add_bitWidth(64),
1042        _ => {}
1043    }
1044
1045    let index_builder = index_builder.finish();
1046
1047    let mut builder = crate::DictionaryEncodingBuilder::new(fbb);
1048    builder.add_id(dict_id);
1049    builder.add_indexType(index_builder);
1050    builder.add_isOrdered(dict_is_ordered);
1051
1052    builder.finish()
1053}
1054
1055/// An owned container for a validated [`Message`]
1056///
1057/// Safely decoding a flatbuffer requires validating the various embedded offsets,
1058/// see [`Verifier`]. This is a potentially expensive operation, and it is therefore desirable
1059/// to only do this once. [`crate::root_as_message`] performs this validation on construction,
1060/// however, it returns a [`Message`] borrowing the provided byte slice. This prevents
1061/// storing this [`Message`] in the same data structure that owns the buffer, as this
1062/// would require self-referential borrows.
1063///
1064/// [`MessageBuffer`] solves this problem by providing a safe API for a [`Message`]
1065/// without a lifetime bound.
1066#[derive(Clone)]
1067pub struct MessageBuffer(Buffer);
1068
1069impl Debug for MessageBuffer {
1070    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1071        self.as_ref().fmt(f)
1072    }
1073}
1074
1075impl MessageBuffer {
1076    /// Try to create a [`MessageBuffer`] from the provided [`Buffer`]
1077    pub fn try_new(buf: Buffer) -> Result<Self, ArrowError> {
1078        let opts = VerifierOptions::default();
1079        let mut v = Verifier::new(&opts, &buf);
1080        <ForwardsUOffset<Message>>::run_verifier(&mut v, 0).map_err(|err| {
1081            ArrowError::ParseError(format!("Unable to get root as message: {err:?}"))
1082        })?;
1083        Ok(Self(buf))
1084    }
1085
1086    /// Return the [`Message`]
1087    #[inline]
1088    pub fn as_ref(&self) -> Message<'_> {
1089        // SAFETY: Run verifier on construction
1090        unsafe { crate::root_as_message_unchecked(&self.0) }
1091    }
1092}
1093
1094#[cfg(test)]
1095mod tests {
1096    use super::*;
1097
1098    /// Build a schema flatbuffer with no `fields` vector, using `build_field` to
1099    /// optionally add a single field. Returns bytes that the flatbuffer verifier
1100    /// accepts, so `try_fb_to_schema` is the layer that must reject them.
1101    fn schema_bytes_without_fields() -> Vec<u8> {
1102        let mut fbb = FlatBufferBuilder::new();
1103        // Schema table with `fields` left unset -> fb.fields() == None.
1104        let schema = crate::SchemaBuilder::new(&mut fbb).finish();
1105        fbb.finish(schema, None);
1106        fbb.finished_data().to_vec()
1107    }
1108
1109    /// Finish `fbb` as a schema holding the single `field`, assert the verifier
1110    /// accepts the bytes but `try_fb_to_schema` rejects them (rather than panics).
1111    fn assert_single_field_schema_is_err<'a>(
1112        mut fbb: FlatBufferBuilder<'a>,
1113        field: WIPOffset<crate::Field<'a>>,
1114    ) {
1115        let fields = fbb.create_vector(&[field]);
1116        let schema = {
1117            let mut b = crate::SchemaBuilder::new(&mut fbb);
1118            b.add_fields(fields);
1119            b.finish()
1120        };
1121        fbb.finish(schema, None);
1122        let fb = crate::root_as_schema(fbb.finished_data()).expect("verifier should accept");
1123        assert!(try_fb_to_schema(fb).is_err());
1124    }
1125
1126    #[test]
1127    fn try_fb_to_schema_missing_fields_is_err() {
1128        let bytes = schema_bytes_without_fields();
1129        // the verifier accepts it...
1130        let fb = crate::root_as_schema(&bytes).expect("verifier should accept");
1131        // ...but conversion must return Err, not panic.
1132        assert!(try_fb_to_schema(fb).is_err());
1133    }
1134
1135    #[test]
1136    fn try_fb_to_schema_unknown_float_precision_is_err() {
1137        let mut fbb = FlatBufferBuilder::new();
1138        let name = fbb.create_string("f");
1139        let float = {
1140            let mut b = crate::FloatingPointBuilder::new(&mut fbb);
1141            // a precision the verifier does not range-check.
1142            b.add_precision(crate::Precision(99));
1143            b.finish()
1144        };
1145        let field = {
1146            let mut b = crate::FieldBuilder::new(&mut fbb);
1147            b.add_name(name);
1148            b.add_type_type(crate::Type::FloatingPoint);
1149            b.add_type_(float.as_union_value());
1150            b.finish()
1151        };
1152        assert_single_field_schema_is_err(fbb, field);
1153    }
1154
1155    #[test]
1156    fn try_fb_to_schema_type_none_is_err() {
1157        let mut fbb = FlatBufferBuilder::new();
1158        let name = fbb.create_string("f");
1159        let field = {
1160            let mut b = crate::FieldBuilder::new(&mut fbb);
1161            b.add_name(name);
1162            // type_type left as the union default (Type::NONE).
1163            b.finish()
1164        };
1165        assert_single_field_schema_is_err(fbb, field);
1166    }
1167
1168    #[test]
1169    fn convert_schema_round_trip() {
1170        let md = HashMap::from([("Key".to_string(), "value".to_string())]);
1171        let field_md = HashMap::from([("k".to_string(), "v".to_string())]);
1172        let schema = Schema::new_with_metadata(
1173            vec![
1174                Field::new("uint8", DataType::UInt8, false).with_metadata(field_md),
1175                Field::new("uint16", DataType::UInt16, true),
1176                Field::new("uint32", DataType::UInt32, false),
1177                Field::new("uint64", DataType::UInt64, true),
1178                Field::new("int8", DataType::Int8, true),
1179                Field::new("int16", DataType::Int16, false),
1180                Field::new("int32", DataType::Int32, true),
1181                Field::new("int64", DataType::Int64, false),
1182                Field::new("float16", DataType::Float16, true),
1183                Field::new("float32", DataType::Float32, false),
1184                Field::new("float64", DataType::Float64, true),
1185                Field::new("null", DataType::Null, false),
1186                Field::new("bool", DataType::Boolean, false),
1187                Field::new("date32", DataType::Date32, false),
1188                Field::new("date64", DataType::Date64, true),
1189                Field::new("time32[s]", DataType::Time32(TimeUnit::Second), true),
1190                Field::new("time32[ms]", DataType::Time32(TimeUnit::Millisecond), false),
1191                Field::new("time64[us]", DataType::Time64(TimeUnit::Microsecond), false),
1192                Field::new("time64[ns]", DataType::Time64(TimeUnit::Nanosecond), true),
1193                Field::new(
1194                    "timestamp[s]",
1195                    DataType::Timestamp(TimeUnit::Second, None),
1196                    false,
1197                ),
1198                Field::new(
1199                    "timestamp[ms]",
1200                    DataType::Timestamp(TimeUnit::Millisecond, None),
1201                    true,
1202                ),
1203                Field::new(
1204                    "timestamp[us]",
1205                    DataType::Timestamp(TimeUnit::Microsecond, Some("Africa/Johannesburg".into())),
1206                    false,
1207                ),
1208                Field::new(
1209                    "timestamp[ns]",
1210                    DataType::Timestamp(TimeUnit::Nanosecond, None),
1211                    true,
1212                ),
1213                Field::new(
1214                    "interval[ym]",
1215                    DataType::Interval(IntervalUnit::YearMonth),
1216                    true,
1217                ),
1218                Field::new(
1219                    "interval[dt]",
1220                    DataType::Interval(IntervalUnit::DayTime),
1221                    true,
1222                ),
1223                Field::new(
1224                    "interval[mdn]",
1225                    DataType::Interval(IntervalUnit::MonthDayNano),
1226                    true,
1227                ),
1228                Field::new("utf8", DataType::Utf8, false),
1229                Field::new("utf8_view", DataType::Utf8View, false),
1230                Field::new("binary", DataType::Binary, false),
1231                Field::new("binary_view", DataType::BinaryView, false),
1232                Field::new_list(
1233                    "list[u8]",
1234                    Field::new_list_field(DataType::UInt8, false),
1235                    true,
1236                ),
1237                Field::new_fixed_size_list(
1238                    "fixed_size_list[u8]",
1239                    Field::new_list_field(DataType::UInt8, false),
1240                    2,
1241                    true,
1242                ),
1243                Field::new_list(
1244                    "list[struct<float32, int32, bool>]",
1245                    Field::new_struct(
1246                        "struct",
1247                        vec![
1248                            Field::new("float32", UInt8, false),
1249                            Field::new("int32", Int32, true),
1250                            Field::new("bool", Boolean, true),
1251                        ],
1252                        true,
1253                    ),
1254                    false,
1255                ),
1256                Field::new_struct(
1257                    "struct<dictionary<int32, utf8>>",
1258                    vec![Field::new(
1259                        "dictionary<int32, utf8>",
1260                        Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
1261                        false,
1262                    )],
1263                    false,
1264                ),
1265                Field::new_struct(
1266                    "struct<int64, list[struct<date32, list[struct<>]>]>",
1267                    vec![
1268                        Field::new("int64", DataType::Int64, true),
1269                        Field::new_list(
1270                            "list[struct<date32, list[struct<>]>]",
1271                            Field::new_struct(
1272                                "struct",
1273                                vec![
1274                                    Field::new("date32", DataType::Date32, true),
1275                                    Field::new_list(
1276                                        "list[struct<>]",
1277                                        Field::new(
1278                                            "struct",
1279                                            DataType::Struct(Fields::empty()),
1280                                            false,
1281                                        ),
1282                                        false,
1283                                    ),
1284                                ],
1285                                false,
1286                            ),
1287                            false,
1288                        ),
1289                    ],
1290                    false,
1291                ),
1292                Field::new_union(
1293                    "union<int64, list[union<date32, list[union<>]>]>",
1294                    vec![0, 1],
1295                    vec![
1296                        Field::new("int64", DataType::Int64, true),
1297                        Field::new_list(
1298                            "list[union<date32, list[union<>]>]",
1299                            Field::new_union(
1300                                "union<date32, list[union<>]>",
1301                                vec![0, 1],
1302                                vec![
1303                                    Field::new("date32", DataType::Date32, true),
1304                                    Field::new_list(
1305                                        "list[union<>]",
1306                                        Field::new(
1307                                            "union",
1308                                            DataType::Union(
1309                                                UnionFields::empty(),
1310                                                UnionMode::Sparse,
1311                                            ),
1312                                            false,
1313                                        ),
1314                                        false,
1315                                    ),
1316                                ],
1317                                UnionMode::Dense,
1318                            ),
1319                            false,
1320                        ),
1321                    ],
1322                    UnionMode::Sparse,
1323                ),
1324                Field::new("struct<>", DataType::Struct(Fields::empty()), true),
1325                Field::new(
1326                    "union<>",
1327                    DataType::Union(UnionFields::empty(), UnionMode::Dense),
1328                    true,
1329                ),
1330                Field::new(
1331                    "union<>",
1332                    DataType::Union(UnionFields::empty(), UnionMode::Sparse),
1333                    true,
1334                ),
1335                Field::new(
1336                    "union<int32, utf8>",
1337                    DataType::Union(
1338                        UnionFields::try_new(
1339                            vec![2, 3], // non-default type ids
1340                            vec![
1341                                Field::new("int32", DataType::Int32, true),
1342                                Field::new("utf8", DataType::Utf8, true),
1343                            ],
1344                        )
1345                        .unwrap(),
1346                        UnionMode::Dense,
1347                    ),
1348                    true,
1349                ),
1350                #[expect(deprecated)]
1351                Field::new_dict(
1352                    "dictionary<int32, utf8>",
1353                    DataType::Dictionary(Box::new(DataType::Int32), Box::new(DataType::Utf8)),
1354                    true,
1355                    123,
1356                    true,
1357                ),
1358                #[expect(deprecated)]
1359                Field::new_dict(
1360                    "dictionary<uint8, uint32>",
1361                    DataType::Dictionary(Box::new(DataType::UInt8), Box::new(DataType::UInt32)),
1362                    true,
1363                    123,
1364                    true,
1365                ),
1366                Field::new("decimal<usize, usize>", DataType::Decimal128(10, 6), false),
1367            ],
1368            md,
1369        );
1370
1371        let mut dictionary_tracker = DictionaryTracker::new(true);
1372        let fb = IpcSchemaEncoder::new()
1373            .with_dictionary_tracker(&mut dictionary_tracker)
1374            .schema_to_fb(&schema);
1375
1376        // read back fields
1377        let ipc = crate::root_as_schema(fb.finished_data()).unwrap();
1378        let schema2 = try_fb_to_schema(ipc).unwrap();
1379        assert_eq!(schema, schema2);
1380    }
1381
1382    #[test]
1383    fn schema_from_bytes() {
1384        // Bytes of a schema generated via following python code, using pyarrow 10.0.1:
1385        //
1386        // import pyarrow as pa
1387        // schema = pa.schema([pa.field('field1', pa.uint32(), nullable=False)])
1388        // sink = pa.BufferOutputStream()
1389        // with pa.ipc.new_stream(sink, schema) as writer:
1390        //     pass
1391        // # stripping continuation & length prefix & suffix bytes to get only schema bytes
1392        // [x for x in sink.getvalue().to_pybytes()][8:-8]
1393        let bytes: Vec<u8> = vec![
1394            16, 0, 0, 0, 0, 0, 10, 0, 12, 0, 6, 0, 5, 0, 8, 0, 10, 0, 0, 0, 0, 1, 4, 0, 12, 0, 0,
1395            0, 8, 0, 8, 0, 0, 0, 4, 0, 8, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 20, 0, 0, 0, 16, 0, 20,
1396            0, 8, 0, 0, 0, 7, 0, 12, 0, 0, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 2, 16, 0, 0, 0, 32, 0,
1397            0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 102, 105, 101, 108, 100, 49, 0, 0, 0, 0, 6,
1398            0, 8, 0, 4, 0, 6, 0, 0, 0, 32, 0, 0, 0,
1399        ];
1400        let ipc = crate::root_as_message(&bytes).unwrap();
1401        let schema = ipc.header_as_schema().unwrap();
1402
1403        // generate same message with Rust
1404        let data_gen = crate::writer::IpcDataGenerator::default();
1405        let mut dictionary_tracker = DictionaryTracker::new(true);
1406        let arrow_schema = Schema::new(vec![Field::new("field1", DataType::UInt32, false)]);
1407        let bytes = data_gen
1408            .schema_to_bytes_with_dictionary_tracker(
1409                &arrow_schema,
1410                &mut dictionary_tracker,
1411                &crate::writer::IpcWriteOptions::default(),
1412            )
1413            .ipc_message;
1414
1415        let ipc2 = crate::root_as_message(&bytes).unwrap();
1416        let schema2 = ipc2.header_as_schema().unwrap();
1417
1418        // can't compare schema directly as it compares the underlying bytes, which can differ
1419        assert!(schema.custom_metadata().is_none());
1420        assert!(schema2.custom_metadata().is_none());
1421        assert_eq!(schema.endianness(), schema2.endianness());
1422        assert!(schema.features().is_none());
1423        assert!(schema2.features().is_none());
1424        assert_eq!(
1425            try_fb_to_schema(schema).unwrap(),
1426            try_fb_to_schema(schema2).unwrap()
1427        );
1428
1429        assert_eq!(ipc.version(), ipc2.version());
1430        assert_eq!(ipc.header_type(), ipc2.header_type());
1431        assert_eq!(ipc.bodyLength(), ipc2.bodyLength());
1432        assert!(ipc.custom_metadata().is_none());
1433        assert!(ipc2.custom_metadata().is_none());
1434    }
1435}