Skip to main content

zerodds_types/type_object/
common.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Gemeinsam genutzte Wire-Types fuer TypeObject (Minimal + Complete).
4//!
5//! XTypes §7.3.4.5 (CommonStructMember, NameHash, MemberId).
6
7use alloc::string::String;
8use alloc::vec::Vec;
9
10use zerodds_cdr::{BufferReader, BufferWriter, DecodeError, EncodeError};
11
12use crate::type_identifier::TypeIdentifier;
13
14use super::flags::{StructMemberFlag, UnionMemberFlag};
15
16/// 32-bit Member-ID (§7.3.4.5). Wird entweder explizit via `@id(n)`
17/// vergeben oder aus dem Member-Namen gehasht (`@autoid(HASH)`).
18pub type MemberId = u32;
19
20/// 4-byte Name-Hash (§7.3.4.5 — "MD5(name)[0..4]").
21///
22/// Wird im MinimalTypeObject statt des vollen Namens gespeichert, um
23/// die Payload klein zu halten. Im CompleteTypeObject liegt der volle
24/// Name zusaetzlich vor.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
26pub struct NameHash(pub [u8; 4]);
27
28impl NameHash {
29    /// Berechnet den 4-byte NameHash aus einem Member-/Literal-Namen.
30    ///
31    /// Spec §7.3.4.5: "the `name_hash` is computed as the first 4
32    /// octets of the MD5 hash of the name, interpreted as ASCII/UTF-8".
33    #[must_use]
34    pub fn from_name(name: &str) -> Self {
35        let digest = zerodds_foundation::md5(name.as_bytes());
36        let out: [u8; 4] = [digest[0], digest[1], digest[2], digest[3]];
37        Self(out)
38    }
39
40    /// Encoded als `octet[4]` (4 byte, keine Laenge, kein Padding).
41    ///
42    /// # Errors
43    /// Buffer-Overflow.
44    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
45        w.write_bytes(&self.0)
46    }
47
48    /// Decoder.
49    ///
50    /// # Errors
51    /// Buffer-Underflow.
52    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
53        let bytes = r.read_bytes(4)?;
54        // `read_bytes(n)` gibt garantiert genau n Bytes zurueck, sonst
55        // UnexpectedEof. Der try_into auf [u8; 4] ist daher infallibel.
56        let Ok(out): Result<[u8; 4], _> = bytes.try_into() else {
57            return Err(DecodeError::UnexpectedEof {
58                needed: 4,
59                offset: 0,
60            });
61        };
62        Ok(Self(out))
63    }
64}
65
66/// CommonStructMember (§7.3.4.5.2).
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct CommonStructMember {
69    /// Member-ID (4 byte).
70    pub member_id: MemberId,
71    /// Flags (IS_KEY, IS_OPTIONAL, etc.).
72    pub member_flags: StructMemberFlag,
73    /// Typ des Members (kann rekursiv sein).
74    pub member_type_id: TypeIdentifier,
75}
76
77impl CommonStructMember {
78    /// Encoded als `{ u32 member_id; u16 member_flags; TypeIdentifier member_type_id; }`.
79    ///
80    /// # Errors
81    /// Buffer-Overflow.
82    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
83        w.write_u32(self.member_id)?;
84        w.write_u16(self.member_flags.0)?;
85        self.member_type_id.encode_into(w)
86    }
87
88    /// Decoder.
89    ///
90    /// # Errors
91    /// Buffer-Underflow.
92    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
93        let member_id = r.read_u32()?;
94        let member_flags = StructMemberFlag(r.read_u16()?);
95        let member_type_id = TypeIdentifier::decode_from(r)?;
96        Ok(Self {
97            member_id,
98            member_flags,
99            member_type_id,
100        })
101    }
102}
103
104/// CommonUnionMember (§7.3.4.5.3). Enthaelt zusaetzlich die Label-Liste.
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct CommonUnionMember {
107    /// Member-ID.
108    pub member_id: MemberId,
109    /// Flags (IS_DEFAULT fuer default-case).
110    pub member_flags: UnionMemberFlag,
111    /// Typ des Members.
112    pub type_id: TypeIdentifier,
113    /// Case-Labels als `i32`-Sequence (Spec §7.3.4.5.3.2: `long[]`).
114    pub label_seq: alloc::vec::Vec<i32>,
115}
116
117impl CommonUnionMember {
118    /// Encode.
119    ///
120    /// # Errors
121    /// Buffer-Overflow.
122    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
123        w.write_u32(self.member_id)?;
124        w.write_u16(self.member_flags.0)?;
125        self.type_id.encode_into(w)?;
126        // sequence<long>: u32 Laenge + N*i32
127        let len =
128            u32::try_from(self.label_seq.len()).map_err(|_| EncodeError::ValueOutOfRange {
129                message: "union label sequence length exceeds u32::MAX",
130            })?;
131        w.write_u32(len)?;
132        for l in &self.label_seq {
133            w.write_u32(*l as u32)?;
134        }
135        Ok(())
136    }
137
138    /// Decoder.
139    ///
140    /// # Errors
141    /// Buffer-Underflow.
142    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
143        let member_id = r.read_u32()?;
144        let member_flags = UnionMemberFlag(r.read_u16()?);
145        let type_id = TypeIdentifier::decode_from(r)?;
146        let len = r.read_u32()? as usize;
147        let cap = safe_capacity(len, 4, r.remaining());
148        let mut label_seq = alloc::vec::Vec::with_capacity(cap);
149        for _ in 0..len {
150            label_seq.push(r.read_u32()? as i32);
151        }
152        Ok(Self {
153            member_id,
154            member_flags,
155            type_id,
156            label_seq,
157        })
158    }
159}
160
161// ============================================================================
162// Complete-TypeObject-Annotations (§7.3.4.5.4)
163// ============================================================================
164
165/// Voller qualified Type-Name, z.B. "::sensors::Chatter". Alias fuer
166/// `String` — im Wire als CDR-String (u32 Laenge + UTF-8 + null-term).
167pub type QualifiedTypeName = String;
168
169/// Placement-Kind einer `@verbatim`-Annotation (§7.3.4.5.4 §PL_*).
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub enum VerbatimPlacement {
172    /// Vor der Typ-Deklaration.
173    Before,
174    /// Nach der Typ-Deklaration.
175    After,
176    /// Innerhalb des Header-Blocks (z.B. `#include`).
177    BeginFile,
178    /// Ende der Datei.
179    EndFile,
180    /// Andere Platzierung (Forward-Compat).
181    Other(String),
182}
183
184/// `@verbatim(language, text, placement)`.
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct AppliedVerbatimAnnotation {
187    /// Platzierung im Code-Gen-Output.
188    pub placement: VerbatimPlacement,
189    /// Zielsprache (z.B. "c++").
190    pub language: String,
191    /// Literal-Text.
192    pub text: String,
193}
194
195impl AppliedVerbatimAnnotation {
196    fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
197        let placement_str = match &self.placement {
198            VerbatimPlacement::Before => "BEFORE_DECLARATION",
199            VerbatimPlacement::After => "AFTER_DECLARATION",
200            VerbatimPlacement::BeginFile => "BEGIN_FILE",
201            VerbatimPlacement::EndFile => "END_FILE",
202            VerbatimPlacement::Other(s) => s.as_str(),
203        };
204        w.write_string(placement_str)?;
205        w.write_string(&self.language)?;
206        w.write_string(&self.text)
207    }
208
209    fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
210        let placement_str = r.read_string()?;
211        let placement = match placement_str.as_str() {
212            "BEFORE_DECLARATION" => VerbatimPlacement::Before,
213            "AFTER_DECLARATION" => VerbatimPlacement::After,
214            "BEGIN_FILE" => VerbatimPlacement::BeginFile,
215            "END_FILE" => VerbatimPlacement::EndFile,
216            _ => VerbatimPlacement::Other(placement_str),
217        };
218        let language = r.read_string()?;
219        let text = r.read_string()?;
220        Ok(Self {
221            placement,
222            language,
223            text,
224        })
225    }
226}
227
228/// AppliedBuiltinTypeAnnotations (§7.3.4.5.4): `@verbatim` auf Typ-Level.
229///
230/// Wire: `sequence<AppliedVerbatimAnnotation, 1>` (0 oder 1 Eintrag =
231/// "absent"/"present"). Weitere Builtin-Type-Annotations (`@unit`,
232/// `@min`, `@max`, `@hash_id`) gehoeren zum Member-Scope, nicht Typ.
233#[derive(Debug, Clone, Default, PartialEq, Eq)]
234pub struct AppliedBuiltinTypeAnnotations {
235    /// Optionale `@verbatim`-Direktive.
236    pub verbatim: Option<AppliedVerbatimAnnotation>,
237}
238
239impl AppliedBuiltinTypeAnnotations {
240    /// Encode als `sequence<T, 1>`.
241    ///
242    /// # Errors
243    /// Buffer-Overflow.
244    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
245        match &self.verbatim {
246            None => w.write_u32(0),
247            Some(v) => {
248                w.write_u32(1)?;
249                v.encode_into(w)
250            }
251        }
252    }
253
254    /// Decode.
255    ///
256    /// # Errors
257    /// Buffer-Underflow.
258    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
259        let len = r.read_u32()?;
260        let verbatim = if len == 0 {
261            None
262        } else {
263            Some(AppliedVerbatimAnnotation::decode_from(r)?)
264        };
265        // Etwaige weitere Eintraege (forward-compat) einfach skippen: wir
266        // akzeptieren bis zu `len` Verbatims aber speichern nur den ersten.
267        for _ in 1..len {
268            let _ = AppliedVerbatimAnnotation::decode_from(r)?;
269        }
270        Ok(Self { verbatim })
271    }
272}
273
274/// AppliedAnnotationParameter (§7.3.4.5.4): ein benannter Parameter
275/// einer Annotation-Instanz. Der Parameter-Name wird als 4-byte-Hash
276/// gespeichert (spart Payload).
277#[derive(Debug, Clone, PartialEq, Eq)]
278pub struct AppliedAnnotationParameter {
279    /// Hash des Parameter-Namens.
280    pub paramname_hash: NameHash,
281    /// Parameter-Wert als opaque bytes (Discriminator-gefuehrt).
282    pub value: Vec<u8>,
283}
284
285impl AppliedAnnotationParameter {
286    fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
287        self.paramname_hash.encode_into(w)?;
288        let len = u32::try_from(self.value.len()).map_err(|_| EncodeError::ValueOutOfRange {
289            message: "annotation parameter value exceeds u32::MAX bytes",
290        })?;
291        w.write_u32(len)?;
292        w.write_bytes(&self.value)
293    }
294
295    fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
296        let paramname_hash = NameHash::decode_from(r)?;
297        let len = r.read_u32()? as usize;
298        let value = r.read_bytes(len)?.to_vec();
299        Ok(Self {
300            paramname_hash,
301            value,
302        })
303    }
304}
305
306/// AppliedAnnotation: Instanz einer Custom-Annotation auf Typ/Member.
307#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct AppliedAnnotation {
309    /// Typ der Annotation (TypeIdentifier auf die Annotation-Definition).
310    pub annotation_typeid: TypeIdentifier,
311    /// Gesetzte Parameter.
312    pub param_seq: Vec<AppliedAnnotationParameter>,
313}
314
315impl AppliedAnnotation {
316    /// Encode.
317    ///
318    /// # Errors
319    /// Buffer-Overflow.
320    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
321        self.annotation_typeid.encode_into(w)?;
322        encode_seq(w, &self.param_seq, |w, p| p.encode_into(w))
323    }
324
325    /// Decode.
326    ///
327    /// # Errors
328    /// Buffer-Underflow.
329    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
330        let annotation_typeid = TypeIdentifier::decode_from(r)?;
331        let param_seq = decode_seq(r, AppliedAnnotationParameter::decode_from)?;
332        Ok(Self {
333            annotation_typeid,
334            param_seq,
335        })
336    }
337}
338
339/// Optionales `sequence<AppliedAnnotation>` — wire: `sequence<T, 1>`.
340#[derive(Debug, Clone, Default, PartialEq, Eq)]
341pub struct OptionalAppliedAnnotationSeq(pub Option<Vec<AppliedAnnotation>>);
342
343impl OptionalAppliedAnnotationSeq {
344    /// Encode.
345    ///
346    /// # Errors
347    /// Buffer-Overflow.
348    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
349        match &self.0 {
350            None => w.write_u32(0),
351            Some(seq) => {
352                w.write_u32(1)?;
353                encode_seq(w, seq, |w, a| a.encode_into(w))
354            }
355        }
356    }
357
358    /// Decode.
359    ///
360    /// # Errors
361    /// Buffer-Underflow.
362    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
363        let len = r.read_u32()?;
364        if len == 0 {
365            return Ok(Self(None));
366        }
367        let seq = decode_seq(r, AppliedAnnotation::decode_from)?;
368        for _ in 1..len {
369            // skip forward-compat duplicate inner seqs
370            let _ = decode_seq(r, AppliedAnnotation::decode_from)?;
371        }
372        Ok(Self(Some(seq)))
373    }
374}
375
376/// CompleteTypeDetail (§7.3.4.5.4): ann_builtin + ann_custom + type_name.
377#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct CompleteTypeDetail {
379    /// Builtin-Annotations (z.B. `@verbatim`).
380    pub ann_builtin: AppliedBuiltinTypeAnnotations,
381    /// Custom-Annotations (optional).
382    pub ann_custom: OptionalAppliedAnnotationSeq,
383    /// Vollqualifizierter Typ-Name (z.B. "::sensors::Chatter").
384    pub type_name: QualifiedTypeName,
385}
386
387impl CompleteTypeDetail {
388    /// Encode.
389    ///
390    /// # Errors
391    /// Buffer-Overflow.
392    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
393        self.ann_builtin.encode_into(w)?;
394        self.ann_custom.encode_into(w)?;
395        w.write_string(&self.type_name)
396    }
397
398    /// Decode.
399    ///
400    /// # Errors
401    /// Buffer-Underflow.
402    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
403        let ann_builtin = AppliedBuiltinTypeAnnotations::decode_from(r)?;
404        let ann_custom = OptionalAppliedAnnotationSeq::decode_from(r)?;
405        let type_name = r.read_string()?;
406        Ok(Self {
407            ann_builtin,
408            ann_custom,
409            type_name,
410        })
411    }
412}
413
414/// AppliedBuiltinMemberAnnotations (§7.3.4.5.4) — Member-spezifische
415/// Builtin-Annotations.
416#[derive(Debug, Clone, Default, PartialEq, Eq)]
417pub struct AppliedBuiltinMemberAnnotations {
418    /// `@unit("...")`.
419    pub unit: Option<String>,
420    /// `@min(val)` als opaque bytes (discriminator-led).
421    pub min: Option<Vec<u8>>,
422    /// `@max(val)`.
423    pub max: Option<Vec<u8>>,
424    /// `@hashid("...")`.
425    pub hash_id: Option<String>,
426    /// `@default(val)` (XTypes 1.3 §7.2.4.4.4.4.9). Wert ist als String
427    /// gespeichert (Caller konvertiert zum Member-Typ); Wire-Form
428    /// liegt am Ende des AppliedBuiltinMemberAnnotations-Records, sodass
429    /// Decoder ohne `default_value`-Wissen am vorletzten String-Feld
430    /// (`hash_id`) korrekt enden — neue Decoder lesen den Trailer; alte
431    /// Decoder lassen ihn liegen.
432    pub default_value: Option<String>,
433}
434
435impl AppliedBuiltinMemberAnnotations {
436    fn write_opt_string(w: &mut BufferWriter, s: &Option<String>) -> Result<(), EncodeError> {
437        match s {
438            None => w.write_u32(0),
439            Some(v) => {
440                w.write_u32(1)?;
441                w.write_string(v)
442            }
443        }
444    }
445
446    fn read_opt_string(r: &mut BufferReader<'_>) -> Result<Option<String>, DecodeError> {
447        let len = r.read_u32()?;
448        if len == 0 {
449            return Ok(None);
450        }
451        // XTypes-Spec §7.3.4.8: AppliedBuiltinMemberAnnotations-Felder
452        // (unit, min, max, hash_id) sind skalar-optional, nicht Sequenz.
453        // len > 1 ist protokollwidrig — strictly rejecten statt still
454        // Mehrfach-Eintraege zu verwerfen (verhinderte bisher die
455        // Diagnose von fehlerhaften Peer-Encodern).
456        if len != 1 {
457            return Err(DecodeError::LengthExceeded {
458                announced: len as usize,
459                remaining: 1,
460                offset: 0,
461            });
462        }
463        let out = r.read_string()?;
464        Ok(Some(out))
465    }
466
467    fn write_opt_bytes(w: &mut BufferWriter, b: &Option<Vec<u8>>) -> Result<(), EncodeError> {
468        match b {
469            None => w.write_u32(0),
470            Some(v) => {
471                w.write_u32(1)?;
472                let len = u32::try_from(v.len()).map_err(|_| EncodeError::ValueOutOfRange {
473                    message: "annotation value exceeds u32::MAX",
474                })?;
475                w.write_u32(len)?;
476                w.write_bytes(v)
477            }
478        }
479    }
480
481    fn read_opt_bytes(r: &mut BufferReader<'_>) -> Result<Option<Vec<u8>>, DecodeError> {
482        let len = r.read_u32()?;
483        if len == 0 {
484            return Ok(None);
485        }
486        // Siehe read_opt_string: skalar-optional, len > 1 ist
487        // Protokoll-Fehler, nicht stiller Datenverlust.
488        if len != 1 {
489            return Err(DecodeError::LengthExceeded {
490                announced: len as usize,
491                remaining: 1,
492                offset: 0,
493            });
494        }
495        let inner_len = r.read_u32()? as usize;
496        let out = r.read_bytes(inner_len)?.to_vec();
497        Ok(Some(out))
498    }
499
500    /// Encode.
501    ///
502    /// # Errors
503    /// Buffer-Overflow.
504    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
505        Self::write_opt_string(w, &self.unit)?;
506        Self::write_opt_bytes(w, &self.min)?;
507        Self::write_opt_bytes(w, &self.max)?;
508        Self::write_opt_string(w, &self.hash_id)?;
509        Self::write_opt_string(w, &self.default_value)
510    }
511
512    /// Decode.
513    ///
514    /// # Errors
515    /// Buffer-Underflow.
516    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
517        let unit = Self::read_opt_string(r)?;
518        let min = Self::read_opt_bytes(r)?;
519        let max = Self::read_opt_bytes(r)?;
520        let hash_id = Self::read_opt_string(r)?;
521        // `default_value` ist neuer Trailer (§7.2.4.4.4.4.9). Falls
522        // Reader-Buffer leer ist, ist es ein Legacy-Encoder ohne den
523        // Trailer — return None, keine Fehler.
524        let default_value = if r.remaining() >= 4 {
525            Self::read_opt_string(r).ok().flatten()
526        } else {
527            None
528        };
529        Ok(Self {
530            unit,
531            min,
532            max,
533            hash_id,
534            default_value,
535        })
536    }
537}
538
539/// CompleteMemberDetail: `name` + `ann_builtin` + `ann_custom`.
540#[derive(Debug, Clone, PartialEq, Eq)]
541pub struct CompleteMemberDetail {
542    /// Voller Member-Name.
543    pub name: String,
544    /// Builtin-Member-Annotations.
545    pub ann_builtin: AppliedBuiltinMemberAnnotations,
546    /// Custom-Annotations.
547    pub ann_custom: OptionalAppliedAnnotationSeq,
548}
549
550impl CompleteMemberDetail {
551    /// Encode.
552    ///
553    /// # Errors
554    /// Buffer-Overflow.
555    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
556        w.write_string(&self.name)?;
557        self.ann_builtin.encode_into(w)?;
558        self.ann_custom.encode_into(w)
559    }
560
561    /// Decode.
562    ///
563    /// # Errors
564    /// Buffer-Underflow.
565    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
566        let name = r.read_string()?;
567        let ann_builtin = AppliedBuiltinMemberAnnotations::decode_from(r)?;
568        let ann_custom = OptionalAppliedAnnotationSeq::decode_from(r)?;
569        Ok(Self {
570            name,
571            ann_builtin,
572            ann_custom,
573        })
574    }
575}
576
577/// Hilfsroutine: sequence<T> encode via Callback.
578pub(crate) fn encode_seq<T, F>(
579    w: &mut BufferWriter,
580    items: &[T],
581    mut f: F,
582) -> Result<(), EncodeError>
583where
584    F: FnMut(&mut BufferWriter, &T) -> Result<(), EncodeError>,
585{
586    let len = u32::try_from(items.len()).map_err(|_| EncodeError::ValueOutOfRange {
587        message: "sequence length exceeds u32::MAX",
588    })?;
589    w.write_u32(len)?;
590    for it in items {
591        f(w, it)?;
592    }
593    Ok(())
594}
595
596/// DoS-Cap fuer Vec-Pre-Allocation beim Decode. Der Wert ist die
597/// Obergrenze in Elementen, die wir initial allozieren. Grosse Sequenzen
598/// werden inkrementell durch `push()` gewachsen.
599///
600/// 16 MiB / min_elem_size ist eine grobzuegige Heuristik — niemand
601/// sendet legitim 16M TypeIdentifiers in einem getTypes-Reply, aber
602/// Vec::with_capacity(u32_wire as usize) wuerde bei u32::MAX ~16 GB
603/// reservieren = OOM-Vektor.
604pub const DECODE_PREALLOC_CAP: usize = 4096;
605
606/// Sichere Allokation: `Vec::with_capacity(len.min(remaining_bytes /
607/// min_elem_size).min(CAP))`. Verhindert "30-byte-PID erzwingt 4 GB RAM".
608#[must_use]
609pub(crate) fn safe_capacity(len: usize, min_elem_size: usize, remaining_bytes: usize) -> usize {
610    let by_bytes = if min_elem_size == 0 {
611        DECODE_PREALLOC_CAP
612    } else {
613        remaining_bytes.saturating_div(min_elem_size)
614    };
615    len.min(by_bytes).min(DECODE_PREALLOC_CAP)
616}
617
618/// Hilfsroutine: sequence<T> decode via Callback.
619///
620/// Verwendet [`safe_capacity`] fuer DoS-Schutz: selbst wenn die
621/// wire-Laenge `u32::MAX` ist, allokieren wir initial hoechstens
622/// `DECODE_PREALLOC_CAP` Eintraege. Die echte Schleife baut trotzdem
623/// bis `len` hoch, bricht aber spaetestens ab wenn `read_*` ueber den
624/// verfuegbaren Puffer hinaus liest.
625pub(crate) fn decode_seq<T, F>(
626    r: &mut BufferReader<'_>,
627    mut f: F,
628) -> Result<alloc::vec::Vec<T>, DecodeError>
629where
630    F: FnMut(&mut BufferReader<'_>) -> Result<T, DecodeError>,
631{
632    let len = r.read_u32()? as usize;
633    let cap = safe_capacity(len, 1, r.remaining());
634    let mut out = alloc::vec::Vec::with_capacity(cap);
635    for _ in 0..len {
636        out.push(f(r)?);
637    }
638    Ok(out)
639}
640
641#[cfg(test)]
642#[allow(clippy::unwrap_used)]
643mod safe_capacity_tests {
644    use super::*;
645
646    #[test]
647    fn safe_capacity_clamps_by_remaining_bytes() {
648        assert_eq!(safe_capacity(1_000_000_000, 4, 100), 25);
649    }
650
651    #[test]
652    fn safe_capacity_caps_at_prealloc_cap() {
653        let cap = safe_capacity(usize::MAX, 1, usize::MAX);
654        assert_eq!(cap, DECODE_PREALLOC_CAP);
655    }
656
657    #[test]
658    fn safe_capacity_returns_len_when_small() {
659        assert_eq!(safe_capacity(10, 4, 1000), 10);
660    }
661
662    #[test]
663    fn safe_capacity_handles_zero_elem_size() {
664        assert_eq!(safe_capacity(usize::MAX, 0, 100), DECODE_PREALLOC_CAP);
665    }
666
667    #[test]
668    fn decode_seq_truncates_preallocation_for_large_lengths() {
669        let mut bytes = alloc::vec::Vec::new();
670        bytes.extend_from_slice(&u32::MAX.to_le_bytes());
671        let mut r = BufferReader::new(&bytes, zerodds_cdr::Endianness::Little);
672        let res: Result<alloc::vec::Vec<u8>, _> = decode_seq(&mut r, |rr| rr.read_u8());
673        assert!(res.is_err());
674    }
675
676    fn roundtrip_verbatim(placement: VerbatimPlacement) {
677        let a = AppliedVerbatimAnnotation {
678            placement: placement.clone(),
679            language: alloc::string::String::from("c++"),
680            text: alloc::string::String::from("// example"),
681        };
682        let mut w = BufferWriter::new(zerodds_cdr::Endianness::Little);
683        a.encode_into(&mut w).unwrap();
684        let bytes = w.into_bytes();
685        let mut r = BufferReader::new(&bytes, zerodds_cdr::Endianness::Little);
686        let decoded = AppliedVerbatimAnnotation::decode_from(&mut r).unwrap();
687        assert_eq!(decoded, a);
688    }
689
690    #[test]
691    fn verbatim_placement_roundtrip_before() {
692        roundtrip_verbatim(VerbatimPlacement::Before);
693    }
694
695    #[test]
696    fn verbatim_placement_roundtrip_after() {
697        roundtrip_verbatim(VerbatimPlacement::After);
698    }
699
700    #[test]
701    fn verbatim_placement_roundtrip_begin_file() {
702        roundtrip_verbatim(VerbatimPlacement::BeginFile);
703    }
704
705    #[test]
706    fn verbatim_placement_roundtrip_end_file() {
707        roundtrip_verbatim(VerbatimPlacement::EndFile);
708    }
709
710    #[test]
711    fn verbatim_placement_roundtrip_other_forward_compat() {
712        roundtrip_verbatim(VerbatimPlacement::Other(alloc::string::String::from(
713            "CUSTOM_PLACEMENT",
714        )));
715    }
716}