1use super::codec::{
19 field_ref_mode, field_type_with_ref_flags, generic_field_type, same_numeric_family, Codec,
20 CodecReadType,
21};
22use super::primitive_list;
23use crate::context::ReadContext;
24use crate::context::WriteContext;
25use crate::error::Error;
26use crate::meta::FieldType;
27use crate::resolver::{RefFlag, RefMode};
28use crate::serializer::{core::read_value_type_info, Serializer};
29use crate::type_id::{self, need_to_write_type_for_field, PRIMITIVE_ARRAY_TYPES};
30
31pub const TRACKING_REF: u8 = 0b1;
32
33pub const HAS_NULL: u8 = 0b10;
34
35pub const DECL_ELEMENT_TYPE: u8 = 0b100;
37
38pub const IS_SAME_TYPE: u8 = 0b1000;
40
41const UNBACKED_CHECK_INTERVAL: usize = 1024;
42
43#[inline(always)]
44fn count_needs_bytes<T, const COUNT_ALLOCATES: bool, const ZST_NO_BACKING: bool>() -> bool {
45 COUNT_ALLOCATES && (!ZST_NO_BACKING || std::mem::size_of::<T>() != 0)
46}
47
48#[inline(always)]
49fn check_collection_len<T, const COUNT_ALLOCATES: bool, const ZST_NO_BACKING: bool>(
50 context: &ReadContext,
51 len: u32,
52 element_read_always_advances: bool,
53) -> Result<(), Error> {
54 let len = len as usize;
55 if count_needs_bytes::<T, COUNT_ALLOCATES, ZST_NO_BACKING>() {
58 let required = if element_read_always_advances {
59 len
60 } else {
61 len.saturating_sub(context.remaining_unbacked_container_items())
62 };
63 context.reader.check_bound(required)?;
64 }
65 Ok(())
66}
67
68#[inline(always)]
69pub(super) fn field_read_data_always_advances(field_type: &FieldType) -> bool {
70 matches!(
71 field_type.type_id,
72 type_id::BOOL..=type_id::NAMED_ENUM
73 | type_id::UNION..=type_id::NAMED_UNION
74 | type_id::DURATION..=type_id::FLOAT64_ARRAY
75 | type_id::U128..=type_id::ISIZE_ARRAY
76 )
77}
78
79#[inline(always)]
80pub(super) fn settle_unbacked_items(
81 context: &mut ReadContext,
82 completed: usize,
83 start: usize,
84) -> Result<(), Error> {
85 let consumed = context.reader.get_cursor().saturating_sub(start);
86 context.reserve_unbacked_container_items(completed.saturating_sub(consumed))
87}
88
89#[inline(always)]
90fn value_read_data_always_advances<S: Serializer>(
91 read_type: Option<&std::rc::Rc<crate::TypeInfo>>,
92) -> bool {
93 S::READ_DATA_ALWAYS_ADVANCES
94 && read_type.map_or(true, |type_info| type_info.has_exact_local_schema())
95}
96
97#[inline(always)]
98fn codec_read_data_always_advances<T: 'static, C: Codec<T>>(
99 read_type: Option<&CodecReadType>,
100) -> bool {
101 C::READ_DATA_ALWAYS_ADVANCES
102 && read_type.map_or(true, |read_type| match read_type {
103 CodecReadType::Field(field_type) => field_read_data_always_advances(field_type),
104 CodecReadType::TypeInfo(type_info) => type_info.has_exact_local_schema(),
105 })
106}
107
108#[cold]
109#[inline(never)]
110fn graph_memory_overflow() -> Error {
111 Error::invalid_data("graph memory estimate overflows")
112}
113
114#[cold]
115#[inline(never)]
116fn missing_collection_type() -> Error {
117 Error::type_error("Unable to determine concrete type for polymorphic collection elements")
118}
119
120#[cold]
121#[inline(never)]
122fn primitive_collection_mismatch() -> Error {
123 Error::type_error(
124 "Vec<number> belongs to the `number_array` type, \
125 and Vec<Option<number>> belongs to the `list` type. \
126 You should not read data of type `number_array` as data of type `list`.",
127 )
128}
129
130#[cold]
131#[inline(never)]
132fn collection_type_mismatch(expected: u32, actual: u32) -> Error {
133 Error::type_mismatch(expected, actual)
134}
135
136#[cold]
137#[inline(never)]
138fn non_polymorphic_collection() -> Error {
139 Error::type_error("Type inconsistent, target type is not polymorphic")
140}
141
142#[cold]
143#[inline(never)]
144fn not_primitive_array() -> Error {
145 Error::type_error("array-compatible field is not a primitive array")
146}
147
148#[cold]
149#[inline(never)]
150fn invalid_primitive_array_len() -> Error {
151 Error::invalid_data("Invalid data length")
152}
153
154#[cold]
155#[inline(never)]
156fn list_array_error(message: &'static str) -> Error {
157 Error::type_error(message)
158}
159
160#[inline(always)]
161fn reserve_collection_storage(
162 context: &mut ReadContext,
163 len: u32,
164 elem_bytes: usize,
165) -> Result<(), Error> {
166 let len = len as usize;
167 let bytes = len
168 .checked_mul(elem_bytes)
169 .ok_or_else(graph_memory_overflow)?;
170 context.reserve_graph_memory(bytes)?;
171 Ok(())
172}
173
174pub fn write_collection_type_info(
175 context: &mut WriteContext,
176 collection_type_id: u32,
177) -> Result<(), Error> {
178 context.writer.write_u8(collection_type_id as u8);
179 Ok(())
180}
181
182macro_rules! collection_write_mode {
183 (value, $T:ty, $S:ty, $value:expr, $context:expr, $ref_mode:expr, $write_type:expr, $has_generics:expr) => {
184 <$S as Serializer>::write($value, $context, $ref_mode, $write_type)
185 };
186 (field, $T:ty, $C:ty, $value:expr, $context:expr, $ref_mode:expr, $write_type:expr, $has_generics:expr) => {
187 <$C as Codec<$T>>::write_with_mode($value, $context, $ref_mode, $write_type, $has_generics)
188 };
189}
190
191macro_rules! collection_write_with_info {
192 (value, $T:ty, $S:ty, $value:expr, $context:expr, $ref_mode:expr, $type_info:expr, $has_generics:expr) => {
193 <$S as Serializer>::write_with_type_info($value, $context, $ref_mode, $type_info)
194 };
195 (field, $T:ty, $C:ty, $value:expr, $context:expr, $ref_mode:expr, $type_info:expr, $has_generics:expr) => {
196 <$C as Codec<$T>>::write_with_type_info(
197 $value,
198 $context,
199 $ref_mode,
200 $type_info,
201 $has_generics,
202 )
203 };
204}
205
206macro_rules! collection_reserved_space {
207 (value, $T:ty, $S:ty) => {
208 <$S as Serializer>::reserved_space()
209 };
210 (field, $T:ty, $C:ty) => {
211 <$C as Codec<$T>>::field_reserved_space()
212 };
213}
214
215macro_rules! write_collection_dyn_body {
216 ($layer:ident, $T:ident, $C:ident, $iter:expr, $context:expr, $has_generics:expr) => {{
217 let context = &mut *$context;
218 let has_generics = $has_generics;
219 let elem_static_type_id = $C::static_type_id();
220 let is_elem_declared = has_generics && !need_to_write_type_for_field(elem_static_type_id);
221 let elem_is_polymorphic = $C::IS_POLYMORPHIC;
222 let elem_is_shared_ref = $C::IS_SHARED_REF;
223 let can_preinspect_dynamic_type = !elem_is_polymorphic || !$C::REQUIRES_SCOPED_ACCESS;
224
225 let iter = $iter.into_iter();
226 let mut has_null = elem_is_polymorphic && !can_preinspect_dynamic_type;
227 let mut is_same_type = can_preinspect_dynamic_type;
228 let mut first_type_id: Option<std::any::TypeId> = None;
229
230 if can_preinspect_dynamic_type {
231 for item in iter.clone() {
232 if elem_is_polymorphic {
233 if let Some(dynamic_type_id) = $C::dynamic_type_id(item)? {
234 if is_same_type {
235 if let Some(first_id) = first_type_id {
236 if first_id != dynamic_type_id {
237 is_same_type = false;
238 }
239 } else {
240 first_type_id = Some(dynamic_type_id);
241 }
242 }
243 } else {
244 has_null = true;
245 }
246 } else if $C::is_none(item) {
247 has_null = true;
248 }
249 }
250 }
251
252 if elem_is_polymorphic && is_same_type && first_type_id.is_none() {
253 is_same_type = false;
255 }
256
257 let mut header = 0u8;
258 if has_null {
259 header |= HAS_NULL;
260 }
261 if is_elem_declared {
262 header |= DECL_ELEMENT_TYPE;
263 }
264 if is_same_type {
265 header |= IS_SAME_TYPE;
266 }
267 if elem_is_shared_ref {
268 header |= TRACKING_REF;
269 }
270 context.writer.write_u8(header);
271
272 let type_info = if is_same_type && !is_elem_declared {
273 if elem_is_polymorphic {
274 let type_id = first_type_id.ok_or_else(missing_collection_type)?;
275 Some($C::write_type_info_value(context, type_id)?)
276 } else {
277 $C::write_type_info(context)?;
278 None
279 }
280 } else {
281 None
282 };
283 let elem_ref_mode = if elem_is_shared_ref {
284 RefMode::Tracking
285 } else if has_null {
286 RefMode::NullOnly
287 } else {
288 RefMode::None
289 };
290
291 if is_same_type {
292 if let Some(type_info) = type_info.as_ref() {
293 for item in iter {
294 collection_write_with_info!(
295 $layer,
296 $T,
297 $C,
298 item,
299 context,
300 elem_ref_mode,
301 type_info,
302 has_generics
303 )?;
304 }
305 } else if elem_ref_mode == RefMode::None {
306 if has_generics {
307 for item in iter {
308 collection_write_mode!(
309 $layer,
310 $T,
311 $C,
312 item,
313 context,
314 RefMode::None,
315 false,
316 true
317 )?;
318 }
319 } else {
320 for item in iter {
321 $C::write_data(item, context)?;
322 }
323 }
324 } else {
325 for item in iter {
326 collection_write_mode!(
327 $layer,
328 $T,
329 $C,
330 item,
331 context,
332 elem_ref_mode,
333 false,
334 has_generics
335 )?;
336 }
337 }
338 } else {
339 for item in iter {
340 collection_write_mode!(
341 $layer,
342 $T,
343 $C,
344 item,
345 context,
346 elem_ref_mode,
347 true,
348 has_generics
349 )?;
350 }
351 }
352 Ok(())
353 }};
354}
355
356macro_rules! write_collection_body {
357 (
358 $layer:ident,
359 $T:ident,
360 $C:ident,
361 $iter:expr,
362 $context:expr,
363 $has_generics:expr,
364 $count_allocates:ident,
365 $zst_no_backing:ident
366 ) => {{
367 let context = &mut *$context;
368 let iter = $iter.into_iter();
369 let len = iter.len();
370 context.writer.write_var_u32(len as u32);
371 if len == 0 {
372 return Ok(());
373 }
374 let has_generics = $has_generics;
375 if $C::IS_POLYMORPHIC || $C::IS_SHARED_REF {
376 write_collection_dyn_body!($layer, $T, $C, iter, context, has_generics)?;
377 return Ok(());
378 }
379 let mut header = IS_SAME_TYPE;
380 let mut has_null = false;
381 let elem_static_type_id = $C::static_type_id();
382 let is_elem_declared = has_generics && !need_to_write_type_for_field(elem_static_type_id);
383 if $C::IS_OPTIONAL {
384 for item in iter.clone() {
385 if $C::is_none(item) {
386 has_null = true;
387 break;
388 }
389 }
390 }
391 if has_null {
392 header |= HAS_NULL;
393 }
394 if is_elem_declared {
395 header |= DECL_ELEMENT_TYPE;
396 context.writer.write_u8(header);
397 } else {
398 context.writer.write_u8(header);
399 $C::write_type_info(context)?;
400 }
401 context
402 .writer
403 .reserve(len * collection_reserved_space!($layer, $T, $C));
404 if !has_null {
405 if has_generics {
406 for item in iter {
407 collection_write_mode!(
408 $layer,
409 $T,
410 $C,
411 item,
412 context,
413 RefMode::None,
414 false,
415 true
416 )?;
417 }
418 } else {
419 for item in iter {
420 $C::write_data(item, context)?;
421 }
422 }
423 } else {
424 for item in iter {
427 collection_write_mode!(
428 $layer,
429 $T,
430 $C,
431 item,
432 context,
433 RefMode::NullOnly,
434 false,
435 has_generics
436 )?;
437 }
438 }
439
440 Ok(())
441 }};
442}
443
444pub fn write_collection_value_data<
445 'a,
446 T,
447 S,
448 I,
449 const COUNT_ALLOCATES: bool,
450 const ZST_NO_BACKING: bool,
451>(
452 iter: I,
453 context: &mut WriteContext,
454) -> Result<(), Error>
455where
456 T: 'static + 'a,
457 S: Serializer<Target = T>,
458 I: IntoIterator<Item = &'a T>,
459 I::IntoIter: ExactSizeIterator + Clone,
460{
461 write_collection_body!(
462 value,
463 T,
464 S,
465 iter,
466 context,
467 false,
468 COUNT_ALLOCATES,
469 ZST_NO_BACKING
470 )
471}
472
473pub fn write_collection_data<'a, T, C, I, const COUNT_ALLOCATES: bool, const ZST_NO_BACKING: bool>(
474 iter: I,
475 context: &mut WriteContext,
476 has_generics: bool,
477) -> Result<(), Error>
478where
479 T: 'static + 'a,
480 C: Codec<T>,
481 I: IntoIterator<Item = &'a T>,
482 I::IntoIter: ExactSizeIterator + Clone,
483{
484 write_collection_body!(
485 field,
486 T,
487 C,
488 iter,
489 context,
490 has_generics,
491 COUNT_ALLOCATES,
492 ZST_NO_BACKING
493 )
494}
495
496pub fn read_collection_type_info(
497 context: &mut ReadContext,
498 collection_type_id: u32,
499) -> Result<(), Error> {
500 let remote_collection_type_id = context.reader.read_u8()? as u32;
501 if PRIMITIVE_ARRAY_TYPES.contains(&remote_collection_type_id) {
502 return Err(primitive_collection_mismatch());
503 }
504 if collection_type_id != remote_collection_type_id {
505 return Err(collection_type_mismatch(
506 collection_type_id,
507 remote_collection_type_id,
508 ));
509 }
510 Ok(())
511}
512
513macro_rules! collection_read_type {
514 (value, $T:ty, $S:ty, $context:expr) => {
515 read_value_type_info::<$S>($context)?
516 };
517 (field, $T:ty, $C:ty, $context:expr) => {
518 Some(<$C as Codec<$T>>::read_type_info_value($context)?)
519 };
520}
521
522macro_rules! collection_read_element {
523 (value, $T:ty, $S:ty, $context:expr, $read_type:expr) => {
524 match $read_type {
525 None => <$S as Serializer>::read_data($context),
526 Some(type_info) => {
527 <$S as Serializer>::read_with_type_info($context, RefMode::None, type_info)
528 }
529 }
530 };
531 (field, $T:ty, $C:ty, $context:expr, $read_type:expr) => {
532 match $read_type {
533 None => <$C as Serializer>::read_data($context),
534 Some(CodecReadType::Field(field_type)) => {
535 <$C as Codec<$T>>::read_data_with_type($context, field_type)
536 }
537 Some(CodecReadType::TypeInfo(type_info)) => {
538 <$C as Codec<$T>>::read_data_with_type_info($context, type_info)
539 }
540 }
541 };
542}
543
544macro_rules! element_read_data_always_advances {
545 (value, $T:ty, $S:ty, $read_type:expr) => {
546 value_read_data_always_advances::<$S>($read_type)
547 };
548 (field, $T:ty, $C:ty, $read_type:expr) => {
549 codec_read_data_always_advances::<$T, $C>($read_type)
550 };
551}
552
553macro_rules! collect_with_unbacked_budget {
554 ($R:ty, $T:ty, $len:expr, $context:expr, $read:expr) => {{
555 let mut window_start = $context.reader.get_cursor();
556 let mut window_items = 0usize;
557 let values = (0..$len)
558 .map(|_| {
559 let value: $T = ($read)?;
560 window_items += 1;
561 if window_items == UNBACKED_CHECK_INTERVAL {
562 settle_unbacked_items($context, window_items, window_start)?;
563 window_start = $context.reader.get_cursor();
564 window_items = 0;
565 }
566 Ok(value)
567 })
568 .collect::<Result<$R, Error>>()?;
569 if window_items != 0 {
570 settle_unbacked_items($context, window_items, window_start)?;
571 }
572 Ok(values)
573 }};
574}
575
576macro_rules! read_collection_body {
577 (
578 $layer:ident,
579 $R:ident,
580 $T:ident,
581 $C:ident,
582 $context:expr,
583 $count_allocates:ident,
584 $zst_no_backing:ident
585 ) => {{
586 let context = &mut *$context;
587 let len = context.reader.read_var_u32()?;
588 reserve_collection_storage(context, len, std::mem::size_of::<$T>())?;
589 if len == 0 {
590 return Ok($R::from_iter(std::iter::empty()));
591 }
592 if $C::IS_POLYMORPHIC || $C::IS_SHARED_REF {
593 return read_collection_data_dyn_ref::<$R, $T, $C, $count_allocates, $zst_no_backing>(
594 context, len,
595 );
596 }
597 let header = context.reader.read_u8()?;
598 let declared = (header & DECL_ELEMENT_TYPE) != 0;
599 let read_type = if declared {
600 None
601 } else {
602 collection_read_type!($layer, $T, $C, context)
603 };
604 let has_null = (header & HAS_NULL) != 0;
605 if (header & IS_SAME_TYPE) == 0 {
606 return Err(non_polymorphic_collection());
607 }
608 let element_read_always_advances =
609 has_null || element_read_data_always_advances!($layer, $T, $C, read_type.as_ref());
610 check_collection_len::<$T, $count_allocates, $zst_no_backing>(
611 context,
612 len,
613 element_read_always_advances,
614 )?;
615 if !has_null {
616 if element_read_always_advances {
617 (0..len)
618 .map(|_| collection_read_element!($layer, $T, $C, context, read_type.as_ref()))
619 .collect::<Result<$R, Error>>()
620 } else {
621 collect_with_unbacked_budget!(
622 $R,
623 $T,
624 len,
625 context,
626 collection_read_element!($layer, $T, $C, context, read_type.as_ref())
627 )
628 }
629 } else {
630 (0..len)
631 .map(|_| {
632 let flag = context.reader.read_i8()?;
633 if flag == RefFlag::Null as i8 {
634 return $C::default_value(context);
635 }
636 collection_read_element!($layer, $T, $C, context, read_type.as_ref())
637 })
638 .collect::<Result<$R, Error>>()
639 }
640 }};
641}
642
643pub fn read_collection_value_data<
644 R,
645 T,
646 S,
647 const COUNT_ALLOCATES: bool,
648 const ZST_NO_BACKING: bool,
649>(
650 context: &mut ReadContext,
651) -> Result<R, Error>
652where
653 T: 'static,
654 S: Serializer<Target = T>,
655 R: FromIterator<T>,
656{
657 read_collection_body!(value, R, T, S, context, COUNT_ALLOCATES, ZST_NO_BACKING)
658}
659
660pub fn read_collection_data<R, T, C, const COUNT_ALLOCATES: bool, const ZST_NO_BACKING: bool>(
661 context: &mut ReadContext,
662) -> Result<R, Error>
663where
664 T: 'static,
665 C: Codec<T>,
666 R: FromIterator<T>,
667{
668 read_collection_body!(field, R, T, C, context, COUNT_ALLOCATES, ZST_NO_BACKING)
669}
670
671pub fn read_collection_data_with_type<
673 R,
674 T,
675 C,
676 const COUNT_ALLOCATES: bool,
677 const ZST_NO_BACKING: bool,
678>(
679 context: &mut ReadContext,
680 remote_field_type: &FieldType,
681) -> Result<R, Error>
682where
683 T: 'static,
684 C: Codec<T>,
685 R: FromIterator<T>,
686{
687 let element_type = generic_field_type(remote_field_type, 0, "collection")?;
688 let len = context.reader.read_var_u32()?;
689 reserve_collection_storage(context, len, std::mem::size_of::<T>())?;
690 if len == 0 {
691 return Ok(R::from_iter(std::iter::empty()));
692 }
693
694 let header = context.reader.read_u8()?;
695 let track_ref = (header & TRACKING_REF) != 0;
696 let same_type = (header & IS_SAME_TYPE) != 0;
697 let has_null = (header & HAS_NULL) != 0;
698 let declared = (header & DECL_ELEMENT_TYPE) != 0;
699 let ref_mode = if track_ref {
700 RefMode::Tracking
701 } else if has_null {
702 RefMode::NullOnly
703 } else {
704 RefMode::None
705 };
706
707 if C::IS_POLYMORPHIC || C::IS_SHARED_REF {
708 if same_type {
709 if declared {
710 let element_type = field_type_with_ref_flags(element_type, has_null, track_ref);
711 let element_read_always_advances = ref_mode != RefMode::None
712 || (C::READ_DATA_ALWAYS_ADVANCES
713 && field_read_data_always_advances(&element_type));
714 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
715 context,
716 len,
717 element_read_always_advances,
718 )?;
719 if element_read_always_advances {
720 return (0..len)
721 .map(|_| C::read_field_with_type(context, &element_type))
722 .collect::<Result<R, Error>>();
723 }
724 return collect_with_unbacked_budget!(
725 R,
726 T,
727 len,
728 context,
729 C::read_field_with_type(context, &element_type)
730 );
731 }
732 let type_info = context.read_any_type_info()?;
733 let element_read_always_advances = ref_mode != RefMode::None
734 || (C::READ_DATA_ALWAYS_ADVANCES && type_info.has_exact_local_schema());
735 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
736 context,
737 len,
738 element_read_always_advances,
739 )?;
740 if element_read_always_advances {
741 return (0..len)
742 .map(|_| C::read_with_type_info(context, ref_mode, &type_info))
743 .collect::<Result<R, Error>>();
744 }
745 return collect_with_unbacked_budget!(
746 R,
747 T,
748 len,
749 context,
750 C::read_with_type_info(context, ref_mode, &type_info)
751 );
752 }
753 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(context, len, true)?;
754 return (0..len)
755 .map(|_| C::read(context, ref_mode, true))
756 .collect::<Result<R, Error>>();
757 }
758
759 if !same_type {
760 return Err(non_polymorphic_collection());
761 }
762 if declared {
763 if has_null {
764 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(context, len, true)?;
765 return (0..len)
766 .map(|_| {
767 if context.reader.read_i8()? == RefFlag::Null as i8 {
768 C::default_value(context)
769 } else {
770 C::read_data_with_type(context, element_type)
771 }
772 })
773 .collect::<Result<R, Error>>();
774 }
775 let element_read_data_always_advances =
776 C::READ_DATA_ALWAYS_ADVANCES && field_read_data_always_advances(element_type);
777 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
778 context,
779 len,
780 element_read_data_always_advances,
781 )?;
782 if element_read_data_always_advances {
783 return (0..len)
784 .map(|_| C::read_data_with_type(context, element_type))
785 .collect::<Result<R, Error>>();
786 }
787 return collect_with_unbacked_budget!(
788 R,
789 T,
790 len,
791 context,
792 C::read_data_with_type(context, element_type)
793 );
794 }
795
796 let read_type = C::read_type_info_value(context)?;
797 if has_null {
798 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(context, len, true)?;
799 (0..len)
800 .map(|_| {
801 if context.reader.read_i8()? == RefFlag::Null as i8 {
802 C::default_value(context)
803 } else {
804 match &read_type {
805 super::codec::CodecReadType::Field(field_type) => {
806 C::read_data_with_type(context, field_type)
807 }
808 super::codec::CodecReadType::TypeInfo(type_info) => {
809 C::read_data_with_type_info(context, type_info)
810 }
811 }
812 }
813 })
814 .collect::<Result<R, Error>>()
815 } else {
816 let element_read_data_always_advances =
817 codec_read_data_always_advances::<T, C>(Some(&read_type));
818 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
819 context,
820 len,
821 element_read_data_always_advances,
822 )?;
823 if element_read_data_always_advances {
824 (0..len)
825 .map(|_| match &read_type {
826 super::codec::CodecReadType::Field(field_type) => {
827 C::read_data_with_type(context, field_type)
828 }
829 super::codec::CodecReadType::TypeInfo(type_info) => {
830 C::read_data_with_type_info(context, type_info)
831 }
832 })
833 .collect::<Result<R, Error>>()
834 } else {
835 collect_with_unbacked_budget!(
836 R,
837 T,
838 len,
839 context,
840 match &read_type {
841 super::codec::CodecReadType::Field(field_type) => {
842 C::read_data_with_type(context, field_type)
843 }
844 super::codec::CodecReadType::TypeInfo(type_info) => {
845 C::read_data_with_type_info(context, type_info)
846 }
847 }
848 )
849 }
850 }
851}
852
853pub fn read_collection_data_dyn_ref<
855 R,
856 T,
857 C,
858 const COUNT_ALLOCATES: bool,
859 const ZST_NO_BACKING: bool,
860>(
861 context: &mut ReadContext,
862 len: u32,
863) -> Result<R, Error>
864where
865 T: 'static,
866 C: Serializer<Target = T>,
867 R: FromIterator<T>,
868{
869 let header = context.reader.read_u8()?;
871 let is_track_ref = (header & TRACKING_REF) != 0;
875 let is_same_type = (header & IS_SAME_TYPE) != 0;
876 let has_null = (header & HAS_NULL) != 0;
877 let is_declared = (header & DECL_ELEMENT_TYPE) != 0;
878
879 let elem_ref_mode = if is_track_ref {
881 RefMode::Tracking
882 } else if has_null {
883 RefMode::NullOnly
884 } else {
885 RefMode::None
886 };
887
888 if is_same_type {
890 if is_declared {
891 let element_read_always_advances =
892 elem_ref_mode != RefMode::None || C::READ_DATA_ALWAYS_ADVANCES;
893 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
894 context,
895 len,
896 element_read_always_advances,
897 )?;
898 if element_read_always_advances {
899 (0..len)
900 .map(|_| C::read(context, elem_ref_mode, false))
901 .collect::<Result<R, Error>>()
902 } else {
903 collect_with_unbacked_budget!(
904 R,
905 T,
906 len,
907 context,
908 C::read(context, elem_ref_mode, false)
909 )
910 }
911 } else {
912 let type_info = context.read_any_type_info()?;
913 let element_read_always_advances = elem_ref_mode != RefMode::None
914 || (C::READ_DATA_ALWAYS_ADVANCES && type_info.has_exact_local_schema());
915 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(
916 context,
917 len,
918 element_read_always_advances,
919 )?;
920 if element_read_always_advances {
921 (0..len)
922 .map(|_| C::read_with_type_info(context, elem_ref_mode, &type_info))
923 .collect::<Result<R, Error>>()
924 } else {
925 collect_with_unbacked_budget!(
926 R,
927 T,
928 len,
929 context,
930 C::read_with_type_info(context, elem_ref_mode, &type_info)
931 )
932 }
933 }
934 } else {
935 check_collection_len::<T, COUNT_ALLOCATES, ZST_NO_BACKING>(context, len, true)?;
936 (0..len)
937 .map(|_| C::read(context, elem_ref_mode, true))
938 .collect::<Result<R, Error>>()
939 }
940}
941
942fn list_element_type_matches_array(
943 list: &FieldType,
944 array: &FieldType,
945 require_unframed_element: bool,
946) -> bool {
947 primitive_list::element_type_id(array.type_id).is_some_and(|element_type_id| {
948 if list.type_id != type_id::LIST
949 || list.generics.len() != 1
950 || list.nullable
951 || list.track_ref
952 || array.nullable
953 || array.track_ref
954 {
955 return false;
956 }
957 let element = &list.generics[0];
958 if require_unframed_element && element.track_ref {
962 return false;
963 }
964 primitive_element_type_matches(element_type_id, element.type_id)
965 })
966}
967
968pub(super) fn compatible_list_array_field(local: &FieldType, remote: &FieldType) -> bool {
969 (local.type_id == type_id::LIST && list_element_type_matches_array(local, remote, false))
970 || (remote.type_id == type_id::LIST && list_element_type_matches_array(remote, local, true))
971}
972
973fn primitive_element_type_matches(array_element_type_id: u32, list_element_type_id: u32) -> bool {
974 array_element_type_id == list_element_type_id
975 || same_numeric_family(array_element_type_id, list_element_type_id)
976}
977
978#[inline(always)]
979fn primitive_element_min_wire_size(element_type_id: u32) -> Option<usize> {
980 match element_type_id {
981 type_id::BOOL
982 | type_id::INT8
983 | type_id::UINT8
984 | type_id::VARINT32
985 | type_id::VARINT64
986 | type_id::VAR_UINT32
987 | type_id::VAR_UINT64 => Some(1),
988 type_id::INT16 | type_id::UINT16 | type_id::FLOAT16 | type_id::BFLOAT16 => Some(2),
989 type_id::INT32
990 | type_id::UINT32
991 | type_id::FLOAT32
992 | type_id::TAGGED_INT64
993 | type_id::TAGGED_UINT64 => Some(4),
994 type_id::INT64 | type_id::UINT64 | type_id::FLOAT64 => Some(8),
995 _ => None,
996 }
997}
998
999fn read_primitive_array_with_codec<T, C>(
1000 context: &mut ReadContext,
1001 remote_field_type: &FieldType,
1002) -> Result<Vec<T>, Error>
1003where
1004 T: 'static,
1005 C: Codec<T>,
1006{
1007 let size_bytes = context.reader.read_var_u32()? as usize;
1008 let elem_size =
1009 primitive_list::element_size(remote_field_type.type_id).ok_or_else(not_primitive_array)?;
1010 if size_bytes % elem_size != 0 {
1011 return Err(invalid_primitive_array_len());
1012 }
1013 context.reader.check_bound(size_bytes)?;
1014 let len = size_bytes / elem_size;
1015 let element_type_id = primitive_list::element_type_id(remote_field_type.type_id)
1016 .ok_or_else(not_primitive_array)?;
1017 reserve_collection_storage(context, len as u32, std::mem::size_of::<T>())?;
1018 let element_type = FieldType::new(element_type_id, false, Vec::new());
1019 let mut vec = Vec::with_capacity(len);
1020 for _ in 0..len {
1021 vec.push(C::read_data_with_type(context, &element_type)?);
1022 }
1023 Ok(vec)
1024}
1025
1026pub(super) fn read_list_as_primitive_vec<T, C>(
1027 context: &mut ReadContext,
1028 remote_field_type: &FieldType,
1029) -> Result<Vec<T>, Error>
1030where
1031 T: 'static,
1032 C: Codec<T>,
1033{
1034 let element_type = generic_field_type(remote_field_type, 0, "list")?;
1035 let len = context.reader.read_var_u32()?;
1036 let len_usize = len as usize;
1037 if len == 0 {
1038 return Ok(Vec::new());
1039 }
1040 let header = context.reader.read_u8()?;
1041 if (header & HAS_NULL) != 0 {
1042 return Err(list_array_error(
1043 "compatible list to array field requires non-null elements",
1044 ));
1045 }
1046 if (header & TRACKING_REF) != 0 {
1047 return Err(list_array_error(
1048 "array-compatible list declares reference-tracked elements",
1049 ));
1050 }
1051 if (header & IS_SAME_TYPE) == 0 {
1052 return Err(list_array_error(
1053 "array-compatible list must declare same-type elements",
1054 ));
1055 }
1056 if (header & DECL_ELEMENT_TYPE) == 0 {
1057 return Err(list_array_error(
1058 "array-compatible list must declare element type",
1059 ));
1060 }
1061 let element_min_size =
1064 primitive_element_min_wire_size(element_type.type_id).ok_or_else(|| {
1065 list_array_error("array-compatible list element is not a supported primitive type")
1066 })?;
1067 let min_size_bytes = len_usize
1068 .checked_mul(element_min_size)
1069 .ok_or_else(invalid_primitive_array_len)?;
1070 context.reader.check_bound(min_size_bytes)?;
1071 reserve_collection_storage(context, len, std::mem::size_of::<T>())?;
1072 let mut vec = Vec::with_capacity(len_usize);
1073 for _ in 0..len {
1074 vec.push(C::read_data_with_type(context, element_type)?);
1075 }
1076 Ok(vec)
1077}
1078
1079#[cold]
1080#[inline(never)]
1081pub(super) fn read_vec_compatible_mismatch<T, C>(
1082 context: &mut ReadContext,
1083 local_field_type: &FieldType,
1084 remote_field_type: &FieldType,
1085) -> Result<Option<Vec<T>>, Error>
1086where
1087 T: 'static,
1088 C: Codec<T>,
1089{
1090 if local_field_type.type_id == type_id::LIST
1091 && list_element_type_matches_array(local_field_type, remote_field_type, false)
1092 {
1093 return read_array_data_as_vec_bridge::<T, C>(context, remote_field_type).map(Some);
1094 }
1095 Ok(None)
1096}
1097
1098fn read_array_data_as_vec_bridge<T, C>(
1099 context: &mut ReadContext,
1100 remote_field_type: &FieldType,
1101) -> Result<Vec<T>, Error>
1102where
1103 T: 'static,
1104 C: Codec<T>,
1105{
1106 if field_ref_mode(remote_field_type) != RefMode::None {
1107 let ref_flag = context.reader.read_i8()?;
1108 if ref_flag == RefFlag::Null as i8 {
1109 return Ok(Vec::new());
1110 }
1111 }
1112 if crate::serializer::util::field_need_read_type_info(remote_field_type.type_id) {
1113 let remote = context.reader.read_u8()? as u32;
1114 if remote != remote_field_type.type_id {
1115 return Err(collection_type_mismatch(remote_field_type.type_id, remote));
1116 }
1117 }
1118 read_primitive_array_with_codec::<T, C>(context, remote_field_type)
1119}
1120
1121#[cold]
1122#[inline(never)]
1123pub(super) fn read_primitive_array_vec_mismatch<T, C>(
1124 context: &mut ReadContext,
1125 local_field_type: &FieldType,
1126 remote_field_type: &FieldType,
1127) -> Result<Option<Vec<T>>, Error>
1128where
1129 T: 'static,
1130 C: Codec<T>,
1131{
1132 if remote_field_type.type_id == type_id::LIST
1133 && !remote_field_type.generics.is_empty()
1134 && list_element_type_matches_array(remote_field_type, local_field_type, true)
1135 {
1136 if field_ref_mode(remote_field_type) != RefMode::None {
1137 let ref_flag = context.reader.read_i8()?;
1138 if ref_flag == RefFlag::Null as i8 {
1139 return Ok(Some(Vec::new()));
1140 }
1141 }
1142 return read_list_as_primitive_vec::<T, C>(context, remote_field_type).map(Some);
1143 }
1144 Ok(None)
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150 use crate::serializer::codec::{I32Codec, I64Codec};
1151 use crate::serializer::VecSerializer;
1152 use crate::{Config, Fory, Reader, TypeId, TypeResolver};
1153
1154 #[derive(Clone, Debug, PartialEq)]
1155 struct EmptyValue(u8);
1156
1157 struct EmptyValueSerializer;
1158
1159 impl Serializer for EmptyValueSerializer {
1160 type Target = EmptyValue;
1161
1162 fn write_data(_: &EmptyValue, _: &mut WriteContext) -> Result<(), Error> {
1163 Ok(())
1164 }
1165
1166 fn read_data(_: &mut ReadContext) -> Result<EmptyValue, Error> {
1167 Ok(EmptyValue(0))
1168 }
1169
1170 fn default_value(_: &mut ReadContext) -> Result<EmptyValue, Error> {
1171 Ok(EmptyValue(0))
1172 }
1173
1174 fn static_type_id() -> TypeId {
1175 TypeId::EXT
1176 }
1177 }
1178
1179 #[test]
1180 fn empty_items_use_root_budget() {
1181 let mut fory = Fory::builder().max_unbacked_container_items(2).build();
1182 fory.register_serializer::<EmptyValueSerializer>(200)
1183 .unwrap();
1184 let mut oversized = fory
1185 .serialize_with::<VecSerializer<EmptyValueSerializer>>(&vec![EmptyValue(0); 3])
1186 .unwrap();
1187 oversized.push(0);
1190 let error = fory
1191 .deserialize_with::<VecSerializer<EmptyValueSerializer>>(&oversized)
1192 .unwrap_err();
1193 assert!(error.to_string().contains("max_unbacked_container_items"));
1194
1195 let valid = fory
1196 .serialize_with::<VecSerializer<EmptyValueSerializer>>(&vec![EmptyValue(0); 2])
1197 .unwrap();
1198 assert_eq!(
1199 fory.deserialize_with::<VecSerializer<EmptyValueSerializer>>(&valid)
1200 .unwrap(),
1201 vec![EmptyValue(0); 2]
1202 );
1203 }
1204
1205 #[test]
1206 fn positive_items_ignore_zero_budget() {
1207 let fory = Fory::builder().max_unbacked_container_items(0).build();
1208 let value = vec![1i32, 2, 3];
1209 let bytes = fory.serialize_with::<VecSerializer<i32>>(&value).unwrap();
1210 assert_eq!(
1211 fory.deserialize_with::<VecSerializer<i32>>(&bytes).unwrap(),
1212 value
1213 );
1214 }
1215
1216 #[test]
1217 fn count_allocation_uses_allowance() {
1218 let bytes = [3, IS_SAME_TYPE | DECL_ELEMENT_TYPE];
1219 let mut context = ReadContext::new(TypeResolver::default(), Config::default());
1220 context.remaining_graph_memory_bytes = usize::MAX;
1221 context.remaining_unbacked_container_items = 1;
1222 context.attach_reader(Reader::new(&bytes));
1223
1224 let error = read_collection_value_data::<
1225 Vec<EmptyValue>,
1226 EmptyValue,
1227 EmptyValueSerializer,
1228 true,
1229 true,
1230 >(&mut context)
1231 .unwrap_err();
1232 assert!(matches!(error, Error::BufferOutOfBound(..)));
1233 assert_eq!(context.reader.get_cursor(), 2);
1234 }
1235
1236 #[test]
1237 fn settles_periodic_window_and_tail() {
1238 let mut bytes = Vec::new();
1239 let mut writer = crate::Writer::from_buffer(&mut bytes);
1240 writer.write_var_u32(1025);
1241 writer.write_u8(IS_SAME_TYPE | DECL_ELEMENT_TYPE);
1242 writer.write_u8(0);
1243 let mut context = ReadContext::new(TypeResolver::default(), Config::default());
1244 context.remaining_graph_memory_bytes = usize::MAX;
1245 context.remaining_unbacked_container_items = 1024;
1246 context.attach_reader(Reader::new(&bytes));
1247
1248 let error = read_collection_value_data::<
1249 Vec<EmptyValue>,
1250 EmptyValue,
1251 EmptyValueSerializer,
1252 true,
1253 true,
1254 >(&mut context)
1255 .unwrap_err();
1256 assert!(error.to_string().contains("max_unbacked_container_items"));
1257 assert_eq!(context.remaining_unbacked_container_items, 0);
1258 }
1259
1260 #[test]
1261 fn list_array_checks_fixed_width_body() {
1262 let bytes = [2, IS_SAME_TYPE | DECL_ELEMENT_TYPE, 1, 0, 0, 0];
1263 let config = Config::default();
1264 let mut context = ReadContext::new(TypeResolver::default(), config);
1265 let graph_memory = 2 * std::mem::size_of::<i32>();
1266 context.remaining_graph_memory_bytes = graph_memory;
1267 context.attach_reader(Reader::new(&bytes));
1268 let remote = FieldType::new(
1269 type_id::LIST,
1270 false,
1271 vec![FieldType::new(type_id::INT32, false, Vec::new())],
1272 );
1273
1274 let error = read_list_as_primitive_vec::<
1275 i32,
1276 I32Codec<{ type_id::INT32 as u8 }, false, false>,
1277 >(&mut context, &remote)
1278 .unwrap_err();
1279
1280 assert!(matches!(error, Error::BufferOutOfBound(..)));
1281 assert_eq!(context.reader.get_cursor(), 2);
1282 assert_eq!(context.remaining_graph_memory_bytes, graph_memory);
1283 }
1284
1285 #[test]
1286 fn list_array_checks_tagged_body() {
1287 let bytes = [2, IS_SAME_TYPE | DECL_ELEMENT_TYPE, 0, 0, 0, 0];
1288 let config = Config::default();
1289 let mut context = ReadContext::new(TypeResolver::default(), config);
1290 let graph_memory = 2 * std::mem::size_of::<i64>();
1291 context.remaining_graph_memory_bytes = graph_memory;
1292 context.attach_reader(Reader::new(&bytes));
1293 let remote = FieldType::new(
1294 type_id::LIST,
1295 false,
1296 vec![FieldType::new(type_id::TAGGED_INT64, false, Vec::new())],
1297 );
1298
1299 let error = read_list_as_primitive_vec::<
1300 i64,
1301 I64Codec<{ type_id::TAGGED_INT64 as u8 }, false, false>,
1302 >(&mut context, &remote)
1303 .unwrap_err();
1304
1305 assert!(matches!(error, Error::BufferOutOfBound(..)));
1306 assert_eq!(context.reader.get_cursor(), 2);
1307 assert_eq!(context.remaining_graph_memory_bytes, graph_memory);
1308 }
1309}