Skip to main content

arrow_avro/reader/
record.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//! Avro Decoder for Arrow types.
19
20use crate::codec::{
21    AvroDataType, AvroLiteral, Codec, EnumMapping, Promotion, ResolutionInfo, ResolvedField,
22    ResolvedRecord, ResolvedUnion, Tz,
23};
24use crate::errors::AvroError;
25use crate::reader::cursor::AvroCursor;
26use crate::schema::Nullability;
27#[cfg(feature = "small_decimals")]
28use arrow_array::builder::{Decimal32Builder, Decimal64Builder};
29use arrow_array::builder::{Decimal128Builder, Decimal256Builder, IntervalMonthDayNanoBuilder};
30use arrow_array::types::*;
31use arrow_array::*;
32use arrow_buffer::*;
33#[cfg(feature = "small_decimals")]
34use arrow_schema::{DECIMAL32_MAX_PRECISION, DECIMAL64_MAX_PRECISION};
35use arrow_schema::{
36    DECIMAL128_MAX_PRECISION, DECIMAL256_MAX_PRECISION, DataType, Field as ArrowField, FieldRef,
37    Fields, Schema as ArrowSchema, SchemaRef, UnionFields, UnionMode,
38};
39#[cfg(feature = "avro_custom_types")]
40use arrow_select::take::{TakeOptions, take};
41use strum_macros::AsRefStr;
42use uuid::Uuid;
43
44use std::cmp::Ordering;
45use std::mem;
46use std::sync::Arc;
47
48const DEFAULT_CAPACITY: usize = 1024;
49
50/// Macro to decode a decimal payload for a given width and integer type.
51macro_rules! decode_decimal {
52    ($size:expr, $buf:expr, $builder:expr, $N:expr, $Int:ty) => {{
53        let bytes = read_decimal_bytes_be::<{ $N }>($buf, *$size)?;
54        $builder.append_value(<$Int>::from_be_bytes(bytes));
55    }};
56}
57
58/// Macro to finish a decimal builder into an array with precision/scale and nulls.
59macro_rules! flush_decimal {
60    ($builder:expr, $precision:expr, $scale:expr, $nulls:expr, $ArrayTy:ty) => {{
61        let (_, vals, _) = $builder.finish().into_parts();
62        let dec = <$ArrayTy>::try_new(vals, $nulls)?
63            .with_precision_and_scale(*$precision as u8, $scale.unwrap_or(0) as i8)?;
64        Arc::new(dec) as ArrayRef
65    }};
66}
67
68/// Macro to append a default decimal value from two's-complement big-endian bytes
69/// into the corresponding decimal builder, with compile-time constructed error text.
70macro_rules! append_decimal_default {
71    ($lit:expr, $builder:expr, $N:literal, $Int:ty, $name:literal) => {{
72        match $lit {
73            AvroLiteral::Bytes(b) => {
74                let ext = sign_cast_to::<$N>(b)?;
75                let val = <$Int>::from_be_bytes(ext);
76                $builder.append_value(val);
77                Ok(())
78            }
79            _ => Err(AvroError::InvalidArgument(
80                concat!(
81                    "Default for ",
82                    $name,
83                    " must be bytes (two's-complement big-endian)"
84                )
85                .to_string(),
86            )),
87        }
88    }};
89}
90
91/// Decodes avro encoded data into [`RecordBatch`]
92#[derive(Debug)]
93pub(crate) struct RecordDecoder {
94    schema: SchemaRef,
95    fields: Vec<Decoder>,
96    projector: Option<Projector>,
97    row_count: usize,
98}
99
100impl RecordDecoder {
101    /// Creates a new [`RecordDecoder`] from the provided [`AvroDataType`] with additional options.
102    ///
103    /// This method allows you to customize how the Avro data is decoded into Arrow arrays.
104    ///
105    /// # Arguments
106    /// * `data_type` - The Avro data type to decode.
107    /// * `use_utf8view` - A flag indicating whether to use `Utf8View` for string types.
108    ///
109    /// # Errors
110    /// This function will return an error if the provided `data_type` is not a `Record`.
111    pub(crate) fn try_new_with_options(data_type: &AvroDataType) -> Result<Self, AvroError> {
112        match data_type.codec() {
113            Codec::Struct(reader_fields) => {
114                // Build Arrow schema fields and per-child decoders
115                let mut arrow_fields = Vec::with_capacity(reader_fields.len());
116                let mut encodings = Vec::with_capacity(reader_fields.len());
117                let mut field_defaults = Vec::with_capacity(reader_fields.len());
118                for avro_field in reader_fields.iter() {
119                    arrow_fields.push(avro_field.field());
120                    encodings.push(Decoder::try_new(avro_field.data_type())?);
121
122                    if let Some(ResolutionInfo::DefaultValue(lit)) =
123                        avro_field.data_type().resolution.as_ref()
124                    {
125                        field_defaults.push(Some(lit.clone()));
126                    } else {
127                        field_defaults.push(None);
128                    }
129                }
130                let projector = match data_type.resolution.as_ref() {
131                    Some(ResolutionInfo::Record(rec)) => {
132                        Some(ProjectorBuilder::try_new(rec, &field_defaults).build()?)
133                    }
134                    _ => None,
135                };
136                Ok(Self {
137                    schema: Arc::new(ArrowSchema::new(arrow_fields)),
138                    fields: encodings,
139                    projector,
140                    row_count: 0,
141                })
142            }
143            other => Err(AvroError::ParseError(format!(
144                "Expected record got {other:?}"
145            ))),
146        }
147    }
148
149    /// Returns the decoder's `SchemaRef`
150    pub(crate) fn schema(&self) -> &SchemaRef {
151        &self.schema
152    }
153
154    /// Decode `count` records from `buf`
155    pub(crate) fn decode(&mut self, buf: &[u8], count: usize) -> Result<usize, AvroError> {
156        let mut cursor = AvroCursor::new(buf);
157        match self.projector.as_mut() {
158            Some(proj) => {
159                for _ in 0..count {
160                    proj.project_record(&mut cursor, &mut self.fields)?;
161                }
162            }
163            None => {
164                for _ in 0..count {
165                    for field in &mut self.fields {
166                        field.decode(&mut cursor)?;
167                    }
168                }
169            }
170        }
171        self.row_count += count;
172        Ok(cursor.position())
173    }
174
175    /// Flush the decoded records into a [`RecordBatch`]
176    pub(crate) fn flush(&mut self) -> Result<RecordBatch, AvroError> {
177        let arrays = self
178            .fields
179            .iter_mut()
180            .map(|x| x.flush(None))
181            .collect::<Result<Vec<_>, _>>()?;
182        let batch_options = RecordBatchOptions::new().with_row_count(Some(self.row_count));
183        self.row_count = 0;
184        RecordBatch::try_new_with_options(self.schema.clone(), arrays, &batch_options)
185            .map_err(Into::into)
186    }
187}
188
189#[derive(Debug, AsRefStr)]
190enum Decoder {
191    Null(usize),
192    Boolean(BooleanBufferBuilder),
193    Int32(Vec<i32>),
194    Int64(Vec<i64>),
195    #[cfg(feature = "avro_custom_types")]
196    DurationSecond(Vec<i64>),
197    #[cfg(feature = "avro_custom_types")]
198    DurationMillisecond(Vec<i64>),
199    #[cfg(feature = "avro_custom_types")]
200    DurationMicrosecond(Vec<i64>),
201    #[cfg(feature = "avro_custom_types")]
202    DurationNanosecond(Vec<i64>),
203    #[cfg(feature = "avro_custom_types")]
204    Int8(Vec<i8>),
205    #[cfg(feature = "avro_custom_types")]
206    Int16(Vec<i16>),
207    #[cfg(feature = "avro_custom_types")]
208    UInt8(Vec<u8>),
209    #[cfg(feature = "avro_custom_types")]
210    UInt16(Vec<u16>),
211    #[cfg(feature = "avro_custom_types")]
212    UInt32(Vec<u32>),
213    #[cfg(feature = "avro_custom_types")]
214    UInt64(Vec<u64>),
215    #[cfg(feature = "avro_custom_types")]
216    Float16(Vec<u16>), // Stored as raw IEEE-754 f16 bits
217    #[cfg(feature = "avro_custom_types")]
218    Date64(Vec<i64>),
219    #[cfg(feature = "avro_custom_types")]
220    TimeNanos(Vec<i64>),
221    #[cfg(feature = "avro_custom_types")]
222    Time32Secs(Vec<i32>),
223    #[cfg(feature = "avro_custom_types")]
224    TimestampSecs(bool, Vec<i64>),
225    #[cfg(feature = "avro_custom_types")]
226    IntervalYearMonth(Vec<i32>),
227    #[cfg(feature = "avro_custom_types")]
228    IntervalMonthDayNano(Vec<IntervalMonthDayNano>),
229    #[cfg(feature = "avro_custom_types")]
230    IntervalDayTime(Vec<IntervalDayTime>),
231    Float32(Vec<f32>),
232    Float64(Vec<f64>),
233    Date32(Vec<i32>),
234    TimeMillis(Vec<i32>),
235    TimeMicros(Vec<i64>),
236    TimestampMillis(Option<Tz>, Vec<i64>),
237    TimestampMicros(Option<Tz>, Vec<i64>),
238    TimestampNanos(Option<Tz>, Vec<i64>),
239    Int32ToInt64(Vec<i64>),
240    Int32ToFloat32(Vec<f32>),
241    Int32ToFloat64(Vec<f64>),
242    Int64ToFloat32(Vec<f32>),
243    Int64ToFloat64(Vec<f64>),
244    Float32ToFloat64(Vec<f64>),
245    BytesToString(OffsetBufferBuilder<i32>, Vec<u8>),
246    StringToBytes(OffsetBufferBuilder<i32>, Vec<u8>),
247    Binary(OffsetBufferBuilder<i32>, Vec<u8>),
248    /// String data encoded as UTF-8 bytes, mapped to Arrow's StringArray
249    String(OffsetBufferBuilder<i32>, Vec<u8>),
250    /// String data encoded as UTF-8 bytes, but mapped to Arrow's StringViewArray
251    StringView(OffsetBufferBuilder<i32>, Vec<u8>),
252    Array(FieldRef, OffsetBufferBuilder<i32>, Box<Decoder>),
253    Record {
254        fields: Fields,
255        decoders: Vec<Decoder>,
256        defaults: Vec<Option<AvroLiteral>>,
257        projector: Option<Projector>,
258        /// Rows appended since the last flush. A record with no fields (legal) has no child
259        /// array to take a length from, so this is the only length available to it; it is
260        /// maintained for every record to keep the arms uniform.
261        row_count: usize,
262    },
263    Map(
264        FieldRef,
265        OffsetBufferBuilder<i32>,
266        OffsetBufferBuilder<i32>,
267        Vec<u8>,
268        Box<Decoder>,
269    ),
270    Fixed(i32, Vec<u8>),
271    Enum(Vec<i32>, Arc<[String]>, Option<EnumResolution>),
272    Duration(IntervalMonthDayNanoBuilder),
273    Uuid(Vec<u8>),
274    #[cfg(feature = "small_decimals")]
275    Decimal32(usize, Option<usize>, Option<usize>, Decimal32Builder),
276    #[cfg(feature = "small_decimals")]
277    Decimal64(usize, Option<usize>, Option<usize>, Decimal64Builder),
278    Decimal128(usize, Option<usize>, Option<usize>, Decimal128Builder),
279    Decimal256(usize, Option<usize>, Option<usize>, Decimal256Builder),
280    #[cfg(feature = "avro_custom_types")]
281    RunEndEncoded(u8, usize, Box<Decoder>),
282    Union(UnionDecoder),
283    /// Nullable value and its deferred validity and child placeholders.
284    Nullable(NullableDecoder),
285}
286
287#[derive(Debug)]
288struct NullableDecoder {
289    plan: NullablePlan,
290    validity: NullBufferBuilder,
291    values: Box<Decoder>,
292    pending_nulls: usize,
293}
294
295impl NullableDecoder {
296    fn new(plan: NullablePlan, values: Decoder) -> Self {
297        Self {
298            plan,
299            validity: NullBufferBuilder::new(DEFAULT_CAPACITY),
300            values: Box::new(values),
301            pending_nulls: 0,
302        }
303    }
304
305    #[inline]
306    fn materialize_pending(&mut self) -> Result<(), AvroError> {
307        if self.pending_nulls == 0 {
308            return Ok(());
309        }
310
311        self.values.append_nulls(self.pending_nulls)?;
312        self.validity.append_n_nulls(self.pending_nulls);
313        self.pending_nulls = 0;
314        Ok(())
315    }
316}
317
318impl Decoder {
319    fn try_new(data_type: &AvroDataType) -> Result<Self, AvroError> {
320        if let Some(ResolutionInfo::Union(info)) = data_type.resolution.as_ref()
321            && info.writer_is_union
322            && !info.reader_is_union
323        {
324            let mut clone = data_type.clone();
325            clone.resolution = None; // Build target base decoder without Union resolution
326            let target = Self::try_new_internal(&clone)?;
327            let decoder = Self::Union(
328                UnionDecoderBuilder::new()
329                    .with_resolved_union(info.clone())
330                    .with_target(target)
331                    .build()?,
332            );
333            return Ok(decoder);
334        }
335        Self::try_new_internal(data_type)
336    }
337
338    fn try_new_internal(data_type: &AvroDataType) -> Result<Self, AvroError> {
339        // Extract just the Promotion (if any) to simplify pattern matching
340        let promotion = match data_type.resolution.as_ref() {
341            Some(ResolutionInfo::Promotion(p)) => Some(*p),
342            _ => None,
343        };
344        let decoder = match (data_type.codec(), promotion) {
345            (Codec::Int64, Some(Promotion::IntToLong)) => {
346                Self::Int32ToInt64(Vec::with_capacity(DEFAULT_CAPACITY))
347            }
348            (Codec::Float32, Some(Promotion::IntToFloat)) => {
349                Self::Int32ToFloat32(Vec::with_capacity(DEFAULT_CAPACITY))
350            }
351            (Codec::Float64, Some(Promotion::IntToDouble)) => {
352                Self::Int32ToFloat64(Vec::with_capacity(DEFAULT_CAPACITY))
353            }
354            (Codec::Float32, Some(Promotion::LongToFloat)) => {
355                Self::Int64ToFloat32(Vec::with_capacity(DEFAULT_CAPACITY))
356            }
357            (Codec::Float64, Some(Promotion::LongToDouble)) => {
358                Self::Int64ToFloat64(Vec::with_capacity(DEFAULT_CAPACITY))
359            }
360            (Codec::Float64, Some(Promotion::FloatToDouble)) => {
361                Self::Float32ToFloat64(Vec::with_capacity(DEFAULT_CAPACITY))
362            }
363            (Codec::Utf8 | Codec::Utf8View, Some(Promotion::BytesToString)) => Self::BytesToString(
364                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
365                Vec::with_capacity(DEFAULT_CAPACITY),
366            ),
367            (Codec::Binary, Some(Promotion::StringToBytes)) => Self::StringToBytes(
368                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
369                Vec::with_capacity(DEFAULT_CAPACITY),
370            ),
371            (Codec::Null, _) => Self::Null(0),
372            (Codec::Boolean, _) => Self::Boolean(BooleanBufferBuilder::new(DEFAULT_CAPACITY)),
373            (Codec::Int32, _) => Self::Int32(Vec::with_capacity(DEFAULT_CAPACITY)),
374            (Codec::Int64, _) => Self::Int64(Vec::with_capacity(DEFAULT_CAPACITY)),
375            (Codec::Float32, _) => Self::Float32(Vec::with_capacity(DEFAULT_CAPACITY)),
376            (Codec::Float64, _) => Self::Float64(Vec::with_capacity(DEFAULT_CAPACITY)),
377            (Codec::Binary, _) => Self::Binary(
378                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
379                Vec::with_capacity(DEFAULT_CAPACITY),
380            ),
381            (Codec::Utf8, _) => Self::String(
382                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
383                Vec::with_capacity(DEFAULT_CAPACITY),
384            ),
385            (Codec::Utf8View, _) => Self::StringView(
386                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
387                Vec::with_capacity(DEFAULT_CAPACITY),
388            ),
389            (Codec::Date32, _) => Self::Date32(Vec::with_capacity(DEFAULT_CAPACITY)),
390            (Codec::TimeMillis, _) => Self::TimeMillis(Vec::with_capacity(DEFAULT_CAPACITY)),
391            (Codec::TimeMicros, _) => Self::TimeMicros(Vec::with_capacity(DEFAULT_CAPACITY)),
392            (Codec::TimestampMillis(tz), _) => {
393                Self::TimestampMillis(*tz, Vec::with_capacity(DEFAULT_CAPACITY))
394            }
395            (Codec::TimestampMicros(tz), _) => {
396                Self::TimestampMicros(*tz, Vec::with_capacity(DEFAULT_CAPACITY))
397            }
398            (Codec::TimestampNanos(tz), _) => {
399                Self::TimestampNanos(*tz, Vec::with_capacity(DEFAULT_CAPACITY))
400            }
401            #[cfg(feature = "avro_custom_types")]
402            (Codec::DurationNanos, _) => {
403                Self::DurationNanosecond(Vec::with_capacity(DEFAULT_CAPACITY))
404            }
405            #[cfg(feature = "avro_custom_types")]
406            (Codec::DurationMicros, _) => {
407                Self::DurationMicrosecond(Vec::with_capacity(DEFAULT_CAPACITY))
408            }
409            #[cfg(feature = "avro_custom_types")]
410            (Codec::DurationMillis, _) => {
411                Self::DurationMillisecond(Vec::with_capacity(DEFAULT_CAPACITY))
412            }
413            #[cfg(feature = "avro_custom_types")]
414            (Codec::DurationSeconds, _) => {
415                Self::DurationSecond(Vec::with_capacity(DEFAULT_CAPACITY))
416            }
417            #[cfg(feature = "avro_custom_types")]
418            (Codec::Int8, _) => Self::Int8(Vec::with_capacity(DEFAULT_CAPACITY)),
419            #[cfg(feature = "avro_custom_types")]
420            (Codec::Int16, _) => Self::Int16(Vec::with_capacity(DEFAULT_CAPACITY)),
421            #[cfg(feature = "avro_custom_types")]
422            (Codec::UInt8, _) => Self::UInt8(Vec::with_capacity(DEFAULT_CAPACITY)),
423            #[cfg(feature = "avro_custom_types")]
424            (Codec::UInt16, _) => Self::UInt16(Vec::with_capacity(DEFAULT_CAPACITY)),
425            #[cfg(feature = "avro_custom_types")]
426            (Codec::UInt32, _) => Self::UInt32(Vec::with_capacity(DEFAULT_CAPACITY)),
427            #[cfg(feature = "avro_custom_types")]
428            (Codec::UInt64, _) => Self::UInt64(Vec::with_capacity(DEFAULT_CAPACITY)),
429            #[cfg(feature = "avro_custom_types")]
430            (Codec::Float16, _) => Self::Float16(Vec::with_capacity(DEFAULT_CAPACITY)),
431            #[cfg(feature = "avro_custom_types")]
432            (Codec::Date64, _) => Self::Date64(Vec::with_capacity(DEFAULT_CAPACITY)),
433            #[cfg(feature = "avro_custom_types")]
434            (Codec::TimeNanos, _) => Self::TimeNanos(Vec::with_capacity(DEFAULT_CAPACITY)),
435            #[cfg(feature = "avro_custom_types")]
436            (Codec::Time32Secs, _) => Self::Time32Secs(Vec::with_capacity(DEFAULT_CAPACITY)),
437            #[cfg(feature = "avro_custom_types")]
438            (Codec::TimestampSecs(is_utc), _) => {
439                Self::TimestampSecs(*is_utc, Vec::with_capacity(DEFAULT_CAPACITY))
440            }
441            #[cfg(feature = "avro_custom_types")]
442            (Codec::IntervalYearMonth, _) => {
443                Self::IntervalYearMonth(Vec::with_capacity(DEFAULT_CAPACITY))
444            }
445            #[cfg(feature = "avro_custom_types")]
446            (Codec::IntervalMonthDayNano, _) => {
447                Self::IntervalMonthDayNano(Vec::with_capacity(DEFAULT_CAPACITY))
448            }
449            #[cfg(feature = "avro_custom_types")]
450            (Codec::IntervalDayTime, _) => {
451                Self::IntervalDayTime(Vec::with_capacity(DEFAULT_CAPACITY))
452            }
453            (Codec::Fixed(sz), _) => Self::Fixed(*sz, Vec::with_capacity(DEFAULT_CAPACITY)),
454            (Codec::Decimal(precision, scale, size), _) => {
455                let p = *precision;
456                let s = *scale;
457                let prec = p as u8;
458                let scl = s.unwrap_or(0) as i8;
459                #[cfg(feature = "small_decimals")]
460                {
461                    if p <= DECIMAL32_MAX_PRECISION as usize {
462                        let builder = Decimal32Builder::with_capacity(DEFAULT_CAPACITY)
463                            .with_precision_and_scale(prec, scl)?;
464                        Self::Decimal32(p, s, *size, builder)
465                    } else if p <= DECIMAL64_MAX_PRECISION as usize {
466                        let builder = Decimal64Builder::with_capacity(DEFAULT_CAPACITY)
467                            .with_precision_and_scale(prec, scl)?;
468                        Self::Decimal64(p, s, *size, builder)
469                    } else if p <= DECIMAL128_MAX_PRECISION as usize {
470                        let builder = Decimal128Builder::with_capacity(DEFAULT_CAPACITY)
471                            .with_precision_and_scale(prec, scl)?;
472                        Self::Decimal128(p, s, *size, builder)
473                    } else if p <= DECIMAL256_MAX_PRECISION as usize {
474                        let builder = Decimal256Builder::with_capacity(DEFAULT_CAPACITY)
475                            .with_precision_and_scale(prec, scl)?;
476                        Self::Decimal256(p, s, *size, builder)
477                    } else {
478                        return Err(AvroError::ParseError(format!(
479                            "Decimal precision {p} exceeds maximum supported"
480                        )));
481                    }
482                }
483                #[cfg(not(feature = "small_decimals"))]
484                {
485                    if p <= DECIMAL128_MAX_PRECISION as usize {
486                        let builder = Decimal128Builder::with_capacity(DEFAULT_CAPACITY)
487                            .with_precision_and_scale(prec, scl)?;
488                        Self::Decimal128(p, s, *size, builder)
489                    } else if p <= DECIMAL256_MAX_PRECISION as usize {
490                        let builder = Decimal256Builder::with_capacity(DEFAULT_CAPACITY)
491                            .with_precision_and_scale(prec, scl)?;
492                        Self::Decimal256(p, s, *size, builder)
493                    } else {
494                        return Err(AvroError::ParseError(format!(
495                            "Decimal precision {p} exceeds maximum supported"
496                        )));
497                    }
498                }
499            }
500            (Codec::Interval, _) => Self::Duration(IntervalMonthDayNanoBuilder::new()),
501            (Codec::List(item), _) => {
502                let decoder = Self::try_new(item)?;
503                Self::Array(
504                    Arc::new(item.field_with_name("item")),
505                    OffsetBufferBuilder::new(DEFAULT_CAPACITY),
506                    Box::new(decoder),
507                )
508            }
509            (Codec::Enum(symbols), _) => {
510                let res = match data_type.resolution.as_ref() {
511                    Some(ResolutionInfo::EnumMapping(mapping)) => {
512                        Some(EnumResolution::new(mapping))
513                    }
514                    _ => None,
515                };
516                Self::Enum(Vec::with_capacity(DEFAULT_CAPACITY), symbols.clone(), res)
517            }
518            (Codec::Struct(fields), _) => {
519                let mut arrow_fields = Vec::with_capacity(fields.len());
520                let mut encodings = Vec::with_capacity(fields.len());
521                let mut field_defaults = Vec::with_capacity(fields.len());
522                for avro_field in fields.iter() {
523                    let encoding = Self::try_new(avro_field.data_type())?;
524                    arrow_fields.push(avro_field.field());
525                    encodings.push(encoding);
526
527                    if let Some(ResolutionInfo::DefaultValue(lit)) =
528                        avro_field.data_type().resolution.as_ref()
529                    {
530                        field_defaults.push(Some(lit.clone()));
531                    } else {
532                        field_defaults.push(None);
533                    }
534                }
535                let projector =
536                    if let Some(ResolutionInfo::Record(rec)) = data_type.resolution.as_ref() {
537                        Some(ProjectorBuilder::try_new(rec, &field_defaults).build()?)
538                    } else {
539                        None
540                    };
541                Self::Record {
542                    fields: arrow_fields.into(),
543                    decoders: encodings,
544                    defaults: field_defaults,
545                    projector,
546                    row_count: 0,
547                }
548            }
549            (Codec::Map(child), _) => {
550                let val_field = child.field_with_name(ArrowField::MAP_VALUE_FIELD_DEFAULT_NAME);
551                let map_field = Arc::new(ArrowField::new(
552                    ArrowField::MAP_ENTRIES_FIELD_DEFAULT_NAME,
553                    DataType::Struct(Fields::from(vec![
554                        ArrowField::new(
555                            ArrowField::MAP_KEY_FIELD_DEFAULT_NAME,
556                            DataType::Utf8,
557                            false,
558                        ),
559                        val_field,
560                    ])),
561                    false,
562                ));
563                let val_dec = Self::try_new(child)?;
564                Self::Map(
565                    map_field,
566                    OffsetBufferBuilder::new(DEFAULT_CAPACITY),
567                    OffsetBufferBuilder::new(DEFAULT_CAPACITY),
568                    Vec::with_capacity(DEFAULT_CAPACITY),
569                    Box::new(val_dec),
570                )
571            }
572            (Codec::Uuid, _) => Self::Uuid(Vec::with_capacity(DEFAULT_CAPACITY)),
573            (Codec::Union(encodings, fields, UnionMode::Dense), _) => {
574                let decoders = encodings
575                    .iter()
576                    .map(Self::try_new_internal)
577                    .collect::<Result<Vec<_>, _>>()?;
578                if fields.len() != decoders.len() {
579                    return Err(AvroError::SchemaError(format!(
580                        "Union has {} fields but {} decoders",
581                        fields.len(),
582                        decoders.len()
583                    )));
584                }
585                // Proactive guard: if a user provides a union with more branches than
586                // a 32-bit Avro index can address, fail fast with a clear message.
587                let branch_count = decoders.len();
588                let max_addr = (i32::MAX as usize) + 1;
589                if branch_count > max_addr {
590                    return Err(AvroError::SchemaError(format!(
591                        "Union has {branch_count} branches, which exceeds the maximum addressable \
592                         branches by an Avro int tag ({} + 1).",
593                        i32::MAX
594                    )));
595                }
596                let mut builder = UnionDecoderBuilder::new()
597                    .with_fields(fields.clone())
598                    .with_branches(decoders);
599                if let Some(ResolutionInfo::Union(info)) = data_type.resolution.as_ref()
600                    && info.reader_is_union
601                {
602                    builder = builder.with_resolved_union(info.clone());
603                }
604                Self::Union(builder.build()?)
605            }
606            (Codec::Union(_, _, _), _) => {
607                return Err(AvroError::NYI(
608                    "Sparse Arrow unions are not yet supported".to_string(),
609                ));
610            }
611            #[cfg(feature = "avro_custom_types")]
612            (Codec::RunEndEncoded(values_dt, width_bits_or_bytes), _) => {
613                let inner = Self::try_new(values_dt)?;
614                let byte_width: u8 = match *width_bits_or_bytes {
615                    2 | 4 | 8 => *width_bits_or_bytes,
616                    16 => 2,
617                    32 => 4,
618                    64 => 8,
619                    other => {
620                        return Err(AvroError::InvalidArgument(format!(
621                            "Unsupported run-end width {other} for RunEndEncoded; \
622                             expected 16/32/64 bits or 2/4/8 bytes"
623                        )));
624                    }
625                };
626                Self::RunEndEncoded(byte_width, 0, Box::new(inner))
627            }
628        };
629        Ok(match data_type.nullability() {
630            Some(nullability) => {
631                // Default to reading a union branch tag unless the resolution directs otherwise.
632                let plan = match &data_type.resolution {
633                    None => NullablePlan::ReadTag {
634                        nullability,
635                        resolution: ResolutionPlan::Promotion(Promotion::Direct),
636                    },
637                    Some(ResolutionInfo::Promotion(_)) => {
638                        // Promotions should have been incorporated
639                        // into the inner decoder.
640                        NullablePlan::FromSingle {
641                            resolution: ResolutionPlan::Promotion(Promotion::Direct),
642                        }
643                    }
644                    Some(ResolutionInfo::Union(info)) if !info.writer_is_union => {
645                        let Some(Some((_, resolution))) = info.writer_to_reader.first() else {
646                            return Err(AvroError::SchemaError(
647                                "unexpected union resolution info for non-union writer and union reader type".into(),
648                            ));
649                        };
650                        let resolution = ResolutionPlan::try_new(&decoder, resolution)?;
651                        NullablePlan::FromSingle { resolution }
652                    }
653                    Some(ResolutionInfo::Union(info)) => {
654                        let Some((_, resolution)) =
655                            info.writer_to_reader[nullability.non_null_index()].as_ref()
656                        else {
657                            return Err(AvroError::SchemaError(
658                                "unexpected union resolution info for nullable writer type".into(),
659                            ));
660                        };
661                        NullablePlan::ReadTag {
662                            nullability,
663                            resolution: ResolutionPlan::try_new(&decoder, resolution)?,
664                        }
665                    }
666                    Some(resolution) => NullablePlan::FromSingle {
667                        resolution: ResolutionPlan::try_new(&decoder, resolution)?,
668                    },
669                };
670                Self::Nullable(NullableDecoder::new(plan, decoder))
671            }
672            None => decoder,
673        })
674    }
675
676    /// Append a null record
677    fn append_null(&mut self) -> Result<(), AvroError> {
678        self.append_nulls(1)
679    }
680
681    /// Append a run of null placeholders, deferring nullable children until their next value or
682    /// flush so sparse record subtrees can be materialized in bulk.
683    fn append_nulls(&mut self, count: usize) -> Result<(), AvroError> {
684        if count == 0 {
685            return Ok(());
686        }
687        match self {
688            Self::Null(size) => *size += count,
689            Self::Boolean(values) => values.append_n(count, false),
690            Self::Int32(values) | Self::Date32(values) | Self::TimeMillis(values) => {
691                values.resize(values.len() + count, 0)
692            }
693            Self::Int64(values)
694            | Self::Int32ToInt64(values)
695            | Self::TimeMicros(values)
696            | Self::TimestampMillis(_, values)
697            | Self::TimestampMicros(_, values)
698            | Self::TimestampNanos(_, values) => values.resize(values.len() + count, 0),
699            #[cfg(feature = "avro_custom_types")]
700            Self::DurationSecond(values)
701            | Self::DurationMillisecond(values)
702            | Self::DurationMicrosecond(values)
703            | Self::DurationNanosecond(values)
704            | Self::Date64(values)
705            | Self::TimeNanos(values)
706            | Self::TimestampSecs(_, values) => values.resize(values.len() + count, 0),
707            #[cfg(feature = "avro_custom_types")]
708            Self::Int8(values) => values.resize(values.len() + count, 0),
709            #[cfg(feature = "avro_custom_types")]
710            Self::Int16(values) => values.resize(values.len() + count, 0),
711            #[cfg(feature = "avro_custom_types")]
712            Self::UInt8(values) => values.resize(values.len() + count, 0),
713            #[cfg(feature = "avro_custom_types")]
714            Self::UInt16(values) | Self::Float16(values) => values.resize(values.len() + count, 0),
715            #[cfg(feature = "avro_custom_types")]
716            Self::UInt32(values) => values.resize(values.len() + count, 0),
717            #[cfg(feature = "avro_custom_types")]
718            Self::UInt64(values) => values.resize(values.len() + count, 0),
719            #[cfg(feature = "avro_custom_types")]
720            Self::Time32Secs(values) | Self::IntervalYearMonth(values) => {
721                values.resize(values.len() + count, 0)
722            }
723            #[cfg(feature = "avro_custom_types")]
724            Self::IntervalDayTime(values) => {
725                values.resize(values.len() + count, IntervalDayTime::new(0, 0))
726            }
727            #[cfg(feature = "avro_custom_types")]
728            Self::IntervalMonthDayNano(values) => {
729                values.resize(values.len() + count, IntervalMonthDayNano::new(0, 0, 0))
730            }
731            Self::Float32(values) | Self::Int32ToFloat32(values) | Self::Int64ToFloat32(values) => {
732                values.resize(values.len() + count, 0.0)
733            }
734            Self::Float64(values)
735            | Self::Int32ToFloat64(values)
736            | Self::Int64ToFloat64(values)
737            | Self::Float32ToFloat64(values) => values.resize(values.len() + count, 0.0),
738            Self::Binary(offsets, _)
739            | Self::String(offsets, _)
740            | Self::StringView(offsets, _)
741            | Self::BytesToString(offsets, _)
742            | Self::StringToBytes(offsets, _)
743            | Self::Array(_, offsets, _)
744            | Self::Map(_, _, offsets, _, _) => {
745                offsets.reserve(count);
746                for _ in 0..count {
747                    offsets.push_length(0);
748                }
749            }
750            Self::Record {
751                decoders,
752                row_count,
753                ..
754            } => {
755                for child in decoders.iter_mut() {
756                    child.append_nulls(count)?;
757                }
758                *row_count += count;
759            }
760            Self::Fixed(width, values) => {
761                values.resize(values.len() + (*width as usize) * count, 0)
762            }
763            Self::Enum(values, _, _) => values.resize(values.len() + count, 0),
764            Self::Duration(builder) => builder.append_nulls(count),
765            Self::Uuid(values) => values.resize(values.len() + 16 * count, 0),
766            #[cfg(feature = "small_decimals")]
767            Self::Decimal32(_, _, _, builder) => builder.append_value_n(0, count),
768            #[cfg(feature = "small_decimals")]
769            Self::Decimal64(_, _, _, builder) => builder.append_value_n(0, count),
770            Self::Decimal128(_, _, _, builder) => builder.append_value_n(0, count),
771            Self::Decimal256(_, _, _, builder) => builder.append_value_n(i256::ZERO, count),
772            #[cfg(feature = "avro_custom_types")]
773            Self::RunEndEncoded(_, len, inner) => {
774                inner.append_nulls(count)?;
775                *len += count;
776            }
777            Self::Union(union) => {
778                for _ in 0..count {
779                    union.append_null()?;
780                }
781            }
782            Self::Nullable(nullable) => nullable.pending_nulls += count,
783        }
784        Ok(())
785    }
786
787    /// Append a single default literal into the decoder's buffers
788    fn append_default(&mut self, lit: &AvroLiteral) -> Result<(), AvroError> {
789        match self {
790            Self::Nullable(nullable) => {
791                if matches!(lit, AvroLiteral::Null) {
792                    nullable.pending_nulls += 1;
793                } else {
794                    nullable.materialize_pending()?;
795                    nullable.values.append_default(lit)?;
796                    nullable.validity.append_non_null();
797                }
798                Ok(())
799            }
800            Self::Null(count) => match lit {
801                AvroLiteral::Null => {
802                    *count += 1;
803                    Ok(())
804                }
805                _ => Err(AvroError::InvalidArgument(
806                    "Non-null default for null type".to_string(),
807                )),
808            },
809            Self::Boolean(b) => match lit {
810                AvroLiteral::Boolean(v) => {
811                    b.append(*v);
812                    Ok(())
813                }
814                _ => Err(AvroError::InvalidArgument(
815                    "Default for boolean must be boolean".to_string(),
816                )),
817            },
818            Self::Int32(v) | Self::Date32(v) | Self::TimeMillis(v) => match lit {
819                AvroLiteral::Int(i) => {
820                    v.push(*i);
821                    Ok(())
822                }
823                _ => Err(AvroError::InvalidArgument(
824                    "Default for int32/date32/time-millis must be int".to_string(),
825                )),
826            },
827            #[cfg(feature = "avro_custom_types")]
828            Self::DurationSecond(v)
829            | Self::DurationMillisecond(v)
830            | Self::DurationMicrosecond(v)
831            | Self::DurationNanosecond(v) => match lit {
832                AvroLiteral::Long(i) => {
833                    v.push(*i);
834                    Ok(())
835                }
836                _ => Err(AvroError::InvalidArgument(
837                    "Default for duration long must be long".to_string(),
838                )),
839            },
840            #[cfg(feature = "avro_custom_types")]
841            Self::Int8(v) => match lit {
842                AvroLiteral::Int(i) => {
843                    let x = i8::try_from(*i).map_err(|_| {
844                        AvroError::InvalidArgument(format!(
845                            "Default for int8 out of range for i8: {i}"
846                        ))
847                    })?;
848                    v.push(x);
849                    Ok(())
850                }
851                _ => Err(AvroError::InvalidArgument(
852                    "Default for int8 must be int".to_string(),
853                )),
854            },
855            #[cfg(feature = "avro_custom_types")]
856            Self::Int16(v) => match lit {
857                AvroLiteral::Int(i) => {
858                    let x = i16::try_from(*i).map_err(|_| {
859                        AvroError::InvalidArgument(format!(
860                            "Default for int16 out of range for i16: {i}"
861                        ))
862                    })?;
863                    v.push(x);
864                    Ok(())
865                }
866                _ => Err(AvroError::InvalidArgument(
867                    "Default for int16 must be int".to_string(),
868                )),
869            },
870            #[cfg(feature = "avro_custom_types")]
871            Self::UInt8(v) => match lit {
872                AvroLiteral::Int(i) => {
873                    let x = u8::try_from(*i).map_err(|_| {
874                        AvroError::InvalidArgument(format!(
875                            "Default for uint8 out of range for u8: {i}"
876                        ))
877                    })?;
878                    v.push(x);
879                    Ok(())
880                }
881                _ => Err(AvroError::InvalidArgument(
882                    "Default for uint8 must be int".to_string(),
883                )),
884            },
885            #[cfg(feature = "avro_custom_types")]
886            Self::UInt16(v) => match lit {
887                AvroLiteral::Int(i) => {
888                    let x = u16::try_from(*i).map_err(|_| {
889                        AvroError::InvalidArgument(format!(
890                            "Default for uint16 out of range for u16: {i}"
891                        ))
892                    })?;
893                    v.push(x);
894                    Ok(())
895                }
896                _ => Err(AvroError::InvalidArgument(
897                    "Default for uint16 must be int".to_string(),
898                )),
899            },
900            #[cfg(feature = "avro_custom_types")]
901            Self::UInt32(v) => match lit {
902                AvroLiteral::Long(i) => {
903                    let x = u32::try_from(*i).map_err(|_| {
904                        AvroError::InvalidArgument(format!(
905                            "Default for uint32 out of range for u32: {i}"
906                        ))
907                    })?;
908                    v.push(x);
909                    Ok(())
910                }
911                _ => Err(AvroError::InvalidArgument(
912                    "Default for uint32 must be long".to_string(),
913                )),
914            },
915            #[cfg(feature = "avro_custom_types")]
916            Self::UInt64(v) => match lit {
917                AvroLiteral::Bytes(b) => {
918                    if b.len() != 8 {
919                        return Err(AvroError::InvalidArgument(format!(
920                            "uint64 default must be exactly 8 bytes, got {}",
921                            b.len()
922                        )));
923                    }
924                    v.push(u64::from_le_bytes([
925                        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
926                    ]));
927                    Ok(())
928                }
929                _ => Err(AvroError::InvalidArgument(
930                    "Default for uint64 must be bytes (8-byte LE)".to_string(),
931                )),
932            },
933            #[cfg(feature = "avro_custom_types")]
934            Self::Float16(v) => match lit {
935                AvroLiteral::Bytes(b) => {
936                    if b.len() != 2 {
937                        return Err(AvroError::InvalidArgument(format!(
938                            "float16 default must be exactly 2 bytes, got {}",
939                            b.len()
940                        )));
941                    }
942                    v.push(u16::from_le_bytes([b[0], b[1]]));
943                    Ok(())
944                }
945                _ => Err(AvroError::InvalidArgument(
946                    "Default for float16 must be bytes (2-byte LE IEEE-754)".to_string(),
947                )),
948            },
949            #[cfg(feature = "avro_custom_types")]
950            Self::Date64(v) | Self::TimeNanos(v) | Self::TimestampSecs(_, v) => match lit {
951                AvroLiteral::Long(i) => {
952                    v.push(*i);
953                    Ok(())
954                }
955                _ => Err(AvroError::InvalidArgument(
956                    "Default for date64/time-nanos/timestamp-secs must be long".to_string(),
957                )),
958            },
959            #[cfg(feature = "avro_custom_types")]
960            Self::Time32Secs(v) => match lit {
961                AvroLiteral::Int(i) => {
962                    v.push(*i);
963                    Ok(())
964                }
965                _ => Err(AvroError::InvalidArgument(
966                    "Default for time32-secs must be int".to_string(),
967                )),
968            },
969            #[cfg(feature = "avro_custom_types")]
970            Self::IntervalYearMonth(v) => match lit {
971                AvroLiteral::Bytes(b) => {
972                    if b.len() != 4 {
973                        return Err(AvroError::InvalidArgument(format!(
974                            "interval-year-month default must be exactly 4 bytes, got {}",
975                            b.len()
976                        )));
977                    }
978                    v.push(i32::from_le_bytes([b[0], b[1], b[2], b[3]]));
979                    Ok(())
980                }
981                _ => Err(AvroError::InvalidArgument(
982                    "Default for interval-year-month must be bytes (4-byte LE)".to_string(),
983                )),
984            },
985            #[cfg(feature = "avro_custom_types")]
986            Self::IntervalMonthDayNano(v) => match lit {
987                AvroLiteral::Bytes(b) => {
988                    if b.len() != 16 {
989                        return Err(AvroError::InvalidArgument(format!(
990                            "interval-month-day-nano default must be exactly 16 bytes, got {}",
991                            b.len()
992                        )));
993                    }
994                    let months = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
995                    let days = i32::from_le_bytes([b[4], b[5], b[6], b[7]]);
996                    let nanos =
997                        i64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]);
998                    v.push(IntervalMonthDayNano::new(months, days, nanos));
999                    Ok(())
1000                }
1001                _ => Err(AvroError::InvalidArgument(
1002                    "Default for interval-month-day-nano must be bytes (16-byte LE)".to_string(),
1003                )),
1004            },
1005            #[cfg(feature = "avro_custom_types")]
1006            Self::IntervalDayTime(v) => match lit {
1007                AvroLiteral::Bytes(b) => {
1008                    if b.len() != 8 {
1009                        return Err(AvroError::InvalidArgument(format!(
1010                            "interval-day-time default must be exactly 8 bytes, got {}",
1011                            b.len()
1012                        )));
1013                    }
1014                    let days = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
1015                    let milliseconds = i32::from_le_bytes([b[4], b[5], b[6], b[7]]);
1016                    v.push(IntervalDayTime::new(days, milliseconds));
1017                    Ok(())
1018                }
1019                _ => Err(AvroError::InvalidArgument(
1020                    "Default for interval-day-time must be bytes (8-byte LE)".to_string(),
1021                )),
1022            },
1023            Self::Int64(v)
1024            | Self::Int32ToInt64(v)
1025            | Self::TimeMicros(v)
1026            | Self::TimestampMillis(_, v)
1027            | Self::TimestampMicros(_, v)
1028            | Self::TimestampNanos(_, v) => match lit {
1029                AvroLiteral::Long(i) => {
1030                    v.push(*i);
1031                    Ok(())
1032                }
1033                AvroLiteral::Int(i) => {
1034                    v.push(*i as i64);
1035                    Ok(())
1036                }
1037                _ => Err(AvroError::InvalidArgument(
1038                    "Default for long/time-micros/timestamp must be long or int".to_string(),
1039                )),
1040            },
1041            Self::Float32(v) | Self::Int32ToFloat32(v) | Self::Int64ToFloat32(v) => match lit {
1042                AvroLiteral::Float(f) => {
1043                    v.push(*f);
1044                    Ok(())
1045                }
1046                _ => Err(AvroError::InvalidArgument(
1047                    "Default for float must be float".to_string(),
1048                )),
1049            },
1050            Self::Float64(v)
1051            | Self::Int32ToFloat64(v)
1052            | Self::Int64ToFloat64(v)
1053            | Self::Float32ToFloat64(v) => match lit {
1054                AvroLiteral::Double(f) => {
1055                    v.push(*f);
1056                    Ok(())
1057                }
1058                _ => Err(AvroError::InvalidArgument(
1059                    "Default for double must be double".to_string(),
1060                )),
1061            },
1062            Self::Binary(offsets, values) | Self::StringToBytes(offsets, values) => match lit {
1063                AvroLiteral::Bytes(b) => {
1064                    offsets.push_length(b.len());
1065                    values.extend_from_slice(b);
1066                    Ok(())
1067                }
1068                _ => Err(AvroError::InvalidArgument(
1069                    "Default for bytes must be bytes".to_string(),
1070                )),
1071            },
1072            Self::BytesToString(offsets, values)
1073            | Self::String(offsets, values)
1074            | Self::StringView(offsets, values) => match lit {
1075                AvroLiteral::String(s) => {
1076                    let b = s.as_bytes();
1077                    offsets.push_length(b.len());
1078                    values.extend_from_slice(b);
1079                    Ok(())
1080                }
1081                _ => Err(AvroError::InvalidArgument(
1082                    "Default for string must be string".to_string(),
1083                )),
1084            },
1085            Self::Uuid(values) => match lit {
1086                AvroLiteral::String(s) => {
1087                    let uuid = Uuid::try_parse(s).map_err(|e| {
1088                        AvroError::InvalidArgument(format!("Invalid UUID default: {s} ({e})"))
1089                    })?;
1090                    values.extend_from_slice(uuid.as_bytes());
1091                    Ok(())
1092                }
1093                _ => Err(AvroError::InvalidArgument(
1094                    "Default for uuid must be string".to_string(),
1095                )),
1096            },
1097            Self::Fixed(sz, accum) => match lit {
1098                AvroLiteral::Bytes(b) => {
1099                    if b.len() != *sz as usize {
1100                        return Err(AvroError::InvalidArgument(format!(
1101                            "Fixed default length {} does not match size {sz}",
1102                            b.len(),
1103                        )));
1104                    }
1105                    accum.extend_from_slice(b);
1106                    Ok(())
1107                }
1108                _ => Err(AvroError::InvalidArgument(
1109                    "Default for fixed must be bytes".to_string(),
1110                )),
1111            },
1112            #[cfg(feature = "small_decimals")]
1113            Self::Decimal32(_, _, _, builder) => {
1114                append_decimal_default!(lit, builder, 4, i32, "decimal32")
1115            }
1116            #[cfg(feature = "small_decimals")]
1117            Self::Decimal64(_, _, _, builder) => {
1118                append_decimal_default!(lit, builder, 8, i64, "decimal64")
1119            }
1120            Self::Decimal128(_, _, _, builder) => {
1121                append_decimal_default!(lit, builder, 16, i128, "decimal128")
1122            }
1123            Self::Decimal256(_, _, _, builder) => {
1124                append_decimal_default!(lit, builder, 32, i256, "decimal256")
1125            }
1126            Self::Duration(builder) => match lit {
1127                AvroLiteral::Bytes(b) => {
1128                    if b.len() != 12 {
1129                        return Err(AvroError::InvalidArgument(format!(
1130                            "Duration default must be exactly 12 bytes, got {}",
1131                            b.len()
1132                        )));
1133                    }
1134                    let months = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
1135                    let days = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
1136                    let millis = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
1137                    let nanos = (millis as i64) * 1_000_000;
1138                    builder.append_value(IntervalMonthDayNano::new(
1139                        months as i32,
1140                        days as i32,
1141                        nanos,
1142                    ));
1143                    Ok(())
1144                }
1145                _ => Err(AvroError::InvalidArgument(
1146                    "Default for duration must be 12-byte little-endian months/days/millis"
1147                        .to_string(),
1148                )),
1149            },
1150            Self::Array(_, offsets, inner) => match lit {
1151                AvroLiteral::Array(items) => {
1152                    offsets.push_length(items.len());
1153                    for item in items {
1154                        inner.append_default(item)?;
1155                    }
1156                    Ok(())
1157                }
1158                _ => Err(AvroError::InvalidArgument(
1159                    "Default for array must be an array literal".to_string(),
1160                )),
1161            },
1162            Self::Map(_, koff, moff, kdata, valdec) => match lit {
1163                AvroLiteral::Map(entries) => {
1164                    moff.push_length(entries.len());
1165                    for (k, v) in entries {
1166                        let kb = k.as_bytes();
1167                        koff.push_length(kb.len());
1168                        kdata.extend_from_slice(kb);
1169                        valdec.append_default(v)?;
1170                    }
1171                    Ok(())
1172                }
1173                _ => Err(AvroError::InvalidArgument(
1174                    "Default for map must be a map/object literal".to_string(),
1175                )),
1176            },
1177            Self::Enum(indices, symbols, _) => match lit {
1178                AvroLiteral::Enum(sym) => {
1179                    let pos = symbols.iter().position(|s| s == sym).ok_or_else(|| {
1180                        AvroError::InvalidArgument(format!(
1181                            "Enum default symbol {sym:?} not in reader symbols"
1182                        ))
1183                    })?;
1184                    indices.push(pos as i32);
1185                    Ok(())
1186                }
1187                _ => Err(AvroError::InvalidArgument(
1188                    "Default for enum must be a symbol".to_string(),
1189                )),
1190            },
1191            #[cfg(feature = "avro_custom_types")]
1192            Self::RunEndEncoded(_, len, inner) => {
1193                *len += 1;
1194                inner.append_default(lit)
1195            }
1196            Self::Union(u) => u.append_default(lit),
1197            Self::Record {
1198                fields: field_meta,
1199                decoders,
1200                defaults: field_defaults,
1201                row_count,
1202                ..
1203            } => match lit {
1204                AvroLiteral::Map(entries) => {
1205                    for (i, dec) in decoders.iter_mut().enumerate() {
1206                        let name = field_meta[i].name();
1207                        if let Some(sub) = entries.get(name) {
1208                            dec.append_default(sub)?;
1209                        } else if let Some(default_literal) = field_defaults[i].as_ref() {
1210                            dec.append_default(default_literal)?;
1211                        } else {
1212                            dec.append_null()?;
1213                        }
1214                    }
1215                    *row_count += 1;
1216                    Ok(())
1217                }
1218                AvroLiteral::Null => {
1219                    for (i, dec) in decoders.iter_mut().enumerate() {
1220                        if let Some(default_literal) = field_defaults[i].as_ref() {
1221                            dec.append_default(default_literal)?;
1222                        } else {
1223                            dec.append_null()?;
1224                        }
1225                    }
1226                    *row_count += 1;
1227                    Ok(())
1228                }
1229                _ => Err(AvroError::InvalidArgument(
1230                    "Default for record must be a map/object or null".to_string(),
1231                )),
1232            },
1233        }
1234    }
1235
1236    /// Decode a single record from `buf`
1237    fn decode(&mut self, buf: &mut AvroCursor<'_>) -> Result<(), AvroError> {
1238        match self {
1239            Self::Null(x) => *x += 1,
1240            Self::Boolean(values) => values.append(buf.get_bool()?),
1241            Self::Int32(values) | Self::Date32(values) | Self::TimeMillis(values) => {
1242                values.push(buf.get_int()?)
1243            }
1244            Self::Int64(values)
1245            | Self::TimeMicros(values)
1246            | Self::TimestampMillis(_, values)
1247            | Self::TimestampMicros(_, values)
1248            | Self::TimestampNanos(_, values) => values.push(buf.get_long()?),
1249            #[cfg(feature = "avro_custom_types")]
1250            Self::DurationSecond(values)
1251            | Self::DurationMillisecond(values)
1252            | Self::DurationMicrosecond(values)
1253            | Self::DurationNanosecond(values) => values.push(buf.get_long()?),
1254            #[cfg(feature = "avro_custom_types")]
1255            Self::Int8(values) => {
1256                let raw = buf.get_int()?;
1257                let x = i8::try_from(raw).map_err(|_| {
1258                    AvroError::ParseError(format!("int8 value {raw} out of range for i8"))
1259                })?;
1260                values.push(x);
1261            }
1262            #[cfg(feature = "avro_custom_types")]
1263            Self::Int16(values) => {
1264                let raw = buf.get_int()?;
1265                let x = i16::try_from(raw).map_err(|_| {
1266                    AvroError::ParseError(format!("int16 value {raw} out of range for i16"))
1267                })?;
1268                values.push(x);
1269            }
1270            #[cfg(feature = "avro_custom_types")]
1271            Self::UInt8(values) => {
1272                let raw = buf.get_int()?;
1273                let x = u8::try_from(raw).map_err(|_| {
1274                    AvroError::ParseError(format!("uint8 value {raw} out of range for u8"))
1275                })?;
1276                values.push(x);
1277            }
1278            #[cfg(feature = "avro_custom_types")]
1279            Self::UInt16(values) => {
1280                let raw = buf.get_int()?;
1281                let x = u16::try_from(raw).map_err(|_| {
1282                    AvroError::ParseError(format!("uint16 value {raw} out of range for u16"))
1283                })?;
1284                values.push(x);
1285            }
1286            #[cfg(feature = "avro_custom_types")]
1287            Self::UInt32(values) => {
1288                let raw = buf.get_long()?;
1289                let x = u32::try_from(raw).map_err(|_| {
1290                    AvroError::ParseError(format!("uint32 value {raw} out of range for u32"))
1291                })?;
1292                values.push(x);
1293            }
1294            #[cfg(feature = "avro_custom_types")]
1295            Self::UInt64(values) => {
1296                let b = buf.get_fixed(8)?;
1297                values.push(u64::from_le_bytes([
1298                    b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
1299                ]));
1300            }
1301            #[cfg(feature = "avro_custom_types")]
1302            Self::Float16(values) => {
1303                let b = buf.get_fixed(2)?;
1304                values.push(u16::from_le_bytes([b[0], b[1]]));
1305            }
1306            #[cfg(feature = "avro_custom_types")]
1307            Self::Date64(values) | Self::TimeNanos(values) | Self::TimestampSecs(_, values) => {
1308                values.push(buf.get_long()?)
1309            }
1310            #[cfg(feature = "avro_custom_types")]
1311            Self::Time32Secs(values) => values.push(buf.get_int()?),
1312            #[cfg(feature = "avro_custom_types")]
1313            Self::IntervalYearMonth(values) => {
1314                let b = buf.get_fixed(4)?;
1315                values.push(i32::from_le_bytes([b[0], b[1], b[2], b[3]]));
1316            }
1317            #[cfg(feature = "avro_custom_types")]
1318            Self::IntervalMonthDayNano(values) => {
1319                let b = buf.get_fixed(16)?;
1320                let months = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
1321                let days = i32::from_le_bytes([b[4], b[5], b[6], b[7]]);
1322                let nanos =
1323                    i64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]);
1324                values.push(IntervalMonthDayNano::new(months, days, nanos));
1325            }
1326            #[cfg(feature = "avro_custom_types")]
1327            Self::IntervalDayTime(values) => {
1328                let b = buf.get_fixed(8)?;
1329                // Read as two i32s: days (4 bytes) and milliseconds (4 bytes)
1330                let days = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
1331                let milliseconds = i32::from_le_bytes([b[4], b[5], b[6], b[7]]);
1332                values.push(IntervalDayTime::new(days, milliseconds));
1333            }
1334            Self::Float32(values) => values.push(buf.get_float()?),
1335            Self::Float64(values) => values.push(buf.get_double()?),
1336            Self::Int32ToInt64(values) => values.push(buf.get_int()? as i64),
1337            Self::Int32ToFloat32(values) => values.push(buf.get_int()? as f32),
1338            Self::Int32ToFloat64(values) => values.push(buf.get_int()? as f64),
1339            Self::Int64ToFloat32(values) => values.push(buf.get_long()? as f32),
1340            Self::Int64ToFloat64(values) => values.push(buf.get_long()? as f64),
1341            Self::Float32ToFloat64(values) => values.push(buf.get_float()? as f64),
1342            Self::StringToBytes(offsets, values)
1343            | Self::BytesToString(offsets, values)
1344            | Self::Binary(offsets, values)
1345            | Self::String(offsets, values)
1346            | Self::StringView(offsets, values) => {
1347                let data = buf.get_bytes()?;
1348                offsets.push_length(data.len());
1349                values.extend_from_slice(data);
1350            }
1351            Self::Uuid(values) => {
1352                let s_bytes = buf.get_bytes()?;
1353                let s = std::str::from_utf8(s_bytes).map_err(|e| {
1354                    AvroError::ParseError(format!("UUID bytes are not valid UTF-8: {e}"))
1355                })?;
1356                let uuid = Uuid::try_parse(s)
1357                    .map_err(|e| AvroError::ParseError(format!("Failed to parse uuid: {e}")))?;
1358                values.extend_from_slice(uuid.as_bytes());
1359            }
1360            Self::Array(_, off, encoding) => {
1361                let total_items = read_blocks(buf, |cursor| encoding.decode(cursor))?;
1362                off.push_length(total_items);
1363            }
1364            Self::Record {
1365                decoders,
1366                projector: None,
1367                row_count,
1368                ..
1369            } => {
1370                for encoding in decoders {
1371                    encoding.decode(buf)?;
1372                }
1373                *row_count += 1;
1374            }
1375            Self::Record {
1376                decoders,
1377                projector: Some(proj),
1378                row_count,
1379                ..
1380            } => {
1381                proj.project_record(buf, decoders)?;
1382                *row_count += 1;
1383            }
1384            Self::Map(_, koff, moff, kdata, valdec) => {
1385                let newly_added = read_blocks(buf, |cur| {
1386                    let kb = cur.get_bytes()?;
1387                    koff.push_length(kb.len());
1388                    kdata.extend_from_slice(kb);
1389                    valdec.decode(cur)
1390                })?;
1391                moff.push_length(newly_added);
1392            }
1393            Self::Fixed(sz, accum) => {
1394                let fx = buf.get_fixed(*sz as usize)?;
1395                accum.extend_from_slice(fx);
1396            }
1397            #[cfg(feature = "small_decimals")]
1398            Self::Decimal32(_, _, size, builder) => {
1399                decode_decimal!(size, buf, builder, 4, i32);
1400            }
1401            #[cfg(feature = "small_decimals")]
1402            Self::Decimal64(_, _, size, builder) => {
1403                decode_decimal!(size, buf, builder, 8, i64);
1404            }
1405            Self::Decimal128(_, _, size, builder) => {
1406                decode_decimal!(size, buf, builder, 16, i128);
1407            }
1408            Self::Decimal256(_, _, size, builder) => {
1409                decode_decimal!(size, buf, builder, 32, i256);
1410            }
1411            Self::Enum(indices, _, None) => {
1412                indices.push(buf.get_int()?);
1413            }
1414            Self::Enum(indices, _, Some(res)) => {
1415                let raw = buf.get_int()?;
1416                let resolved = res.resolve(raw)?;
1417                indices.push(resolved);
1418            }
1419            Self::Duration(builder) => {
1420                let b = buf.get_fixed(12)?;
1421                let months = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
1422                let days = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
1423                let millis = u32::from_le_bytes([b[8], b[9], b[10], b[11]]);
1424                let nanos = (millis as i64) * 1_000_000;
1425                builder.append_value(IntervalMonthDayNano::new(months as i32, days as i32, nanos));
1426            }
1427            #[cfg(feature = "avro_custom_types")]
1428            Self::RunEndEncoded(_, len, inner) => {
1429                *len += 1;
1430                inner.decode(buf)?;
1431            }
1432            Self::Union(u) => u.decode(buf)?,
1433            Self::Nullable(nullable) => {
1434                let is_not_null = match &nullable.plan {
1435                    NullablePlan::FromSingle { .. } => true,
1436                    NullablePlan::ReadTag { nullability, .. } => {
1437                        let branch = buf.read_vlq()?;
1438                        match *nullability {
1439                            Nullability::NullFirst => branch != 0,
1440                            Nullability::NullSecond => branch == 0,
1441                        }
1442                    }
1443                };
1444
1445                if is_not_null {
1446                    nullable.materialize_pending()?;
1447                    let resolution = match &nullable.plan {
1448                        NullablePlan::FromSingle { resolution }
1449                        | NullablePlan::ReadTag { resolution, .. } => resolution,
1450                    };
1451                    // Append validity only after decoding succeeds.
1452                    nullable.values.decode_with_resolution(buf, resolution)?;
1453                    nullable.validity.append_non_null();
1454                } else {
1455                    nullable.pending_nulls += 1;
1456                }
1457            }
1458        }
1459        Ok(())
1460    }
1461
1462    fn decode_with_promotion(
1463        &mut self,
1464        buf: &mut AvroCursor<'_>,
1465        promotion: Promotion,
1466    ) -> Result<(), AvroError> {
1467        #[cfg(feature = "avro_custom_types")]
1468        if let Self::RunEndEncoded(_, len, inner) = self {
1469            *len += 1;
1470            return inner.decode_with_promotion(buf, promotion);
1471        }
1472
1473        macro_rules! promote_numeric_to {
1474            ($variant:ident, $getter:ident, $to:ty) => {{
1475                match self {
1476                    Self::$variant(v) => {
1477                        let x = buf.$getter()?;
1478                        v.push(x as $to);
1479                        Ok(())
1480                    }
1481                    other => Err(AvroError::ParseError(format!(
1482                        "Promotion {promotion} target mismatch: expected {}, got {}",
1483                        stringify!($variant),
1484                        <Self as ::std::convert::AsRef<str>>::as_ref(other)
1485                    ))),
1486                }
1487            }};
1488        }
1489        match promotion {
1490            Promotion::Direct => self.decode(buf),
1491            Promotion::IntToLong => promote_numeric_to!(Int64, get_int, i64),
1492            Promotion::IntToFloat => promote_numeric_to!(Float32, get_int, f32),
1493            Promotion::IntToDouble => promote_numeric_to!(Float64, get_int, f64),
1494            Promotion::LongToFloat => promote_numeric_to!(Float32, get_long, f32),
1495            Promotion::LongToDouble => promote_numeric_to!(Float64, get_long, f64),
1496            Promotion::FloatToDouble => promote_numeric_to!(Float64, get_float, f64),
1497            Promotion::StringToBytes => match self {
1498                Self::Binary(offsets, values) | Self::StringToBytes(offsets, values) => {
1499                    let data = buf.get_bytes()?;
1500                    offsets.push_length(data.len());
1501                    values.extend_from_slice(data);
1502                    Ok(())
1503                }
1504                other => Err(AvroError::ParseError(format!(
1505                    "Promotion {promotion} target mismatch: expected bytes (Binary/StringToBytes), got {}",
1506                    <Self as AsRef<str>>::as_ref(other)
1507                ))),
1508            },
1509            Promotion::BytesToString => match self {
1510                Self::String(offsets, values)
1511                | Self::StringView(offsets, values)
1512                | Self::BytesToString(offsets, values) => {
1513                    let data = buf.get_bytes()?;
1514                    offsets.push_length(data.len());
1515                    values.extend_from_slice(data);
1516                    Ok(())
1517                }
1518                other => Err(AvroError::ParseError(format!(
1519                    "Promotion {promotion} target mismatch: expected string (String/StringView/BytesToString), got {}",
1520                    <Self as AsRef<str>>::as_ref(other)
1521                ))),
1522            },
1523        }
1524    }
1525
1526    fn decode_with_resolution<'d>(
1527        &'d mut self,
1528        buf: &mut AvroCursor<'_>,
1529        resolution: &'d ResolutionPlan,
1530    ) -> Result<(), AvroError> {
1531        #[cfg(feature = "avro_custom_types")]
1532        if let Self::RunEndEncoded(_, len, inner) = self {
1533            *len += 1;
1534            return inner.decode_with_resolution(buf, resolution);
1535        }
1536
1537        match resolution {
1538            ResolutionPlan::Promotion(promotion) => {
1539                let promotion = *promotion;
1540                self.decode_with_promotion(buf, promotion)
1541            }
1542            ResolutionPlan::DefaultValue(lit) => self.append_default(lit),
1543            ResolutionPlan::EnumMapping(res) => {
1544                let Self::Enum(indices, _, _) = self else {
1545                    return Err(AvroError::SchemaError(
1546                        "enum mapping resolution provided for non-enum decoder".into(),
1547                    ));
1548                };
1549                let raw = buf.get_int()?;
1550                let resolved = res.resolve(raw)?;
1551                indices.push(resolved);
1552                Ok(())
1553            }
1554            ResolutionPlan::Record(proj) => {
1555                let Self::Record {
1556                    decoders,
1557                    row_count,
1558                    ..
1559                } = self
1560                else {
1561                    return Err(AvroError::SchemaError(
1562                        "record projection provided for non-record decoder".into(),
1563                    ));
1564                };
1565                proj.project_record(buf, decoders)?;
1566                *row_count += 1;
1567                Ok(())
1568            }
1569        }
1570    }
1571
1572    /// Flush decoded records to an [`ArrayRef`]
1573    fn flush(&mut self, nulls: Option<NullBuffer>) -> Result<ArrayRef, AvroError> {
1574        Ok(match self {
1575            Self::Nullable(nullable) => {
1576                nullable.materialize_pending()?;
1577                nullable.values.flush(nullable.validity.finish())?
1578            }
1579            Self::Null(size) => Arc::new(NullArray::new(std::mem::replace(size, 0))),
1580            Self::Boolean(b) => Arc::new(BooleanArray::new(b.finish(), nulls)),
1581            Self::Int32(values) => Arc::new(flush_primitive::<Int32Type>(values, nulls)),
1582            Self::Date32(values) => Arc::new(flush_primitive::<Date32Type>(values, nulls)),
1583            Self::Int64(values) => Arc::new(flush_primitive::<Int64Type>(values, nulls)),
1584            Self::TimeMillis(values) => {
1585                Arc::new(flush_primitive::<Time32MillisecondType>(values, nulls))
1586            }
1587            Self::TimeMicros(values) => {
1588                Arc::new(flush_primitive::<Time64MicrosecondType>(values, nulls))
1589            }
1590            Self::TimestampMillis(tz, values) => Arc::new(
1591                flush_primitive::<TimestampMillisecondType>(values, nulls)
1592                    .with_timezone_opt(tz.as_ref().map(|tz| tz.to_string())),
1593            ),
1594            Self::TimestampMicros(tz, values) => Arc::new(
1595                flush_primitive::<TimestampMicrosecondType>(values, nulls)
1596                    .with_timezone_opt(tz.as_ref().map(|tz| tz.to_string())),
1597            ),
1598            Self::TimestampNanos(tz, values) => Arc::new(
1599                flush_primitive::<TimestampNanosecondType>(values, nulls)
1600                    .with_timezone_opt(tz.as_ref().map(|tz| tz.to_string())),
1601            ),
1602            #[cfg(feature = "avro_custom_types")]
1603            Self::DurationSecond(values) => {
1604                Arc::new(flush_primitive::<DurationSecondType>(values, nulls))
1605            }
1606            #[cfg(feature = "avro_custom_types")]
1607            Self::DurationMillisecond(values) => {
1608                Arc::new(flush_primitive::<DurationMillisecondType>(values, nulls))
1609            }
1610            #[cfg(feature = "avro_custom_types")]
1611            Self::DurationMicrosecond(values) => {
1612                Arc::new(flush_primitive::<DurationMicrosecondType>(values, nulls))
1613            }
1614            #[cfg(feature = "avro_custom_types")]
1615            Self::DurationNanosecond(values) => {
1616                Arc::new(flush_primitive::<DurationNanosecondType>(values, nulls))
1617            }
1618            #[cfg(feature = "avro_custom_types")]
1619            Self::Int8(values) => Arc::new(flush_primitive::<Int8Type>(values, nulls)),
1620            #[cfg(feature = "avro_custom_types")]
1621            Self::Int16(values) => Arc::new(flush_primitive::<Int16Type>(values, nulls)),
1622            #[cfg(feature = "avro_custom_types")]
1623            Self::UInt8(values) => Arc::new(flush_primitive::<UInt8Type>(values, nulls)),
1624            #[cfg(feature = "avro_custom_types")]
1625            Self::UInt16(values) => Arc::new(flush_primitive::<UInt16Type>(values, nulls)),
1626            #[cfg(feature = "avro_custom_types")]
1627            Self::UInt32(values) => Arc::new(flush_primitive::<UInt32Type>(values, nulls)),
1628            #[cfg(feature = "avro_custom_types")]
1629            Self::UInt64(values) => Arc::new(flush_primitive::<UInt64Type>(values, nulls)),
1630            #[cfg(feature = "avro_custom_types")]
1631            Self::Float16(values) => {
1632                // Convert Vec<u16> to Float16Array by reinterpreting the raw bytes.
1633                // This is safe because f16 and u16 have the same size and alignment.
1634                let len = values.len();
1635                let buf: Buffer = std::mem::take(values).into();
1636                let scalar_buf = ScalarBuffer::new(buf, 0, len);
1637                Arc::new(Float16Array::new(scalar_buf, nulls))
1638            }
1639            #[cfg(feature = "avro_custom_types")]
1640            Self::Date64(values) => Arc::new(flush_primitive::<Date64Type>(values, nulls)),
1641            #[cfg(feature = "avro_custom_types")]
1642            Self::TimeNanos(values) => {
1643                Arc::new(flush_primitive::<Time64NanosecondType>(values, nulls))
1644            }
1645            #[cfg(feature = "avro_custom_types")]
1646            Self::Time32Secs(values) => {
1647                Arc::new(flush_primitive::<Time32SecondType>(values, nulls))
1648            }
1649            #[cfg(feature = "avro_custom_types")]
1650            Self::TimestampSecs(is_utc, values) => Arc::new(
1651                flush_primitive::<TimestampSecondType>(values, nulls)
1652                    .with_timezone_opt(is_utc.then(|| "+00:00")),
1653            ),
1654            #[cfg(feature = "avro_custom_types")]
1655            Self::IntervalYearMonth(values) => {
1656                Arc::new(flush_primitive::<IntervalYearMonthType>(values, nulls))
1657            }
1658            #[cfg(feature = "avro_custom_types")]
1659            Self::IntervalMonthDayNano(values) => {
1660                Arc::new(flush_primitive::<IntervalMonthDayNanoType>(values, nulls))
1661            }
1662            #[cfg(feature = "avro_custom_types")]
1663            Self::IntervalDayTime(values) => {
1664                Arc::new(flush_primitive::<IntervalDayTimeType>(values, nulls))
1665            }
1666            Self::Float32(values) => Arc::new(flush_primitive::<Float32Type>(values, nulls)),
1667            Self::Float64(values) => Arc::new(flush_primitive::<Float64Type>(values, nulls)),
1668            Self::Int32ToInt64(values) => Arc::new(flush_primitive::<Int64Type>(values, nulls)),
1669            Self::Int32ToFloat32(values) | Self::Int64ToFloat32(values) => {
1670                Arc::new(flush_primitive::<Float32Type>(values, nulls))
1671            }
1672            Self::Int32ToFloat64(values)
1673            | Self::Int64ToFloat64(values)
1674            | Self::Float32ToFloat64(values) => {
1675                Arc::new(flush_primitive::<Float64Type>(values, nulls))
1676            }
1677            Self::StringToBytes(offsets, values) | Self::Binary(offsets, values) => {
1678                let offsets = flush_offsets(offsets);
1679                let values = flush_values(values).into();
1680                Arc::new(BinaryArray::try_new(offsets, values, nulls)?)
1681            }
1682            Self::BytesToString(offsets, values) | Self::String(offsets, values) => {
1683                let offsets = flush_offsets(offsets);
1684                let values = flush_values(values).into();
1685                Arc::new(StringArray::try_new(offsets, values, nulls)?)
1686            }
1687            Self::StringView(offsets, values) => {
1688                let offsets = flush_offsets(offsets);
1689                let values = flush_values(values);
1690                let array = StringArray::try_new(offsets, values.into(), nulls.clone())?;
1691                let values: Vec<&str> = (0..array.len())
1692                    .map(|i| {
1693                        if array.is_valid(i) {
1694                            array.value(i)
1695                        } else {
1696                            ""
1697                        }
1698                    })
1699                    .collect();
1700                Arc::new(StringViewArray::from(values))
1701            }
1702            Self::Array(field, offsets, values) => {
1703                let values = values.flush(None)?;
1704                let offsets = flush_offsets(offsets);
1705                Arc::new(ListArray::try_new(field.clone(), offsets, values, nulls)?)
1706            }
1707            Self::Record {
1708                fields,
1709                decoders,
1710                row_count,
1711                ..
1712            } => {
1713                let arrays = decoders
1714                    .iter_mut()
1715                    .map(|x| x.flush(None))
1716                    .collect::<Result<Vec<_>, _>>()?;
1717                // In case there are no fields (legal, encodes to zero bytes), manually specify
1718                // the length for the output struct to account for this.
1719                Arc::new(StructArray::try_new_with_length(
1720                    fields.clone(),
1721                    arrays,
1722                    nulls,
1723                    std::mem::replace(row_count, 0),
1724                )?)
1725            }
1726            Self::Map(map_field, k_off, m_off, kdata, valdec) => {
1727                let moff = flush_offsets(m_off);
1728                let koff = flush_offsets(k_off);
1729                let kd = flush_values(kdata).into();
1730                let val_arr = valdec.flush(None)?;
1731                let key_arr = StringArray::try_new(koff, kd, None)?;
1732                if key_arr.len() != val_arr.len() {
1733                    return Err(AvroError::InvalidArgument(format!(
1734                        "Map keys length ({}) != map values length ({})",
1735                        key_arr.len(),
1736                        val_arr.len()
1737                    )));
1738                }
1739                let final_len = moff.len() - 1;
1740                if let Some(n) = &nulls
1741                    && n.len() != final_len
1742                {
1743                    return Err(AvroError::InvalidArgument(format!(
1744                        "Map array null buffer length {} != final map length {final_len}",
1745                        n.len()
1746                    )));
1747                }
1748                let entries_fields = match map_field.data_type() {
1749                    DataType::Struct(fields) => fields.clone(),
1750                    other => {
1751                        return Err(AvroError::InvalidArgument(format!(
1752                            "Map entries field must be a Struct, got {other:?}"
1753                        )));
1754                    }
1755                };
1756                let entries_struct =
1757                    StructArray::try_new(entries_fields, vec![Arc::new(key_arr), val_arr], None)?;
1758                let map_arr =
1759                    MapArray::try_new(map_field.clone(), moff, entries_struct, nulls, false)?;
1760                Arc::new(map_arr)
1761            }
1762            Self::Fixed(sz, accum) => {
1763                let b: Buffer = flush_values(accum).into();
1764                let arr = FixedSizeBinaryArray::try_new(*sz, b, nulls)
1765                    .map_err(|e| AvroError::ParseError(e.to_string()))?;
1766                Arc::new(arr)
1767            }
1768            Self::Uuid(values) => {
1769                let arr = FixedSizeBinaryArray::try_new(16, std::mem::take(values).into(), nulls)
1770                    .map_err(|e| AvroError::ParseError(e.to_string()))?;
1771                Arc::new(arr)
1772            }
1773            #[cfg(feature = "small_decimals")]
1774            Self::Decimal32(precision, scale, _, builder) => {
1775                flush_decimal!(builder, precision, scale, nulls, Decimal32Array)
1776            }
1777            #[cfg(feature = "small_decimals")]
1778            Self::Decimal64(precision, scale, _, builder) => {
1779                flush_decimal!(builder, precision, scale, nulls, Decimal64Array)
1780            }
1781            Self::Decimal128(precision, scale, _, builder) => {
1782                flush_decimal!(builder, precision, scale, nulls, Decimal128Array)
1783            }
1784            Self::Decimal256(precision, scale, _, builder) => {
1785                flush_decimal!(builder, precision, scale, nulls, Decimal256Array)
1786            }
1787            Self::Enum(indices, symbols, _) => flush_dict(indices, symbols, nulls)?,
1788            Self::Duration(builder) => {
1789                let (_, vals, _) = builder.finish().into_parts();
1790                let vals = IntervalMonthDayNanoArray::try_new(vals, nulls)
1791                    .map_err(|e| AvroError::ParseError(e.to_string()))?;
1792                Arc::new(vals)
1793            }
1794            #[cfg(feature = "avro_custom_types")]
1795            Self::RunEndEncoded(width, len, inner) => {
1796                let values = inner.flush(nulls)?;
1797                let n = *len;
1798                let arr = values.as_ref();
1799                let mut run_starts: Vec<usize> = Vec::with_capacity(n);
1800                if n > 0 {
1801                    run_starts.push(0);
1802                    for i in 1..n {
1803                        if !values_equal_at(arr, i - 1, i) {
1804                            run_starts.push(i);
1805                        }
1806                    }
1807                }
1808                if n > (u32::MAX as usize) {
1809                    return Err(AvroError::InvalidArgument(format!(
1810                        "RunEndEncoded length {n} exceeds maximum supported by UInt32 indices for take",
1811                    )));
1812                }
1813                let run_count = run_starts.len();
1814                let take_idx: PrimitiveArray<UInt32Type> =
1815                    run_starts.iter().map(|&s| s as u32).collect();
1816                let per_run_values = if run_count == 0 {
1817                    values.slice(0, 0)
1818                } else {
1819                    take(arr, &take_idx, Option::from(TakeOptions::default())).map_err(|e| {
1820                        AvroError::ParseError(format!("take() for REE values failed: {e}"))
1821                    })?
1822                };
1823
1824                macro_rules! build_run_array {
1825                    ($Native:ty, $ArrowTy:ty) => {{
1826                        let mut ends: Vec<$Native> = Vec::with_capacity(run_count);
1827                        for (idx, &_start) in run_starts.iter().enumerate() {
1828                            let end = if idx + 1 < run_count {
1829                                run_starts[idx + 1]
1830                            } else {
1831                                n
1832                            };
1833                            ends.push(end as $Native);
1834                        }
1835                        let ends: PrimitiveArray<$ArrowTy> = ends.into_iter().collect();
1836                        let run_arr = RunArray::<$ArrowTy>::try_new(&ends, per_run_values.as_ref())
1837                            .map_err(|e| AvroError::ParseError(e.to_string()))?;
1838                        Arc::new(run_arr) as ArrayRef
1839                    }};
1840                }
1841                match *width {
1842                    2 => {
1843                        if n > i16::MAX as usize {
1844                            return Err(AvroError::InvalidArgument(format!(
1845                                "RunEndEncoded length {n} exceeds i16::MAX for run end width 2"
1846                            )));
1847                        }
1848                        build_run_array!(i16, Int16Type)
1849                    }
1850                    4 => build_run_array!(i32, Int32Type),
1851                    8 => build_run_array!(i64, Int64Type),
1852                    other => {
1853                        return Err(AvroError::InvalidArgument(format!(
1854                            "Unsupported run-end width {other} for RunEndEncoded"
1855                        )));
1856                    }
1857                }
1858            }
1859            Self::Union(u) => u.flush(nulls)?,
1860        })
1861    }
1862}
1863
1864/// Runtime plan for decoding reader-side `["null", T]` types.
1865#[derive(Debug)]
1866enum NullablePlan {
1867    /// Writer actually wrote a union (branch tag present).
1868    ReadTag {
1869        nullability: Nullability,
1870        resolution: ResolutionPlan,
1871    },
1872    /// Writer wrote a single (non-union) value resolved to the non-null branch
1873    /// of the reader union; do NOT read a branch tag, but apply any resolution.
1874    FromSingle { resolution: ResolutionPlan },
1875}
1876
1877/// Runtime plan for resolving writer-reader type differences.
1878#[derive(Debug)]
1879enum ResolutionPlan {
1880    /// Indicates that the writer's type should be promoted to the reader's type.
1881    Promotion(Promotion),
1882    /// Provides a default value for the field missing in the writer type.
1883    DefaultValue(AvroLiteral),
1884    /// Provides mapping information for resolving enums.
1885    EnumMapping(EnumResolution),
1886    /// Provides projection information for record fields.
1887    Record(Projector),
1888}
1889
1890impl ResolutionPlan {
1891    fn try_new(decoder: &Decoder, resolution: &ResolutionInfo) -> Result<Self, AvroError> {
1892        match (decoder, resolution) {
1893            (_, ResolutionInfo::Promotion(p)) => Ok(ResolutionPlan::Promotion(*p)),
1894            (_, ResolutionInfo::DefaultValue(lit)) => Ok(ResolutionPlan::DefaultValue(lit.clone())),
1895            (_, ResolutionInfo::EnumMapping(m)) => {
1896                Ok(ResolutionPlan::EnumMapping(EnumResolution::new(m)))
1897            }
1898            (
1899                Decoder::Record {
1900                    defaults: field_defaults,
1901                    ..
1902                },
1903                ResolutionInfo::Record(r),
1904            ) => Ok(ResolutionPlan::Record(
1905                ProjectorBuilder::try_new(r, field_defaults).build()?,
1906            )),
1907            (_, ResolutionInfo::Record(_)) => Err(AvroError::SchemaError(
1908                "record resolution on non-record decoder".into(),
1909            )),
1910            (_, ResolutionInfo::Union(_)) => Err(AvroError::SchemaError(
1911                "union variant cannot be resolved to a union type".into(),
1912            )),
1913        }
1914    }
1915}
1916
1917#[derive(Debug)]
1918struct EnumResolution {
1919    mapping: Arc<[i32]>,
1920    default_index: i32,
1921}
1922
1923impl EnumResolution {
1924    fn new(mapping: &EnumMapping) -> Self {
1925        EnumResolution {
1926            mapping: mapping.mapping.clone(),
1927            default_index: mapping.default_index,
1928        }
1929    }
1930
1931    fn resolve(&self, index: i32) -> Result<i32, AvroError> {
1932        let resolved = usize::try_from(index)
1933            .ok()
1934            .and_then(|idx| self.mapping.get(idx).copied())
1935            .filter(|&idx| idx >= 0)
1936            .unwrap_or(self.default_index);
1937        if resolved >= 0 {
1938            Ok(resolved)
1939        } else {
1940            Err(AvroError::ParseError(format!(
1941                "Enum symbol index {index} not resolvable and no default provided",
1942            )))
1943        }
1944    }
1945}
1946
1947// A lookup table for resolving fields between writer and reader schemas during record projection.
1948#[derive(Debug)]
1949struct DispatchLookupTable {
1950    // Maps each reader field index `r` to the corresponding writer field index.
1951    //
1952    // Semantics:
1953    // - `to_reader[r] >= 0`: The value is an index into the writer's fields. The value from
1954    //   the writer field is decoded, and `promotion[r]` is applied.
1955    // - `to_reader[r] == NO_SOURCE` (-1): No matching writer field exists. The reader field's
1956    //   default value is used.
1957    //
1958    // Representation (`i8`):
1959    // `i8` is used for a dense, cache-friendly dispatch table, consistent with Arrow's use of
1960    // `i8` for union type IDs. This requires that writer field indices do not exceed `i8::MAX`.
1961    //
1962    // Invariants:
1963    // - `to_reader.len() == promotion.len()` and matches the reader field count.
1964    // - If `to_reader[r] == NO_SOURCE`, `promotion[r]` is ignored.
1965    to_reader: Box<[i8]>,
1966    // For each reader field `r`, specifies the resolution to apply to the writer's value.
1967    //
1968    // This is used when a writer field's type can be promoted to a reader field's type
1969    // (e.g., `Int` to `Long`). It is ignored if `to_reader[r] == NO_SOURCE`.
1970    resolution: Box<[ResolutionPlan]>,
1971}
1972
1973// Sentinel used in `DispatchLookupTable::to_reader` to mark
1974// "no matching writer field".
1975const NO_SOURCE: i8 = -1;
1976
1977impl DispatchLookupTable {
1978    fn from_writer_to_reader(
1979        reader_branches: &[Decoder],
1980        resolution_map: &[Option<(usize, ResolutionInfo)>],
1981    ) -> Result<Self, AvroError> {
1982        let mut to_reader = Vec::with_capacity(resolution_map.len());
1983        let mut resolution = Vec::with_capacity(resolution_map.len());
1984        for map in resolution_map {
1985            match map {
1986                Some((idx, res)) => {
1987                    let idx = *idx;
1988                    let idx_i8 = i8::try_from(idx).map_err(|_| {
1989                        AvroError::SchemaError(format!(
1990                            "Reader branch index {idx} exceeds i8 range (max {})",
1991                            i8::MAX
1992                        ))
1993                    })?;
1994                    let plan = ResolutionPlan::try_new(&reader_branches[idx], res)?;
1995                    to_reader.push(idx_i8);
1996                    resolution.push(plan);
1997                }
1998                None => {
1999                    to_reader.push(NO_SOURCE);
2000                    resolution.push(ResolutionPlan::DefaultValue(AvroLiteral::Null));
2001                }
2002            }
2003        }
2004        Ok(Self {
2005            to_reader: to_reader.into_boxed_slice(),
2006            resolution: resolution.into_boxed_slice(),
2007        })
2008    }
2009
2010    // Resolve a writer branch index to (reader_idx, resolution)
2011    #[inline]
2012    fn resolve(&self, writer_index: usize) -> Option<(usize, &ResolutionPlan)> {
2013        let reader_index = *self.to_reader.get(writer_index)?;
2014        (reader_index >= 0).then(|| (reader_index as usize, &self.resolution[writer_index]))
2015    }
2016}
2017
2018#[derive(Debug)]
2019struct UnionDecoder {
2020    fields: UnionFields,
2021    branches: UnionDecoderBranches,
2022    default_emit_idx: usize,
2023    null_emit_idx: usize,
2024    plan: UnionReadPlan,
2025}
2026
2027#[derive(Debug, Default)]
2028struct UnionDecoderBranches {
2029    decoders: Vec<Decoder>,
2030    reader_type_codes: Vec<i8>,
2031    type_ids: Vec<i8>,
2032    offsets: Vec<i32>,
2033    counts: Vec<i32>,
2034}
2035
2036impl UnionDecoderBranches {
2037    fn new(decoders: Vec<Decoder>, reader_type_codes: Vec<i8>) -> Self {
2038        let branch_len = decoders.len().max(reader_type_codes.len());
2039        Self {
2040            decoders,
2041            reader_type_codes,
2042            type_ids: Vec::with_capacity(DEFAULT_CAPACITY),
2043            offsets: Vec::with_capacity(DEFAULT_CAPACITY),
2044            counts: vec![0; branch_len],
2045        }
2046    }
2047
2048    fn emit_to(&mut self, reader_idx: usize) -> Result<&mut Decoder, AvroError> {
2049        let branches_len = self.decoders.len();
2050        let Some(reader_branch) = self.decoders.get_mut(reader_idx) else {
2051            return Err(AvroError::ParseError(format!(
2052                "Union branch index {reader_idx} out of range ({branches_len} branches)"
2053            )));
2054        };
2055        self.type_ids.push(self.reader_type_codes[reader_idx]);
2056        self.offsets.push(self.counts[reader_idx]);
2057        self.counts[reader_idx] += 1;
2058        Ok(reader_branch)
2059    }
2060}
2061
2062impl Default for UnionDecoder {
2063    fn default() -> Self {
2064        Self {
2065            fields: UnionFields::empty(),
2066            branches: Default::default(),
2067            default_emit_idx: 0,
2068            null_emit_idx: 0,
2069            plan: UnionReadPlan::Passthrough,
2070        }
2071    }
2072}
2073
2074#[derive(Debug)]
2075enum UnionReadPlan {
2076    ReaderUnion {
2077        lookup_table: DispatchLookupTable,
2078    },
2079    FromSingle {
2080        reader_idx: usize,
2081        resolution: ResolutionPlan,
2082    },
2083    ToSingle {
2084        target: Box<Decoder>,
2085        lookup_table: DispatchLookupTable,
2086    },
2087    Passthrough,
2088}
2089
2090impl UnionReadPlan {
2091    fn from_resolved(
2092        reader_branches: &[Decoder],
2093        resolved: Option<ResolvedUnion>,
2094    ) -> Result<Self, AvroError> {
2095        let Some(info) = resolved else {
2096            return Ok(Self::Passthrough);
2097        };
2098        match (info.writer_is_union, info.reader_is_union) {
2099            (true, true) => {
2100                let lookup_table =
2101                    DispatchLookupTable::from_writer_to_reader(reader_branches, &info.writer_to_reader)?;
2102                Ok(Self::ReaderUnion { lookup_table })
2103            }
2104            (false, true) => {
2105                let Some((idx, resolution)) =
2106                    info.writer_to_reader.first().and_then(Option::as_ref)
2107                else {
2108                    return Err(AvroError::SchemaError(
2109                        "Writer type does not match any reader union branch".to_string(),
2110                    ));
2111                };
2112                let reader_idx = *idx;
2113                Ok(Self::FromSingle {
2114                    reader_idx,
2115                    resolution: ResolutionPlan::try_new(&reader_branches[reader_idx], resolution)?,
2116                })
2117            }
2118            (true, false) => Err(AvroError::InvalidArgument(
2119                "UnionDecoder::try_new cannot build writer-union to single; use UnionDecoderBuilder with a target"
2120                    .to_string(),
2121            )),
2122            // (false, false) is invalid and should never be constructed by the resolver.
2123            _ => Err(AvroError::SchemaError(
2124                "ResolvedUnion constructed for non-union sides; resolver should return None"
2125                    .to_string(),
2126            )),
2127        }
2128    }
2129}
2130
2131impl UnionDecoder {
2132    fn try_new(
2133        fields: UnionFields,
2134        branches: Vec<Decoder>,
2135        resolved: Option<ResolvedUnion>,
2136    ) -> Result<Self, AvroError> {
2137        let reader_type_codes = fields.iter().map(|(tid, _)| tid).collect::<Vec<i8>>();
2138        let null_branch = branches.iter().position(|b| matches!(b, Decoder::Null(_)));
2139        let default_emit_idx = 0;
2140        let null_emit_idx = null_branch.unwrap_or(default_emit_idx);
2141        // Guard against impractically large unions that cannot be indexed by an Avro int
2142        let max_addr = (i32::MAX as usize) + 1;
2143        if branches.len() > max_addr {
2144            return Err(AvroError::SchemaError(format!(
2145                "Reader union has {} branches, which exceeds the maximum addressable \
2146                 branches by an Avro int tag ({} + 1).",
2147                branches.len(),
2148                i32::MAX
2149            )));
2150        }
2151        let plan = UnionReadPlan::from_resolved(&branches, resolved)?;
2152        Ok(Self {
2153            fields,
2154            branches: UnionDecoderBranches::new(branches, reader_type_codes),
2155            default_emit_idx,
2156            null_emit_idx,
2157            plan,
2158        })
2159    }
2160
2161    fn with_single_target(target: Decoder, info: ResolvedUnion) -> Result<Self, AvroError> {
2162        // This constructor is only for writer-union to single-type resolution
2163        debug_assert!(info.writer_is_union && !info.reader_is_union);
2164        let mut reader_branches = [target];
2165        let lookup_table =
2166            DispatchLookupTable::from_writer_to_reader(&reader_branches, &info.writer_to_reader)?;
2167        let target = Box::new(mem::replace(&mut reader_branches[0], Decoder::Null(0)));
2168        Ok(Self {
2169            plan: UnionReadPlan::ToSingle {
2170                target,
2171                lookup_table,
2172            },
2173            ..Self::default()
2174        })
2175    }
2176
2177    #[inline]
2178    fn read_tag(buf: &mut AvroCursor<'_>) -> Result<usize, AvroError> {
2179        // Avro unions are encoded by first writing the zero-based branch index.
2180        // In Avro 1.11.1 this is specified as an *int*; older specs said *long*,
2181        // but both use zig-zag varint encoding, so decoding as long is compatible
2182        // with either form and widely used in practice.
2183        let raw = buf.get_long()?;
2184        if raw < 0 {
2185            return Err(AvroError::ParseError(format!(
2186                "Negative union branch index {raw}"
2187            )));
2188        }
2189        usize::try_from(raw).map_err(|_| {
2190            AvroError::ParseError(format!(
2191                "Union branch index {raw} does not fit into usize on this platform ({}-bit)",
2192                (usize::BITS as usize)
2193            ))
2194        })
2195    }
2196
2197    #[inline]
2198    fn on_decoder<F>(&mut self, fallback_idx: usize, action: F) -> Result<(), AvroError>
2199    where
2200        F: FnOnce(&mut Decoder) -> Result<(), AvroError>,
2201    {
2202        if let UnionReadPlan::ToSingle { target, .. } = &mut self.plan {
2203            return action(target);
2204        }
2205        let reader_idx = match &self.plan {
2206            UnionReadPlan::FromSingle { reader_idx, .. } => *reader_idx,
2207            _ => fallback_idx,
2208        };
2209        self.branches.emit_to(reader_idx).and_then(action)
2210    }
2211
2212    fn append_null(&mut self) -> Result<(), AvroError> {
2213        self.on_decoder(self.null_emit_idx, |decoder| decoder.append_null())
2214    }
2215
2216    fn append_default(&mut self, lit: &AvroLiteral) -> Result<(), AvroError> {
2217        self.on_decoder(self.default_emit_idx, |decoder| decoder.append_default(lit))
2218    }
2219
2220    fn decode(&mut self, buf: &mut AvroCursor<'_>) -> Result<(), AvroError> {
2221        match &mut self.plan {
2222            UnionReadPlan::Passthrough => {
2223                let reader_idx = Self::read_tag(buf)?;
2224                let decoder = self.branches.emit_to(reader_idx)?;
2225                decoder.decode(buf)
2226            }
2227            UnionReadPlan::ReaderUnion { lookup_table } => {
2228                let idx = Self::read_tag(buf)?;
2229                let Some((reader_idx, resolution)) = lookup_table.resolve(idx) else {
2230                    return Err(AvroError::ParseError(format!(
2231                        "Union branch index {idx} not resolvable by reader schema"
2232                    )));
2233                };
2234                let decoder = self.branches.emit_to(reader_idx)?;
2235                decoder.decode_with_resolution(buf, resolution)
2236            }
2237            UnionReadPlan::FromSingle {
2238                reader_idx,
2239                resolution,
2240            } => {
2241                let decoder = self.branches.emit_to(*reader_idx)?;
2242                decoder.decode_with_resolution(buf, resolution)
2243            }
2244            UnionReadPlan::ToSingle {
2245                target,
2246                lookup_table,
2247            } => {
2248                let idx = Self::read_tag(buf)?;
2249                let Some((_, resolution)) = lookup_table.resolve(idx) else {
2250                    return Err(AvroError::ParseError(format!(
2251                        "Writer union branch index {idx} not resolvable by reader schema"
2252                    )));
2253                };
2254                target.decode_with_resolution(buf, resolution)
2255            }
2256        }
2257    }
2258
2259    fn flush(&mut self, nulls: Option<NullBuffer>) -> Result<ArrayRef, AvroError> {
2260        if let UnionReadPlan::ToSingle { target, .. } = &mut self.plan {
2261            return target.flush(nulls);
2262        }
2263        debug_assert!(
2264            nulls.is_none(),
2265            "UnionArray does not accept a validity bitmap; \
2266                     nulls should have been materialized as a Null child during decode"
2267        );
2268        let children = self
2269            .branches
2270            .decoders
2271            .iter_mut()
2272            .map(|d| d.flush(None))
2273            .collect::<Result<Vec<_>, _>>()?;
2274        let arr = UnionArray::try_new(
2275            self.fields.clone(),
2276            flush_values(&mut self.branches.type_ids)
2277                .into_iter()
2278                .collect(),
2279            Some(
2280                flush_values(&mut self.branches.offsets)
2281                    .into_iter()
2282                    .collect(),
2283            ),
2284            children,
2285        )
2286        .map_err(|e| AvroError::ParseError(e.to_string()))?;
2287        self.branches.counts.fill(0);
2288        Ok(Arc::new(arr))
2289    }
2290}
2291
2292#[derive(Debug, Default)]
2293struct UnionDecoderBuilder {
2294    fields: Option<UnionFields>,
2295    branches: Option<Vec<Decoder>>,
2296    resolved: Option<ResolvedUnion>,
2297    target: Option<Decoder>,
2298}
2299
2300impl UnionDecoderBuilder {
2301    fn new() -> Self {
2302        Self::default()
2303    }
2304
2305    fn with_fields(mut self, fields: UnionFields) -> Self {
2306        self.fields = Some(fields);
2307        self
2308    }
2309
2310    fn with_branches(mut self, branches: Vec<Decoder>) -> Self {
2311        self.branches = Some(branches);
2312        self
2313    }
2314
2315    fn with_resolved_union(mut self, resolved_union: ResolvedUnion) -> Self {
2316        self.resolved = Some(resolved_union);
2317        self
2318    }
2319
2320    fn with_target(mut self, target: Decoder) -> Self {
2321        self.target = Some(target);
2322        self
2323    }
2324
2325    fn build(self) -> Result<UnionDecoder, AvroError> {
2326        match (self.resolved, self.fields, self.branches, self.target) {
2327            (resolved, Some(fields), Some(branches), None) => {
2328                UnionDecoder::try_new(fields, branches, resolved)
2329            }
2330            (Some(info), None, None, Some(target))
2331                if info.writer_is_union && !info.reader_is_union =>
2332            {
2333                UnionDecoder::with_single_target(target, info)
2334            }
2335            _ => Err(AvroError::InvalidArgument(
2336                "Invalid UnionDecoderBuilder configuration: expected either \
2337                 (fields + branches + resolved) with no target for reader-unions, or \
2338                 (resolved + target) with no fields/branches for writer-union to single."
2339                    .to_string(),
2340            )),
2341        }
2342    }
2343}
2344
2345#[derive(Debug, Copy, Clone)]
2346enum NegativeBlockBehavior {
2347    ProcessItems,
2348    SkipBySize,
2349}
2350
2351#[inline]
2352fn skip_blocks(
2353    buf: &mut AvroCursor,
2354    mut skip_item: impl FnMut(&mut AvroCursor) -> Result<(), AvroError>,
2355) -> Result<usize, AvroError> {
2356    process_blockwise(
2357        buf,
2358        move |c| skip_item(c),
2359        NegativeBlockBehavior::SkipBySize,
2360    )
2361}
2362
2363#[inline]
2364fn flush_dict(
2365    indices: &mut Vec<i32>,
2366    symbols: &[String],
2367    nulls: Option<NullBuffer>,
2368) -> Result<ArrayRef, AvroError> {
2369    let keys = flush_primitive::<Int32Type>(indices, nulls);
2370    let values = Arc::new(StringArray::from_iter_values(
2371        symbols.iter().map(|s| s.as_str()),
2372    ));
2373    DictionaryArray::try_new(keys, values)
2374        .map_err(Into::into)
2375        .map(|arr| Arc::new(arr) as ArrayRef)
2376}
2377
2378#[inline]
2379fn read_blocks(
2380    buf: &mut AvroCursor,
2381    decode_entry: impl FnMut(&mut AvroCursor) -> Result<(), AvroError>,
2382) -> Result<usize, AvroError> {
2383    process_blockwise(buf, decode_entry, NegativeBlockBehavior::ProcessItems)
2384}
2385
2386#[inline]
2387fn process_blockwise(
2388    buf: &mut AvroCursor,
2389    mut on_item: impl FnMut(&mut AvroCursor) -> Result<(), AvroError>,
2390    negative_behavior: NegativeBlockBehavior,
2391) -> Result<usize, AvroError> {
2392    let mut total = 0usize;
2393    loop {
2394        // Read the block count
2395        //  positive = that many items
2396        //  negative = that many items + read block size
2397        //  See: https://avro.apache.org/docs/1.11.1/specification/#maps
2398        let block_count = buf.get_long()?;
2399        match block_count.cmp(&0) {
2400            Ordering::Equal => break,
2401            Ordering::Less => {
2402                // `unsigned_abs` avoids overflowing `-block_count` for `i64::MIN` (#10235)
2403                let count = block_count.unsigned_abs() as usize;
2404                // A negative count is followed by a long of the size in bytes
2405                let raw_size = buf.get_long()?;
2406                let size_in_bytes = usize::try_from(raw_size).map_err(|_| {
2407                    AvroError::ParseError(format!("Block size cannot be negative, got {raw_size}"))
2408                })?;
2409                match negative_behavior {
2410                    NegativeBlockBehavior::ProcessItems => {
2411                        // Process items one-by-one after reading size
2412                        total = process_block_items(buf, count, total, &mut on_item)?;
2413                    }
2414                    NegativeBlockBehavior::SkipBySize => {
2415                        // Skip the entire block payload at once
2416                        let _ = buf.get_fixed(size_in_bytes)?;
2417                        total = total.saturating_add(count);
2418                    }
2419                }
2420            }
2421            Ordering::Greater => {
2422                let count = block_count as usize;
2423                total = process_block_items(buf, count, total, &mut on_item)?;
2424            }
2425        }
2426    }
2427    Ok(total)
2428}
2429
2430/// Decode `count` items, capping the running total at `i32::MAX` (the largest index
2431/// an Arrow list/map offset holds). Otherwise a crafted `i64::MAX` count of a zero-byte
2432/// item like `null` spins the loop forever (#10235); byte-consuming items self-terminate
2433/// on cursor exhaustion, so valid blocks (including `array<null>`) are unaffected.
2434#[inline]
2435fn process_block_items(
2436    buf: &mut AvroCursor,
2437    count: usize,
2438    total: usize,
2439    on_item: &mut impl FnMut(&mut AvroCursor) -> Result<(), AvroError>,
2440) -> Result<usize, AvroError> {
2441    let Some(new_total) = total
2442        .checked_add(count)
2443        .filter(|&t| i32::try_from(t).is_ok())
2444    else {
2445        return Err(AvroError::ParseError(
2446            "Capacity overflow when decoding array/map item blocks".to_string(),
2447        ));
2448    };
2449    for _ in 0..count {
2450        on_item(buf)?;
2451    }
2452    Ok(new_total)
2453}
2454
2455#[inline]
2456fn flush_values<T>(values: &mut Vec<T>) -> Vec<T> {
2457    std::mem::replace(values, Vec::with_capacity(DEFAULT_CAPACITY))
2458}
2459
2460#[inline]
2461fn flush_offsets(offsets: &mut OffsetBufferBuilder<i32>) -> OffsetBuffer<i32> {
2462    std::mem::replace(offsets, OffsetBufferBuilder::new(DEFAULT_CAPACITY)).finish()
2463}
2464
2465#[inline]
2466fn flush_primitive<T: ArrowPrimitiveType>(
2467    values: &mut Vec<T::Native>,
2468    nulls: Option<NullBuffer>,
2469) -> PrimitiveArray<T> {
2470    PrimitiveArray::new(flush_values(values).into(), nulls)
2471}
2472
2473#[inline]
2474fn read_decimal_bytes_be<const N: usize>(
2475    buf: &mut AvroCursor<'_>,
2476    size: Option<usize>,
2477) -> Result<[u8; N], AvroError> {
2478    match size {
2479        Some(n) if n == N => {
2480            let raw = buf.get_fixed(N)?;
2481            let mut arr = [0u8; N];
2482            arr.copy_from_slice(raw);
2483            Ok(arr)
2484        }
2485        Some(n) => {
2486            let raw = buf.get_fixed(n)?;
2487            sign_cast_to::<N>(raw)
2488        }
2489        None => {
2490            let raw = buf.get_bytes()?;
2491            sign_cast_to::<N>(raw)
2492        }
2493    }
2494}
2495
2496/// Sign-extend or (when larger) validate-and-truncate a big-endian two's-complement
2497/// integer into exactly `N` bytes. This matches Avro's decimal binary encoding:
2498/// the payload is a big-endian two's-complement integer, and when narrowing it must
2499/// be representable without changing sign or value.
2500///
2501/// If `raw.len() < N`, the value is sign-extended.
2502/// If `raw.len() > N`, all truncated leading bytes must match the sign-extension byte
2503/// and the MSB of the first kept byte must match the sign (to avoid silent overflow).
2504#[inline]
2505fn sign_cast_to<const N: usize>(raw: &[u8]) -> Result<[u8; N], AvroError> {
2506    let len = raw.len();
2507    // Fast path: exact width, just copy
2508    if len == N {
2509        let mut out = [0u8; N];
2510        out.copy_from_slice(raw);
2511        return Ok(out);
2512    }
2513    // Determine sign byte from MSB of first byte (empty => positive)
2514    let first = raw.first().copied().unwrap_or(0u8);
2515    let sign_byte = if (first & 0x80) == 0 { 0x00 } else { 0xFF };
2516    // Pre-fill with sign byte to support sign extension
2517    let mut out = [sign_byte; N];
2518    if len > N {
2519        // Validate truncation: all dropped leading bytes must equal sign_byte,
2520        // and the MSB of the first kept byte must match the sign.
2521        let extra = len - N;
2522        // Any non-sign byte in the truncated prefix indicates overflow
2523        if raw[..extra].iter().any(|&b| b != sign_byte) {
2524            return Err(AvroError::ParseError(format!(
2525                "Decimal value with {len} bytes cannot be represented in {N} bytes without overflow"
2526            )));
2527        }
2528        if N > 0 {
2529            let first_kept = raw[extra];
2530            let sign_bit_mismatch = ((first_kept ^ sign_byte) & 0x80) != 0;
2531            if sign_bit_mismatch {
2532                return Err(AvroError::ParseError(format!(
2533                    "Decimal value with {len} bytes cannot be represented in {N} bytes without overflow"
2534                )));
2535            }
2536        }
2537        out.copy_from_slice(&raw[extra..]);
2538        return Ok(out);
2539    }
2540    out[N - len..].copy_from_slice(raw);
2541    Ok(out)
2542}
2543
2544#[cfg(feature = "avro_custom_types")]
2545#[inline]
2546fn values_equal_at(arr: &dyn Array, i: usize, j: usize) -> bool {
2547    match (arr.is_null(i), arr.is_null(j)) {
2548        (true, true) => true,
2549        (true, false) | (false, true) => false,
2550        (false, false) => {
2551            let a = arr.slice(i, 1);
2552            let b = arr.slice(j, 1);
2553            a == b
2554        }
2555    }
2556}
2557
2558#[derive(Debug)]
2559struct Projector {
2560    writer_projections: Vec<FieldProjection>,
2561    default_injections: Arc<[(usize, AvroLiteral)]>,
2562}
2563
2564#[derive(Debug)]
2565enum FieldProjection {
2566    ToReader(usize),
2567    Skip(Skipper),
2568}
2569
2570#[derive(Debug)]
2571struct ProjectorBuilder<'a> {
2572    rec: &'a ResolvedRecord,
2573    field_defaults: &'a [Option<AvroLiteral>],
2574}
2575
2576impl<'a> ProjectorBuilder<'a> {
2577    #[inline]
2578    fn try_new(rec: &'a ResolvedRecord, field_defaults: &'a [Option<AvroLiteral>]) -> Self {
2579        Self {
2580            rec,
2581            field_defaults,
2582        }
2583    }
2584
2585    #[inline]
2586    fn build(self) -> Result<Projector, AvroError> {
2587        let mut default_injections: Vec<(usize, AvroLiteral)> =
2588            Vec::with_capacity(self.rec.default_fields.len());
2589        for &idx in self.rec.default_fields.as_ref() {
2590            let lit = self
2591                .field_defaults
2592                .get(idx)
2593                .and_then(|lit| lit.clone())
2594                .unwrap_or(AvroLiteral::Null);
2595            default_injections.push((idx, lit));
2596        }
2597        let writer_projections = self
2598            .rec
2599            .writer_fields
2600            .iter()
2601            .map(|field| match field {
2602                ResolvedField::ToReader(index, _) => Ok(FieldProjection::ToReader(*index)),
2603                ResolvedField::Skip(datatype) => {
2604                    let skipper = Skipper::from_avro(datatype)?;
2605                    Ok(FieldProjection::Skip(skipper))
2606                }
2607            })
2608            .collect::<Result<_, AvroError>>()?;
2609        Ok(Projector {
2610            writer_projections,
2611            default_injections: default_injections.into(),
2612        })
2613    }
2614}
2615
2616impl Projector {
2617    #[inline]
2618    fn project_record(
2619        &self,
2620        buf: &mut AvroCursor<'_>,
2621        encodings: &mut [Decoder],
2622    ) -> Result<(), AvroError> {
2623        for field_proj in &self.writer_projections {
2624            match field_proj {
2625                FieldProjection::ToReader(index) => encodings[*index].decode(buf)?,
2626                FieldProjection::Skip(skipper) => skipper.skip(buf)?,
2627            }
2628        }
2629        for (reader_index, lit) in self.default_injections.as_ref() {
2630            encodings[*reader_index].append_default(lit)?;
2631        }
2632        Ok(())
2633    }
2634}
2635
2636/// Lightweight skipper for non‑projected writer fields
2637/// (fields present in the writer schema but omitted by the reader/projection);
2638/// per Avro 1.11.1 schema resolution these fields are ignored.
2639///
2640/// <https://avro.apache.org/docs/1.11.1/specification/#schema-resolution>
2641#[derive(Debug)]
2642enum Skipper {
2643    Null,
2644    Boolean,
2645    Int32,
2646    Int64,
2647    Float32,
2648    Float64,
2649    Bytes,
2650    String,
2651    TimeMicros,
2652    TimestampMillis,
2653    TimestampMicros,
2654    TimestampNanos,
2655    Fixed(usize),
2656    Decimal(Option<usize>),
2657    UuidString,
2658    Enum,
2659    DurationFixed12,
2660    List(Box<Skipper>),
2661    Map(Box<Skipper>),
2662    Struct(Vec<Skipper>),
2663    Union(Vec<Skipper>),
2664    Nullable(Nullability, Box<Skipper>),
2665    #[cfg(feature = "avro_custom_types")]
2666    RunEndEncoded(Box<Skipper>),
2667}
2668
2669impl Skipper {
2670    fn from_avro(dt: &AvroDataType) -> Result<Self, AvroError> {
2671        let mut base = match dt.codec() {
2672            Codec::Null => Self::Null,
2673            Codec::Boolean => Self::Boolean,
2674            Codec::Int32 | Codec::Date32 | Codec::TimeMillis => Self::Int32,
2675            Codec::Int64 => Self::Int64,
2676            Codec::TimeMicros => Self::TimeMicros,
2677            Codec::TimestampMillis(_) => Self::TimestampMillis,
2678            Codec::TimestampMicros(_) => Self::TimestampMicros,
2679            Codec::TimestampNanos(_) => Self::TimestampNanos,
2680            #[cfg(feature = "avro_custom_types")]
2681            Codec::DurationNanos
2682            | Codec::DurationMicros
2683            | Codec::DurationMillis
2684            | Codec::DurationSeconds => Self::Int64,
2685            #[cfg(feature = "avro_custom_types")]
2686            Codec::Int8 | Codec::Int16 | Codec::UInt8 | Codec::UInt16 | Codec::Time32Secs => {
2687                Self::Int32
2688            }
2689            #[cfg(feature = "avro_custom_types")]
2690            Codec::UInt32 | Codec::Date64 | Codec::TimeNanos | Codec::TimestampSecs(_) => {
2691                Self::Int64
2692            }
2693            #[cfg(feature = "avro_custom_types")]
2694            Codec::UInt64 => Self::Fixed(8),
2695            #[cfg(feature = "avro_custom_types")]
2696            Codec::Float16 => Self::Fixed(2),
2697            #[cfg(feature = "avro_custom_types")]
2698            Codec::IntervalYearMonth => Self::Fixed(4),
2699            #[cfg(feature = "avro_custom_types")]
2700            Codec::IntervalMonthDayNano => Self::Fixed(16),
2701            #[cfg(feature = "avro_custom_types")]
2702            Codec::IntervalDayTime => Self::Fixed(8),
2703            Codec::Float32 => Self::Float32,
2704            Codec::Float64 => Self::Float64,
2705            Codec::Binary => Self::Bytes,
2706            Codec::Utf8 | Codec::Utf8View => Self::String,
2707            Codec::Fixed(sz) => Self::Fixed(*sz as usize),
2708            Codec::Decimal(_, _, size) => Self::Decimal(*size),
2709            Codec::Uuid => Self::UuidString, // encoded as string
2710            Codec::Enum(_) => Self::Enum,
2711            Codec::List(item) => Self::List(Box::new(Skipper::from_avro(item)?)),
2712            Codec::Struct(fields) => {
2713                if let Some(ResolutionInfo::Record(rec)) = dt.resolution.as_ref() {
2714                    Self::Struct(
2715                        rec.writer_fields
2716                            .iter()
2717                            .map(|wf| match wf {
2718                                ResolvedField::ToReader(_, wdt) | ResolvedField::Skip(wdt) => {
2719                                    Skipper::from_avro(wdt)
2720                                }
2721                            })
2722                            .collect::<Result<_, _>>()?,
2723                    )
2724                } else {
2725                    Self::Struct(
2726                        fields
2727                            .iter()
2728                            .map(|f| Skipper::from_avro(f.data_type()))
2729                            .collect::<Result<_, _>>()?,
2730                    )
2731                }
2732            }
2733            Codec::Map(values) => Self::Map(Box::new(Skipper::from_avro(values)?)),
2734            Codec::Interval => Self::DurationFixed12,
2735            Codec::Union(encodings, _, _) => {
2736                let max_addr = (i32::MAX as usize) + 1;
2737                if encodings.len() > max_addr {
2738                    return Err(AvroError::SchemaError(format!(
2739                        "Writer union has {} branches, which exceeds the maximum addressable \
2740                         branches by an Avro int tag ({} + 1).",
2741                        encodings.len(),
2742                        i32::MAX
2743                    )));
2744                }
2745                Self::Union(
2746                    encodings
2747                        .iter()
2748                        .map(Skipper::from_avro)
2749                        .collect::<Result<_, _>>()?,
2750                )
2751            }
2752            #[cfg(feature = "avro_custom_types")]
2753            Codec::RunEndEncoded(inner, _w) => {
2754                Self::RunEndEncoded(Box::new(Skipper::from_avro(inner)?))
2755            }
2756        };
2757        if let Some(n) = dt.nullability() {
2758            base = Self::Nullable(n, Box::new(base));
2759        }
2760        Ok(base)
2761    }
2762
2763    fn skip(&self, buf: &mut AvroCursor<'_>) -> Result<(), AvroError> {
2764        match self {
2765            Self::Null => Ok(()),
2766            Self::Boolean => {
2767                buf.get_bool()?;
2768                Ok(())
2769            }
2770            Self::Int32 => {
2771                buf.skip_int()?;
2772                Ok(())
2773            }
2774            Self::Int64
2775            | Self::TimeMicros
2776            | Self::TimestampMillis
2777            | Self::TimestampMicros
2778            | Self::TimestampNanos => {
2779                buf.skip_long()?;
2780                Ok(())
2781            }
2782            Self::Float32 => {
2783                buf.get_float()?;
2784                Ok(())
2785            }
2786            Self::Float64 => {
2787                buf.get_double()?;
2788                Ok(())
2789            }
2790            Self::Bytes | Self::String | Self::UuidString => {
2791                buf.get_bytes()?;
2792                Ok(())
2793            }
2794            Self::Fixed(sz) => {
2795                buf.get_fixed(*sz)?;
2796                Ok(())
2797            }
2798            Self::Decimal(size) => {
2799                if let Some(s) = size {
2800                    buf.get_fixed(*s)
2801                } else {
2802                    buf.get_bytes()
2803                }?;
2804                Ok(())
2805            }
2806            Self::Enum => {
2807                buf.skip_int()?;
2808                Ok(())
2809            }
2810            Self::DurationFixed12 => {
2811                buf.get_fixed(12)?;
2812                Ok(())
2813            }
2814            Self::List(item) => {
2815                skip_blocks(buf, |c| item.skip(c))?;
2816                Ok(())
2817            }
2818            Self::Map(value) => {
2819                skip_blocks(buf, |c| {
2820                    c.get_bytes()?; // key
2821                    value.skip(c)
2822                })?;
2823                Ok(())
2824            }
2825            Self::Struct(fields) => {
2826                for f in fields {
2827                    f.skip(buf)?
2828                }
2829                Ok(())
2830            }
2831            Self::Union(encodings) => {
2832                // Union tag must be ZigZag-decoded
2833                let raw = buf.get_long()?;
2834                if raw < 0 {
2835                    return Err(AvroError::ParseError(format!(
2836                        "Negative union branch index {raw}"
2837                    )));
2838                }
2839                let idx: usize = usize::try_from(raw).map_err(|_| {
2840                    AvroError::ParseError(format!(
2841                        "Union branch index {raw} does not fit into usize on this platform ({}-bit)",
2842                        (usize::BITS as usize)
2843                    ))
2844                })?;
2845                let Some(encoding) = encodings.get(idx) else {
2846                    return Err(AvroError::ParseError(format!(
2847                        "Union branch index {idx} out of range for skipper ({} branches)",
2848                        encodings.len()
2849                    )));
2850                };
2851                encoding.skip(buf)
2852            }
2853            Self::Nullable(order, inner) => {
2854                let branch = buf.read_vlq()?;
2855                let is_not_null = match *order {
2856                    Nullability::NullFirst => branch != 0,
2857                    Nullability::NullSecond => branch == 0,
2858                };
2859                if is_not_null {
2860                    inner.skip(buf)?;
2861                }
2862                Ok(())
2863            }
2864            #[cfg(feature = "avro_custom_types")]
2865            Self::RunEndEncoded(inner) => inner.skip(buf),
2866        }
2867    }
2868}
2869
2870#[cfg(test)]
2871mod tests {
2872    use super::*;
2873    use crate::codec::AvroFieldBuilder;
2874    use crate::schema::{Attributes, ComplexType, Field, PrimitiveType, Record, Schema, TypeName};
2875    use arrow_array::cast::AsArray;
2876    use indexmap::IndexMap;
2877    use std::collections::HashMap;
2878
2879    fn encode_avro_int(value: i32) -> Vec<u8> {
2880        let mut buf = Vec::new();
2881        let mut v = (value << 1) ^ (value >> 31);
2882        while v & !0x7F != 0 {
2883            buf.push(((v & 0x7F) | 0x80) as u8);
2884            v >>= 7;
2885        }
2886        buf.push(v as u8);
2887        buf
2888    }
2889
2890    fn encode_avro_long(value: i64) -> Vec<u8> {
2891        let mut buf = Vec::new();
2892        let mut v = (value << 1) ^ (value >> 63);
2893        while v & !0x7F != 0 {
2894            buf.push(((v & 0x7F) | 0x80) as u8);
2895            v >>= 7;
2896        }
2897        buf.push(v as u8);
2898        buf
2899    }
2900
2901    fn encode_avro_bytes(bytes: &[u8]) -> Vec<u8> {
2902        let mut buf = encode_avro_long(bytes.len() as i64);
2903        buf.extend_from_slice(bytes);
2904        buf
2905    }
2906
2907    fn avro_from_codec(codec: Codec) -> AvroDataType {
2908        AvroDataType::new(codec, Default::default(), None)
2909    }
2910
2911    fn resolved_root_datatype(
2912        writer: Schema<'static>,
2913        reader: Schema<'static>,
2914        use_utf8view: bool,
2915        strict_mode: bool,
2916    ) -> AvroDataType {
2917        // Wrap writer schema in a single-field record
2918        let writer_record = Schema::Complex(ComplexType::Record(Record {
2919            name: "Root",
2920            namespace: None,
2921            doc: None,
2922            aliases: vec![],
2923            fields: vec![Field {
2924                name: "v",
2925                r#type: writer,
2926                default: None,
2927                doc: None,
2928                aliases: vec![],
2929            }],
2930            attributes: Attributes::default(),
2931        }));
2932
2933        // Wrap reader schema in a single-field record
2934        let reader_record = Schema::Complex(ComplexType::Record(Record {
2935            name: "Root",
2936            namespace: None,
2937            doc: None,
2938            aliases: vec![],
2939            fields: vec![Field {
2940                name: "v",
2941                r#type: reader,
2942                default: None,
2943                doc: None,
2944                aliases: vec![],
2945            }],
2946            attributes: Attributes::default(),
2947        }));
2948
2949        // Build resolved record, then extract the inner field's resolved AvroDataType
2950        let field = AvroFieldBuilder::new(&writer_record)
2951            .with_reader_schema(&reader_record)
2952            .with_utf8view(use_utf8view)
2953            .with_strict_mode(strict_mode)
2954            .build()
2955            .expect("schema resolution should succeed");
2956
2957        match field.data_type().codec() {
2958            Codec::Struct(fields) => fields[0].data_type().clone(),
2959            other => panic!("expected wrapper struct, got {other:?}"),
2960        }
2961    }
2962
2963    fn decoder_for_promotion(
2964        writer: PrimitiveType,
2965        reader: PrimitiveType,
2966        use_utf8view: bool,
2967    ) -> Decoder {
2968        let ws = Schema::TypeName(TypeName::Primitive(writer));
2969        let rs = Schema::TypeName(TypeName::Primitive(reader));
2970        let dt = resolved_root_datatype(ws, rs, use_utf8view, false);
2971        Decoder::try_new(&dt).unwrap()
2972    }
2973
2974    fn make_avro_dt(codec: Codec, nullability: Option<Nullability>) -> AvroDataType {
2975        AvroDataType::new(codec, HashMap::new(), nullability)
2976    }
2977
2978    #[cfg(feature = "avro_custom_types")]
2979    fn encode_vlq_u64(mut x: u64) -> Vec<u8> {
2980        let mut out = Vec::with_capacity(10);
2981        while x >= 0x80 {
2982            out.push((x as u8) | 0x80);
2983            x >>= 7;
2984        }
2985        out.push(x as u8);
2986        out
2987    }
2988
2989    #[test]
2990    fn test_union_resolution_writer_union_reader_union_reorder_and_promotion_dense() {
2991        let ws = Schema::Union(vec![
2992            Schema::TypeName(TypeName::Primitive(PrimitiveType::Int)),
2993            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
2994        ]);
2995        let rs = Schema::Union(vec![
2996            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
2997            Schema::TypeName(TypeName::Primitive(PrimitiveType::Long)),
2998        ]);
2999
3000        let dt = resolved_root_datatype(ws, rs, false, false);
3001        let mut dec = Decoder::try_new(&dt).unwrap();
3002
3003        let mut rec1 = encode_avro_long(0);
3004        rec1.extend(encode_avro_int(7));
3005        let mut cur1 = AvroCursor::new(&rec1);
3006        dec.decode(&mut cur1).unwrap();
3007
3008        let mut rec2 = encode_avro_long(1);
3009        rec2.extend(encode_avro_bytes(b"abc"));
3010        let mut cur2 = AvroCursor::new(&rec2);
3011        dec.decode(&mut cur2).unwrap();
3012
3013        let arr = dec.flush(None).unwrap();
3014        let ua = arr
3015            .as_any()
3016            .downcast_ref::<UnionArray>()
3017            .expect("dense union output");
3018
3019        assert_eq!(
3020            ua.type_id(0),
3021            1,
3022            "first value must select reader 'long' branch"
3023        );
3024        assert_eq!(ua.value_offset(0), 0);
3025
3026        assert_eq!(
3027            ua.type_id(1),
3028            0,
3029            "second value must select reader 'string' branch"
3030        );
3031        assert_eq!(ua.value_offset(1), 0);
3032
3033        let long_child = ua.child(1).as_any().downcast_ref::<Int64Array>().unwrap();
3034        assert_eq!(long_child.len(), 1);
3035        assert_eq!(long_child.value(0), 7);
3036
3037        let str_child = ua.child(0).as_any().downcast_ref::<StringArray>().unwrap();
3038        assert_eq!(str_child.len(), 1);
3039        assert_eq!(str_child.value(0), "abc");
3040    }
3041
3042    #[test]
3043    fn test_union_resolution_writer_union_reader_nonunion_promotion_int_to_long() {
3044        let ws = Schema::Union(vec![
3045            Schema::TypeName(TypeName::Primitive(PrimitiveType::Int)),
3046            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
3047        ]);
3048        let rs = Schema::TypeName(TypeName::Primitive(PrimitiveType::Long));
3049
3050        let dt = resolved_root_datatype(ws, rs, false, false);
3051        let mut dec = Decoder::try_new(&dt).unwrap();
3052
3053        let mut data = encode_avro_long(0);
3054        data.extend(encode_avro_int(5));
3055        let mut cur = AvroCursor::new(&data);
3056        dec.decode(&mut cur).unwrap();
3057
3058        let arr = dec.flush(None).unwrap();
3059        let out = arr.as_any().downcast_ref::<Int64Array>().unwrap();
3060        assert_eq!(out.len(), 1);
3061        assert_eq!(out.value(0), 5);
3062    }
3063
3064    #[test]
3065    fn test_union_resolution_writer_union_reader_nonunion_mismatch_errors() {
3066        let ws = Schema::Union(vec![
3067            Schema::TypeName(TypeName::Primitive(PrimitiveType::Int)),
3068            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
3069        ]);
3070        let rs = Schema::TypeName(TypeName::Primitive(PrimitiveType::Long));
3071
3072        let dt = resolved_root_datatype(ws, rs, false, false);
3073        let mut dec = Decoder::try_new(&dt).unwrap();
3074
3075        let mut data = encode_avro_long(1);
3076        data.extend(encode_avro_bytes(b"z"));
3077        let mut cur = AvroCursor::new(&data);
3078        let res = dec.decode(&mut cur);
3079        assert!(
3080            res.is_err(),
3081            "expected error when writer union branch does not resolve to reader non-union type"
3082        );
3083    }
3084
3085    #[test]
3086    fn test_union_resolution_writer_nonunion_reader_union_selects_matching_branch() {
3087        let ws = Schema::TypeName(TypeName::Primitive(PrimitiveType::Int));
3088        let rs = Schema::Union(vec![
3089            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
3090            Schema::TypeName(TypeName::Primitive(PrimitiveType::Long)),
3091        ]);
3092
3093        let dt = resolved_root_datatype(ws, rs, false, false);
3094        let mut dec = Decoder::try_new(&dt).unwrap();
3095
3096        let data = encode_avro_int(6);
3097        let mut cur = AvroCursor::new(&data);
3098        dec.decode(&mut cur).unwrap();
3099
3100        let arr = dec.flush(None).unwrap();
3101        let ua = arr
3102            .as_any()
3103            .downcast_ref::<UnionArray>()
3104            .expect("dense union output");
3105        assert_eq!(ua.len(), 1);
3106        assert_eq!(
3107            ua.type_id(0),
3108            1,
3109            "must resolve to reader 'long' branch (type_id 1)"
3110        );
3111        assert_eq!(ua.value_offset(0), 0);
3112
3113        let long_child = ua.child(1).as_any().downcast_ref::<Int64Array>().unwrap();
3114        assert_eq!(long_child.len(), 1);
3115        assert_eq!(long_child.value(0), 6);
3116
3117        let str_child = ua.child(0).as_any().downcast_ref::<StringArray>().unwrap();
3118        assert_eq!(str_child.len(), 0, "string branch must be empty");
3119    }
3120
3121    #[test]
3122    fn test_union_resolution_writer_union_reader_union_unmapped_branch_errors() {
3123        let ws = Schema::Union(vec![
3124            Schema::TypeName(TypeName::Primitive(PrimitiveType::Int)),
3125            Schema::TypeName(TypeName::Primitive(PrimitiveType::Boolean)),
3126        ]);
3127        let rs = Schema::Union(vec![
3128            Schema::TypeName(TypeName::Primitive(PrimitiveType::String)),
3129            Schema::TypeName(TypeName::Primitive(PrimitiveType::Long)),
3130        ]);
3131
3132        let dt = resolved_root_datatype(ws, rs, false, false);
3133        let mut dec = Decoder::try_new(&dt).unwrap();
3134
3135        let mut data = encode_avro_long(1);
3136        data.push(1);
3137        let mut cur = AvroCursor::new(&data);
3138        let res = dec.decode(&mut cur);
3139        assert!(
3140            res.is_err(),
3141            "expected error for unmapped writer 'boolean' branch"
3142        );
3143    }
3144
3145    #[test]
3146    fn test_schema_resolution_promotion_int_to_long() {
3147        let mut dec = decoder_for_promotion(PrimitiveType::Int, PrimitiveType::Long, false);
3148        assert!(matches!(dec, Decoder::Int32ToInt64(_)));
3149        for v in [0, 1, -2, 123456] {
3150            let data = encode_avro_int(v);
3151            let mut cur = AvroCursor::new(&data);
3152            dec.decode(&mut cur).unwrap();
3153        }
3154        let arr = dec.flush(None).unwrap();
3155        let a = arr.as_any().downcast_ref::<Int64Array>().unwrap();
3156        assert_eq!(a.value(0), 0);
3157        assert_eq!(a.value(1), 1);
3158        assert_eq!(a.value(2), -2);
3159        assert_eq!(a.value(3), 123456);
3160    }
3161
3162    #[test]
3163    fn test_schema_resolution_promotion_int_to_float() {
3164        let mut dec = decoder_for_promotion(PrimitiveType::Int, PrimitiveType::Float, false);
3165        assert!(matches!(dec, Decoder::Int32ToFloat32(_)));
3166        for v in [0, 42, -7] {
3167            let data = encode_avro_int(v);
3168            let mut cur = AvroCursor::new(&data);
3169            dec.decode(&mut cur).unwrap();
3170        }
3171        let arr = dec.flush(None).unwrap();
3172        let a = arr.as_any().downcast_ref::<Float32Array>().unwrap();
3173        assert_eq!(a.value(0), 0.0);
3174        assert_eq!(a.value(1), 42.0);
3175        assert_eq!(a.value(2), -7.0);
3176    }
3177
3178    #[test]
3179    fn test_schema_resolution_promotion_int_to_double() {
3180        let mut dec = decoder_for_promotion(PrimitiveType::Int, PrimitiveType::Double, false);
3181        assert!(matches!(dec, Decoder::Int32ToFloat64(_)));
3182        for v in [1, -1, 10_000] {
3183            let data = encode_avro_int(v);
3184            let mut cur = AvroCursor::new(&data);
3185            dec.decode(&mut cur).unwrap();
3186        }
3187        let arr = dec.flush(None).unwrap();
3188        let a = arr.as_any().downcast_ref::<Float64Array>().unwrap();
3189        assert_eq!(a.value(0), 1.0);
3190        assert_eq!(a.value(1), -1.0);
3191        assert_eq!(a.value(2), 10_000.0);
3192    }
3193
3194    #[test]
3195    fn test_schema_resolution_promotion_long_to_float() {
3196        let mut dec = decoder_for_promotion(PrimitiveType::Long, PrimitiveType::Float, false);
3197        assert!(matches!(dec, Decoder::Int64ToFloat32(_)));
3198        for v in [0_i64, 1_000_000_i64, -123_i64] {
3199            let data = encode_avro_long(v);
3200            let mut cur = AvroCursor::new(&data);
3201            dec.decode(&mut cur).unwrap();
3202        }
3203        let arr = dec.flush(None).unwrap();
3204        let a = arr.as_any().downcast_ref::<Float32Array>().unwrap();
3205        assert_eq!(a.value(0), 0.0);
3206        assert_eq!(a.value(1), 1_000_000.0);
3207        assert_eq!(a.value(2), -123.0);
3208    }
3209
3210    #[test]
3211    fn test_schema_resolution_promotion_long_to_double() {
3212        let mut dec = decoder_for_promotion(PrimitiveType::Long, PrimitiveType::Double, false);
3213        assert!(matches!(dec, Decoder::Int64ToFloat64(_)));
3214        for v in [2_i64, -2_i64, 9_223_372_i64] {
3215            let data = encode_avro_long(v);
3216            let mut cur = AvroCursor::new(&data);
3217            dec.decode(&mut cur).unwrap();
3218        }
3219        let arr = dec.flush(None).unwrap();
3220        let a = arr.as_any().downcast_ref::<Float64Array>().unwrap();
3221        assert_eq!(a.value(0), 2.0);
3222        assert_eq!(a.value(1), -2.0);
3223        assert_eq!(a.value(2), 9_223_372.0);
3224    }
3225
3226    #[test]
3227    fn test_schema_resolution_promotion_float_to_double() {
3228        let mut dec = decoder_for_promotion(PrimitiveType::Float, PrimitiveType::Double, false);
3229        assert!(matches!(dec, Decoder::Float32ToFloat64(_)));
3230        for v in [0.5_f32, -3.25_f32, 1.0e6_f32] {
3231            let data = v.to_le_bytes().to_vec();
3232            let mut cur = AvroCursor::new(&data);
3233            dec.decode(&mut cur).unwrap();
3234        }
3235        let arr = dec.flush(None).unwrap();
3236        let a = arr.as_any().downcast_ref::<Float64Array>().unwrap();
3237        assert_eq!(a.value(0), 0.5_f64);
3238        assert_eq!(a.value(1), -3.25_f64);
3239        assert_eq!(a.value(2), 1.0e6_f64);
3240    }
3241
3242    #[test]
3243    fn test_schema_resolution_promotion_bytes_to_string_utf8() {
3244        let mut dec = decoder_for_promotion(PrimitiveType::Bytes, PrimitiveType::String, false);
3245        assert!(matches!(dec, Decoder::BytesToString(_, _)));
3246        for s in ["hello", "world", "héllo"] {
3247            let data = encode_avro_bytes(s.as_bytes());
3248            let mut cur = AvroCursor::new(&data);
3249            dec.decode(&mut cur).unwrap();
3250        }
3251        let arr = dec.flush(None).unwrap();
3252        let a = arr.as_any().downcast_ref::<StringArray>().unwrap();
3253        assert_eq!(a.value(0), "hello");
3254        assert_eq!(a.value(1), "world");
3255        assert_eq!(a.value(2), "héllo");
3256    }
3257
3258    #[test]
3259    fn test_schema_resolution_promotion_bytes_to_string_utf8view_enabled() {
3260        let mut dec = decoder_for_promotion(PrimitiveType::Bytes, PrimitiveType::String, true);
3261        assert!(matches!(dec, Decoder::BytesToString(_, _)));
3262        let data = encode_avro_bytes(b"abc");
3263        let mut cur = AvroCursor::new(&data);
3264        dec.decode(&mut cur).unwrap();
3265        let arr = dec.flush(None).unwrap();
3266        let a = arr.as_any().downcast_ref::<StringArray>().unwrap();
3267        assert_eq!(a.value(0), "abc");
3268    }
3269
3270    #[test]
3271    fn test_schema_resolution_promotion_string_to_bytes() {
3272        let mut dec = decoder_for_promotion(PrimitiveType::String, PrimitiveType::Bytes, false);
3273        assert!(matches!(dec, Decoder::StringToBytes(_, _)));
3274        for s in ["", "abc", "data"] {
3275            let data = encode_avro_bytes(s.as_bytes());
3276            let mut cur = AvroCursor::new(&data);
3277            dec.decode(&mut cur).unwrap();
3278        }
3279        let arr = dec.flush(None).unwrap();
3280        let a = arr.as_any().downcast_ref::<BinaryArray>().unwrap();
3281        assert_eq!(a.value(0), b"");
3282        assert_eq!(a.value(1), b"abc");
3283        assert_eq!(a.value(2), b"data");
3284    }
3285
3286    #[test]
3287    fn test_schema_resolution_no_promotion_passthrough_int() {
3288        let ws = Schema::TypeName(TypeName::Primitive(PrimitiveType::Int));
3289        let rs = Schema::TypeName(TypeName::Primitive(PrimitiveType::Int));
3290        // Wrap both in a synthetic single-field record and resolve with AvroFieldBuilder
3291        let writer_record = Schema::Complex(ComplexType::Record(Record {
3292            name: "Root",
3293            namespace: None,
3294            doc: None,
3295            aliases: vec![],
3296            fields: vec![Field {
3297                name: "v",
3298                r#type: ws,
3299                default: None,
3300                doc: None,
3301                aliases: vec![],
3302            }],
3303            attributes: Attributes::default(),
3304        }));
3305        let reader_record = Schema::Complex(ComplexType::Record(Record {
3306            name: "Root",
3307            namespace: None,
3308            doc: None,
3309            aliases: vec![],
3310            fields: vec![Field {
3311                name: "v",
3312                r#type: rs,
3313                default: None,
3314                doc: None,
3315                aliases: vec![],
3316            }],
3317            attributes: Attributes::default(),
3318        }));
3319        let field = AvroFieldBuilder::new(&writer_record)
3320            .with_reader_schema(&reader_record)
3321            .with_utf8view(false)
3322            .with_strict_mode(false)
3323            .build()
3324            .unwrap();
3325        // Extract the resolved inner field's AvroDataType
3326        let dt = match field.data_type().codec() {
3327            Codec::Struct(fields) => fields[0].data_type().clone(),
3328            other => panic!("expected wrapper struct, got {other:?}"),
3329        };
3330        let mut dec = Decoder::try_new(&dt).unwrap();
3331        assert!(matches!(dec, Decoder::Int32(_)));
3332        for v in [7, -9] {
3333            let data = encode_avro_int(v);
3334            let mut cur = AvroCursor::new(&data);
3335            dec.decode(&mut cur).unwrap();
3336        }
3337        let arr = dec.flush(None).unwrap();
3338        let a = arr.as_any().downcast_ref::<Int32Array>().unwrap();
3339        assert_eq!(a.value(0), 7);
3340        assert_eq!(a.value(1), -9);
3341    }
3342
3343    #[test]
3344    fn test_schema_resolution_illegal_promotion_int_to_boolean_errors() {
3345        let ws = Schema::TypeName(TypeName::Primitive(PrimitiveType::Int));
3346        let rs = Schema::TypeName(TypeName::Primitive(PrimitiveType::Boolean));
3347        let writer_record = Schema::Complex(ComplexType::Record(Record {
3348            name: "Root",
3349            namespace: None,
3350            doc: None,
3351            aliases: vec![],
3352            fields: vec![Field {
3353                name: "v",
3354                r#type: ws,
3355                default: None,
3356                doc: None,
3357                aliases: vec![],
3358            }],
3359            attributes: Attributes::default(),
3360        }));
3361        let reader_record = Schema::Complex(ComplexType::Record(Record {
3362            name: "Root",
3363            namespace: None,
3364            doc: None,
3365            aliases: vec![],
3366            fields: vec![Field {
3367                name: "v",
3368                r#type: rs,
3369                default: None,
3370                doc: None,
3371                aliases: vec![],
3372            }],
3373            attributes: Attributes::default(),
3374        }));
3375        let res = AvroFieldBuilder::new(&writer_record)
3376            .with_reader_schema(&reader_record)
3377            .with_utf8view(false)
3378            .with_strict_mode(false)
3379            .build();
3380        assert!(res.is_err(), "expected error for illegal promotion");
3381    }
3382
3383    #[test]
3384    fn test_map_decoding_one_entry() {
3385        let value_type = avro_from_codec(Codec::Utf8);
3386        let map_type = avro_from_codec(Codec::Map(Arc::new(value_type)));
3387        let mut decoder = Decoder::try_new(&map_type).unwrap();
3388        // Encode a single map with one entry: {"hello": "world"}
3389        let mut data = Vec::new();
3390        data.extend_from_slice(&encode_avro_long(1));
3391        data.extend_from_slice(&encode_avro_bytes(b"hello")); // key
3392        data.extend_from_slice(&encode_avro_bytes(b"world")); // value
3393        data.extend_from_slice(&encode_avro_long(0));
3394        let mut cursor = AvroCursor::new(&data);
3395        decoder.decode(&mut cursor).unwrap();
3396        let array = decoder.flush(None).unwrap();
3397        let map_arr = array.as_any().downcast_ref::<MapArray>().unwrap();
3398        assert_eq!(map_arr.len(), 1); // one map
3399        assert_eq!(map_arr.value_length(0), 1);
3400        let entries = map_arr.value(0);
3401        let struct_entries = entries.as_any().downcast_ref::<StructArray>().unwrap();
3402        assert_eq!(struct_entries.len(), 1);
3403        let key_arr = struct_entries
3404            .column_by_name("key")
3405            .unwrap()
3406            .as_any()
3407            .downcast_ref::<StringArray>()
3408            .unwrap();
3409        let val_arr = struct_entries
3410            .column_by_name("value")
3411            .unwrap()
3412            .as_any()
3413            .downcast_ref::<StringArray>()
3414            .unwrap();
3415        assert_eq!(key_arr.value(0), "hello");
3416        assert_eq!(val_arr.value(0), "world");
3417    }
3418
3419    #[test]
3420    fn test_map_decoding_empty() {
3421        let value_type = avro_from_codec(Codec::Utf8);
3422        let map_type = avro_from_codec(Codec::Map(Arc::new(value_type)));
3423        let mut decoder = Decoder::try_new(&map_type).unwrap();
3424        let data = encode_avro_long(0);
3425        decoder.decode(&mut AvroCursor::new(&data)).unwrap();
3426        let array = decoder.flush(None).unwrap();
3427        let map_arr = array.as_any().downcast_ref::<MapArray>().unwrap();
3428        assert_eq!(map_arr.len(), 1);
3429        assert_eq!(map_arr.value_length(0), 0);
3430    }
3431
3432    #[test]
3433    fn test_fixed_decoding() {
3434        let avro_type = avro_from_codec(Codec::Fixed(3));
3435        let mut decoder = Decoder::try_new(&avro_type).expect("Failed to create decoder");
3436
3437        let data1 = [1u8, 2, 3];
3438        let mut cursor1 = AvroCursor::new(&data1);
3439        decoder
3440            .decode(&mut cursor1)
3441            .expect("Failed to decode data1");
3442        assert_eq!(cursor1.position(), 3, "Cursor should advance by fixed size");
3443        let data2 = [4u8, 5, 6];
3444        let mut cursor2 = AvroCursor::new(&data2);
3445        decoder
3446            .decode(&mut cursor2)
3447            .expect("Failed to decode data2");
3448        assert_eq!(cursor2.position(), 3, "Cursor should advance by fixed size");
3449        let array = decoder.flush(None).expect("Failed to flush decoder");
3450        assert_eq!(array.len(), 2, "Array should contain two items");
3451        let fixed_size_binary_array = array
3452            .as_any()
3453            .downcast_ref::<FixedSizeBinaryArray>()
3454            .expect("Failed to downcast to FixedSizeBinaryArray");
3455        assert_eq!(
3456            fixed_size_binary_array.value_length(),
3457            3,
3458            "Fixed size of binary values should be 3"
3459        );
3460        assert_eq!(
3461            fixed_size_binary_array.value(0),
3462            &[1, 2, 3],
3463            "First item mismatch"
3464        );
3465        assert_eq!(
3466            fixed_size_binary_array.value(1),
3467            &[4, 5, 6],
3468            "Second item mismatch"
3469        );
3470    }
3471
3472    #[test]
3473    fn test_fixed_decoding_empty() {
3474        let avro_type = avro_from_codec(Codec::Fixed(5));
3475        let mut decoder = Decoder::try_new(&avro_type).expect("Failed to create decoder");
3476
3477        let array = decoder
3478            .flush(None)
3479            .expect("Failed to flush decoder for empty input");
3480
3481        assert_eq!(array.len(), 0, "Array should be empty");
3482        let fixed_size_binary_array = array
3483            .as_any()
3484            .downcast_ref::<FixedSizeBinaryArray>()
3485            .expect("Failed to downcast to FixedSizeBinaryArray for empty array");
3486
3487        assert_eq!(
3488            fixed_size_binary_array.value_length(),
3489            5,
3490            "Fixed size of binary values should be 5 as per type"
3491        );
3492    }
3493
3494    #[test]
3495    fn test_uuid_decoding() {
3496        let avro_type = avro_from_codec(Codec::Uuid);
3497        let mut decoder = Decoder::try_new(&avro_type).expect("Failed to create decoder");
3498        let uuid_str = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6";
3499        let data = encode_avro_bytes(uuid_str.as_bytes());
3500        let mut cursor = AvroCursor::new(&data);
3501        decoder.decode(&mut cursor).expect("Failed to decode data");
3502        assert_eq!(
3503            cursor.position(),
3504            data.len(),
3505            "Cursor should advance by varint size + data size"
3506        );
3507        let array = decoder.flush(None).expect("Failed to flush decoder");
3508        let fixed_size_binary_array = array
3509            .as_any()
3510            .downcast_ref::<FixedSizeBinaryArray>()
3511            .expect("Array should be a FixedSizeBinaryArray");
3512        assert_eq!(fixed_size_binary_array.len(), 1);
3513        assert_eq!(fixed_size_binary_array.value_length(), 16);
3514        let expected_bytes = [
3515            0xf8, 0x1d, 0x4f, 0xae, 0x7d, 0xec, 0x11, 0xd0, 0xa7, 0x65, 0x00, 0xa0, 0xc9, 0x1e,
3516            0x6b, 0xf6,
3517        ];
3518        assert_eq!(fixed_size_binary_array.value(0), &expected_bytes);
3519    }
3520
3521    #[test]
3522    fn test_array_decoding() {
3523        let item_dt = avro_from_codec(Codec::Int32);
3524        let list_dt = avro_from_codec(Codec::List(Arc::new(item_dt)));
3525        let mut decoder = Decoder::try_new(&list_dt).unwrap();
3526        let mut row1 = Vec::new();
3527        row1.extend_from_slice(&encode_avro_long(2));
3528        row1.extend_from_slice(&encode_avro_int(10));
3529        row1.extend_from_slice(&encode_avro_int(20));
3530        row1.extend_from_slice(&encode_avro_long(0));
3531        let row2 = encode_avro_long(0);
3532        let mut cursor = AvroCursor::new(&row1);
3533        decoder.decode(&mut cursor).unwrap();
3534        let mut cursor2 = AvroCursor::new(&row2);
3535        decoder.decode(&mut cursor2).unwrap();
3536        let array = decoder.flush(None).unwrap();
3537        let list_arr = array.as_any().downcast_ref::<ListArray>().unwrap();
3538        assert_eq!(list_arr.len(), 2);
3539        let offsets = list_arr.value_offsets();
3540        assert_eq!(offsets, &[0, 2, 2]);
3541        let values = list_arr.values();
3542        let int_arr = values.as_primitive::<Int32Type>();
3543        assert_eq!(int_arr.len(), 2);
3544        assert_eq!(int_arr.value(0), 10);
3545        assert_eq!(int_arr.value(1), 20);
3546    }
3547
3548    #[test]
3549    fn test_array_decoding_with_negative_block_count() {
3550        let item_dt = avro_from_codec(Codec::Int32);
3551        let list_dt = avro_from_codec(Codec::List(Arc::new(item_dt)));
3552        let mut decoder = Decoder::try_new(&list_dt).unwrap();
3553        let mut data = encode_avro_long(-3);
3554        data.extend_from_slice(&encode_avro_long(12));
3555        data.extend_from_slice(&encode_avro_int(1));
3556        data.extend_from_slice(&encode_avro_int(2));
3557        data.extend_from_slice(&encode_avro_int(3));
3558        data.extend_from_slice(&encode_avro_long(0));
3559        let mut cursor = AvroCursor::new(&data);
3560        decoder.decode(&mut cursor).unwrap();
3561        let array = decoder.flush(None).unwrap();
3562        let list_arr = array.as_any().downcast_ref::<ListArray>().unwrap();
3563        assert_eq!(list_arr.len(), 1);
3564        assert_eq!(list_arr.value_length(0), 3);
3565        let values = list_arr.values().as_primitive::<Int32Type>();
3566        assert_eq!(values.len(), 3);
3567        assert_eq!(values.value(0), 1);
3568        assert_eq!(values.value(1), 2);
3569        assert_eq!(values.value(2), 3);
3570    }
3571
3572    /// Zig-zag + unsigned-LEB128 encode, correct for all `i64` including `MIN`/`MAX`
3573    /// (`encode_avro_long` loops forever on those two values).
3574    fn encode_avro_long_extreme(value: i64) -> Vec<u8> {
3575        let mut n = ((value << 1) ^ (value >> 63)) as u64;
3576        let mut out = Vec::new();
3577        while n >= 0x80 {
3578            out.push((n as u8) | 0x80);
3579            n >>= 7;
3580        }
3581        out.push(n as u8);
3582        out
3583    }
3584
3585    // `array<null>` is the worst case: items consume no bytes, so an unbounded
3586    // `block_count` spins the item loop without ever advancing the cursor (#10235).
3587    fn array_of_null_decoder() -> Decoder {
3588        let list_dt = avro_from_codec(Codec::List(Arc::new(avro_from_codec(Codec::Null))));
3589        Decoder::try_new(&list_dt).unwrap()
3590    }
3591
3592    #[test]
3593    fn test_array_of_null_decodes() {
3594        let mut decoder = array_of_null_decoder();
3595        let mut data = encode_avro_long(3); // three null items
3596        data.extend_from_slice(&encode_avro_long(0)); // empty-block terminator
3597        decoder.decode(&mut AvroCursor::new(&data)).unwrap();
3598    }
3599
3600    #[test]
3601    fn test_array_block_count_i64_max_errors() {
3602        // A positive `i64::MAX` block count must error rather than spin the item loop.
3603        let mut decoder = array_of_null_decoder();
3604        let mut data = encode_avro_long_extreme(i64::MAX); // item count
3605        data.extend_from_slice(&encode_avro_long(0)); // empty-block terminator
3606        let err = decoder.decode(&mut AvroCursor::new(&data)).unwrap_err();
3607        assert!(
3608            err.to_string().contains("Capacity overflow"),
3609            "unexpected error: {err}",
3610        );
3611    }
3612
3613    #[test]
3614    fn test_array_block_count_i64_min_errors() {
3615        // `i64::MIN` previously overflowed `-block_count` before spinning the loop.
3616        let mut decoder = array_of_null_decoder();
3617        let mut data = encode_avro_long_extreme(i64::MIN); // negative item count
3618        data.extend_from_slice(&encode_avro_long(0)); // block size in bytes
3619        let err = decoder.decode(&mut AvroCursor::new(&data)).unwrap_err();
3620        assert!(
3621            err.to_string().contains("Capacity overflow"),
3622            "unexpected error: {err}",
3623        );
3624    }
3625
3626    #[test]
3627    fn test_nested_array_decoding() {
3628        let inner_ty = avro_from_codec(Codec::List(Arc::new(avro_from_codec(Codec::Int32))));
3629        let nested_ty = avro_from_codec(Codec::List(Arc::new(inner_ty.clone())));
3630        let mut decoder = Decoder::try_new(&nested_ty).unwrap();
3631        let mut buf = Vec::new();
3632        buf.extend(encode_avro_long(1));
3633        buf.extend(encode_avro_long(2));
3634        buf.extend(encode_avro_int(5));
3635        buf.extend(encode_avro_int(6));
3636        buf.extend(encode_avro_long(0));
3637        buf.extend(encode_avro_long(0));
3638        let mut cursor = AvroCursor::new(&buf);
3639        decoder.decode(&mut cursor).unwrap();
3640        let arr = decoder.flush(None).unwrap();
3641        let outer = arr.as_any().downcast_ref::<ListArray>().unwrap();
3642        assert_eq!(outer.len(), 1);
3643        assert_eq!(outer.value_length(0), 1);
3644        let inner = outer.values().as_any().downcast_ref::<ListArray>().unwrap();
3645        assert_eq!(inner.len(), 1);
3646        assert_eq!(inner.value_length(0), 2);
3647        let values = inner
3648            .values()
3649            .as_any()
3650            .downcast_ref::<Int32Array>()
3651            .unwrap();
3652        assert_eq!(values.values(), &[5, 6]);
3653    }
3654
3655    #[test]
3656    fn test_array_decoding_empty_array() {
3657        let value_type = avro_from_codec(Codec::Utf8);
3658        let map_type = avro_from_codec(Codec::List(Arc::new(value_type)));
3659        let mut decoder = Decoder::try_new(&map_type).unwrap();
3660        let data = encode_avro_long(0);
3661        decoder.decode(&mut AvroCursor::new(&data)).unwrap();
3662        let array = decoder.flush(None).unwrap();
3663        let list_arr = array.as_any().downcast_ref::<ListArray>().unwrap();
3664        assert_eq!(list_arr.len(), 1);
3665        assert_eq!(list_arr.value_length(0), 0);
3666    }
3667
3668    #[test]
3669    fn test_array_decoding_writer_nonunion_items_reader_nullable_items() {
3670        use crate::schema::Array;
3671        let writer_schema = Schema::Complex(ComplexType::Array(Array {
3672            items: Box::new(Schema::TypeName(TypeName::Primitive(PrimitiveType::Int))),
3673            attributes: Attributes::default(),
3674        }));
3675        let reader_schema = Schema::Complex(ComplexType::Array(Array {
3676            items: Box::new(Schema::Union(vec![
3677                Schema::TypeName(TypeName::Primitive(PrimitiveType::Null)),
3678                Schema::TypeName(TypeName::Primitive(PrimitiveType::Int)),
3679            ])),
3680            attributes: Attributes::default(),
3681        }));
3682        let dt = resolved_root_datatype(writer_schema, reader_schema, false, false);
3683        if let Codec::List(inner) = dt.codec() {
3684            assert_eq!(
3685                inner.nullability(),
3686                Some(Nullability::NullFirst),
3687                "items should be nullable"
3688            );
3689        } else {
3690            panic!("expected List codec");
3691        }
3692        let mut decoder = Decoder::try_new(&dt).unwrap();
3693        let mut data = encode_avro_long(2);
3694        data.extend(encode_avro_int(10));
3695        data.extend(encode_avro_int(20));
3696        data.extend(encode_avro_long(0));
3697        let mut cursor = AvroCursor::new(&data);
3698        decoder.decode(&mut cursor).unwrap();
3699        assert_eq!(
3700            cursor.position(),
3701            data.len(),
3702            "all bytes should be consumed"
3703        );
3704        let array = decoder.flush(None).unwrap();
3705        let list_arr = array.as_any().downcast_ref::<ListArray>().unwrap();
3706        assert_eq!(list_arr.len(), 1, "one list/row");
3707        assert_eq!(list_arr.value_length(0), 2, "two items in the list");
3708        let values = list_arr.values().as_primitive::<Int32Type>();
3709        assert_eq!(values.len(), 2);
3710        assert_eq!(values.value(0), 10);
3711        assert_eq!(values.value(1), 20);
3712        assert!(!values.is_null(0));
3713        assert!(!values.is_null(1));
3714    }
3715
3716    #[test]
3717    fn test_decimal_decoding_fixed256() {
3718        let dt = avro_from_codec(Codec::Decimal(50, Some(2), Some(32)));
3719        let mut decoder = Decoder::try_new(&dt).unwrap();
3720        let row1 = [
3721            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3722            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3723            0x00, 0x00, 0x30, 0x39,
3724        ];
3725        let row2 = [
3726            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3727            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3728            0xFF, 0xFF, 0xFF, 0x85,
3729        ];
3730        let mut data = Vec::new();
3731        data.extend_from_slice(&row1);
3732        data.extend_from_slice(&row2);
3733        let mut cursor = AvroCursor::new(&data);
3734        decoder.decode(&mut cursor).unwrap();
3735        decoder.decode(&mut cursor).unwrap();
3736        let arr = decoder.flush(None).unwrap();
3737        let dec = arr.as_any().downcast_ref::<Decimal256Array>().unwrap();
3738        assert_eq!(dec.len(), 2);
3739        assert_eq!(dec.value_as_string(0), "123.45");
3740        assert_eq!(dec.value_as_string(1), "-1.23");
3741    }
3742
3743    #[test]
3744    fn test_decimal_decoding_fixed128() {
3745        let dt = avro_from_codec(Codec::Decimal(28, Some(2), Some(16)));
3746        let mut decoder = Decoder::try_new(&dt).unwrap();
3747        let row1 = [
3748            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3749            0x30, 0x39,
3750        ];
3751        let row2 = [
3752            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3753            0xFF, 0x85,
3754        ];
3755        let mut data = Vec::new();
3756        data.extend_from_slice(&row1);
3757        data.extend_from_slice(&row2);
3758        let mut cursor = AvroCursor::new(&data);
3759        decoder.decode(&mut cursor).unwrap();
3760        decoder.decode(&mut cursor).unwrap();
3761        let arr = decoder.flush(None).unwrap();
3762        let dec = arr.as_any().downcast_ref::<Decimal128Array>().unwrap();
3763        assert_eq!(dec.len(), 2);
3764        assert_eq!(dec.value_as_string(0), "123.45");
3765        assert_eq!(dec.value_as_string(1), "-1.23");
3766    }
3767
3768    #[test]
3769    fn test_decimal_decoding_fixed32_from_32byte_fixed_storage() {
3770        let dt = avro_from_codec(Codec::Decimal(5, Some(2), Some(32)));
3771        let mut decoder = Decoder::try_new(&dt).unwrap();
3772        let row1 = [
3773            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3774            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3775            0x00, 0x00, 0x30, 0x39,
3776        ];
3777        let row2 = [
3778            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3779            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3780            0xFF, 0xFF, 0xFF, 0x85,
3781        ];
3782        let mut data = Vec::new();
3783        data.extend_from_slice(&row1);
3784        data.extend_from_slice(&row2);
3785        let mut cursor = AvroCursor::new(&data);
3786        decoder.decode(&mut cursor).unwrap();
3787        decoder.decode(&mut cursor).unwrap();
3788        let arr = decoder.flush(None).unwrap();
3789        #[cfg(feature = "small_decimals")]
3790        {
3791            let dec = arr.as_any().downcast_ref::<Decimal32Array>().unwrap();
3792            assert_eq!(dec.len(), 2);
3793            assert_eq!(dec.value_as_string(0), "123.45");
3794            assert_eq!(dec.value_as_string(1), "-1.23");
3795        }
3796        #[cfg(not(feature = "small_decimals"))]
3797        {
3798            let dec = arr.as_any().downcast_ref::<Decimal128Array>().unwrap();
3799            assert_eq!(dec.len(), 2);
3800            assert_eq!(dec.value_as_string(0), "123.45");
3801            assert_eq!(dec.value_as_string(1), "-1.23");
3802        }
3803    }
3804
3805    #[test]
3806    fn test_decimal_decoding_fixed32_from_16byte_fixed_storage() {
3807        let dt = avro_from_codec(Codec::Decimal(5, Some(2), Some(16)));
3808        let mut decoder = Decoder::try_new(&dt).unwrap();
3809        let row1 = [
3810            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3811            0x30, 0x39,
3812        ];
3813        let row2 = [
3814            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
3815            0xFF, 0x85,
3816        ];
3817        let mut data = Vec::new();
3818        data.extend_from_slice(&row1);
3819        data.extend_from_slice(&row2);
3820        let mut cursor = AvroCursor::new(&data);
3821        decoder.decode(&mut cursor).unwrap();
3822        decoder.decode(&mut cursor).unwrap();
3823
3824        let arr = decoder.flush(None).unwrap();
3825        #[cfg(feature = "small_decimals")]
3826        {
3827            let dec = arr.as_any().downcast_ref::<Decimal32Array>().unwrap();
3828            assert_eq!(dec.len(), 2);
3829            assert_eq!(dec.value_as_string(0), "123.45");
3830            assert_eq!(dec.value_as_string(1), "-1.23");
3831        }
3832        #[cfg(not(feature = "small_decimals"))]
3833        {
3834            let dec = arr.as_any().downcast_ref::<Decimal128Array>().unwrap();
3835            assert_eq!(dec.len(), 2);
3836            assert_eq!(dec.value_as_string(0), "123.45");
3837            assert_eq!(dec.value_as_string(1), "-1.23");
3838        }
3839    }
3840
3841    #[test]
3842    fn test_decimal_decoding_bytes_with_nulls() {
3843        let dt = avro_from_codec(Codec::Decimal(4, Some(1), None));
3844        let inner = Decoder::try_new(&dt).unwrap();
3845        let mut decoder = Decoder::Nullable(NullableDecoder::new(
3846            NullablePlan::ReadTag {
3847                nullability: Nullability::NullSecond,
3848                resolution: ResolutionPlan::Promotion(Promotion::Direct),
3849            },
3850            inner,
3851        ));
3852        let mut data = Vec::new();
3853        data.extend_from_slice(&encode_avro_int(0));
3854        data.extend_from_slice(&encode_avro_bytes(&[0x04, 0xD2]));
3855        data.extend_from_slice(&encode_avro_int(1));
3856        data.extend_from_slice(&encode_avro_int(0));
3857        data.extend_from_slice(&encode_avro_bytes(&[0xFB, 0x2E]));
3858        let mut cursor = AvroCursor::new(&data);
3859        decoder.decode(&mut cursor).unwrap();
3860        decoder.decode(&mut cursor).unwrap();
3861        decoder.decode(&mut cursor).unwrap();
3862        let arr = decoder.flush(None).unwrap();
3863        #[cfg(feature = "small_decimals")]
3864        {
3865            let dec_arr = arr.as_any().downcast_ref::<Decimal32Array>().unwrap();
3866            assert_eq!(dec_arr.len(), 3);
3867            assert!(dec_arr.is_valid(0));
3868            assert!(!dec_arr.is_valid(1));
3869            assert!(dec_arr.is_valid(2));
3870            assert_eq!(dec_arr.value_as_string(0), "123.4");
3871            assert_eq!(dec_arr.value_as_string(2), "-123.4");
3872        }
3873        #[cfg(not(feature = "small_decimals"))]
3874        {
3875            let dec_arr = arr.as_any().downcast_ref::<Decimal128Array>().unwrap();
3876            assert_eq!(dec_arr.len(), 3);
3877            assert!(dec_arr.is_valid(0));
3878            assert!(!dec_arr.is_valid(1));
3879            assert!(dec_arr.is_valid(2));
3880            assert_eq!(dec_arr.value_as_string(0), "123.4");
3881            assert_eq!(dec_arr.value_as_string(2), "-123.4");
3882        }
3883    }
3884
3885    #[test]
3886    fn test_decimal_decoding_bytes_with_nulls_fixed_size_narrow_result() {
3887        let dt = avro_from_codec(Codec::Decimal(6, Some(2), Some(16)));
3888        let inner = Decoder::try_new(&dt).unwrap();
3889        let mut decoder = Decoder::Nullable(NullableDecoder::new(
3890            NullablePlan::ReadTag {
3891                nullability: Nullability::NullSecond,
3892                resolution: ResolutionPlan::Promotion(Promotion::Direct),
3893            },
3894            inner,
3895        ));
3896        let row1 = [
3897            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
3898            0xE2, 0x40,
3899        ];
3900        let row3 = [
3901            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
3902            0x1D, 0xC0,
3903        ];
3904        let mut data = Vec::new();
3905        data.extend_from_slice(&encode_avro_int(0));
3906        data.extend_from_slice(&row1);
3907        data.extend_from_slice(&encode_avro_int(1));
3908        data.extend_from_slice(&encode_avro_int(0));
3909        data.extend_from_slice(&row3);
3910        let mut cursor = AvroCursor::new(&data);
3911        decoder.decode(&mut cursor).unwrap();
3912        decoder.decode(&mut cursor).unwrap();
3913        decoder.decode(&mut cursor).unwrap();
3914        let arr = decoder.flush(None).unwrap();
3915        #[cfg(feature = "small_decimals")]
3916        {
3917            let dec_arr = arr.as_any().downcast_ref::<Decimal32Array>().unwrap();
3918            assert_eq!(dec_arr.len(), 3);
3919            assert!(dec_arr.is_valid(0));
3920            assert!(!dec_arr.is_valid(1));
3921            assert!(dec_arr.is_valid(2));
3922            assert_eq!(dec_arr.value_as_string(0), "1234.56");
3923            assert_eq!(dec_arr.value_as_string(2), "-1234.56");
3924        }
3925        #[cfg(not(feature = "small_decimals"))]
3926        {
3927            let dec_arr = arr.as_any().downcast_ref::<Decimal128Array>().unwrap();
3928            assert_eq!(dec_arr.len(), 3);
3929            assert!(dec_arr.is_valid(0));
3930            assert!(!dec_arr.is_valid(1));
3931            assert!(dec_arr.is_valid(2));
3932            assert_eq!(dec_arr.value_as_string(0), "1234.56");
3933            assert_eq!(dec_arr.value_as_string(2), "-1234.56");
3934        }
3935    }
3936
3937    #[test]
3938    fn test_enum_decoding() {
3939        let symbols: Arc<[String]> = vec!["A", "B", "C"].into_iter().map(String::from).collect();
3940        let avro_type = avro_from_codec(Codec::Enum(symbols.clone()));
3941        let mut decoder = Decoder::try_new(&avro_type).unwrap();
3942        let mut data = Vec::new();
3943        data.extend_from_slice(&encode_avro_int(2));
3944        data.extend_from_slice(&encode_avro_int(0));
3945        data.extend_from_slice(&encode_avro_int(1));
3946        let mut cursor = AvroCursor::new(&data);
3947        decoder.decode(&mut cursor).unwrap();
3948        decoder.decode(&mut cursor).unwrap();
3949        decoder.decode(&mut cursor).unwrap();
3950        let array = decoder.flush(None).unwrap();
3951        let dict_array = array
3952            .as_any()
3953            .downcast_ref::<DictionaryArray<Int32Type>>()
3954            .unwrap();
3955        assert_eq!(dict_array.len(), 3);
3956        let values = dict_array
3957            .values()
3958            .as_any()
3959            .downcast_ref::<StringArray>()
3960            .unwrap();
3961        assert_eq!(values.value(0), "A");
3962        assert_eq!(values.value(1), "B");
3963        assert_eq!(values.value(2), "C");
3964        assert_eq!(dict_array.keys().values(), &[2, 0, 1]);
3965    }
3966
3967    #[test]
3968    fn test_enum_decoding_with_nulls() {
3969        let symbols: Arc<[String]> = vec!["X", "Y"].into_iter().map(String::from).collect();
3970        let enum_codec = Codec::Enum(symbols.clone());
3971        let avro_type =
3972            AvroDataType::new(enum_codec, Default::default(), Some(Nullability::NullFirst));
3973        let mut decoder = Decoder::try_new(&avro_type).unwrap();
3974        let mut data = Vec::new();
3975        data.extend_from_slice(&encode_avro_long(1));
3976        data.extend_from_slice(&encode_avro_int(1));
3977        data.extend_from_slice(&encode_avro_long(0));
3978        data.extend_from_slice(&encode_avro_long(1));
3979        data.extend_from_slice(&encode_avro_int(0));
3980        let mut cursor = AvroCursor::new(&data);
3981        decoder.decode(&mut cursor).unwrap();
3982        decoder.decode(&mut cursor).unwrap();
3983        decoder.decode(&mut cursor).unwrap();
3984        let array = decoder.flush(None).unwrap();
3985        let dict_array = array
3986            .as_any()
3987            .downcast_ref::<DictionaryArray<Int32Type>>()
3988            .unwrap();
3989        assert_eq!(dict_array.len(), 3);
3990        assert!(dict_array.is_valid(0));
3991        assert!(dict_array.is_null(1));
3992        assert!(dict_array.is_valid(2));
3993        let expected_keys = Int32Array::from(vec![Some(1), None, Some(0)]);
3994        assert_eq!(dict_array.keys(), &expected_keys);
3995        let values = dict_array
3996            .values()
3997            .as_any()
3998            .downcast_ref::<StringArray>()
3999            .unwrap();
4000        assert_eq!(values.value(0), "X");
4001        assert_eq!(values.value(1), "Y");
4002    }
4003
4004    #[test]
4005    fn test_duration_decoding_with_nulls() {
4006        let duration_codec = Codec::Interval;
4007        let avro_type = AvroDataType::new(
4008            duration_codec,
4009            Default::default(),
4010            Some(Nullability::NullFirst),
4011        );
4012        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4013        let mut data = Vec::new();
4014        // First value: 1 month, 2 days, 3 millis
4015        data.extend_from_slice(&encode_avro_long(1)); // not null
4016        let mut duration1 = Vec::new();
4017        duration1.extend_from_slice(&1u32.to_le_bytes());
4018        duration1.extend_from_slice(&2u32.to_le_bytes());
4019        duration1.extend_from_slice(&3u32.to_le_bytes());
4020        data.extend_from_slice(&duration1);
4021        // Second value: null
4022        data.extend_from_slice(&encode_avro_long(0)); // null
4023        data.extend_from_slice(&encode_avro_long(1)); // not null
4024        let mut duration2 = Vec::new();
4025        duration2.extend_from_slice(&4u32.to_le_bytes());
4026        duration2.extend_from_slice(&5u32.to_le_bytes());
4027        duration2.extend_from_slice(&6u32.to_le_bytes());
4028        data.extend_from_slice(&duration2);
4029        let mut cursor = AvroCursor::new(&data);
4030        decoder.decode(&mut cursor).unwrap();
4031        decoder.decode(&mut cursor).unwrap();
4032        decoder.decode(&mut cursor).unwrap();
4033        let array = decoder.flush(None).unwrap();
4034        let interval_array = array
4035            .as_any()
4036            .downcast_ref::<IntervalMonthDayNanoArray>()
4037            .unwrap();
4038        assert_eq!(interval_array.len(), 3);
4039        assert!(interval_array.is_valid(0));
4040        assert!(interval_array.is_null(1));
4041        assert!(interval_array.is_valid(2));
4042        let expected = IntervalMonthDayNanoArray::from(vec![
4043            Some(IntervalMonthDayNano {
4044                months: 1,
4045                days: 2,
4046                nanoseconds: 3_000_000,
4047            }),
4048            None,
4049            Some(IntervalMonthDayNano {
4050                months: 4,
4051                days: 5,
4052                nanoseconds: 6_000_000,
4053            }),
4054        ]);
4055        assert_eq!(interval_array, &expected);
4056    }
4057
4058    #[cfg(feature = "avro_custom_types")]
4059    #[test]
4060    fn test_interval_month_day_nano_custom_decoding_with_nulls() {
4061        let avro_type = AvroDataType::new(
4062            Codec::IntervalMonthDayNano,
4063            Default::default(),
4064            Some(Nullability::NullFirst),
4065        );
4066        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4067        let mut data = Vec::new();
4068        // First value: months=1, days=-2, nanos=3
4069        data.extend_from_slice(&encode_avro_long(1));
4070        data.extend_from_slice(&1i32.to_le_bytes());
4071        data.extend_from_slice(&(-2i32).to_le_bytes());
4072        data.extend_from_slice(&3i64.to_le_bytes());
4073        // Second value: null
4074        data.extend_from_slice(&encode_avro_long(0));
4075        // Third value: months=-4, days=5, nanos=-6
4076        data.extend_from_slice(&encode_avro_long(1));
4077        data.extend_from_slice(&(-4i32).to_le_bytes());
4078        data.extend_from_slice(&5i32.to_le_bytes());
4079        data.extend_from_slice(&(-6i64).to_le_bytes());
4080        let mut cursor = AvroCursor::new(&data);
4081        decoder.decode(&mut cursor).unwrap();
4082        decoder.decode(&mut cursor).unwrap();
4083        decoder.decode(&mut cursor).unwrap();
4084        let array = decoder.flush(None).unwrap();
4085        let interval_array = array
4086            .as_any()
4087            .downcast_ref::<IntervalMonthDayNanoArray>()
4088            .unwrap();
4089        assert_eq!(interval_array.len(), 3);
4090        let expected = IntervalMonthDayNanoArray::from(vec![
4091            Some(IntervalMonthDayNano::new(1, -2, 3)),
4092            None,
4093            Some(IntervalMonthDayNano::new(-4, 5, -6)),
4094        ]);
4095        assert_eq!(interval_array, &expected);
4096    }
4097
4098    #[test]
4099    fn test_duration_decoding_empty() {
4100        let duration_codec = Codec::Interval;
4101        let avro_type = AvroDataType::new(duration_codec, Default::default(), None);
4102        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4103        let array = decoder.flush(None).unwrap();
4104        assert_eq!(array.len(), 0);
4105    }
4106
4107    #[test]
4108    #[cfg(feature = "avro_custom_types")]
4109    fn test_duration_seconds_decoding() {
4110        let avro_type = AvroDataType::new(Codec::DurationSeconds, Default::default(), None);
4111        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4112        let mut data = Vec::new();
4113        // Three values: 0, -1, 2
4114        data.extend_from_slice(&encode_avro_long(0));
4115        data.extend_from_slice(&encode_avro_long(-1));
4116        data.extend_from_slice(&encode_avro_long(2));
4117        let mut cursor = AvroCursor::new(&data);
4118        decoder.decode(&mut cursor).unwrap();
4119        decoder.decode(&mut cursor).unwrap();
4120        decoder.decode(&mut cursor).unwrap();
4121        let array = decoder.flush(None).unwrap();
4122        let dur = array
4123            .as_any()
4124            .downcast_ref::<DurationSecondArray>()
4125            .unwrap();
4126        assert_eq!(dur.values(), &[0, -1, 2]);
4127    }
4128
4129    #[test]
4130    #[cfg(feature = "avro_custom_types")]
4131    fn test_duration_milliseconds_decoding() {
4132        let avro_type = AvroDataType::new(Codec::DurationMillis, Default::default(), None);
4133        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4134        let mut data = Vec::new();
4135        for v in [1i64, 0, -2] {
4136            data.extend_from_slice(&encode_avro_long(v));
4137        }
4138        let mut cursor = AvroCursor::new(&data);
4139        for _ in 0..3 {
4140            decoder.decode(&mut cursor).unwrap();
4141        }
4142        let array = decoder.flush(None).unwrap();
4143        let dur = array
4144            .as_any()
4145            .downcast_ref::<DurationMillisecondArray>()
4146            .unwrap();
4147        assert_eq!(dur.values(), &[1, 0, -2]);
4148    }
4149
4150    #[test]
4151    #[cfg(feature = "avro_custom_types")]
4152    fn test_duration_microseconds_decoding() {
4153        let avro_type = AvroDataType::new(Codec::DurationMicros, Default::default(), None);
4154        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4155        let mut data = Vec::new();
4156        for v in [5i64, -6, 7] {
4157            data.extend_from_slice(&encode_avro_long(v));
4158        }
4159        let mut cursor = AvroCursor::new(&data);
4160        for _ in 0..3 {
4161            decoder.decode(&mut cursor).unwrap();
4162        }
4163        let array = decoder.flush(None).unwrap();
4164        let dur = array
4165            .as_any()
4166            .downcast_ref::<DurationMicrosecondArray>()
4167            .unwrap();
4168        assert_eq!(dur.values(), &[5, -6, 7]);
4169    }
4170
4171    #[test]
4172    #[cfg(feature = "avro_custom_types")]
4173    fn test_duration_nanoseconds_decoding() {
4174        let avro_type = AvroDataType::new(Codec::DurationNanos, Default::default(), None);
4175        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4176        let mut data = Vec::new();
4177        for v in [8i64, 9, -10] {
4178            data.extend_from_slice(&encode_avro_long(v));
4179        }
4180        let mut cursor = AvroCursor::new(&data);
4181        for _ in 0..3 {
4182            decoder.decode(&mut cursor).unwrap();
4183        }
4184        let array = decoder.flush(None).unwrap();
4185        let dur = array
4186            .as_any()
4187            .downcast_ref::<DurationNanosecondArray>()
4188            .unwrap();
4189        assert_eq!(dur.values(), &[8, 9, -10]);
4190    }
4191
4192    #[test]
4193    fn test_nullable_decode_error_bitmap_corruption() {
4194        // Nullable Int32 with ['T','null'] encoding (NullSecond)
4195        let avro_type = AvroDataType::new(
4196            Codec::Int32,
4197            Default::default(),
4198            Some(Nullability::NullSecond),
4199        );
4200        let mut decoder = Decoder::try_new(&avro_type).unwrap();
4201
4202        // Row 1: union branch 1 (null)
4203        let mut row1 = Vec::new();
4204        row1.extend_from_slice(&encode_avro_int(1));
4205
4206        // Row 2: union branch 0 (non-null) but missing the int payload -> decode error
4207        let mut row2 = Vec::new();
4208        row2.extend_from_slice(&encode_avro_int(0)); // branch = 0 => non-null
4209
4210        // Row 3: union branch 0 (non-null) with correct int payload -> should succeed
4211        let mut row3 = Vec::new();
4212        row3.extend_from_slice(&encode_avro_int(0)); // branch
4213        row3.extend_from_slice(&encode_avro_int(42)); // actual value
4214
4215        decoder.decode(&mut AvroCursor::new(&row1)).unwrap();
4216        assert!(decoder.decode(&mut AvroCursor::new(&row2)).is_err()); // decode error
4217        decoder.decode(&mut AvroCursor::new(&row3)).unwrap();
4218
4219        let array = decoder.flush(None).unwrap();
4220
4221        // Should contain 2 elements: row1 (null) and row3 (42)
4222        assert_eq!(array.len(), 2);
4223        let int_array = array.as_any().downcast_ref::<Int32Array>().unwrap();
4224        assert!(int_array.is_null(0)); // row1 is null
4225        assert_eq!(int_array.value(1), 42); // row3 value is 42
4226    }
4227
4228    #[test]
4229    fn test_enum_mapping_reordered_symbols() {
4230        let reader_symbols: Arc<[String]> =
4231            vec!["B".to_string(), "C".to_string(), "A".to_string()].into();
4232        let mapping: Arc<[i32]> = Arc::from(vec![2, 0, 1]);
4233        let default_index: i32 = -1;
4234        let mut dec = Decoder::Enum(
4235            Vec::with_capacity(DEFAULT_CAPACITY),
4236            reader_symbols.clone(),
4237            Some(EnumResolution {
4238                mapping,
4239                default_index,
4240            }),
4241        );
4242        let mut data = Vec::new();
4243        data.extend_from_slice(&encode_avro_int(0));
4244        data.extend_from_slice(&encode_avro_int(1));
4245        data.extend_from_slice(&encode_avro_int(2));
4246        let mut cur = AvroCursor::new(&data);
4247        dec.decode(&mut cur).unwrap();
4248        dec.decode(&mut cur).unwrap();
4249        dec.decode(&mut cur).unwrap();
4250        let arr = dec.flush(None).unwrap();
4251        let dict = arr
4252            .as_any()
4253            .downcast_ref::<DictionaryArray<Int32Type>>()
4254            .unwrap();
4255        let expected_keys = Int32Array::from(vec![2, 0, 1]);
4256        assert_eq!(dict.keys(), &expected_keys);
4257        let values = dict
4258            .values()
4259            .as_any()
4260            .downcast_ref::<StringArray>()
4261            .unwrap();
4262        assert_eq!(values.value(0), "B");
4263        assert_eq!(values.value(1), "C");
4264        assert_eq!(values.value(2), "A");
4265    }
4266
4267    #[test]
4268    fn test_enum_mapping_unknown_symbol_and_out_of_range_fall_back_to_default() {
4269        let reader_symbols: Arc<[String]> = vec!["A".to_string(), "B".to_string()].into();
4270        let default_index: i32 = 1;
4271        let mapping: Arc<[i32]> = Arc::from(vec![0, 1]);
4272        let mut dec = Decoder::Enum(
4273            Vec::with_capacity(DEFAULT_CAPACITY),
4274            reader_symbols.clone(),
4275            Some(EnumResolution {
4276                mapping,
4277                default_index,
4278            }),
4279        );
4280        let mut data = Vec::new();
4281        data.extend_from_slice(&encode_avro_int(0));
4282        data.extend_from_slice(&encode_avro_int(1));
4283        data.extend_from_slice(&encode_avro_int(99));
4284        let mut cur = AvroCursor::new(&data);
4285        dec.decode(&mut cur).unwrap();
4286        dec.decode(&mut cur).unwrap();
4287        dec.decode(&mut cur).unwrap();
4288        let arr = dec.flush(None).unwrap();
4289        let dict = arr
4290            .as_any()
4291            .downcast_ref::<DictionaryArray<Int32Type>>()
4292            .unwrap();
4293        let expected_keys = Int32Array::from(vec![0, 1, 1]);
4294        assert_eq!(dict.keys(), &expected_keys);
4295        let values = dict
4296            .values()
4297            .as_any()
4298            .downcast_ref::<StringArray>()
4299            .unwrap();
4300        assert_eq!(values.value(0), "A");
4301        assert_eq!(values.value(1), "B");
4302    }
4303
4304    #[test]
4305    fn test_enum_mapping_unknown_symbol_without_default_errors() {
4306        let reader_symbols: Arc<[String]> = vec!["A".to_string()].into();
4307        let default_index: i32 = -1; // indicates no default at type-level
4308        let mapping: Arc<[i32]> = Arc::from(vec![-1]);
4309        let mut dec = Decoder::Enum(
4310            Vec::with_capacity(DEFAULT_CAPACITY),
4311            reader_symbols,
4312            Some(EnumResolution {
4313                mapping,
4314                default_index,
4315            }),
4316        );
4317        let data = encode_avro_int(0);
4318        let mut cur = AvroCursor::new(&data);
4319        let err = dec
4320            .decode(&mut cur)
4321            .expect_err("expected decode error for unresolved enum without default");
4322        let msg = err.to_string();
4323        assert!(
4324            msg.contains("not resolvable") && msg.contains("no default"),
4325            "unexpected error message: {msg}"
4326        );
4327    }
4328
4329    fn make_record_resolved_decoder(
4330        reader_fields: &[(&str, DataType, bool)],
4331        writer_projections: Vec<FieldProjection>,
4332    ) -> Decoder {
4333        let mut field_refs: Vec<FieldRef> = Vec::with_capacity(reader_fields.len());
4334        let mut encodings: Vec<Decoder> = Vec::with_capacity(reader_fields.len());
4335        for (name, dt, nullable) in reader_fields {
4336            field_refs.push(Arc::new(ArrowField::new(*name, dt.clone(), *nullable)));
4337            let enc = match dt {
4338                DataType::Int32 => Decoder::Int32(Vec::new()),
4339                DataType::Int64 => Decoder::Int64(Vec::new()),
4340                DataType::Utf8 => {
4341                    Decoder::String(OffsetBufferBuilder::new(DEFAULT_CAPACITY), Vec::new())
4342                }
4343                other => panic!("Unsupported test reader field type: {other:?}"),
4344            };
4345            encodings.push(enc);
4346        }
4347        let fields: Fields = field_refs.into();
4348        Decoder::Record {
4349            fields,
4350            decoders: encodings,
4351            defaults: vec![None; reader_fields.len()],
4352            projector: Some(Projector {
4353                writer_projections,
4354                default_injections: Arc::from(Vec::<(usize, AvroLiteral)>::new()),
4355            }),
4356            row_count: 0,
4357        }
4358    }
4359
4360    #[test]
4361    fn test_skip_writer_trailing_field_int32() {
4362        let mut dec = make_record_resolved_decoder(
4363            &[("id", arrow_schema::DataType::Int32, false)],
4364            vec![
4365                FieldProjection::ToReader(0),
4366                FieldProjection::Skip(super::Skipper::Int32),
4367            ],
4368        );
4369        let mut data = Vec::new();
4370        data.extend_from_slice(&encode_avro_int(7));
4371        data.extend_from_slice(&encode_avro_int(999));
4372        let mut cur = AvroCursor::new(&data);
4373        dec.decode(&mut cur).unwrap();
4374        assert_eq!(cur.position(), data.len());
4375        let arr = dec.flush(None).unwrap();
4376        let struct_arr = arr.as_any().downcast_ref::<StructArray>().unwrap();
4377        assert_eq!(struct_arr.len(), 1);
4378        let id = struct_arr
4379            .column_by_name("id")
4380            .unwrap()
4381            .as_any()
4382            .downcast_ref::<Int32Array>()
4383            .unwrap();
4384        assert_eq!(id.value(0), 7);
4385    }
4386
4387    #[test]
4388    fn test_skip_writer_middle_field_string() {
4389        let mut dec = make_record_resolved_decoder(
4390            &[
4391                ("id", DataType::Int32, false),
4392                ("score", DataType::Int64, false),
4393            ],
4394            vec![
4395                FieldProjection::ToReader(0),
4396                FieldProjection::Skip(Skipper::String),
4397                FieldProjection::ToReader(1),
4398            ],
4399        );
4400        let mut data = Vec::new();
4401        data.extend_from_slice(&encode_avro_int(42));
4402        data.extend_from_slice(&encode_avro_bytes(b"abcdef"));
4403        data.extend_from_slice(&encode_avro_long(1000));
4404        let mut cur = AvroCursor::new(&data);
4405        dec.decode(&mut cur).unwrap();
4406        assert_eq!(cur.position(), data.len());
4407        let arr = dec.flush(None).unwrap();
4408        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
4409        let id = s
4410            .column_by_name("id")
4411            .unwrap()
4412            .as_any()
4413            .downcast_ref::<Int32Array>()
4414            .unwrap();
4415        let score = s
4416            .column_by_name("score")
4417            .unwrap()
4418            .as_any()
4419            .downcast_ref::<Int64Array>()
4420            .unwrap();
4421        assert_eq!(id.value(0), 42);
4422        assert_eq!(score.value(0), 1000);
4423    }
4424
4425    #[test]
4426    fn test_skip_writer_array_with_negative_block_count_fast() {
4427        let mut dec = make_record_resolved_decoder(
4428            &[("id", DataType::Int32, false)],
4429            vec![
4430                FieldProjection::Skip(super::Skipper::List(Box::new(Skipper::Int32))),
4431                FieldProjection::ToReader(0),
4432            ],
4433        );
4434        let mut array_payload = Vec::new();
4435        array_payload.extend_from_slice(&encode_avro_int(1));
4436        array_payload.extend_from_slice(&encode_avro_int(2));
4437        array_payload.extend_from_slice(&encode_avro_int(3));
4438        let mut data = Vec::new();
4439        data.extend_from_slice(&encode_avro_long(-3));
4440        data.extend_from_slice(&encode_avro_long(array_payload.len() as i64));
4441        data.extend_from_slice(&array_payload);
4442        data.extend_from_slice(&encode_avro_long(0));
4443        data.extend_from_slice(&encode_avro_int(5));
4444        let mut cur = AvroCursor::new(&data);
4445        dec.decode(&mut cur).unwrap();
4446        assert_eq!(cur.position(), data.len());
4447        let arr = dec.flush(None).unwrap();
4448        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
4449        let id = s
4450            .column_by_name("id")
4451            .unwrap()
4452            .as_any()
4453            .downcast_ref::<Int32Array>()
4454            .unwrap();
4455        assert_eq!(id.len(), 1);
4456        assert_eq!(id.value(0), 5);
4457    }
4458
4459    #[test]
4460    fn test_skip_writer_map_with_negative_block_count_fast() {
4461        let mut dec = make_record_resolved_decoder(
4462            &[("id", DataType::Int32, false)],
4463            vec![
4464                FieldProjection::Skip(Skipper::Map(Box::new(Skipper::Int32))),
4465                FieldProjection::ToReader(0),
4466            ],
4467        );
4468        let mut entries = Vec::new();
4469        entries.extend_from_slice(&encode_avro_bytes(b"k1"));
4470        entries.extend_from_slice(&encode_avro_int(10));
4471        entries.extend_from_slice(&encode_avro_bytes(b"k2"));
4472        entries.extend_from_slice(&encode_avro_int(20));
4473        let mut data = Vec::new();
4474        data.extend_from_slice(&encode_avro_long(-2));
4475        data.extend_from_slice(&encode_avro_long(entries.len() as i64));
4476        data.extend_from_slice(&entries);
4477        data.extend_from_slice(&encode_avro_long(0));
4478        data.extend_from_slice(&encode_avro_int(123));
4479        let mut cur = AvroCursor::new(&data);
4480        dec.decode(&mut cur).unwrap();
4481        assert_eq!(cur.position(), data.len());
4482        let arr = dec.flush(None).unwrap();
4483        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
4484        let id = s
4485            .column_by_name("id")
4486            .unwrap()
4487            .as_any()
4488            .downcast_ref::<Int32Array>()
4489            .unwrap();
4490        assert_eq!(id.len(), 1);
4491        assert_eq!(id.value(0), 123);
4492    }
4493
4494    #[test]
4495    fn test_skip_writer_nullable_field_union_nullfirst() {
4496        let mut dec = make_record_resolved_decoder(
4497            &[("id", DataType::Int32, false)],
4498            vec![
4499                FieldProjection::Skip(super::Skipper::Nullable(
4500                    Nullability::NullFirst,
4501                    Box::new(super::Skipper::Int32),
4502                )),
4503                FieldProjection::ToReader(0),
4504            ],
4505        );
4506        let mut row1 = Vec::new();
4507        row1.extend_from_slice(&encode_avro_long(0));
4508        row1.extend_from_slice(&encode_avro_int(5));
4509        let mut row2 = Vec::new();
4510        row2.extend_from_slice(&encode_avro_long(1));
4511        row2.extend_from_slice(&encode_avro_int(123));
4512        row2.extend_from_slice(&encode_avro_int(7));
4513        let mut cur1 = AvroCursor::new(&row1);
4514        let mut cur2 = AvroCursor::new(&row2);
4515        dec.decode(&mut cur1).unwrap();
4516        dec.decode(&mut cur2).unwrap();
4517        assert_eq!(cur1.position(), row1.len());
4518        assert_eq!(cur2.position(), row2.len());
4519        let arr = dec.flush(None).unwrap();
4520        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
4521        let id = s
4522            .column_by_name("id")
4523            .unwrap()
4524            .as_any()
4525            .downcast_ref::<Int32Array>()
4526            .unwrap();
4527        assert_eq!(id.len(), 2);
4528        assert_eq!(id.value(0), 5);
4529        assert_eq!(id.value(1), 7);
4530    }
4531
4532    fn make_dense_union_avro(
4533        children: Vec<(Codec, &'_ str, DataType)>,
4534        type_ids: Vec<i8>,
4535    ) -> AvroDataType {
4536        let mut avro_children: Vec<AvroDataType> = Vec::with_capacity(children.len());
4537        let mut fields: Vec<arrow_schema::Field> = Vec::with_capacity(children.len());
4538        for (codec, name, dt) in children {
4539            avro_children.push(AvroDataType::new(codec, Default::default(), None));
4540            fields.push(arrow_schema::Field::new(name, dt, true));
4541        }
4542        let union_fields = UnionFields::try_new(type_ids, fields).unwrap();
4543        let union_codec = Codec::Union(avro_children.into(), union_fields, UnionMode::Dense);
4544        AvroDataType::new(union_codec, Default::default(), None)
4545    }
4546
4547    #[test]
4548    fn test_union_dense_two_children_custom_type_ids() {
4549        let union_dt = make_dense_union_avro(
4550            vec![
4551                (Codec::Int32, "i", DataType::Int32),
4552                (Codec::Utf8, "s", DataType::Utf8),
4553            ],
4554            vec![2, 5],
4555        );
4556        let mut dec = Decoder::try_new(&union_dt).unwrap();
4557        let mut r1 = Vec::new();
4558        r1.extend_from_slice(&encode_avro_long(0));
4559        r1.extend_from_slice(&encode_avro_int(7));
4560        let mut r2 = Vec::new();
4561        r2.extend_from_slice(&encode_avro_long(1));
4562        r2.extend_from_slice(&encode_avro_bytes(b"x"));
4563        let mut r3 = Vec::new();
4564        r3.extend_from_slice(&encode_avro_long(0));
4565        r3.extend_from_slice(&encode_avro_int(-1));
4566        dec.decode(&mut AvroCursor::new(&r1)).unwrap();
4567        dec.decode(&mut AvroCursor::new(&r2)).unwrap();
4568        dec.decode(&mut AvroCursor::new(&r3)).unwrap();
4569        let array = dec.flush(None).unwrap();
4570        let ua = array
4571            .as_any()
4572            .downcast_ref::<UnionArray>()
4573            .expect("expected UnionArray");
4574        assert_eq!(ua.len(), 3);
4575        assert_eq!(ua.type_id(0), 2);
4576        assert_eq!(ua.type_id(1), 5);
4577        assert_eq!(ua.type_id(2), 2);
4578        assert_eq!(ua.value_offset(0), 0);
4579        assert_eq!(ua.value_offset(1), 0);
4580        assert_eq!(ua.value_offset(2), 1);
4581        let int_child = ua
4582            .child(2)
4583            .as_any()
4584            .downcast_ref::<Int32Array>()
4585            .expect("int child");
4586        assert_eq!(int_child.len(), 2);
4587        assert_eq!(int_child.value(0), 7);
4588        assert_eq!(int_child.value(1), -1);
4589        let str_child = ua
4590            .child(5)
4591            .as_any()
4592            .downcast_ref::<StringArray>()
4593            .expect("string child");
4594        assert_eq!(str_child.len(), 1);
4595        assert_eq!(str_child.value(0), "x");
4596    }
4597
4598    #[test]
4599    fn test_union_dense_with_null_and_string_children() {
4600        let union_dt = make_dense_union_avro(
4601            vec![
4602                (Codec::Null, "n", DataType::Null),
4603                (Codec::Utf8, "s", DataType::Utf8),
4604            ],
4605            vec![42, 7],
4606        );
4607        let mut dec = Decoder::try_new(&union_dt).unwrap();
4608        let r1 = encode_avro_long(0);
4609        let mut r2 = Vec::new();
4610        r2.extend_from_slice(&encode_avro_long(1));
4611        r2.extend_from_slice(&encode_avro_bytes(b"abc"));
4612        let r3 = encode_avro_long(0);
4613        dec.decode(&mut AvroCursor::new(&r1)).unwrap();
4614        dec.decode(&mut AvroCursor::new(&r2)).unwrap();
4615        dec.decode(&mut AvroCursor::new(&r3)).unwrap();
4616        let array = dec.flush(None).unwrap();
4617        let ua = array
4618            .as_any()
4619            .downcast_ref::<UnionArray>()
4620            .expect("expected UnionArray");
4621        assert_eq!(ua.len(), 3);
4622        assert_eq!(ua.type_id(0), 42);
4623        assert_eq!(ua.type_id(1), 7);
4624        assert_eq!(ua.type_id(2), 42);
4625        assert_eq!(ua.value_offset(0), 0);
4626        assert_eq!(ua.value_offset(1), 0);
4627        assert_eq!(ua.value_offset(2), 1);
4628        let null_child = ua
4629            .child(42)
4630            .as_any()
4631            .downcast_ref::<NullArray>()
4632            .expect("null child");
4633        assert_eq!(null_child.len(), 2);
4634        let str_child = ua
4635            .child(7)
4636            .as_any()
4637            .downcast_ref::<StringArray>()
4638            .expect("string child");
4639        assert_eq!(str_child.len(), 1);
4640        assert_eq!(str_child.value(0), "abc");
4641    }
4642
4643    #[test]
4644    fn test_union_dense_offsets_reset_after_flush() {
4645        let union_dt = make_dense_union_avro(
4646            vec![
4647                (Codec::Null, "n", DataType::Null),
4648                (Codec::Utf8, "s", DataType::Utf8),
4649            ],
4650            vec![0, 1],
4651        );
4652        let mut dec = Decoder::try_new(&union_dt).unwrap();
4653
4654        for _ in 0..2 {
4655            dec.decode(&mut AvroCursor::new(&encode_avro_long(0)))
4656                .unwrap();
4657            let array = dec.flush(None).unwrap();
4658            let union = array.as_union();
4659            assert_eq!(union.value_offset(0), 0);
4660        }
4661    }
4662
4663    #[test]
4664    fn test_union_decode_negative_branch_index_errors() {
4665        let union_dt = make_dense_union_avro(
4666            vec![
4667                (Codec::Int32, "i", DataType::Int32),
4668                (Codec::Utf8, "s", DataType::Utf8),
4669            ],
4670            vec![0, 1],
4671        );
4672        let mut dec = Decoder::try_new(&union_dt).unwrap();
4673        let row = encode_avro_long(-1); // decodes back to -1
4674        let err = dec
4675            .decode(&mut AvroCursor::new(&row))
4676            .expect_err("expected error for negative branch index");
4677        let msg = err.to_string();
4678        assert!(
4679            msg.contains("Negative union branch index"),
4680            "unexpected error message: {msg}"
4681        );
4682    }
4683
4684    #[test]
4685    fn test_union_decode_out_of_range_branch_index_errors() {
4686        let union_dt = make_dense_union_avro(
4687            vec![
4688                (Codec::Int32, "i", DataType::Int32),
4689                (Codec::Utf8, "s", DataType::Utf8),
4690            ],
4691            vec![10, 11],
4692        );
4693        let mut dec = Decoder::try_new(&union_dt).unwrap();
4694        let row = encode_avro_long(2);
4695        let err = dec
4696            .decode(&mut AvroCursor::new(&row))
4697            .expect_err("expected error for out-of-range branch index");
4698        let msg = err.to_string();
4699        assert!(
4700            msg.contains("out of range"),
4701            "unexpected error message: {msg}"
4702        );
4703    }
4704
4705    #[test]
4706    fn test_union_sparse_mode_not_supported() {
4707        let children: Vec<AvroDataType> = vec![
4708            AvroDataType::new(Codec::Int32, Default::default(), None),
4709            AvroDataType::new(Codec::Utf8, Default::default(), None),
4710        ];
4711        let uf = UnionFields::try_new(
4712            vec![1, 3],
4713            vec![
4714                arrow_schema::Field::new("i", DataType::Int32, true),
4715                arrow_schema::Field::new("s", DataType::Utf8, true),
4716            ],
4717        )
4718        .unwrap();
4719        let codec = Codec::Union(children.into(), uf, UnionMode::Sparse);
4720        let dt = AvroDataType::new(codec, Default::default(), None);
4721        let err = Decoder::try_new(&dt).expect_err("sparse union should not be supported");
4722        let msg = err.to_string();
4723        assert!(
4724            msg.contains("Sparse Arrow unions are not yet supported"),
4725            "unexpected error message: {msg}"
4726        );
4727    }
4728
4729    fn make_record_decoder_with_projector_defaults(
4730        reader_fields: &[(&str, DataType, bool)],
4731        field_defaults: Vec<Option<AvroLiteral>>,
4732        default_injections: Vec<(usize, AvroLiteral)>,
4733    ) -> Decoder {
4734        assert_eq!(
4735            field_defaults.len(),
4736            reader_fields.len(),
4737            "field_defaults must have one entry per reader field"
4738        );
4739        let mut field_refs: Vec<FieldRef> = Vec::with_capacity(reader_fields.len());
4740        let mut encodings: Vec<Decoder> = Vec::with_capacity(reader_fields.len());
4741        for (name, dt, nullable) in reader_fields {
4742            field_refs.push(Arc::new(ArrowField::new(*name, dt.clone(), *nullable)));
4743            let enc = match dt {
4744                DataType::Int32 => Decoder::Int32(Vec::with_capacity(DEFAULT_CAPACITY)),
4745                DataType::Int64 => Decoder::Int64(Vec::with_capacity(DEFAULT_CAPACITY)),
4746                DataType::Utf8 => Decoder::String(
4747                    OffsetBufferBuilder::new(DEFAULT_CAPACITY),
4748                    Vec::with_capacity(DEFAULT_CAPACITY),
4749                ),
4750                other => panic!("Unsupported test field type in helper: {other:?}"),
4751            };
4752            encodings.push(enc);
4753        }
4754        let fields: Fields = field_refs.into();
4755        let projector = Projector {
4756            writer_projections: vec![],
4757            default_injections: Arc::from(default_injections),
4758        };
4759        Decoder::Record {
4760            fields,
4761            decoders: encodings,
4762            defaults: field_defaults,
4763            projector: Some(projector),
4764            row_count: 0,
4765        }
4766    }
4767
4768    #[cfg(feature = "avro_custom_types")]
4769    #[test]
4770    fn test_default_append_custom_integer_range_validation() {
4771        let mut d_i8 = Decoder::Int8(Vec::with_capacity(DEFAULT_CAPACITY));
4772        d_i8.append_default(&AvroLiteral::Int(i8::MIN as i32))
4773            .unwrap();
4774        d_i8.append_default(&AvroLiteral::Int(i8::MAX as i32))
4775            .unwrap();
4776        let err_i8_high = d_i8
4777            .append_default(&AvroLiteral::Int(i8::MAX as i32 + 1))
4778            .unwrap_err();
4779        assert!(err_i8_high.to_string().contains("out of range for i8"));
4780        let err_i8_low = d_i8
4781            .append_default(&AvroLiteral::Int(i8::MIN as i32 - 1))
4782            .unwrap_err();
4783        assert!(err_i8_low.to_string().contains("out of range for i8"));
4784        let arr_i8 = d_i8.flush(None).unwrap();
4785        let values_i8 = arr_i8.as_any().downcast_ref::<Int8Array>().unwrap();
4786        assert_eq!(values_i8.values(), &[i8::MIN, i8::MAX]);
4787
4788        let mut d_i16 = Decoder::Int16(Vec::with_capacity(DEFAULT_CAPACITY));
4789        d_i16
4790            .append_default(&AvroLiteral::Int(i16::MIN as i32))
4791            .unwrap();
4792        d_i16
4793            .append_default(&AvroLiteral::Int(i16::MAX as i32))
4794            .unwrap();
4795        let err_i16_high = d_i16
4796            .append_default(&AvroLiteral::Int(i16::MAX as i32 + 1))
4797            .unwrap_err();
4798        assert!(err_i16_high.to_string().contains("out of range for i16"));
4799        let err_i16_low = d_i16
4800            .append_default(&AvroLiteral::Int(i16::MIN as i32 - 1))
4801            .unwrap_err();
4802        assert!(err_i16_low.to_string().contains("out of range for i16"));
4803        let arr_i16 = d_i16.flush(None).unwrap();
4804        let values_i16 = arr_i16.as_any().downcast_ref::<Int16Array>().unwrap();
4805        assert_eq!(values_i16.values(), &[i16::MIN, i16::MAX]);
4806
4807        let mut d_u8 = Decoder::UInt8(Vec::with_capacity(DEFAULT_CAPACITY));
4808        d_u8.append_default(&AvroLiteral::Int(0)).unwrap();
4809        d_u8.append_default(&AvroLiteral::Int(u8::MAX as i32))
4810            .unwrap();
4811        let err_u8_neg = d_u8.append_default(&AvroLiteral::Int(-1)).unwrap_err();
4812        assert!(err_u8_neg.to_string().contains("out of range for u8"));
4813        let err_u8_high = d_u8
4814            .append_default(&AvroLiteral::Int(u8::MAX as i32 + 1))
4815            .unwrap_err();
4816        assert!(err_u8_high.to_string().contains("out of range for u8"));
4817        let arr_u8 = d_u8.flush(None).unwrap();
4818        let values_u8 = arr_u8.as_any().downcast_ref::<UInt8Array>().unwrap();
4819        assert_eq!(values_u8.values(), &[0, u8::MAX]);
4820
4821        let mut d_u16 = Decoder::UInt16(Vec::with_capacity(DEFAULT_CAPACITY));
4822        d_u16.append_default(&AvroLiteral::Int(0)).unwrap();
4823        d_u16
4824            .append_default(&AvroLiteral::Int(u16::MAX as i32))
4825            .unwrap();
4826        let err_u16_neg = d_u16.append_default(&AvroLiteral::Int(-1)).unwrap_err();
4827        assert!(err_u16_neg.to_string().contains("out of range for u16"));
4828        let err_u16_high = d_u16
4829            .append_default(&AvroLiteral::Int(u16::MAX as i32 + 1))
4830            .unwrap_err();
4831        assert!(err_u16_high.to_string().contains("out of range for u16"));
4832        let arr_u16 = d_u16.flush(None).unwrap();
4833        let values_u16 = arr_u16.as_any().downcast_ref::<UInt16Array>().unwrap();
4834        assert_eq!(values_u16.values(), &[0, u16::MAX]);
4835
4836        let mut d_u32 = Decoder::UInt32(Vec::with_capacity(DEFAULT_CAPACITY));
4837        d_u32.append_default(&AvroLiteral::Long(0)).unwrap();
4838        d_u32
4839            .append_default(&AvroLiteral::Long(u32::MAX as i64))
4840            .unwrap();
4841        let err_u32_neg = d_u32.append_default(&AvroLiteral::Long(-1)).unwrap_err();
4842        assert!(err_u32_neg.to_string().contains("out of range for u32"));
4843        let err_u32_high = d_u32
4844            .append_default(&AvroLiteral::Long(u32::MAX as i64 + 1))
4845            .unwrap_err();
4846        assert!(err_u32_high.to_string().contains("out of range for u32"));
4847        let arr_u32 = d_u32.flush(None).unwrap();
4848        let values_u32 = arr_u32.as_any().downcast_ref::<UInt32Array>().unwrap();
4849        assert_eq!(values_u32.values(), &[0, u32::MAX]);
4850    }
4851
4852    #[cfg(feature = "avro_custom_types")]
4853    #[test]
4854    fn test_decode_custom_integer_range_validation() {
4855        let mut d_i8 = Decoder::try_new(&avro_from_codec(Codec::Int8)).unwrap();
4856        d_i8.decode(&mut AvroCursor::new(&encode_avro_int(i8::MIN as i32)))
4857            .unwrap();
4858        d_i8.decode(&mut AvroCursor::new(&encode_avro_int(i8::MAX as i32)))
4859            .unwrap();
4860        let err_i8_high = d_i8
4861            .decode(&mut AvroCursor::new(&encode_avro_int(i8::MAX as i32 + 1)))
4862            .unwrap_err();
4863        assert!(err_i8_high.to_string().contains("out of range for i8"));
4864        let err_i8_low = d_i8
4865            .decode(&mut AvroCursor::new(&encode_avro_int(i8::MIN as i32 - 1)))
4866            .unwrap_err();
4867        assert!(err_i8_low.to_string().contains("out of range for i8"));
4868        let arr_i8 = d_i8.flush(None).unwrap();
4869        let values_i8 = arr_i8.as_any().downcast_ref::<Int8Array>().unwrap();
4870        assert_eq!(values_i8.values(), &[i8::MIN, i8::MAX]);
4871
4872        let mut d_i16 = Decoder::try_new(&avro_from_codec(Codec::Int16)).unwrap();
4873        d_i16
4874            .decode(&mut AvroCursor::new(&encode_avro_int(i16::MIN as i32)))
4875            .unwrap();
4876        d_i16
4877            .decode(&mut AvroCursor::new(&encode_avro_int(i16::MAX as i32)))
4878            .unwrap();
4879        let err_i16_high = d_i16
4880            .decode(&mut AvroCursor::new(&encode_avro_int(i16::MAX as i32 + 1)))
4881            .unwrap_err();
4882        assert!(err_i16_high.to_string().contains("out of range for i16"));
4883        let err_i16_low = d_i16
4884            .decode(&mut AvroCursor::new(&encode_avro_int(i16::MIN as i32 - 1)))
4885            .unwrap_err();
4886        assert!(err_i16_low.to_string().contains("out of range for i16"));
4887        let arr_i16 = d_i16.flush(None).unwrap();
4888        let values_i16 = arr_i16.as_any().downcast_ref::<Int16Array>().unwrap();
4889        assert_eq!(values_i16.values(), &[i16::MIN, i16::MAX]);
4890
4891        let mut d_u8 = Decoder::try_new(&avro_from_codec(Codec::UInt8)).unwrap();
4892        d_u8.decode(&mut AvroCursor::new(&encode_avro_int(0)))
4893            .unwrap();
4894        d_u8.decode(&mut AvroCursor::new(&encode_avro_int(u8::MAX as i32)))
4895            .unwrap();
4896        let err_u8_neg = d_u8
4897            .decode(&mut AvroCursor::new(&encode_avro_int(-1)))
4898            .unwrap_err();
4899        assert!(err_u8_neg.to_string().contains("out of range for u8"));
4900        let err_u8_high = d_u8
4901            .decode(&mut AvroCursor::new(&encode_avro_int(u8::MAX as i32 + 1)))
4902            .unwrap_err();
4903        assert!(err_u8_high.to_string().contains("out of range for u8"));
4904        let arr_u8 = d_u8.flush(None).unwrap();
4905        let values_u8 = arr_u8.as_any().downcast_ref::<UInt8Array>().unwrap();
4906        assert_eq!(values_u8.values(), &[0, u8::MAX]);
4907
4908        let mut d_u16 = Decoder::try_new(&avro_from_codec(Codec::UInt16)).unwrap();
4909        d_u16
4910            .decode(&mut AvroCursor::new(&encode_avro_int(0)))
4911            .unwrap();
4912        d_u16
4913            .decode(&mut AvroCursor::new(&encode_avro_int(u16::MAX as i32)))
4914            .unwrap();
4915        let err_u16_neg = d_u16
4916            .decode(&mut AvroCursor::new(&encode_avro_int(-1)))
4917            .unwrap_err();
4918        assert!(err_u16_neg.to_string().contains("out of range for u16"));
4919        let err_u16_high = d_u16
4920            .decode(&mut AvroCursor::new(&encode_avro_int(u16::MAX as i32 + 1)))
4921            .unwrap_err();
4922        assert!(err_u16_high.to_string().contains("out of range for u16"));
4923        let arr_u16 = d_u16.flush(None).unwrap();
4924        let values_u16 = arr_u16.as_any().downcast_ref::<UInt16Array>().unwrap();
4925        assert_eq!(values_u16.values(), &[0, u16::MAX]);
4926
4927        let mut d_u32 = Decoder::try_new(&avro_from_codec(Codec::UInt32)).unwrap();
4928        d_u32
4929            .decode(&mut AvroCursor::new(&encode_avro_long(0)))
4930            .unwrap();
4931        d_u32
4932            .decode(&mut AvroCursor::new(&encode_avro_long(u32::MAX as i64)))
4933            .unwrap();
4934        let err_u32_neg = d_u32
4935            .decode(&mut AvroCursor::new(&encode_avro_long(-1)))
4936            .unwrap_err();
4937        assert!(err_u32_neg.to_string().contains("out of range for u32"));
4938        let err_u32_high = d_u32
4939            .decode(&mut AvroCursor::new(&encode_avro_long(u32::MAX as i64 + 1)))
4940            .unwrap_err();
4941        assert!(err_u32_high.to_string().contains("out of range for u32"));
4942        let arr_u32 = d_u32.flush(None).unwrap();
4943        let values_u32 = arr_u32.as_any().downcast_ref::<UInt32Array>().unwrap();
4944        assert_eq!(values_u32.values(), &[0, u32::MAX]);
4945    }
4946
4947    #[test]
4948    fn test_default_append_int32_and_int64_from_int_and_long() {
4949        let mut d_i32 = Decoder::Int32(Vec::with_capacity(DEFAULT_CAPACITY));
4950        d_i32.append_default(&AvroLiteral::Int(42)).unwrap();
4951        let arr = d_i32.flush(None).unwrap();
4952        let a = arr.as_any().downcast_ref::<Int32Array>().unwrap();
4953        assert_eq!(a.len(), 1);
4954        assert_eq!(a.value(0), 42);
4955        let mut d_i64 = Decoder::Int64(Vec::with_capacity(DEFAULT_CAPACITY));
4956        d_i64.append_default(&AvroLiteral::Int(5)).unwrap();
4957        d_i64.append_default(&AvroLiteral::Long(7)).unwrap();
4958        let arr64 = d_i64.flush(None).unwrap();
4959        let a64 = arr64.as_any().downcast_ref::<Int64Array>().unwrap();
4960        assert_eq!(a64.len(), 2);
4961        assert_eq!(a64.value(0), 5);
4962        assert_eq!(a64.value(1), 7);
4963    }
4964
4965    #[test]
4966    fn test_default_append_floats_and_doubles() {
4967        let mut d_f32 = Decoder::Float32(Vec::with_capacity(DEFAULT_CAPACITY));
4968        d_f32.append_default(&AvroLiteral::Float(1.5)).unwrap();
4969        let arr32 = d_f32.flush(None).unwrap();
4970        let a = arr32.as_any().downcast_ref::<Float32Array>().unwrap();
4971        assert_eq!(a.value(0), 1.5);
4972        let mut d_f64 = Decoder::Float64(Vec::with_capacity(DEFAULT_CAPACITY));
4973        d_f64.append_default(&AvroLiteral::Double(2.25)).unwrap();
4974        let arr64 = d_f64.flush(None).unwrap();
4975        let b = arr64.as_any().downcast_ref::<Float64Array>().unwrap();
4976        assert_eq!(b.value(0), 2.25);
4977    }
4978
4979    #[test]
4980    fn test_default_append_string_and_bytes() {
4981        let mut d_str = Decoder::String(
4982            OffsetBufferBuilder::new(DEFAULT_CAPACITY),
4983            Vec::with_capacity(DEFAULT_CAPACITY),
4984        );
4985        d_str
4986            .append_default(&AvroLiteral::String("hi".into()))
4987            .unwrap();
4988        let s_arr = d_str.flush(None).unwrap();
4989        let arr = s_arr.as_any().downcast_ref::<StringArray>().unwrap();
4990        assert_eq!(arr.value(0), "hi");
4991        let mut d_bytes = Decoder::Binary(
4992            OffsetBufferBuilder::new(DEFAULT_CAPACITY),
4993            Vec::with_capacity(DEFAULT_CAPACITY),
4994        );
4995        d_bytes
4996            .append_default(&AvroLiteral::Bytes(vec![1, 2, 3]))
4997            .unwrap();
4998        let b_arr = d_bytes.flush(None).unwrap();
4999        let barr = b_arr.as_any().downcast_ref::<BinaryArray>().unwrap();
5000        assert_eq!(barr.value(0), &[1, 2, 3]);
5001        let mut d_str_err = Decoder::String(
5002            OffsetBufferBuilder::new(DEFAULT_CAPACITY),
5003            Vec::with_capacity(DEFAULT_CAPACITY),
5004        );
5005        let err = d_str_err
5006            .append_default(&AvroLiteral::Bytes(vec![0x61, 0x62]))
5007            .unwrap_err();
5008        assert!(
5009            err.to_string()
5010                .contains("Default for string must be string"),
5011            "unexpected error: {err:?}"
5012        );
5013    }
5014
5015    #[test]
5016    fn test_nullable_null_runs_defer_validity_and_values_together() {
5017        let mut decoder = Decoder::Nullable(NullableDecoder::new(
5018            NullablePlan::ReadTag {
5019                nullability: Nullability::NullFirst,
5020                resolution: ResolutionPlan::Promotion(Promotion::Direct),
5021            },
5022            Decoder::Int32(Vec::new()),
5023        ));
5024
5025        decoder.append_nulls(64).unwrap();
5026        let Decoder::Nullable(nullable) = &decoder else {
5027            unreachable!();
5028        };
5029        assert_eq!(nullable.pending_nulls, 64);
5030        assert_eq!(nullable.validity.len(), 0);
5031        let Decoder::Int32(values) = nullable.values.as_ref() else {
5032            unreachable!();
5033        };
5034        assert!(values.is_empty());
5035
5036        decoder.append_default(&AvroLiteral::Int(7)).unwrap();
5037        let Decoder::Nullable(nullable) = &decoder else {
5038            unreachable!();
5039        };
5040        assert_eq!(nullable.pending_nulls, 0);
5041        assert_eq!(nullable.validity.len(), 65);
5042        let Decoder::Int32(values) = nullable.values.as_ref() else {
5043            unreachable!();
5044        };
5045        assert_eq!(values.len(), 65);
5046        assert_eq!(values[64], 7);
5047
5048        decoder.append_nulls(32).unwrap();
5049        let values = decoder.flush(None).unwrap();
5050        assert_eq!(values.len(), 97);
5051        assert_eq!(values.null_count(), 96);
5052        assert_eq!(values.as_primitive::<Int32Type>().value(64), 7);
5053    }
5054
5055    #[test]
5056    fn test_default_append_nullable_int32_null_and_value() {
5057        let inner = Decoder::Int32(Vec::with_capacity(DEFAULT_CAPACITY));
5058        let mut dec = Decoder::Nullable(NullableDecoder::new(
5059            NullablePlan::ReadTag {
5060                nullability: Nullability::NullFirst,
5061                resolution: ResolutionPlan::Promotion(Promotion::Direct),
5062            },
5063            inner,
5064        ));
5065        dec.append_default(&AvroLiteral::Null).unwrap();
5066        dec.append_default(&AvroLiteral::Null).unwrap();
5067        dec.append_default(&AvroLiteral::Int(11)).unwrap();
5068        dec.append_default(&AvroLiteral::Null).unwrap();
5069        dec.append_default(&AvroLiteral::Null).unwrap();
5070        dec.append_default(&AvroLiteral::Int(12)).unwrap();
5071        let arr = dec.flush(None).unwrap();
5072        let a = arr.as_any().downcast_ref::<Int32Array>().unwrap();
5073        assert_eq!(a.len(), 6);
5074        assert!(a.is_null(0));
5075        assert!(a.is_null(1));
5076        assert_eq!(a.value(2), 11);
5077        assert!(a.is_null(3));
5078        assert!(a.is_null(4));
5079        assert_eq!(a.value(5), 12);
5080    }
5081
5082    #[test]
5083    fn test_default_append_array_of_ints() {
5084        let list_dt = avro_from_codec(Codec::List(Arc::new(avro_from_codec(Codec::Int32))));
5085        let mut d = Decoder::try_new(&list_dt).unwrap();
5086        let items = vec![
5087            AvroLiteral::Int(1),
5088            AvroLiteral::Int(2),
5089            AvroLiteral::Int(3),
5090        ];
5091        d.append_default(&AvroLiteral::Array(items)).unwrap();
5092        let arr = d.flush(None).unwrap();
5093        let list = arr.as_any().downcast_ref::<ListArray>().unwrap();
5094        assert_eq!(list.len(), 1);
5095        assert_eq!(list.value_length(0), 3);
5096        let vals = list.values().as_any().downcast_ref::<Int32Array>().unwrap();
5097        assert_eq!(vals.values(), &[1, 2, 3]);
5098    }
5099
5100    #[test]
5101    fn test_default_append_map_string_to_int() {
5102        let map_dt = avro_from_codec(Codec::Map(Arc::new(avro_from_codec(Codec::Int32))));
5103        let mut d = Decoder::try_new(&map_dt).unwrap();
5104        let mut m: IndexMap<String, AvroLiteral> = IndexMap::new();
5105        m.insert("k1".to_string(), AvroLiteral::Int(10));
5106        m.insert("k2".to_string(), AvroLiteral::Int(20));
5107        d.append_default(&AvroLiteral::Map(m)).unwrap();
5108        let arr = d.flush(None).unwrap();
5109        let map = arr.as_any().downcast_ref::<MapArray>().unwrap();
5110        assert_eq!(map.len(), 1);
5111        assert_eq!(map.value_length(0), 2);
5112        let binding = map.value(0);
5113        let entries = binding.as_any().downcast_ref::<StructArray>().unwrap();
5114        let k = entries
5115            .column_by_name("key")
5116            .unwrap()
5117            .as_any()
5118            .downcast_ref::<StringArray>()
5119            .unwrap();
5120        let v = entries
5121            .column_by_name("value")
5122            .unwrap()
5123            .as_any()
5124            .downcast_ref::<Int32Array>()
5125            .unwrap();
5126        let keys: std::collections::HashSet<&str> = (0..k.len()).map(|i| k.value(i)).collect();
5127        assert_eq!(keys, ["k1", "k2"].into_iter().collect());
5128        let vals: std::collections::HashSet<i32> = (0..v.len()).map(|i| v.value(i)).collect();
5129        assert_eq!(vals, [10, 20].into_iter().collect());
5130    }
5131
5132    #[test]
5133    fn test_default_append_enum_by_symbol() {
5134        let symbols: Arc<[String]> = vec!["A".into(), "B".into(), "C".into()].into();
5135        let mut d = Decoder::Enum(Vec::with_capacity(DEFAULT_CAPACITY), symbols.clone(), None);
5136        d.append_default(&AvroLiteral::Enum("B".into())).unwrap();
5137        let arr = d.flush(None).unwrap();
5138        let dict = arr
5139            .as_any()
5140            .downcast_ref::<DictionaryArray<Int32Type>>()
5141            .unwrap();
5142        assert_eq!(dict.len(), 1);
5143        let expected = Int32Array::from(vec![1]);
5144        assert_eq!(dict.keys(), &expected);
5145        let values = dict
5146            .values()
5147            .as_any()
5148            .downcast_ref::<StringArray>()
5149            .unwrap();
5150        assert_eq!(values.value(1), "B");
5151    }
5152
5153    #[test]
5154    fn test_default_append_uuid_and_type_error() {
5155        let mut d = Decoder::Uuid(Vec::with_capacity(DEFAULT_CAPACITY));
5156        let uuid_str = "123e4567-e89b-12d3-a456-426614174000";
5157        d.append_default(&AvroLiteral::String(uuid_str.into()))
5158            .unwrap();
5159        let arr_ref = d.flush(None).unwrap();
5160        let arr = arr_ref
5161            .as_any()
5162            .downcast_ref::<FixedSizeBinaryArray>()
5163            .unwrap();
5164        assert_eq!(arr.value_length(), 16);
5165        assert_eq!(arr.len(), 1);
5166        let mut d2 = Decoder::Uuid(Vec::with_capacity(DEFAULT_CAPACITY));
5167        let err = d2
5168            .append_default(&AvroLiteral::Bytes(vec![0u8; 16]))
5169            .unwrap_err();
5170        assert!(
5171            err.to_string().contains("Default for uuid must be string"),
5172            "unexpected error: {err:?}"
5173        );
5174    }
5175
5176    #[test]
5177    fn test_default_append_fixed_and_length_mismatch() {
5178        let mut d = Decoder::Fixed(4, Vec::with_capacity(DEFAULT_CAPACITY));
5179        d.append_default(&AvroLiteral::Bytes(vec![1, 2, 3, 4]))
5180            .unwrap();
5181        let arr_ref = d.flush(None).unwrap();
5182        let arr = arr_ref
5183            .as_any()
5184            .downcast_ref::<FixedSizeBinaryArray>()
5185            .unwrap();
5186        assert_eq!(arr.value_length(), 4);
5187        assert_eq!(arr.value(0), &[1, 2, 3, 4]);
5188        let mut d_err = Decoder::Fixed(4, Vec::with_capacity(DEFAULT_CAPACITY));
5189        let err = d_err
5190            .append_default(&AvroLiteral::Bytes(vec![1, 2, 3]))
5191            .unwrap_err();
5192        assert!(
5193            err.to_string().contains("Fixed default length"),
5194            "unexpected error: {err:?}"
5195        );
5196    }
5197
5198    #[test]
5199    fn test_default_append_duration_and_length_validation() {
5200        let dt = avro_from_codec(Codec::Interval);
5201        let mut d = Decoder::try_new(&dt).unwrap();
5202        let mut bytes = Vec::with_capacity(12);
5203        bytes.extend_from_slice(&1u32.to_le_bytes());
5204        bytes.extend_from_slice(&2u32.to_le_bytes());
5205        bytes.extend_from_slice(&3u32.to_le_bytes());
5206        d.append_default(&AvroLiteral::Bytes(bytes)).unwrap();
5207        let arr_ref = d.flush(None).unwrap();
5208        let arr = arr_ref
5209            .as_any()
5210            .downcast_ref::<IntervalMonthDayNanoArray>()
5211            .unwrap();
5212        assert_eq!(arr.len(), 1);
5213        let v = arr.value(0);
5214        assert_eq!(v.months, 1);
5215        assert_eq!(v.days, 2);
5216        assert_eq!(v.nanoseconds, 3_000_000);
5217        let mut d_err = Decoder::try_new(&avro_from_codec(Codec::Interval)).unwrap();
5218        let err = d_err
5219            .append_default(&AvroLiteral::Bytes(vec![0u8; 11]))
5220            .unwrap_err();
5221        assert!(
5222            err.to_string()
5223                .contains("Duration default must be exactly 12 bytes"),
5224            "unexpected error: {err:?}"
5225        );
5226    }
5227
5228    #[test]
5229    fn test_default_append_decimal256_from_bytes() {
5230        let dt = avro_from_codec(Codec::Decimal(50, Some(2), Some(32)));
5231        let mut d = Decoder::try_new(&dt).unwrap();
5232        let pos: [u8; 32] = [
5233            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5234            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5235            0x00, 0x00, 0x30, 0x39,
5236        ];
5237        d.append_default(&AvroLiteral::Bytes(pos.to_vec())).unwrap();
5238        let neg: [u8; 32] = [
5239            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5240            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
5241            0xFF, 0xFF, 0xFF, 0x85,
5242        ];
5243        d.append_default(&AvroLiteral::Bytes(neg.to_vec())).unwrap();
5244        let arr = d.flush(None).unwrap();
5245        let dec = arr.as_any().downcast_ref::<Decimal256Array>().unwrap();
5246        assert_eq!(dec.len(), 2);
5247        assert_eq!(dec.value_as_string(0), "123.45");
5248        assert_eq!(dec.value_as_string(1), "-1.23");
5249    }
5250
5251    #[test]
5252    fn test_record_append_default_map_missing_fields_uses_projector_field_defaults() {
5253        let field_defaults = vec![None, Some(AvroLiteral::String("hi".into()))];
5254        let mut rec = make_record_decoder_with_projector_defaults(
5255            &[("a", DataType::Int32, false), ("b", DataType::Utf8, false)],
5256            field_defaults,
5257            vec![],
5258        );
5259        let mut map: IndexMap<String, AvroLiteral> = IndexMap::new();
5260        map.insert("a".to_string(), AvroLiteral::Int(7));
5261        rec.append_default(&AvroLiteral::Map(map)).unwrap();
5262        let arr = rec.flush(None).unwrap();
5263        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
5264        let a = s
5265            .column_by_name("a")
5266            .unwrap()
5267            .as_any()
5268            .downcast_ref::<Int32Array>()
5269            .unwrap();
5270        let b = s
5271            .column_by_name("b")
5272            .unwrap()
5273            .as_any()
5274            .downcast_ref::<StringArray>()
5275            .unwrap();
5276        assert_eq!(a.value(0), 7);
5277        assert_eq!(b.value(0), "hi");
5278    }
5279
5280    #[test]
5281    fn test_record_append_default_null_uses_projector_field_defaults() {
5282        let field_defaults = vec![
5283            Some(AvroLiteral::Int(5)),
5284            Some(AvroLiteral::String("x".into())),
5285        ];
5286        let mut rec = make_record_decoder_with_projector_defaults(
5287            &[("a", DataType::Int32, false), ("b", DataType::Utf8, false)],
5288            field_defaults,
5289            vec![],
5290        );
5291        rec.append_default(&AvroLiteral::Null).unwrap();
5292        let arr = rec.flush(None).unwrap();
5293        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
5294        let a = s
5295            .column_by_name("a")
5296            .unwrap()
5297            .as_any()
5298            .downcast_ref::<Int32Array>()
5299            .unwrap();
5300        let b = s
5301            .column_by_name("b")
5302            .unwrap()
5303            .as_any()
5304            .downcast_ref::<StringArray>()
5305            .unwrap();
5306        assert_eq!(a.value(0), 5);
5307        assert_eq!(b.value(0), "x");
5308    }
5309
5310    #[test]
5311    fn test_record_append_default_missing_fields_without_projector_defaults_yields_type_nulls_or_empties()
5312     {
5313        let fields = vec![("a", DataType::Int32, true), ("b", DataType::Utf8, true)];
5314        let mut field_refs: Vec<FieldRef> = Vec::new();
5315        let mut encoders: Vec<Decoder> = Vec::new();
5316        for (name, dt, nullable) in &fields {
5317            field_refs.push(Arc::new(ArrowField::new(*name, dt.clone(), *nullable)));
5318        }
5319        let enc_a = Decoder::Nullable(NullableDecoder::new(
5320            NullablePlan::ReadTag {
5321                nullability: Nullability::NullSecond,
5322                resolution: ResolutionPlan::Promotion(Promotion::Direct),
5323            },
5324            Decoder::Int32(Vec::with_capacity(DEFAULT_CAPACITY)),
5325        ));
5326        let enc_b = Decoder::Nullable(NullableDecoder::new(
5327            NullablePlan::ReadTag {
5328                nullability: Nullability::NullSecond,
5329                resolution: ResolutionPlan::Promotion(Promotion::Direct),
5330            },
5331            Decoder::String(
5332                OffsetBufferBuilder::new(DEFAULT_CAPACITY),
5333                Vec::with_capacity(DEFAULT_CAPACITY),
5334            ),
5335        ));
5336        encoders.push(enc_a);
5337        encoders.push(enc_b);
5338        let field_defaults = vec![None, None]; // no defaults -> append_null
5339        let projector = Projector {
5340            writer_projections: vec![],
5341            default_injections: Arc::from(Vec::<(usize, AvroLiteral)>::new()),
5342        };
5343        let mut rec = Decoder::Record {
5344            fields: field_refs.into(),
5345            decoders: encoders,
5346            defaults: field_defaults,
5347            projector: Some(projector),
5348            row_count: 0,
5349        };
5350        let mut map: IndexMap<String, AvroLiteral> = IndexMap::new();
5351        map.insert("a".to_string(), AvroLiteral::Int(9));
5352        rec.append_default(&AvroLiteral::Map(map)).unwrap();
5353        let arr = rec.flush(None).unwrap();
5354        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
5355        let a = s
5356            .column_by_name("a")
5357            .unwrap()
5358            .as_any()
5359            .downcast_ref::<Int32Array>()
5360            .unwrap();
5361        let b = s
5362            .column_by_name("b")
5363            .unwrap()
5364            .as_any()
5365            .downcast_ref::<StringArray>()
5366            .unwrap();
5367        assert!(a.is_valid(0));
5368        assert_eq!(a.value(0), 9);
5369        assert!(b.is_null(0));
5370    }
5371
5372    #[test]
5373    fn test_projector_default_injection_when_writer_lacks_fields() {
5374        let defaults = vec![None, None];
5375        let injections = vec![
5376            (0, AvroLiteral::Int(99)),
5377            (1, AvroLiteral::String("alice".into())),
5378        ];
5379        let mut rec = make_record_decoder_with_projector_defaults(
5380            &[
5381                ("id", DataType::Int32, false),
5382                ("name", DataType::Utf8, false),
5383            ],
5384            defaults,
5385            injections,
5386        );
5387        rec.decode(&mut AvroCursor::new(&[])).unwrap();
5388        let arr = rec.flush(None).unwrap();
5389        let s = arr.as_any().downcast_ref::<StructArray>().unwrap();
5390        let id = s
5391            .column_by_name("id")
5392            .unwrap()
5393            .as_any()
5394            .downcast_ref::<Int32Array>()
5395            .unwrap();
5396        let name = s
5397            .column_by_name("name")
5398            .unwrap()
5399            .as_any()
5400            .downcast_ref::<StringArray>()
5401            .unwrap();
5402        assert_eq!(id.value(0), 99);
5403        assert_eq!(name.value(0), "alice");
5404    }
5405
5406    #[test]
5407    fn union_type_ids_are_not_child_indexes() {
5408        let encodings: Vec<AvroDataType> =
5409            vec![avro_from_codec(Codec::Int32), avro_from_codec(Codec::Utf8)];
5410        let fields: UnionFields = [
5411            (42_i8, Arc::new(ArrowField::new("a", DataType::Int32, true))),
5412            (7_i8, Arc::new(ArrowField::new("b", DataType::Utf8, true))),
5413        ]
5414        .into_iter()
5415        .collect();
5416        let dt = avro_from_codec(Codec::Union(
5417            encodings.into(),
5418            fields.clone(),
5419            UnionMode::Dense,
5420        ));
5421        let mut dec = Decoder::try_new(&dt).expect("decoder");
5422        let mut b1 = encode_avro_long(1);
5423        b1.extend(encode_avro_bytes(b"hi"));
5424        dec.decode(&mut AvroCursor::new(&b1)).expect("decode b1");
5425        let mut b0 = encode_avro_long(0);
5426        b0.extend(encode_avro_int(5));
5427        dec.decode(&mut AvroCursor::new(&b0)).expect("decode b0");
5428        let arr = dec.flush(None).expect("flush");
5429        let ua = arr.as_any().downcast_ref::<UnionArray>().expect("union");
5430        assert_eq!(ua.len(), 2);
5431        assert_eq!(ua.type_id(0), 7, "type id must come from UnionFields");
5432        assert_eq!(ua.type_id(1), 42, "type id must come from UnionFields");
5433        assert_eq!(ua.value_offset(0), 0);
5434        assert_eq!(ua.value_offset(1), 0);
5435        let utf8_child = ua.child(7).as_any().downcast_ref::<StringArray>().unwrap();
5436        assert_eq!(utf8_child.len(), 1);
5437        assert_eq!(utf8_child.value(0), "hi");
5438        let int_child = ua.child(42).as_any().downcast_ref::<Int32Array>().unwrap();
5439        assert_eq!(int_child.len(), 1);
5440        assert_eq!(int_child.value(0), 5);
5441        let type_ids: Vec<i8> = fields.iter().map(|(tid, _)| tid).collect();
5442        assert_eq!(type_ids, vec![42_i8, 7_i8]);
5443    }
5444
5445    #[cfg(feature = "avro_custom_types")]
5446    #[test]
5447    fn skipper_from_avro_maps_custom_duration_variants_to_int64() -> Result<(), AvroError> {
5448        for codec in [
5449            Codec::DurationNanos,
5450            Codec::DurationMicros,
5451            Codec::DurationMillis,
5452            Codec::DurationSeconds,
5453        ] {
5454            let dt = make_avro_dt(codec.clone(), None);
5455            let s = Skipper::from_avro(&dt)?;
5456            match s {
5457                Skipper::Int64 => {}
5458                other => panic!("expected Int64 skipper for {codec:?}, got {other:?}"),
5459            }
5460        }
5461        Ok(())
5462    }
5463
5464    #[cfg(feature = "avro_custom_types")]
5465    #[test]
5466    fn skipper_skip_consumes_one_long_for_custom_durations() -> Result<(), AvroError> {
5467        let values: [i64; 7] = [0, 1, -1, 150, -150, i64::MAX / 3, i64::MIN / 3];
5468        for codec in [
5469            Codec::DurationNanos,
5470            Codec::DurationMicros,
5471            Codec::DurationMillis,
5472            Codec::DurationSeconds,
5473        ] {
5474            let dt = make_avro_dt(codec.clone(), None);
5475            let s = Skipper::from_avro(&dt)?;
5476            for &v in &values {
5477                let bytes = encode_avro_long(v);
5478                let mut cursor = AvroCursor::new(&bytes);
5479                s.skip(&mut cursor)?;
5480                assert_eq!(
5481                    cursor.position(),
5482                    bytes.len(),
5483                    "did not consume all bytes for {codec:?} value {v}"
5484                );
5485            }
5486        }
5487        Ok(())
5488    }
5489
5490    #[cfg(feature = "avro_custom_types")]
5491    #[test]
5492    fn skipper_nullable_custom_duration_respects_null_first() -> Result<(), AvroError> {
5493        let dt = make_avro_dt(Codec::DurationNanos, Some(Nullability::NullFirst));
5494        let s = Skipper::from_avro(&dt)?;
5495        match &s {
5496            Skipper::Nullable(Nullability::NullFirst, inner) => match **inner {
5497                Skipper::Int64 => {}
5498                ref other => panic!("expected inner Int64, got {other:?}"),
5499            },
5500            other => panic!("expected Nullable(NullFirst, Int64), got {other:?}"),
5501        }
5502        {
5503            let buf = encode_vlq_u64(0);
5504            let mut cursor = AvroCursor::new(&buf);
5505            s.skip(&mut cursor)?;
5506            assert_eq!(cursor.position(), 1, "expected to consume only tag=0");
5507        }
5508        {
5509            let mut buf = encode_vlq_u64(1);
5510            buf.extend(encode_avro_long(0));
5511            let mut cursor = AvroCursor::new(&buf);
5512            s.skip(&mut cursor)?;
5513            assert_eq!(cursor.position(), 2, "expected to consume tag=1 + long(0)");
5514        }
5515
5516        Ok(())
5517    }
5518
5519    #[cfg(feature = "avro_custom_types")]
5520    #[test]
5521    fn skipper_nullable_custom_duration_respects_null_second() -> Result<(), AvroError> {
5522        let dt = make_avro_dt(Codec::DurationMicros, Some(Nullability::NullSecond));
5523        let s = Skipper::from_avro(&dt)?;
5524        match &s {
5525            Skipper::Nullable(Nullability::NullSecond, inner) => match **inner {
5526                Skipper::Int64 => {}
5527                ref other => panic!("expected inner Int64, got {other:?}"),
5528            },
5529            other => panic!("expected Nullable(NullSecond, Int64), got {other:?}"),
5530        }
5531        {
5532            let buf = encode_vlq_u64(1);
5533            let mut cursor = AvroCursor::new(&buf);
5534            s.skip(&mut cursor)?;
5535            assert_eq!(cursor.position(), 1, "expected to consume only tag=1");
5536        }
5537        {
5538            let mut buf = encode_vlq_u64(0);
5539            buf.extend(encode_avro_long(-1));
5540            let mut cursor = AvroCursor::new(&buf);
5541            s.skip(&mut cursor)?;
5542            assert_eq!(
5543                cursor.position(),
5544                1 + encode_avro_long(-1).len(),
5545                "expected to consume tag=0 + long(-1)"
5546            );
5547        }
5548        Ok(())
5549    }
5550
5551    #[test]
5552    fn skipper_interval_is_fixed12_and_skips_12_bytes() -> Result<(), AvroError> {
5553        let dt = make_avro_dt(Codec::Interval, None);
5554        let s = Skipper::from_avro(&dt)?;
5555        match s {
5556            Skipper::DurationFixed12 => {}
5557            other => panic!("expected DurationFixed12, got {other:?}"),
5558        }
5559        let payload = vec![0u8; 12];
5560        let mut cursor = AvroCursor::new(&payload);
5561        s.skip(&mut cursor)?;
5562        assert_eq!(cursor.position(), 12, "expected to consume 12 fixed bytes");
5563        Ok(())
5564    }
5565
5566    #[cfg(feature = "avro_custom_types")]
5567    #[test]
5568    fn test_run_end_encoded_width16_int32_basic_grouping() {
5569        use arrow_array::RunArray;
5570        use std::sync::Arc;
5571        let inner = avro_from_codec(Codec::Int32);
5572        let ree = AvroDataType::new(
5573            Codec::RunEndEncoded(Arc::new(inner), 16),
5574            Default::default(),
5575            None,
5576        );
5577        let mut dec = Decoder::try_new(&ree).expect("create REE decoder");
5578        for v in [1, 1, 1, 2, 2, 3, 3, 3, 3] {
5579            let bytes = encode_avro_int(v);
5580            dec.decode(&mut AvroCursor::new(&bytes)).expect("decode");
5581        }
5582        let arr = dec.flush(None).expect("flush");
5583        let ra = arr
5584            .as_any()
5585            .downcast_ref::<RunArray<Int16Type>>()
5586            .expect("RunArray<Int16Type>");
5587        assert_eq!(ra.len(), 9);
5588        assert_eq!(ra.run_ends().values(), &[3, 5, 9]);
5589        let vals = ra
5590            .values()
5591            .as_ref()
5592            .as_any()
5593            .downcast_ref::<Int32Array>()
5594            .expect("values Int32");
5595        assert_eq!(vals.values(), &[1, 2, 3]);
5596    }
5597
5598    #[cfg(feature = "avro_custom_types")]
5599    #[test]
5600    fn test_run_end_encoded_width32_nullable_values_group_nulls() {
5601        use arrow_array::RunArray;
5602        use std::sync::Arc;
5603        let inner = AvroDataType::new(
5604            Codec::Int32,
5605            Default::default(),
5606            Some(Nullability::NullSecond),
5607        );
5608        let ree = AvroDataType::new(
5609            Codec::RunEndEncoded(Arc::new(inner), 32),
5610            Default::default(),
5611            None,
5612        );
5613        let mut dec = Decoder::try_new(&ree).expect("create REE decoder");
5614        let seq: [Option<i32>; 8] = [
5615            None,
5616            None,
5617            Some(7),
5618            Some(7),
5619            Some(7),
5620            None,
5621            Some(5),
5622            Some(5),
5623        ];
5624        for item in seq {
5625            let mut bytes = Vec::new();
5626            match item {
5627                None => bytes.extend_from_slice(&encode_vlq_u64(1)),
5628                Some(v) => {
5629                    bytes.extend_from_slice(&encode_vlq_u64(0));
5630                    bytes.extend_from_slice(&encode_avro_int(v));
5631                }
5632            }
5633            dec.decode(&mut AvroCursor::new(&bytes)).expect("decode");
5634        }
5635        let arr = dec.flush(None).expect("flush");
5636        let ra = arr
5637            .as_any()
5638            .downcast_ref::<RunArray<Int32Type>>()
5639            .expect("RunArray<Int32Type>");
5640        assert_eq!(ra.len(), 8);
5641        assert_eq!(ra.run_ends().values(), &[2, 5, 6, 8]);
5642        let vals = ra
5643            .values()
5644            .as_ref()
5645            .as_any()
5646            .downcast_ref::<Int32Array>()
5647            .expect("values Int32 (nullable)");
5648        assert_eq!(vals.len(), 4);
5649        assert!(vals.is_null(0));
5650        assert_eq!(vals.value(1), 7);
5651        assert!(vals.is_null(2));
5652        assert_eq!(vals.value(3), 5);
5653    }
5654
5655    #[cfg(feature = "avro_custom_types")]
5656    #[test]
5657    fn test_run_end_encoded_decode_with_promotion_int_to_double_via_nullable_from_single() {
5658        use arrow_array::RunArray;
5659        let inner_values = Decoder::Float64(Vec::with_capacity(DEFAULT_CAPACITY));
5660        let ree = Decoder::RunEndEncoded(
5661            8, /* bytes => Int64 run-ends */
5662            0,
5663            Box::new(inner_values),
5664        );
5665        let mut dec = Decoder::Nullable(NullableDecoder::new(
5666            NullablePlan::FromSingle {
5667                resolution: ResolutionPlan::Promotion(Promotion::IntToDouble),
5668            },
5669            ree,
5670        ));
5671        for v in [1, 1, 2, 2, 2] {
5672            let bytes = encode_avro_int(v);
5673            dec.decode(&mut AvroCursor::new(&bytes)).expect("decode");
5674        }
5675        let arr = dec.flush(None).expect("flush");
5676        let ra = arr
5677            .as_any()
5678            .downcast_ref::<RunArray<Int64Type>>()
5679            .expect("RunArray<Int64Type>");
5680        assert_eq!(ra.len(), 5);
5681        assert_eq!(ra.run_ends().values(), &[2, 5]);
5682        let vals = ra
5683            .values()
5684            .as_ref()
5685            .as_any()
5686            .downcast_ref::<Float64Array>()
5687            .expect("values Float64");
5688        assert_eq!(vals.values(), &[1.0, 2.0]);
5689    }
5690
5691    #[cfg(feature = "avro_custom_types")]
5692    #[test]
5693    fn test_run_end_encoded_unsupported_run_end_width_errors() {
5694        use std::sync::Arc;
5695        let inner = avro_from_codec(Codec::Int32);
5696        let dt = AvroDataType::new(
5697            Codec::RunEndEncoded(Arc::new(inner), 3),
5698            Default::default(),
5699            None,
5700        );
5701        let err = Decoder::try_new(&dt).expect_err("must reject unsupported width");
5702        let msg = err.to_string();
5703        assert!(
5704            msg.contains("Unsupported run-end width")
5705                && msg.contains("16/32/64 bits or 2/4/8 bytes"),
5706            "unexpected error message: {msg}"
5707        );
5708    }
5709
5710    #[cfg(feature = "avro_custom_types")]
5711    #[test]
5712    fn test_run_end_encoded_empty_input_is_empty_runarray() {
5713        use arrow_array::RunArray;
5714        use std::sync::Arc;
5715        let inner = avro_from_codec(Codec::Utf8);
5716        let dt = AvroDataType::new(
5717            Codec::RunEndEncoded(Arc::new(inner), 4),
5718            Default::default(),
5719            None,
5720        );
5721        let mut dec = Decoder::try_new(&dt).expect("create REE decoder");
5722        let arr = dec.flush(None).expect("flush");
5723        let ra = arr
5724            .as_any()
5725            .downcast_ref::<RunArray<Int32Type>>()
5726            .expect("RunArray<Int32Type>");
5727        assert_eq!(ra.len(), 0);
5728        assert_eq!(ra.run_ends().len(), 0);
5729        assert_eq!(ra.values().len(), 0);
5730    }
5731
5732    #[cfg(feature = "avro_custom_types")]
5733    #[test]
5734    fn test_run_end_encoded_strings_grouping_width32_bits() {
5735        use arrow_array::RunArray;
5736        use std::sync::Arc;
5737        let inner = avro_from_codec(Codec::Utf8);
5738        let dt = AvroDataType::new(
5739            Codec::RunEndEncoded(Arc::new(inner), 32),
5740            Default::default(),
5741            None,
5742        );
5743        let mut dec = Decoder::try_new(&dt).expect("create REE decoder");
5744        for s in ["a", "a", "bb", "bb", "bb", "a"] {
5745            let bytes = encode_avro_bytes(s.as_bytes());
5746            dec.decode(&mut AvroCursor::new(&bytes)).expect("decode");
5747        }
5748        let arr = dec.flush(None).expect("flush");
5749        let ra = arr
5750            .as_any()
5751            .downcast_ref::<RunArray<Int32Type>>()
5752            .expect("RunArray<Int32Type>");
5753        assert_eq!(ra.run_ends().values(), &[2, 5, 6]);
5754        let vals = ra
5755            .values()
5756            .as_ref()
5757            .as_any()
5758            .downcast_ref::<StringArray>()
5759            .expect("values String");
5760        assert_eq!(vals.len(), 3);
5761        assert_eq!(vals.value(0), "a");
5762        assert_eq!(vals.value(1), "bb");
5763        assert_eq!(vals.value(2), "a");
5764    }
5765
5766    #[cfg(not(feature = "avro_custom_types"))]
5767    #[test]
5768    fn test_no_custom_types_feature_smoke_decodes_plain_int32() {
5769        let dt = avro_from_codec(Codec::Int32);
5770        let mut dec = Decoder::try_new(&dt).expect("create Int32 decoder");
5771        for v in [1, 2, 3] {
5772            let bytes = encode_avro_int(v);
5773            dec.decode(&mut AvroCursor::new(&bytes)).expect("decode");
5774        }
5775        let arr = dec.flush(None).expect("flush");
5776        let a = arr
5777            .as_any()
5778            .downcast_ref::<Int32Array>()
5779            .expect("Int32Array");
5780        assert_eq!(a.values(), &[1, 2, 3]);
5781    }
5782
5783    #[test]
5784    fn test_timestamp_nanos_decoding_offset_zero() {
5785        let avro_type = avro_from_codec(Codec::TimestampNanos(Some(Tz::OffsetZero)));
5786        let mut decoder = Decoder::try_new(&avro_type).expect("create TimestampNanos decoder");
5787        let mut data = Vec::new();
5788        for v in [0_i64, 1_i64, -1_i64, 1_234_567_890_i64] {
5789            data.extend_from_slice(&encode_avro_long(v));
5790        }
5791        let mut cur = AvroCursor::new(&data);
5792        for _ in 0..4 {
5793            decoder.decode(&mut cur).expect("decode nanos ts");
5794        }
5795        let array = decoder.flush(None).expect("flush nanos ts");
5796        let ts = array
5797            .as_any()
5798            .downcast_ref::<TimestampNanosecondArray>()
5799            .expect("TimestampNanosecondArray");
5800        assert_eq!(ts.values(), &[0, 1, -1, 1_234_567_890]);
5801        match ts.data_type() {
5802            DataType::Timestamp(arrow_schema::TimeUnit::Nanosecond, tz) => {
5803                assert_eq!(tz.as_deref(), Some("+00:00"));
5804            }
5805            other => panic!("expected Timestamp(Nanosecond, Some(\"+00:00\")), got {other:?}"),
5806        }
5807    }
5808
5809    #[test]
5810    fn test_timestamp_nanos_decoding_utc() {
5811        let avro_type = avro_from_codec(Codec::TimestampNanos(Some(Tz::Utc)));
5812        let mut decoder = Decoder::try_new(&avro_type).expect("create TimestampNanos decoder");
5813        let mut data = Vec::new();
5814        for v in [0_i64, 1_i64, -1_i64, 1_234_567_890_i64] {
5815            data.extend_from_slice(&encode_avro_long(v));
5816        }
5817        let mut cur = AvroCursor::new(&data);
5818        for _ in 0..4 {
5819            decoder.decode(&mut cur).expect("decode nanos ts");
5820        }
5821        let array = decoder.flush(None).expect("flush nanos ts");
5822        let ts = array
5823            .as_any()
5824            .downcast_ref::<TimestampNanosecondArray>()
5825            .expect("TimestampNanosecondArray");
5826        assert_eq!(ts.values(), &[0, 1, -1, 1_234_567_890]);
5827        match ts.data_type() {
5828            DataType::Timestamp(arrow_schema::TimeUnit::Nanosecond, tz) => {
5829                assert_eq!(tz.as_deref(), Some("UTC"));
5830            }
5831            other => panic!("expected Timestamp(Nanosecond, Some(\"UTC\")), got {other:?}"),
5832        }
5833    }
5834
5835    #[test]
5836    fn test_timestamp_nanos_decoding_local() {
5837        let avro_type = avro_from_codec(Codec::TimestampNanos(None));
5838        let mut decoder = Decoder::try_new(&avro_type).expect("create TimestampNanos decoder");
5839        let mut data = Vec::new();
5840        for v in [10_i64, 20_i64, -30_i64] {
5841            data.extend_from_slice(&encode_avro_long(v));
5842        }
5843        let mut cur = AvroCursor::new(&data);
5844        for _ in 0..3 {
5845            decoder.decode(&mut cur).expect("decode nanos ts");
5846        }
5847        let array = decoder.flush(None).expect("flush nanos ts");
5848        let ts = array
5849            .as_any()
5850            .downcast_ref::<TimestampNanosecondArray>()
5851            .expect("TimestampNanosecondArray");
5852        assert_eq!(ts.values(), &[10, 20, -30]);
5853        match ts.data_type() {
5854            DataType::Timestamp(arrow_schema::TimeUnit::Nanosecond, tz) => {
5855                assert_eq!(tz.as_deref(), None);
5856            }
5857            other => panic!("expected Timestamp(Nanosecond, None), got {other:?}"),
5858        }
5859    }
5860
5861    #[test]
5862    fn test_timestamp_nanos_decoding_with_nulls() {
5863        let avro_type = AvroDataType::new(
5864            Codec::TimestampNanos(None),
5865            Default::default(),
5866            Some(Nullability::NullFirst),
5867        );
5868        let mut decoder = Decoder::try_new(&avro_type).expect("create nullable TimestampNanos");
5869        let mut data = Vec::new();
5870        data.extend_from_slice(&encode_avro_long(1));
5871        data.extend_from_slice(&encode_avro_long(42));
5872        data.extend_from_slice(&encode_avro_long(0));
5873        data.extend_from_slice(&encode_avro_long(1));
5874        data.extend_from_slice(&encode_avro_long(-7));
5875        let mut cur = AvroCursor::new(&data);
5876        for _ in 0..3 {
5877            decoder.decode(&mut cur).expect("decode nullable nanos ts");
5878        }
5879        let array = decoder.flush(None).expect("flush nullable nanos ts");
5880        let ts = array
5881            .as_any()
5882            .downcast_ref::<TimestampNanosecondArray>()
5883            .expect("TimestampNanosecondArray");
5884        assert_eq!(ts.len(), 3);
5885        assert!(ts.is_valid(0));
5886        assert!(ts.is_null(1));
5887        assert!(ts.is_valid(2));
5888        assert_eq!(ts.value(0), 42);
5889        assert_eq!(ts.value(2), -7);
5890        match ts.data_type() {
5891            DataType::Timestamp(arrow_schema::TimeUnit::Nanosecond, tz) => {
5892                assert_eq!(tz.as_deref(), None);
5893            }
5894            other => panic!("expected Timestamp(Nanosecond, None), got {other:?}"),
5895        }
5896    }
5897}