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}