Skip to main content

parquet_variant/variant/
list.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.
17use crate::decoder::{OffsetSizeBytes, map_bytes_to_offsets};
18use crate::utils::{
19    first_byte_from_slice, overflow_error, slice_from_slice, slice_from_slice_at_offset,
20};
21use crate::variant::{MAX_NESTING_DEPTH, Variant, VariantMetadata};
22
23use arrow_schema::ArrowError;
24
25// The value header occupies one byte; use a named constant for readability
26const NUM_HEADER_BYTES: u32 = 1;
27
28/// A parsed version of the variant array value header byte.
29#[derive(Debug, Clone, PartialEq)]
30pub(crate) struct VariantListHeader {
31    num_elements_size: OffsetSizeBytes,
32    offset_size: OffsetSizeBytes,
33}
34
35impl VariantListHeader {
36    // Hide the ugly casting
37    const fn num_elements_size(&self) -> u32 {
38        self.num_elements_size as _
39    }
40    const fn offset_size(&self) -> u32 {
41        self.offset_size as _
42    }
43
44    // Avoid materializing this offset, since it's cheaply and safely computable
45    const fn first_offset_byte(&self) -> u32 {
46        NUM_HEADER_BYTES + self.num_elements_size()
47    }
48
49    pub(crate) fn try_new(header_byte: u8) -> Result<Self, ArrowError> {
50        // The 6 first bits to the left are the value_header and the 2 bits
51        // to the right are the basic type, so we shift to get only the value_header
52        let value_header = header_byte >> 2;
53        let is_large = (value_header & 0x04) != 0; // 3rd bit from the right
54        let field_offset_size_minus_one = value_header & 0x03; // Last two bits
55
56        // The size of the num_elements entry in the array value_data is 4 bytes if
57        // is_large is true, otherwise 1 byte.
58        let num_elements_size = match is_large {
59            true => OffsetSizeBytes::Four,
60            false => OffsetSizeBytes::One,
61        };
62        let offset_size = OffsetSizeBytes::try_new(field_offset_size_minus_one)?;
63
64        Ok(Self {
65            num_elements_size,
66            offset_size,
67        })
68    }
69}
70
71/// [`Variant`] Array.
72///
73/// See the [Variant spec] for details.
74///
75/// NOTE: The "list" naming differs from the variant spec -- which calls it "array" -- in order to be
76/// consistent with Parquet and Arrow type naming. Otherwise, the name would conflict with the
77/// `VariantArray : Array` we must eventually define for variant-typed arrow arrays.
78///
79/// # Validation
80///
81/// Every instance of variant list is either _valid_ or _invalid_. depending on whether the
82/// underlying bytes are a valid encoding of a variant array (see below).
83///
84/// Instances produced by [`Self::try_new`] or [`Self::with_full_validation`] are fully _validated_. They always
85/// contain _valid_ data, and infallible accesses such as iteration and indexing are panic-free. The
86/// validation cost is linear in the number of underlying bytes.
87///
88/// Instances produced by [`Self::new`] are _unvalidated_ and so they may contain either _valid_ or
89/// _invalid_ data. Infallible accesses such as iteration and indexing will panic if the underlying
90/// bytes are _invalid_, and fallible alternatives such as [`Self::iter_try`] and [`Self::get`] are
91/// provided as panic-free alternatives. [`Self::with_full_validation`] can also be used to _validate_ an
92/// _unvalidated_ instance, if desired.
93///
94/// _Unvalidated_ instances can be constructed in constant time. This can be useful if the caller
95/// knows the underlying bytes were already validated previously, or if the caller intends to
96/// perform a small number of (fallible) accesses to a large list.
97///
98/// A _validated_ variant list instance guarantees that:
99///
100/// - header byte is valid
101/// - num_elements is in bounds
102/// - offset array content is in-bounds
103/// - first offset is zero
104/// - last offset is in-bounds
105/// - all other offsets are in-bounds (*)
106/// - all offsets are monotonically increasing (*)
107/// - all values are (recursively) valid variant objects (*)
108/// - the associated variant metadata is [valid] (*)
109///
110/// NOTE: [`Self::new`] only skips expensive (non-constant cost) validation checks (marked by `(*)`
111/// in the list above); it panics any of the other checks fails.
112///
113/// # Safety
114///
115/// Even an _invalid_ variant list instance is still _safe_ to use in the Rust sense. Accessing
116/// it with infallible methods may cause panics but will never lead to undefined behavior.
117///
118/// [valid]: VariantMetadata#Validation
119/// [Variant spec]: https://github.com/apache/parquet-format/blob/master/VariantEncoding.md#value-data-for-array-basic_type3
120#[derive(Debug, Clone)]
121pub struct VariantList<'m, 'v> {
122    pub metadata: VariantMetadata<'m>,
123    pub value: &'v [u8],
124    header: VariantListHeader,
125    num_elements: 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::<VariantList>(64);
133
134#[cfg(target_pointer_width = "32")]
135const _: () = crate::utils::expect_size_of::<VariantList>(40);
136
137impl<'m, 'v> VariantList<'m, 'v> {
138    /// Attempts to interpret `value` as a variant array value.
139    ///
140    /// # Validation
141    ///
142    /// This constructor verifies that `value` points to a valid variant array value. In particular,
143    /// that all offsets are in-bounds and point to valid (recursively validated) objects.
144    pub fn try_new(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Result<Self, ArrowError> {
145        Self::try_new_with_shallow_validation(metadata, value)?.with_full_validation()
146    }
147
148    /// Interprets `metadata` and `value` as a variant list, performing only basic
149    /// (constant-cost) [validation].
150    ///
151    /// # Panics
152    ///
153    /// Panics if basic validation fails. Use [`Self::try_new`] for a fallible version.
154    ///
155    /// [validation]: Self#Validation
156    pub fn new(metadata: VariantMetadata<'m>, value: &'v [u8]) -> Self {
157        Self::try_new_with_shallow_validation(metadata, value).expect("Invalid variant list value")
158    }
159
160    /// Attempts to interpet `metadata` and `value` as a variant array, performing only basic
161    /// (constant-cost) [validation].
162    ///
163    /// [validation]: Self#Validation
164    pub(crate) fn try_new_with_shallow_validation(
165        metadata: VariantMetadata<'m>,
166        value: &'v [u8],
167    ) -> Result<Self, ArrowError> {
168        let header_byte = first_byte_from_slice(value)?;
169        let header = VariantListHeader::try_new(header_byte)?;
170
171        // Skip the header byte to read the num_elements; the offset array immediately follows
172        let num_elements =
173            header
174                .num_elements_size
175                .unpack_u32_at_offset(value, NUM_HEADER_BYTES as _, 0)?;
176
177        // (num_elements + 1) * offset_size + first_offset_byte
178        let first_value_byte = num_elements
179            .checked_add(1)
180            .and_then(|n| n.checked_mul(header.offset_size()))
181            .and_then(|n| n.checked_add(header.first_offset_byte()))
182            .ok_or_else(|| overflow_error("offset of variant list values"))?;
183
184        let mut new_self = Self {
185            metadata,
186            value,
187            header,
188            num_elements,
189            first_value_byte,
190            validated: false,
191        };
192
193        // Validate just the first and last offset, ignoring the other offsets and all value bytes.
194        let first_offset = new_self.get_offset(0)?;
195        if first_offset != 0 {
196            return Err(ArrowError::InvalidArgumentError(format!(
197                "First offset is not zero: {first_offset}"
198            )));
199        }
200
201        // Use the last offset to upper-bound the value buffer
202        let last_offset = new_self
203            .get_offset(num_elements as _)?
204            .checked_add(first_value_byte)
205            .ok_or_else(|| overflow_error("variant array size"))?;
206        new_self.value = slice_from_slice(value, ..last_offset as _)?;
207        Ok(new_self)
208    }
209
210    /// True if this instance is fully [validated] for panic-free infallible accesses.
211    ///
212    /// [validated]: Self#Validation
213    pub fn is_fully_validated(&self) -> bool {
214        self.validated
215    }
216
217    /// Performs a full [validation] of this variant array and returns the result.
218    ///
219    /// Values nested more than [`MAX_NESTING_DEPTH`] deep are rejected.
220    ///
221    /// [validation]: Self#Validation
222    pub fn with_full_validation(self) -> Result<Self, ArrowError> {
223        self.with_full_validation_at_depth(0)
224    }
225
226    // Same as [`Self::with_full_validation`], tracking how deeply validation has recursed.
227    pub(crate) fn with_full_validation_at_depth(
228        mut self,
229        depth: usize,
230    ) -> Result<Self, ArrowError> {
231        if !self.validated {
232            if depth >= MAX_NESTING_DEPTH {
233                return Err(ArrowError::InvalidArgumentError(format!(
234                    "Variant nesting depth exceeds the maximum of {MAX_NESTING_DEPTH}"
235                )));
236            }
237
238            // Validate the metadata dictionary first, if not already validated, because we pass it
239            // by value to all the children (who would otherwise re-validate it repeatedly).
240            self.metadata = self.metadata.with_full_validation()?;
241
242            let offset_buffer = slice_from_slice(
243                self.value,
244                self.header.first_offset_byte() as _..self.first_value_byte as _,
245            )?;
246
247            let value_buffer = slice_from_slice(self.value, self.first_value_byte as _..)?;
248
249            // Validate whether values are valid variant objects
250            //
251            // Since we use offsets to slice into the value buffer, this also verifies all offsets are in-bounds
252            // and monotonically increasing
253            let mut offset_iter = map_bytes_to_offsets(offset_buffer, self.header.offset_size);
254            let mut current_offset = offset_iter.next().unwrap_or(0);
255
256            for next_offset in offset_iter {
257                let value_bytes = slice_from_slice(value_buffer, current_offset..next_offset)?;
258                Variant::try_new_with_metadata_at_depth(
259                    self.metadata.clone(),
260                    value_bytes,
261                    depth + 1,
262                )?;
263                current_offset = next_offset;
264            }
265
266            self.validated = true;
267        }
268        Ok(self)
269    }
270
271    /// Return the length of this array
272    pub fn len(&self) -> usize {
273        self.num_elements as _
274    }
275
276    /// Is the array of zero length
277    pub fn is_empty(&self) -> bool {
278        self.len() == 0
279    }
280
281    /// Returns element by index in `0..self.len()`, if any.
282    ///
283    /// # Panics
284    ///
285    /// Panics if this list is [invalid]. Use [`Self::try_get`] for a fallible version.
286    ///
287    /// [invalid]: Self#Validation
288    pub fn get(&self, index: usize) -> Option<Variant<'m, 'v>> {
289        (index < self.len()).then(|| {
290            self.try_get_with_shallow_validation(index)
291                .expect("Invalid variant array element")
292        })
293    }
294
295    /// Fallible version of `get`. Returns element by index, capturing validation errors
296    pub fn try_get(&self, index: usize) -> Result<Variant<'m, 'v>, ArrowError> {
297        self.try_get_with_shallow_validation(index)?
298            .with_full_validation()
299    }
300
301    // Fallible version of `get`, performing only basic (constant-time) validation.
302    fn try_get_with_shallow_validation(&self, index: usize) -> Result<Variant<'m, 'v>, ArrowError> {
303        // Fetch the value bytes between the two offsets for this index, from the value array region
304        // of the byte buffer
305        let byte_range = self.get_offset(index)? as _..self.get_offset(index + 1)? as _;
306        let value_bytes =
307            slice_from_slice_at_offset(self.value, self.first_value_byte as _, byte_range)?;
308        Variant::try_new_with_metadata_and_shallow_validation(self.metadata.clone(), value_bytes)
309    }
310
311    /// Iterates over the values of this list. When working with [unvalidated] input, consider
312    /// [`Self::iter_try`] to avoid panics due to invalid data.
313    ///
314    /// [unvalidated]: Self#Validation
315    ///
316    /// # Panics
317    ///
318    /// Panics if the underlying bytes are invalid. Use [`Self::iter_try`] for a
319    /// fallible version.
320    pub fn iter(&self) -> impl Iterator<Item = Variant<'m, 'v>> + '_ {
321        self.iter_try_with_shallow_validation()
322            .map(|result| result.expect("Invalid variant list entry"))
323    }
324
325    /// Fallible iteration over the elements of this list.
326    pub fn iter_try(&self) -> impl Iterator<Item = Result<Variant<'m, 'v>, ArrowError>> + '_ {
327        self.iter_try_with_shallow_validation()
328            .map(|result| result?.with_full_validation())
329    }
330
331    // Fallible iteration that only performs basic (constant-time) validation.
332    fn iter_try_with_shallow_validation(
333        &self,
334    ) -> impl Iterator<Item = Result<Variant<'m, 'v>, ArrowError>> + '_ {
335        (0..self.len()).map(|i| self.try_get_with_shallow_validation(i))
336    }
337
338    // Attempts to retrieve the ith offset from the offset array region of the byte buffer.
339    fn get_offset(&self, index: usize) -> Result<u32, ArrowError> {
340        let byte_range = self.header.first_offset_byte() as _..self.first_value_byte as _;
341        let offset_bytes = slice_from_slice(self.value, byte_range)?;
342        self.header.offset_size.unpack_u32(offset_bytes, index)
343    }
344}
345
346// Custom implementation of PartialEq for variant arrays
347//
348// Instead of comparing the raw bytes of 2 variant lists, this implementation recursively
349// checks whether their elements are equal.
350impl PartialEq for VariantList<'_, '_> {
351    fn eq(&self, other: &Self) -> bool {
352        if self.num_elements != other.num_elements {
353            return false;
354        }
355
356        self.iter().zip(other.iter()).all(|(a, b)| a == b)
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363    use crate::VariantBuilder;
364    use std::iter::repeat_n;
365    use std::ops::Range;
366
367    #[test]
368    fn test_variant_list_simple() {
369        // Create simple metadata (empty dictionary for this test)
370        let metadata_bytes = vec![
371            0x01, // header: version=1, sorted=0, offset_size_minus_one=0
372            0,    // dictionary_size = 0
373            0,    // offset[0] = 0 (end of dictionary)
374        ];
375        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
376
377        // Create list value data for: [42, true, "hi"]
378        // Header: basic_type=3 (array), field_offset_size_minus_one=0, is_large=0
379        // value_header = 0000_0_0_00 = 0x00
380        // So header byte = (0x00 << 2) | 3 = 0x03
381        let list_value = vec![
382            0x03, // header: basic_type=3, value_header=0x00
383            3,    // num_elements = 3
384            // Offsets (1 byte each): 4 offsets total
385            0, // offset to first value (int8)
386            2, // offset to second value (boolean true)
387            3, // offset to third value (short string)
388            6, // end offset
389            // Values:
390            0x0C,
391            42,   // int8: primitive_header=3, basic_type=0 -> (3 << 2) | 0 = 0x0C, then value 42
392            0x04, // boolean true: primitive_header=1, basic_type=0 -> (1 << 2) | 0 = 0x04
393            0x09, b'h', b'i', // short string: length=2, basic_type=1 -> (2 << 2) | 1 = 0x09
394        ];
395
396        let variant_list = VariantList::try_new(metadata, &list_value).unwrap();
397
398        // Test basic properties
399        assert_eq!(variant_list.len(), 3);
400        assert!(!variant_list.is_empty());
401
402        // Test individual element access
403        let elem0 = variant_list.get(0).unwrap();
404        assert_eq!(elem0.as_int8(), Some(42));
405
406        let elem1 = variant_list.get(1).unwrap();
407        assert_eq!(elem1.as_boolean(), Some(true));
408
409        let elem2 = variant_list.get(2).unwrap();
410        assert_eq!(elem2.as_string(), Some("hi"));
411
412        // Test out of bounds access
413        let out_of_bounds = variant_list.get(3);
414        assert!(out_of_bounds.is_none());
415
416        // Test values iterator
417        let values: Vec<_> = variant_list.iter().collect();
418        assert_eq!(values.len(), 3);
419        assert_eq!(values[0].as_int8(), Some(42));
420        assert_eq!(values[1].as_boolean(), Some(true));
421        assert_eq!(values[2].as_string(), Some("hi"));
422    }
423
424    #[test]
425    fn test_variant_list_empty() {
426        // Create simple metadata (empty dictionary)
427        let metadata_bytes = vec![
428            0x01, // header: version=1, sorted=0, offset_size_minus_one=0
429            0,    // dictionary_size = 0
430            0,    // offset[0] = 0 (end of dictionary)
431        ];
432        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
433
434        // Create empty list value data: []
435        let list_value = vec![
436            0x03, // header: basic_type=3, value_header=0x00
437            0,    // num_elements = 0
438            0,    // single offset pointing to end
439                  // No values
440        ];
441
442        let variant_list = VariantList::try_new(metadata, &list_value).unwrap();
443
444        // Test basic properties
445        assert_eq!(variant_list.len(), 0);
446        assert!(variant_list.is_empty());
447
448        // Test out of bounds access on empty list
449        let out_of_bounds = variant_list.get(0);
450        assert!(out_of_bounds.is_none());
451
452        // Test values iterator on empty list
453        let values: Vec<_> = variant_list.iter().collect();
454        assert_eq!(values.len(), 0);
455    }
456
457    #[test]
458    fn test_variant_list_large() {
459        // Create simple metadata (empty dictionary)
460        let metadata_bytes = vec![
461            0x01, // header: version=1, sorted=0, offset_size_minus_one=0
462            0,    // dictionary_size = 0
463            0,    // offset[0] = 0 (end of dictionary)
464        ];
465        let metadata = VariantMetadata::try_new(&metadata_bytes).unwrap();
466
467        // Create large list value data with 2-byte offsets: [null, false]
468        // Header: is_large=1, field_offset_size_minus_one=1, basic_type=3 (array)
469        let list_bytes = vec![
470            0x17, // header = 000_1_01_11 = 0x17
471            2, 0, 0, 0, // num_elements = 2 (4 bytes because is_large=1)
472            // Offsets (2 bytes each): 3 offsets total
473            0x00, 0x00, 0x01, 0x00, // first value (null)
474            0x02, 0x00, // second value (boolean false)
475            // Values:
476            0x00, // null: primitive_header=0, basic_type=0 -> (0 << 2) | 0 = 0x00
477            0x08, // boolean false: primitive_header=2, basic_type=0 -> (2 << 2) | 0 = 0x08
478        ];
479
480        let variant_list = VariantList::try_new(metadata, &list_bytes).unwrap();
481
482        // Test basic properties
483        assert_eq!(variant_list.len(), 2);
484        assert!(!variant_list.is_empty());
485
486        // Test individual element access
487        let elem0 = variant_list.get(0).unwrap();
488        assert_eq!(elem0.as_null(), Some(()));
489
490        let elem1 = variant_list.get(1).unwrap();
491        assert_eq!(elem1.as_boolean(), Some(false));
492    }
493
494    #[test]
495    fn test_large_variant_list_with_total_child_length_between_2_pow_8_and_2_pow_16() {
496        // all the tests below will set the total child size to ~500,
497        // which is larger than 2^8 but less than 2^16.
498        // total child size = list_size * single_child_item_len
499
500        let mut list_size: usize = 1;
501        let mut single_child_item_len: usize = 500;
502
503        // offset size will be OffSizeBytes::Two as the total child length between 2^8 and 2^16
504        let expected_offset_size = OffsetSizeBytes::Two;
505
506        test_large_variant_list_with_child_length(
507            list_size,             // the elements in the list
508            single_child_item_len, // this will control the total child size in the list
509            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
510            expected_offset_size,
511        );
512
513        list_size = 255;
514        single_child_item_len = 2;
515        test_large_variant_list_with_child_length(
516            list_size,
517            single_child_item_len,
518            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
519            expected_offset_size,
520        );
521
522        list_size = 256;
523        single_child_item_len = 2;
524        test_large_variant_list_with_child_length(
525            list_size,
526            single_child_item_len,
527            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
528            expected_offset_size,
529        );
530
531        list_size = 300;
532        single_child_item_len = 2;
533        test_large_variant_list_with_child_length(
534            list_size,
535            single_child_item_len,
536            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
537            expected_offset_size,
538        );
539    }
540
541    #[test]
542    fn test_large_variant_list_with_total_child_length_between_2_pow_16_and_2_pow_24() {
543        // all the tests below will set the total child size to ~70,000,
544        // which is larger than 2^16 but less than 2^24.
545        // total child size = list_size * single_child_item_len
546
547        let mut list_size: usize = 1;
548        let mut single_child_item_len: usize = 70000;
549
550        // offset size will be OffSizeBytes::Two as the total child length between 2^16 and 2^24
551        let expected_offset_size = OffsetSizeBytes::Three;
552
553        test_large_variant_list_with_child_length(
554            list_size,
555            single_child_item_len,
556            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
557            expected_offset_size,
558        );
559
560        list_size = 255;
561        single_child_item_len = 275;
562        // total child size = 255 * 275 = 70,125
563        test_large_variant_list_with_child_length(
564            list_size,
565            single_child_item_len,
566            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
567            expected_offset_size,
568        );
569
570        list_size = 256;
571        single_child_item_len = 274;
572        // total child size = 256 * 274 = 70,144
573        test_large_variant_list_with_child_length(
574            list_size,
575            single_child_item_len,
576            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
577            expected_offset_size,
578        );
579
580        list_size = 300;
581        single_child_item_len = 234;
582        // total child size = 300 * 234 = 70,200
583        test_large_variant_list_with_child_length(
584            list_size,
585            single_child_item_len,
586            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
587            expected_offset_size,
588        );
589    }
590
591    #[test]
592    fn test_large_variant_list_with_total_child_length_between_2_pow_24_and_2_pow_32() {
593        // all the tests below will set the total child size to ~20,000,000,
594        // which is larger than 2^24 but less than 2^32.
595        // total child size = list_size * single_child_item_len
596
597        let mut list_size: usize = 1;
598        let mut single_child_item_len: usize = 20000000;
599
600        // offset size will be OffSizeBytes::Two as the total child length between 2^24 and 2^32
601        let expected_offset_size = OffsetSizeBytes::Four;
602
603        test_large_variant_list_with_child_length(
604            list_size,
605            single_child_item_len,
606            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
607            expected_offset_size,
608        );
609
610        list_size = 255;
611        single_child_item_len = 78432;
612        // total child size = 255 * 78,432 = 20,000,160
613        test_large_variant_list_with_child_length(
614            list_size,
615            single_child_item_len,
616            OffsetSizeBytes::One, // will be OffsetSizeBytes::One as the size of the list is less than 256
617            expected_offset_size,
618        );
619
620        list_size = 256;
621        single_child_item_len = 78125;
622        // total child size = 256 * 78,125 = 20,000,000
623        test_large_variant_list_with_child_length(
624            list_size,
625            single_child_item_len,
626            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
627            expected_offset_size,
628        );
629
630        list_size = 300;
631        single_child_item_len = 66667;
632        // total child size = 300 * 66,667 = 20,000,100
633        test_large_variant_list_with_child_length(
634            list_size,
635            single_child_item_len,
636            OffsetSizeBytes::Four, // will be OffsetSizeBytes::Four as the size of the list is bigger than 255
637            expected_offset_size,
638        );
639    }
640
641    // this function will create a large variant list from VariantBuilder
642    // with specified size and each child item with the given length.
643    // and verify the content and some meta for the variant list in the final.
644    fn test_large_variant_list_with_child_length(
645        list_size: usize,
646        single_child_item_len: usize,
647        expected_num_element_size: OffsetSizeBytes,
648        expected_offset_size_bytes: OffsetSizeBytes,
649    ) {
650        let mut builder = VariantBuilder::new();
651        let mut list_builder = builder.new_list();
652
653        let mut expected_list = vec![];
654        for i in 0..list_size {
655            let random_string: String =
656                repeat_n(char::from((i % 256) as u8), single_child_item_len).collect();
657
658            list_builder.append_value(Variant::String(random_string.as_str()));
659            expected_list.push(random_string);
660        }
661
662        list_builder.finish();
663        // Finish the builder to get the metadata and value
664        let (metadata, value) = builder.finish();
665        // use the Variant API to verify the result
666        let variant = Variant::try_new(&metadata, &value).unwrap();
667
668        let variant_list = variant.as_list().unwrap();
669
670        // verify that the head is expected
671        assert_eq!(expected_offset_size_bytes, variant_list.header.offset_size);
672        assert_eq!(
673            expected_num_element_size,
674            variant_list.header.num_elements_size
675        );
676        assert_eq!(list_size, variant_list.num_elements as usize);
677
678        // verify the data in the variant
679        assert_eq!(list_size, variant_list.len());
680        for i in 0..list_size {
681            let item = variant_list.get(i).unwrap();
682            let item_str = item.as_string().unwrap();
683            assert_eq!(expected_list.get(i).unwrap(), item_str);
684        }
685    }
686
687    #[test]
688    fn test_variant_list_equality() {
689        // Create two lists with the same values (0..10)
690        let (metadata1, value1) = make_listi32(0..10);
691        let list1 = Variant::new(&metadata1, &value1);
692        let (metadata2, value2) = make_listi32(0..10);
693        let list2 = Variant::new(&metadata2, &value2);
694        // They should be equal
695        assert_eq!(list1, list2);
696    }
697
698    #[test]
699    fn test_variant_list_equality_different_length() {
700        // Create two lists with different lengths
701        let (metadata1, value1) = make_listi32(0..10);
702        let list1 = Variant::new(&metadata1, &value1);
703        let (metadata2, value2) = make_listi32(0..5);
704        let list2 = Variant::new(&metadata2, &value2);
705        // They should not be equal
706        assert_ne!(list1, list2);
707    }
708
709    #[test]
710    fn test_variant_list_equality_different_values() {
711        // Create two lists with different values
712        let (metadata1, value1) = make_listi32(0..10);
713        let list1 = Variant::new(&metadata1, &value1);
714        let (metadata2, value2) = make_listi32(5..15);
715        let list2 = Variant::new(&metadata2, &value2);
716        // They should not be equal
717        assert_ne!(list1, list2);
718    }
719
720    #[test]
721    fn test_variant_list_equality_different_types() {
722        // Create two lists with different types
723        let (metadata1, value1) = make_listi32(0i32..10i32);
724        let list1 = Variant::new(&metadata1, &value1);
725        let (metadata2, value2) = make_listi64(0..10);
726        let list2 = Variant::new(&metadata2, &value2);
727        // They should not be equal due to type mismatch
728        assert_ne!(list1, list2);
729    }
730
731    #[test]
732    fn test_variant_list_equality_slices() {
733        // Make an object like this and make sure equality works
734        // when the lists are sub fields
735        //
736        // {
737        //   "list1": [0, 1, 2, ..., 9],
738        //   "list2": [0, 1, 2, ..., 9],
739        //   "list3": [10, 11, 12, ..., 19],
740        //  }
741        let (metadata, value) = {
742            let mut builder = VariantBuilder::new();
743            let mut object_builder = builder.new_object();
744            // list1 (0..10)
745            let (metadata1, value1) = make_listi32(0i32..10i32);
746            object_builder.insert("list1", Variant::new(&metadata1, &value1));
747
748            // list2 (0..10)
749            let (metadata2, value2) = make_listi32(0i32..10i32);
750            object_builder.insert("list2", Variant::new(&metadata2, &value2));
751
752            // list3 (10..20)
753            let (metadata3, value3) = make_listi32(10i32..20i32);
754            object_builder.insert("list3", Variant::new(&metadata3, &value3));
755            object_builder.finish();
756            builder.finish()
757        };
758
759        let variant = Variant::try_new(&metadata, &value).unwrap();
760        let object = variant.as_object().unwrap();
761        // Check that list1 and list2 are equal
762        assert_eq!(object.get("list1").unwrap(), object.get("list2").unwrap());
763        // Check that list1 and list3 are not equal
764        assert_ne!(object.get("list1").unwrap(), object.get("list3").unwrap());
765    }
766
767    /// return the value bytes for `depth` single-element lists nested around a null
768    fn make_nested_lists(depth: usize) -> Vec<u8> {
769        let mut value = vec![0u8]; // null primitive
770        for _ in 0..depth {
771            // header: array basic type, 4-byte offsets, then num_elements=1 and two offsets
772            let mut outer = vec![0x0F, 1];
773            outer.extend_from_slice(&0u32.to_le_bytes());
774            outer.extend_from_slice(&(value.len() as u32).to_le_bytes());
775            outer.append(&mut value);
776            value = outer;
777        }
778        value
779    }
780
781    #[test]
782    fn test_variant_list_nesting_depth_limit() {
783        let metadata = [0x01, 0, 0]; // empty dictionary
784
785        let value = make_nested_lists(MAX_NESTING_DEPTH);
786        Variant::try_new(&metadata, &value).unwrap();
787
788        // One level deeper would recurse past the limit -- previously a stack overflow
789        let value = make_nested_lists(MAX_NESTING_DEPTH + 1);
790        let err = Variant::try_new(&metadata, &value).unwrap_err();
791        assert!(
792            err.to_string().contains("nesting depth exceeds"),
793            "unexpected error: {err}"
794        );
795    }
796
797    /// return metadata/value for a simple variant list with values in a range
798    fn make_listi32(range: Range<i32>) -> (Vec<u8>, Vec<u8>) {
799        let mut variant_builder = VariantBuilder::new();
800        let mut list_builder = variant_builder.new_list();
801        list_builder.extend(range);
802        list_builder.finish();
803        variant_builder.finish()
804    }
805
806    /// return metadata/value for a simple variant list with values in a range
807    fn make_listi64(range: Range<i64>) -> (Vec<u8>, Vec<u8>) {
808        let mut variant_builder = VariantBuilder::new();
809        let mut list_builder = variant_builder.new_list();
810        list_builder.extend(range);
811        list_builder.finish();
812        variant_builder.finish()
813    }
814}