1use 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#[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 pub fn new() -> IpcSchemaEncoder<'a> {
79 IpcSchemaEncoder {
80 dictionary_tracker: None,
81 }
82 }
83
84 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 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 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
131pub fn metadata_to_fb<'a>(
133 fbb: &mut FlatBufferBuilder<'a>,
134 metadata: &Metadata,
135) -> WIPOffset<Vector<'a, ForwardsUOffset<KeyValue<'a>>>> {
136 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
152pub 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
160impl From<crate::Field<'_>> for Field {
165 fn from(field: crate::Field) -> Field {
166 try_field_from(field).expect("invalid IPC field")
167 }
168}
169
170fn 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#[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
207pub 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
247pub 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
264pub fn try_schema_from_ipc_buffer(buffer: &[u8]) -> Result<Schema, ArrowError> {
266 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
314pub(crate) fn get_data_type(
316 field: crate::Field,
317 may_be_dictionary: bool,
318) -> Result<DataType, ArrowError> {
319 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
591fn 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
611pub(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 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
663pub(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 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 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 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
1020pub(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 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#[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 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 #[inline]
1088 pub fn as_ref(&self) -> Message<'_> {
1089 unsafe { crate::root_as_message_unchecked(&self.0) }
1091 }
1092}
1093
1094#[cfg(test)]
1095mod tests {
1096 use super::*;
1097
1098 fn schema_bytes_without_fields() -> Vec<u8> {
1102 let mut fbb = FlatBufferBuilder::new();
1103 let schema = crate::SchemaBuilder::new(&mut fbb).finish();
1105 fbb.finish(schema, None);
1106 fbb.finished_data().to_vec()
1107 }
1108
1109 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 let fb = crate::root_as_schema(&bytes).expect("verifier should accept");
1131 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 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 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], 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 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 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 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 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}