Skip to main content

parquet_variant/variant/
object.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
18use crate::decoder::{OffsetSizeBytes, map_bytes_to_offsets};
19use crate::utils::{
20    first_byte_from_slice, overflow_error, slice_from_slice, try_binary_search_range_by,
21};
22use crate::variant::{MAX_NESTING_DEPTH, Variant, VariantMetadata};
23
24use arrow_schema::ArrowError;
25
26// The value header occupies one byte; use a named constant for readability
27const NUM_HEADER_BYTES: u32 = 1;
28
29/// Header structure for [`VariantObject`]
30#[derive(Debug, Clone, PartialEq)]
31pub(crate) struct VariantObjectHeader {
32    num_elements_size: OffsetSizeBytes,
33    field_id_size: OffsetSizeBytes,
34    field_offset_size: OffsetSizeBytes,
35}
36
37impl VariantObjectHeader {
38    // Hide the ugly casting
39    const fn num_elements_size(&self) -> u32 {
40        self.num_elements_size as _
41    }
42    const fn field_id_size(&self) -> u32 {
43        self.field_id_size as _
44    }
45    const fn field_offset_size(&self) -> u32 {
46        self.field_offset_size as _
47    }
48
49    // Avoid materializing this offset, since it's cheaply and safely computable
50    const fn field_ids_start_byte(&self) -> u32 {
51        NUM_HEADER_BYTES + self.num_elements_size()
52    }
53
54    pub(crate) fn try_new(header_byte: u8) -> Result<Self, ArrowError> {
55        // Parse the header byte to get object parameters
56        let value_header = header_byte >> 2;
57        let field_offset_size_minus_one = value_header & 0x03; // Last 2 bits
58        let field_id_size_minus_one = (value_header >> 2) & 0x03; // Next 2 bits
59        let is_large = (value_header & 0x10) != 0; // 5th bit
60        let num_elements_size = match is_large {
61            true => OffsetSizeBytes::Four,
62            false => OffsetSizeBytes::One,
63        };
64        Ok(Self {
65            num_elements_size,
66            field_id_size: OffsetSizeBytes::try_new(field_id_size_minus_one)?,
67            field_offset_size: OffsetSizeBytes::try_new(field_offset_size_minus_one)?,
68        })
69    }
70}
71
72/// A [`Variant`] Object (struct with named fields).
73///
74/// See the [Variant spec] file for more information.
75///
76/// # Validation
77///
78/// Every instance of variant object is either _valid_ or _invalid_. depending on whether the
79/// underlying bytes are a valid encoding of a variant object subtype (see below).
80///
81/// Instances produced by [`Self::try_new`] or [`Self::with_full_validation`] are fully (and recursively)
82/// _validated_. They always contain _valid_ data, and infallible accesses such as iteration and
83/// indexing are panic-free. The validation cost is linear in the number of underlying bytes.
84///
85/// Instances produced by [`Self::new`] are _unvalidated_ and so they may contain either _valid_ or
86/// _invalid_ data. Infallible accesses such as iteration and indexing will panic if the underlying
87/// bytes are _invalid_, and fallible alternatives such as [`Self::iter_try`] and [`Self::get`] are
88/// provided as panic-free alternatives. [`Self::with_full_validation`] can also be used to _validate_ an
89/// _unvalidated_ instance, if desired.
90///
91/// _Unvalidated_ instances can be constructed in constant time. They can be useful if the caller
92/// knows the underlying bytes were already validated previously, or if the caller intends to
93/// perform a small number of (fallible) field accesses against a large object.
94///
95/// A _validated_ instance guarantees that:
96///
97/// - header byte is valid
98/// - num_elements is in bounds
99/// - field id array is in bounds
100/// - field offset array is in bounds
101/// - field value array is in bounds
102/// - all field ids are valid metadata dictionary entries (*)
103/// - field ids are lexically ordered according by their corresponding string values (*)
104/// - field names are unique; no field name appears more than once (*)
105/// - all field offsets are in bounds (*)
106/// - all field values are (recursively) _valid_ variant values (*)
107/// - the associated variant metadata is [valid] (*)
108///
109/// NOTE: [`Self::new`] only skips expensive (non-constant cost) validation checks (marked by `(*)`
110/// in the list above); it panics any of the other checks fails.
111///
112/// # Safety
113///
114/// Even an _invalid_ variant object instance is still _safe_ to use in the Rust sense. Accessing it
115/// with infallible methods may cause panics but will never lead to undefined behavior.
116///
117/// [valid]: VariantMetadata#Validation
118/// [Variant spec]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md#value-data-for-object-basic_type2
119#[derive(Debug, Clone)]
120pub struct VariantObject<'m, 'v> {
121    pub metadata: VariantMetadata<'m>,
122    pub value: &'v [u8],
123    header: VariantObjectHeader,
124    num_elements: u32,
125    first_field_offset_byte: u32,
126    first_value_byte: u32,
127    validated: bool,
128}
129
130// We don't want this to grow because it could increase the size of `Variant` and hurt performance.
131#[cfg(target_pointer_width = "64")]
132const _: () = crate::utils::expect_size_of::<VariantObject>(64);
133
134#[cfg(target_pointer_width = "32")]
135const _: () = crate::utils::expect_size_of::<VariantObject>(44);
136
137impl<'m, 'v> VariantObject<'m, 'v> {
138    /// Interprets `metadata` and `value` as a variant object, performing only basic
139    /// (constant-cost) [validation].
140    ///
141    /// # Panics
142    ///
143    /// Panics if basic validation fails. Use [`Self::try_new`] for a fallible version.
144    ///
145    /// [validation]: Self#Validation
146    pub fn new(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Self {
147        Self::try_new_with_shallow_validation(metadata, value).expect("Invalid variant object")
148    }
149
150    /// Attempts to interpet `metadata` and `value` as a variant object.
151    ///
152    /// # Validation
153    ///
154    /// This constructor verifies that `value` points to a valid variant object value. In
155    /// particular, that all field ids exist in `metadata`, and all offsets are in-bounds and point
156    /// to valid objects.
157    pub fn try_new(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Result<Self, ArrowError> {
158        Self::try_new_with_shallow_validation(metadata, value)?.with_full_validation()
159    }
160
161    /// Attempts to interpet `metadata` and `value` as a variant object, performing only basic
162    /// (constant-cost) [validation].
163    ///
164    /// [validation]: Self#Validation
165    pub(crate) fn try_new_with_shallow_validation(
166        metadata: VariantMetadata<'m>,
167        value: &'v [u8],
168    ) -> Result<Self, ArrowError> {
169        let header_byte = first_byte_from_slice(value)?;
170        let header = VariantObjectHeader::try_new(header_byte)?;
171
172        // Determine num_elements size based on is_large flag and fetch the value
173        let num_elements =
174            header
175                .num_elements_size
176                .unpack_u32_at_offset(value, NUM_HEADER_BYTES as _, 0)?;
177
178        // Calculate byte offsets for field offsets and values with overflow protection, and verify
179        // they're in bounds
180        let first_field_offset_byte = num_elements
181            .checked_mul(header.field_id_size())
182            .and_then(|n| n.checked_add(header.field_ids_start_byte()))
183            .ok_or_else(|| overflow_error("offset of variant object field offsets"))?;
184
185        let first_value_byte = num_elements
186            .checked_add(1)
187            .and_then(|n| n.checked_mul(header.field_offset_size()))
188            .and_then(|n| n.checked_add(first_field_offset_byte))
189            .ok_or_else(|| overflow_error("offset of variant object field values"))?;
190
191        let mut new_self = Self {
192            metadata,
193            value,
194            header,
195            num_elements,
196            first_field_offset_byte,
197            first_value_byte,
198            validated: false,
199        };
200
201        // Spec says: "The last field_offset points to the byte after the end of the last value"
202        //
203        // Use it to upper-bound the value bytes, which also verifies that the field id and field
204        // offset arrays are in bounds.
205        let last_offset = new_self
206            .get_offset(num_elements as _)?
207            .checked_add(first_value_byte)
208            .ok_or_else(|| overflow_error("variant object size"))?;
209        new_self.value = slice_from_slice(value, ..last_offset as _)?;
210        Ok(new_self)
211    }
212
213    /// True if this instance is fully [validated] for panic-free infallible accesses.
214    ///
215    /// [validated]: Self#Validation
216    pub fn is_fully_validated(&self) -> bool {
217        self.validated
218    }
219
220    /// Performs a full [validation] of this variant object.
221    ///
222    /// Values nested more than [`MAX_NESTING_DEPTH`] deep are rejected.
223    ///
224    /// [validation]: Self#Validation
225    pub fn with_full_validation(self) -> Result<Self, ArrowError> {
226        self.with_full_validation_at_depth(0)
227    }
228
229    // Same as [`Self::with_full_validation`], tracking how deeply validation has recursed.
230    pub(crate) fn with_full_validation_at_depth(
231        mut self,
232        depth: usize,
233    ) -> Result<Self, ArrowError> {
234        if !self.validated {
235            if depth >= MAX_NESTING_DEPTH {
236                return Err(ArrowError::InvalidArgumentError(format!(
237                    "Variant nesting depth exceeds the maximum of {MAX_NESTING_DEPTH}"
238                )));
239            }
240
241            // Validate the metadata dictionary first, if not already validated, because we pass it
242            // by value to all the children (who would otherwise re-validate it repeatedly).
243            self.metadata = self.metadata.with_full_validation()?;
244
245            let field_id_buffer = slice_from_slice(
246                self.value,
247                self.header.field_ids_start_byte() as _..self.first_field_offset_byte as _,
248            )?;
249
250            let mut field_ids_iter =
251                map_bytes_to_offsets(field_id_buffer, self.header.field_id_size);
252
253            // Validate all field ids exist in the metadata dictionary and the corresponding field names are lexicographically sorted
254            if self.metadata.is_sorted() {
255                // Since the metadata dictionary has unique and sorted field names, we can also guarantee this object's field names
256                // are lexicographically sorted by their field id ordering
257                let dictionary_size = self.metadata.len();
258
259                if let Some(mut current_id) = field_ids_iter.next() {
260                    for next_id in field_ids_iter {
261                        if current_id >= dictionary_size {
262                            return Err(ArrowError::InvalidArgumentError(
263                                "field id is not valid".to_string(),
264                            ));
265                        }
266
267                        if next_id <= current_id {
268                            return Err(ArrowError::InvalidArgumentError(
269                                "field names not sorted".to_string(),
270                            ));
271                        }
272                        current_id = next_id;
273                    }
274
275                    if current_id >= dictionary_size {
276                        return Err(ArrowError::InvalidArgumentError(
277                            "field id is not valid".to_string(),
278                        ));
279                    }
280                }
281            } else {
282                // The metadata dictionary can't guarantee uniqueness or sortedness, so we have to parse out the corresponding field names
283                // to check lexicographical order
284                //
285                // Since we are probing the metadata dictionary by field id, this also verifies field ids are in-bounds
286                let mut current_field_name = match field_ids_iter.next() {
287                    Some(field_id) => Some(self.metadata.get(field_id)?),
288                    None => None,
289                };
290
291                for field_id in field_ids_iter {
292                    let next_field_name = self.metadata.get(field_id)?;
293
294                    // Equal names are rejected as well: field names must be unique within an
295                    // object. The sorted branch above enforces this via strictly ordered ids.
296                    if let Some(current_name) = current_field_name
297                        && next_field_name <= current_name
298                    {
299                        return Err(ArrowError::InvalidArgumentError(
300                            "field names not sorted".to_string(),
301                        ));
302                    }
303                    current_field_name = Some(next_field_name);
304                }
305            }
306
307            // Validate whether values are valid variant objects
308            let field_offset_buffer = slice_from_slice(
309                self.value,
310                self.first_field_offset_byte as _..self.first_value_byte as _,
311            )?;
312            let num_offsets = field_offset_buffer.len() / self.header.field_offset_size() as usize;
313
314            let value_buffer = slice_from_slice(self.value, self.first_value_byte as _..)?;
315
316            map_bytes_to_offsets(field_offset_buffer, self.header.field_offset_size)
317                .take(num_offsets.saturating_sub(1))
318                .try_for_each(|offset| {
319                    let value_bytes = slice_from_slice(value_buffer, offset..)?;
320                    Variant::try_new_with_metadata_at_depth(
321                        self.metadata.clone(),
322                        value_bytes,
323                        depth + 1,
324                    )?;
325
326                    Ok::<_, ArrowError>(())
327                })?;
328
329            self.validated = true;
330        }
331        Ok(self)
332    }
333
334    /// Returns the number of key-value pairs in this object
335    pub fn len(&self) -> usize {
336        self.num_elements as _
337    }
338
339    /// Returns true if the object contains no key-value pairs
340    pub fn is_empty(&self) -> bool {
341        self.len() == 0
342    }
343
344    /// Get a field's value by index in `0..self.len()`
345    ///
346    /// # Panics
347    ///
348    /// If the index is out of bounds. Also if variant object is corrupted (e.g., invalid offsets or
349    /// field IDs). The latter can only happen when working with an unvalidated object produced by
350    /// [`Self::new`].
351    pub fn field(&self, i: usize) -> Option<Variant<'m, 'v>> {
352        (i < self.len()).then(|| {
353            self.try_field_with_shallow_validation(i)
354                .expect("Invalid object field value")
355        })
356    }
357
358    /// Fallible version of `field`. Returns field value by index, capturing validation errors
359    pub fn try_field(&self, i: usize) -> Result<Variant<'m, 'v>, ArrowError> {
360        self.try_field_with_shallow_validation(i)?
361            .with_full_validation()
362    }
363
364    // Attempts to retrieve the ith field value from the value region of the byte buffer; it
365    // performs only basic (constant-cost) validation.
366    fn try_field_with_shallow_validation(&self, i: usize) -> Result<Variant<'m, 'v>, ArrowError> {
367        let value_bytes = slice_from_slice(self.value, self.first_value_byte as _..)?;
368        let value_bytes = slice_from_slice(value_bytes, self.get_offset(i)? as _..)?;
369        Variant::try_new_with_metadata_and_shallow_validation(self.metadata.clone(), value_bytes)
370    }
371
372    // Attempts to retrieve the ith offset from the field offset region of the byte buffer.
373    fn get_offset(&self, i: usize) -> Result<u32, ArrowError> {
374        let byte_range = self.first_field_offset_byte as _..self.first_value_byte as _;
375        let field_offsets = slice_from_slice(self.value, byte_range)?;
376        self.header.field_offset_size.unpack_u32(field_offsets, i)
377    }
378
379    /// Get a field's name by index in `0..self.len()`
380    ///
381    /// # Panics
382    /// If the variant object is corrupted (e.g., invalid offsets or field IDs).
383    /// This should never happen since the constructor validates all data upfront.
384    pub fn field_name(&self, i: usize) -> Option<&'m str> {
385        (i < self.len()).then(|| {
386            self.try_field_name(i)
387                .expect("Invalid variant object field name")
388        })
389    }
390
391    /// Fallible version of `field_name`. Returns field name by index, capturing validation errors
392    fn try_field_name(&self, i: usize) -> Result<&'m str, ArrowError> {
393        let byte_range = self.header.field_ids_start_byte() as _..self.first_field_offset_byte as _;
394        let field_id_bytes = slice_from_slice(self.value, byte_range)?;
395        let field_id = self.header.field_id_size.unpack_u32(field_id_bytes, i)?;
396        self.metadata.get(field_id as _)
397    }
398
399    /// Returns an iterator of (name, value) pairs over the fields of this object.
400    ///
401    /// # Panics
402    ///
403    /// Panics if this object is invalid. Use [`Self::iter_try`] for a fallible version.
404    pub fn iter(&self) -> impl Iterator<Item = (&'m str, Variant<'m, 'v>)> + '_ {
405        self.iter_try_with_shallow_validation()
406            .map(|result| result.expect("Invalid variant object field value"))
407    }
408
409    /// Fallible iteration over the fields of this object.
410    pub fn iter_try(
411        &self,
412    ) -> impl Iterator<Item = Result<(&'m str, Variant<'m, 'v>), ArrowError>> + '_ {
413        self.iter_try_with_shallow_validation().map(|result| {
414            let (name, value) = result?;
415            Ok((name, value.with_full_validation()?))
416        })
417    }
418
419    // Fallible iteration over the fields of this object that performs only shallow (constant-cost)
420    // validation of field values.
421    fn iter_try_with_shallow_validation(
422        &self,
423    ) -> impl Iterator<Item = Result<(&'m str, Variant<'m, 'v>), ArrowError>> + '_ {
424        (0..self.len()).map(|i| {
425            let field = self.try_field_with_shallow_validation(i)?;
426            Ok((self.try_field_name(i)?, field))
427        })
428    }
429
430    /// Returns the value of the field with the specified name, if any.
431    ///
432    /// Returns `Some(Variant)` if the field exists, or `None` if the field does not exist.
433    pub fn get(&self, name: &str) -> Option<Variant<'m, 'v>> {
434        // Binary search through the field IDs of this object to find the requested field name.
435        //
436        // NOTE: This does not require a sorted metadata dictionary, because the variant spec
437        // requires object field ids to be lexically sorted by their corresponding string values,
438        // and probing the dictionary for a field id is always O(1) work.
439        let cmp = |i| Some(self.field_name(i)?.cmp(name));
440        let i = try_binary_search_range_by(0..self.len(), cmp)?.ok()?;
441        self.field(i)
442    }
443}
444
445// Custom implementation of PartialEq for variant objects
446//
447// According to the spec, field values are not required to be in the same order as the field IDs,
448// to enable flexibility when constructing Variant values
449//
450// Instead of comparing the raw bytes of 2 variant objects, this implementation recursively
451// checks whether the field values are equal -- regardless of their order
452impl PartialEq for VariantObject<'_, '_> {
453    fn eq(&self, other: &Self) -> bool {
454        if self.num_elements != other.num_elements {
455            return false;
456        }
457
458        // IFF two objects are valid and logically equal, they will have the same
459        // field names in the same order, because the spec requires the object
460        // fields to be sorted lexicographically.
461        self.iter()
462            .zip(other.iter())
463            .all(|((name_a, value_a), (name_b, value_b))| name_a == name_b && value_a == value_b)
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use crate::VariantBuilder;
470
471    use super::*;
472
473    #[test]
474    fn test_variant_object_simple() {
475        // Create metadata with field names: "age", "name", "active" (sorted)
476        // Header: version=1, sorted=1, offset_size=1 (offset_size_minus_one=0)
477        // So header byte = 00_0_1_0001 = 0x11
478        let metadata_bytes = vec![
479            0b0001_0001,
480            3, // dictionary size
481            0, // "active"
482            6, // "age"
483            9, // "name"
484            13,
485            b'a',
486            b'c',
487            b't',
488            b'i',
489            b'v',
490            b'e',
491            b'a',
492            b'g',
493            b'e',
494            b'n',
495            b'a',
496            b'm',
497            b'e',
498        ];
499        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
500
501        // Create object value data for: {"active": true, "age": 42, "name": "hello"}
502        // Field IDs in sorted order: [0, 1, 2] (active, age, name)
503        // Header: basic_type=2, field_offset_size_minus_one=0, field_id_size_minus_one=0, is_large=0
504        // value_header = 0000_00_00 = 0x00
505        // So header byte = (0x00 << 2) | 2 = 0x02
506        let object_value = vec![
507            0x02, // header: basic_type=2, value_header=0x00
508            3,    // num_elements = 3
509            // Field IDs (1 byte each): active=0, age=1, name=2
510            0, 1, 2,
511            // Field offsets (1 byte each): 4 offsets total
512            0, // offset to first value (boolean true)
513            1, // offset to second value (int8)
514            3, // offset to third value (short string)
515            9, // end offset
516            // Values:
517            0x04, // boolean true: primitive_header=1, basic_type=0 -> (1 << 2) | 0 = 0x04
518            0x0C,
519            42, // int8: primitive_header=3, basic_type=0 -> (3 << 2) | 0 = 0x0C, then value 42
520            0x15, b'h', b'e', b'l', b'l',
521            b'o', // short string: length=5, basic_type=1 -> (5 << 2) | 1 = 0x15
522        ];
523
524        let variant_obj = VariantObject::try_new(metadata, &object_value).unwrap();
525
526        // Test basic properties
527        assert_eq!(variant_obj.len(), 3);
528        assert!(!variant_obj.is_empty());
529
530        // Test field access
531        let active_field = variant_obj.get("active");
532        assert!(active_field.is_some());
533        assert_eq!(active_field.unwrap().as_boolean(), Some(true));
534
535        let age_field = variant_obj.get("age");
536        assert!(age_field.is_some());
537        assert_eq!(age_field.unwrap().as_int8(), Some(42));
538
539        let name_field = variant_obj.get("name");
540        assert!(name_field.is_some());
541        assert_eq!(name_field.unwrap().as_string(), Some("hello"));
542
543        // Test non-existent field
544        let missing_field = variant_obj.get("missing");
545        assert!(missing_field.is_none());
546
547        let missing_field_name = variant_obj.field_name(3);
548        assert!(missing_field_name.is_none());
549
550        let missing_field_name = variant_obj.field_name(300);
551        assert!(missing_field_name.is_none());
552
553        let missing_field_value = variant_obj.field(3);
554        assert!(missing_field_value.is_none());
555
556        let missing_field_value = variant_obj.field(300);
557        assert!(missing_field_value.is_none());
558
559        // Test fields iterator
560        let fields: Vec<_> = variant_obj.iter().collect();
561        assert_eq!(fields.len(), 3);
562
563        // Fields should be in sorted order: active, age, name
564        assert_eq!(fields[0].0, "active");
565        assert_eq!(fields[0].1.as_boolean(), Some(true));
566
567        assert_eq!(fields[1].0, "age");
568        assert_eq!(fields[1].1.as_int8(), Some(42));
569
570        assert_eq!(fields[2].0, "name");
571        assert_eq!(fields[2].1.as_string(), Some("hello"));
572
573        // Test field access by index
574        // Fields should be in sorted order: active, age, name
575        assert_eq!(variant_obj.field_name(0), Some("active"));
576        assert_eq!(variant_obj.field(0).unwrap().as_boolean(), Some(true));
577
578        assert_eq!(variant_obj.field_name(1), Some("age"));
579        assert_eq!(variant_obj.field(1).unwrap().as_int8(), Some(42));
580
581        assert_eq!(variant_obj.field_name(2), Some("name"));
582        assert_eq!(variant_obj.field(2).unwrap().as_string(), Some("hello"));
583    }
584
585    #[test]
586    fn test_variant_object_empty_fields() {
587        let mut builder = VariantBuilder::new();
588        builder.new_object().with_field("", 42).finish();
589        let (metadata, value) = builder.finish();
590
591        // Resulting object is valid and has a single empty field
592        let variant = Variant::try_new(&metadata, &value).unwrap();
593        let variant_obj = variant.as_object().unwrap();
594        assert_eq!(variant_obj.len(), 1);
595        assert_eq!(variant_obj.get(""), Some(Variant::from(42)));
596    }
597
598    #[test]
599    fn test_variant_object_empty() {
600        // Create metadata with no fields
601        let metadata_bytes = vec![
602            0x11, // header: version=1, sorted=0, offset_size_minus_one=0
603            0,    // dictionary_size = 0
604            0,    // offset[0] = 0 (end of dictionary)
605        ];
606        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
607
608        // Create empty object value data: {}
609        let object_value = vec![
610            0x02, // header: basic_type=2, value_header=0x00
611            0,    // num_elements = 0
612            0,    // single offset pointing to end
613                  // No field IDs, no values
614        ];
615
616        let variant_obj = VariantObject::try_new(metadata, &object_value).unwrap();
617
618        // Test basic properties
619        assert_eq!(variant_obj.len(), 0);
620        assert!(variant_obj.is_empty());
621
622        // Test field access on empty object
623        let missing_field = variant_obj.get("anything");
624        assert!(missing_field.is_none());
625
626        // Test fields iterator on empty object
627        let fields: Vec<_> = variant_obj.iter().collect();
628        assert_eq!(fields.len(), 0);
629    }
630
631    #[test]
632    fn test_variant_object_invalid_metadata_end_offset() {
633        // Create metadata with field names: "age", "name" (sorted)
634        let metadata_bytes = vec![
635            0b0001_0001, // header: version=1, sorted=1, offset_size_minus_one=0
636            2,           // dictionary size
637            0,           // "age"
638            3,           // "name"
639            8,           // Invalid end offset (should be 7)
640            b'a',
641            b'g',
642            b'e',
643            b'n',
644            b'a',
645            b'm',
646            b'e',
647        ];
648        let err = VariantMetadata::try_new(&metadata_bytes);
649        let err = err.unwrap_err();
650        assert!(matches!(
651            err,
652            ArrowError::InvalidArgumentError(ref msg) if msg.contains("Tried to extract byte(s) ..13 from 12-byte buffer")
653        ));
654    }
655
656    #[test]
657    fn test_variant_object_invalid_end_offset() {
658        // Create metadata with field names: "age", "name" (sorted)
659        let metadata_bytes = vec![
660            0b0001_0001, // header: version=1, sorted=1, offset_size_minus_one=0
661            2,           // dictionary size
662            0,           // "age"
663            3,           // "name"
664            7,
665            b'a',
666            b'g',
667            b'e',
668            b'n',
669            b'a',
670            b'm',
671            b'e',
672        ];
673        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
674
675        // Create object value data for: {"age": 42, "name": "hello"}
676        // Field IDs in sorted order: [0, 1] (age, name)
677        // Header: basic_type=2, field_offset_size_minus_one=0, field_id_size_minus_one=0, is_large=0
678        // value_header = 0000_00_00 = 0x00
679        let object_value = vec![
680            0x02, // header: basic_type=2, value_header=0x00
681            2,    // num_elements = 2
682            // Field IDs (1 byte each): age=0, name=1
683            0, 1,
684            // Field offsets (1 byte each): 3 offsets total
685            0, // offset to first value (int8)
686            2, // offset to second value (short string)
687            9, // invalid end offset (correct would be 8)
688            // Values:
689            0x0C,
690            42, // int8: primitive_header=3, basic_type=0 -> (3 << 2) | 0 = 0x0C, then value 42
691            0x15, b'h', b'e', b'l', b'l',
692            b'o', // short string: length=5, basic_type=1 -> (5 << 2) | 1 = 0x15
693        ];
694
695        let err = VariantObject::try_new(metadata, &object_value);
696        let err = err.unwrap_err();
697        assert!(matches!(
698            err,
699            ArrowError::InvalidArgumentError(ref msg) if msg.contains("Tried to extract byte(s) ..16 from 15-byte buffer")
700        ));
701    }
702
703    fn test_variant_object_with_count(count: i32, expected_field_id_size: OffsetSizeBytes) {
704        let field_names: Vec<_> = (0..count).map(|val| val.to_string()).collect();
705        let mut builder =
706            VariantBuilder::new().with_field_names(field_names.iter().map(|s| s.as_str()));
707
708        let mut obj = builder.new_object();
709
710        for i in 0..count {
711            obj.insert(&field_names[i as usize], i);
712        }
713
714        obj.finish();
715        let (metadata, value) = builder.finish();
716        let variant = Variant::new(&metadata, &value);
717
718        if let Variant::Object(obj) = variant {
719            assert_eq!(obj.len(), count as usize);
720
721            assert_eq!(obj.get(&field_names[0]).unwrap(), Variant::Int32(0));
722            assert_eq!(
723                obj.get(&field_names[(count - 1) as usize]).unwrap(),
724                Variant::Int32(count - 1)
725            );
726            assert_eq!(
727                obj.header.field_id_size, expected_field_id_size,
728                "Expected {}-byte field IDs, got {}-byte field IDs",
729                expected_field_id_size as usize, obj.header.field_id_size as usize
730            );
731        } else {
732            panic!("Expected object variant");
733        }
734    }
735
736    #[test]
737    fn test_variant_object_257_elements() {
738        test_variant_object_with_count((1 << 8) + 1, OffsetSizeBytes::Two); // 2^8 + 1, expected 2-byte field IDs
739    }
740
741    #[test]
742    fn test_variant_object_65537_elements() {
743        test_variant_object_with_count((1 << 16) + 1, OffsetSizeBytes::Three);
744        // 2^16 + 1, expected 3-byte field IDs
745    }
746
747    /* Can't run this test now as it takes 45x longer than other tests
748    #[test]
749    fn test_variant_object_16777217_elements() {
750        test_variant_object_with_count((1 << 24) + 1, OffsetSizeBytes::Four);
751        // 2^24 + 1, expected 4-byte field IDs
752    }
753     */
754
755    #[test]
756    fn test_variant_object_small_sizes_255_elements() {
757        test_variant_object_with_count(255, OffsetSizeBytes::One);
758    }
759
760    fn test_variant_object_with_large_data(
761        data_size_per_field: usize,
762        expected_field_offset_size: OffsetSizeBytes,
763    ) {
764        let num_fields = 20;
765        let mut builder = VariantBuilder::new();
766        let mut obj = builder.new_object();
767
768        let str_val = "a".repeat(data_size_per_field);
769
770        for val in 0..num_fields {
771            let key = format!("id_{val}");
772            obj.insert(&key, str_val.as_str());
773        }
774
775        obj.finish();
776        let (metadata, value) = builder.finish();
777        let variant = Variant::new(&metadata, &value);
778
779        if let Variant::Object(obj) = variant {
780            assert_eq!(obj.len(), num_fields);
781            assert_eq!(
782                obj.header.field_offset_size, expected_field_offset_size,
783                "Expected {}-byte field offsets, got {}-byte field offsets",
784                expected_field_offset_size as usize, obj.header.field_offset_size as usize
785            );
786        } else {
787            panic!("Expected object variant");
788        }
789    }
790
791    #[test]
792    fn test_variant_object_child_data_0_byte_offsets_minus_one() {
793        test_variant_object_with_large_data(10, OffsetSizeBytes::One);
794    }
795
796    #[test]
797    fn test_variant_object_256_bytes_child_data_3_byte_offsets() {
798        test_variant_object_with_large_data(256 + 1, OffsetSizeBytes::Two); // 2^8 - 2^16 elements
799    }
800
801    #[test]
802    fn test_variant_object_16777216_bytes_child_data_4_byte_offsets() {
803        test_variant_object_with_large_data(65536 + 1, OffsetSizeBytes::Three); // 2^16 - 2^24 elements
804    }
805
806    #[test]
807    fn test_variant_object_65535_bytes_child_data_2_byte_offsets() {
808        test_variant_object_with_large_data(16777216 + 1, OffsetSizeBytes::Four);
809        // 2^24
810    }
811
812    #[test]
813    fn test_objects_with_same_fields_are_equal() {
814        let mut b = VariantBuilder::new();
815        let mut o = b.new_object();
816
817        o.insert("b", ());
818        o.insert("c", ());
819        o.insert("a", ());
820
821        o.finish();
822
823        let (m, v) = b.finish();
824
825        let v1 = Variant::try_new(&m, &v).unwrap();
826        let v2 = Variant::try_new(&m, &v).unwrap();
827
828        assert_eq!(v1, v2);
829    }
830
831    #[test]
832    fn test_same_objects_with_different_builder_are_equal() {
833        let mut b = VariantBuilder::new();
834        let mut o = b.new_object();
835
836        o.insert("a", ());
837        o.insert("b", false);
838
839        o.finish();
840        let (m, v) = b.finish();
841
842        let v1 = Variant::try_new(&m, &v).unwrap();
843
844        let mut b = VariantBuilder::new();
845        let mut o = b.new_object();
846
847        o.insert("a", ());
848        o.insert("b", false);
849
850        o.finish();
851        let (m, v) = b.finish();
852
853        let v2 = Variant::try_new(&m, &v).unwrap();
854
855        assert_eq!(v1, v2);
856    }
857
858    #[test]
859    fn test_objects_with_different_values_are_not_equal() {
860        let mut b = VariantBuilder::new();
861        let mut o = b.new_object();
862
863        o.insert("a", ());
864        o.insert("b", 4.3);
865
866        o.finish();
867
868        let (m, v) = b.finish();
869
870        let v1 = Variant::try_new(&m, &v).unwrap();
871
872        // second object, same field name but different values
873        let mut b = VariantBuilder::new();
874        let mut o = b.new_object();
875
876        o.insert("a", ());
877        let mut inner_o = o.new_object("b");
878        inner_o.insert("a", 3.3);
879        inner_o.finish();
880        o.finish();
881
882        let (m, v) = b.finish();
883
884        let v2 = Variant::try_new(&m, &v).unwrap();
885
886        let m1 = v1.metadata();
887        let m2 = v2.metadata();
888
889        // metadata would be equal since they contain the same keys
890        assert_eq!(m1, m2);
891
892        // but the objects are not equal
893        assert_ne!(v1, v2);
894    }
895
896    #[test]
897    fn test_objects_with_different_field_names_are_not_equal() {
898        let mut b = VariantBuilder::new();
899        let mut o = b.new_object();
900
901        o.insert("a", ());
902        o.insert("b", 4.3);
903
904        o.finish();
905
906        let (m, v) = b.finish();
907
908        let v1 = Variant::try_new(&m, &v).unwrap();
909
910        // second object, same field name but different values
911        let mut b = VariantBuilder::new();
912        let mut o = b.new_object();
913
914        o.insert("aardvark", ());
915        o.insert("barracuda", 3.3);
916
917        o.finish();
918
919        let (m, v) = b.finish();
920        let v2 = Variant::try_new(&m, &v).unwrap();
921
922        assert_ne!(v1, v2);
923    }
924
925    #[test]
926    fn test_objects_with_different_insertion_order_are_equal() {
927        let mut b = VariantBuilder::new();
928        let mut o = b.new_object();
929
930        o.insert("b", false);
931        o.insert("a", ());
932
933        o.finish();
934
935        let (m, v) = b.finish();
936
937        let v1 = Variant::try_new(&m, &v).unwrap();
938        assert!(!v1.metadata().is_sorted());
939
940        // create another object pre-filled with field names, b and a
941        // but insert the fields in the order of a, b
942        let mut b = VariantBuilder::new().with_field_names(["b", "a"]);
943        let mut o = b.new_object();
944
945        o.insert("a", ());
946        o.insert("b", false);
947
948        o.finish();
949
950        let (m, v) = b.finish();
951
952        let v2 = Variant::try_new(&m, &v).unwrap();
953
954        // v2 should also have a unsorted dictionary
955        assert!(!v2.metadata().is_sorted());
956
957        assert_eq!(v1, v2);
958    }
959
960    #[test]
961    fn test_objects_with_differing_metadata_are_equal() {
962        let mut b = VariantBuilder::new();
963        let mut o = b.new_object();
964
965        o.insert("a", ());
966        o.insert("b", 4.3);
967
968        o.finish();
969
970        let (meta1, value1) = b.finish();
971
972        let v1 = Variant::try_new(&meta1, &value1).unwrap();
973        // v1 is sorted
974        assert!(v1.metadata().is_sorted());
975
976        // create a second object with different insertion order
977        let mut b = VariantBuilder::new().with_field_names(["d", "c", "b", "a"]);
978        let mut o = b.new_object();
979
980        o.insert("b", 4.3);
981        o.insert("a", ());
982
983        o.finish();
984
985        let (meta2, value2) = b.finish();
986
987        let v2 = Variant::try_new(&meta2, &value2).unwrap();
988        // v2 is not sorted
989        assert!(!v2.metadata().is_sorted());
990
991        // object metadata are not the same
992        assert_ne!(v1.metadata(), v2.metadata());
993
994        // objects are still logically equal
995        assert_eq!(v1, v2);
996    }
997
998    #[test]
999    fn test_compare_object_with_unsorted_dictionary_vs_sorted_dictionary() {
1000        // create a sorted object
1001        let mut b = VariantBuilder::new();
1002        let mut o = b.new_object();
1003
1004        o.insert("a", false);
1005        o.insert("b", false);
1006
1007        o.finish();
1008
1009        let (m, v) = b.finish();
1010
1011        let v1 = Variant::try_new(&m, &v).unwrap();
1012
1013        // Create metadata with an unsorted dictionary (field names are "a", "a", "b")
1014        // Since field names are not unique, it is considered not sorted.
1015        let metadata_bytes = vec![
1016            0b0000_0001,
1017            3, // dictionary size
1018            0, // "a"
1019            1, // "b"
1020            2, // "a"
1021            3,
1022            b'a',
1023            b'b',
1024            b'a',
1025        ];
1026        let m = VariantMetadata::try_new(&metadata_bytes).unwrap();
1027        assert!(!m.is_sorted());
1028
1029        let v2 = Variant::new_with_metadata(m, &v);
1030        assert_eq!(v1, v2);
1031    }
1032
1033    #[test]
1034    fn test_object_rejects_duplicate_field_names() {
1035        // An unsorted dictionary may legally hold duplicate entries, but an object must not
1036        // reference the same field name twice.
1037        let metadata_bytes = vec![
1038            0b0000_0001,
1039            2, // dictionary size
1040            0, // "a"
1041            1, // "a"
1042            2,
1043            b'a',
1044            b'a',
1045        ];
1046        assert!(
1047            !VariantMetadata::try_new(&metadata_bytes)
1048                .unwrap()
1049                .is_sorted()
1050        );
1051
1052        let value_bytes = vec![
1053            0b0000_0010, // object header
1054            2,           // num_elements
1055            0,           // field id 0 -> "a"
1056            1,           // field id 1 -> "a"
1057            0,
1058            1,
1059            2,           // field offsets
1060            0b0000_1100, // true
1061            0b0000_1000, // false
1062        ];
1063        let err = Variant::try_new(&metadata_bytes, &value_bytes).unwrap_err();
1064        assert!(
1065            matches!(err, ArrowError::InvalidArgumentError(_)),
1066            "unexpected error: {err:?}"
1067        );
1068    }
1069
1070    /// return the value bytes for `depth` single-field objects nested around a null
1071    fn make_nested_objects(depth: usize) -> Vec<u8> {
1072        let mut value = vec![0u8]; // null primitive
1073        for _ in 0..depth {
1074            // header: object basic type, 1-byte field ids, 4-byte field offsets
1075            let mut outer = vec![0b0000_1110, 1, 0];
1076            outer.extend_from_slice(&0u32.to_le_bytes());
1077            outer.extend_from_slice(&(value.len() as u32).to_le_bytes());
1078            outer.append(&mut value);
1079            value = outer;
1080        }
1081        value
1082    }
1083
1084    #[test]
1085    fn test_variant_object_nesting_depth_limit() {
1086        let metadata = [0b0000_0001, 1, 0, 1, b'a']; // dictionary of one field name
1087
1088        let value = make_nested_objects(MAX_NESTING_DEPTH);
1089        Variant::try_new(&metadata, &value).unwrap();
1090
1091        // One level deeper would recurse past the limit -- previously a stack overflow
1092        let value = make_nested_objects(MAX_NESTING_DEPTH + 1);
1093        let err = Variant::try_new(&metadata, &value).unwrap_err();
1094        assert!(
1095            err.to_string().contains("nesting depth exceeds"),
1096            "unexpected error: {err}"
1097        );
1098    }
1099}