Skip to main content

zerodds_cdr/
struct_enc.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Struct-Encoding mit Extensibility (W3, XCDR2 §7.4.3, §7.4.5).
4//!
5//! XCDR2 kennt drei Struct-Layouts:
6//!
7//! - **`@final`**: tight-packed, kein Header. Members in deklarierter
8//!   Reihenfolge. Reader und Writer muessen die Member-Liste exakt
9//!   teilen — keine forward/backward compatibility.
10//! - **`@appendable`**: 4-byte **DHEADER** (uint32 = Byte-Laenge des
11//!   Bodys nach dem Header) + tight-packed Members. Forward-Kompatibel:
12//!   Reader kann Bytes nach den bekannten Members ueberspringen.
13//! - **`@mutable`**: Pro Member ein **EMHEADER** + Wert. EMHEADER =
14//!   `uint32` mit Member-ID + Length-Code. Backward-/forward-kompatibel
15//!   durch Member-ID-basierte Zuordnung.
16//!
17//! Helpers für alle 3 Modi mit klassischem 2-pass Encoder (inner
18//! Buffer für Body, dann Header + Body in outer). Length-Codes:
19//! LC0..LC7 vollständig implementiert (Default-Konstruktor `LC4`
20//! Variable-Length mit separatem `uint32` NEXTINT; LC0..LC3 kompakte
21//! 1/2/4/8-byte ohne NEXTINT; LC5..LC7 sequence/array-spezifisch).
22//!
23//! Alignment: Body-Inhalt eines DHEADER-/EMHEADER-Frames startet bei
24//! Offset 0 relativ zum Body-Start (XCDR2 §7.4.3.4.5).
25
26extern crate alloc;
27use alloc::vec::Vec;
28
29use crate::buffer::{BufferReader, BufferWriter};
30use crate::error::{DecodeError, EncodeError};
31
32// ============================================================================
33// @appendable
34// ============================================================================
35
36/// Encoded eine `@appendable`-Struktur. Der Body wird in einen inneren
37/// Buffer geschrieben, dann Length + Body in den Outer-Writer.
38///
39/// # Errors
40/// Encoder-Fehler aus dem Body oder `ValueOutOfRange`, wenn der Body
41/// `u32::MAX` Bytes ueberschreitet.
42pub fn encode_appendable<F>(writer: &mut BufferWriter, body: F) -> Result<(), EncodeError>
43where
44    F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
45{
46    let mut inner = BufferWriter::new(writer.endianness());
47    body(&mut inner)?;
48    let bytes = inner.into_bytes();
49    let len = u32::try_from(bytes.len()).map_err(|_| EncodeError::ValueOutOfRange {
50        message: "appendable struct body exceeds u32::MAX",
51    })?;
52    writer.write_u32(len)?;
53    writer.write_bytes(&bytes)?;
54    Ok(())
55}
56
57/// Decoded eine `@appendable`-Struktur. Liest die DHEADER-Length, baut
58/// einen Sub-Reader auf den Body und uebergibt ihn an `body`. Der
59/// Sub-Reader erlaubt dem Body, weniger Bytes zu konsumieren als
60/// announced — ungenutzte Bytes werden uebersprungen.
61///
62/// # Errors
63/// Decoder-Fehler aus dem Body oder `LengthExceeded`/`UnexpectedEof`,
64/// wenn die Length nicht in den Stream passt.
65pub fn decode_appendable<T, F>(reader: &mut BufferReader<'_>, body: F) -> Result<T, DecodeError>
66where
67    F: FnOnce(&mut BufferReader<'_>) -> Result<T, DecodeError>,
68{
69    let len = reader.read_u32()? as usize;
70    if len > reader.remaining() {
71        return Err(DecodeError::LengthExceeded {
72            announced: len,
73            remaining: reader.remaining(),
74            offset: reader.position(),
75        });
76    }
77    let body_bytes = reader.read_bytes(len)?;
78    let mut sub = BufferReader::new(body_bytes, reader.endianness());
79    body(&mut sub)
80}
81
82// ============================================================================
83// @mutable
84// ============================================================================
85
86/// Length-Code-Variante (XTypes 1.3 §7.4.3.4.2). WP 1.A: alle 8 LCs.
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88#[repr(u8)]
89pub enum LengthCode {
90    /// 1-byte body, no NEXTINT.
91    Lc0 = 0,
92    /// 2-byte body, no NEXTINT.
93    Lc1 = 1,
94    /// 4-byte body, no NEXTINT.
95    Lc2 = 2,
96    /// 8-byte body, no NEXTINT.
97    Lc3 = 3,
98    /// Variable-length body, NEXTINT (uint32) = body length in bytes.
99    Lc4 = 4,
100    /// Variable-length aggregate, NEXTINT = body length INKL. DHEADER.
101    Lc5 = 5,
102    /// Array of 4-Byte-Primitives, NEXTINT = #Elemente, body = `4 + 4*N`.
103    Lc6 = 6,
104    /// Array of 8-Byte-Primitives, NEXTINT = #Elemente, body = `4 + 8*N`.
105    Lc7 = 7,
106}
107
108impl LengthCode {
109    /// Body-Laenge in Bytes fuer diesen LC, gegeben der NEXTINT-Wert.
110    #[must_use]
111    pub fn body_len(self, nextint: u32) -> u64 {
112        match self {
113            Self::Lc0 => 1,
114            Self::Lc1 => 2,
115            Self::Lc2 => 4,
116            Self::Lc3 => 8,
117            Self::Lc4 | Self::Lc5 => u64::from(nextint),
118            Self::Lc6 => 4 * u64::from(nextint) + 4,
119            Self::Lc7 => 8 * u64::from(nextint) + 4,
120        }
121    }
122
123    /// `true` wenn der LC ein NEXTINT-Feld nach dem EMHEADER traegt.
124    #[must_use]
125    pub const fn has_nextint(self) -> bool {
126        matches!(self, Self::Lc4 | Self::Lc5 | Self::Lc6 | Self::Lc7)
127    }
128
129    /// Decode aus dem 3-bit Wire-Feld eines EMHEADER.
130    #[must_use]
131    pub const fn from_wire(value: u8) -> Option<Self> {
132        match value {
133            0 => Some(Self::Lc0),
134            1 => Some(Self::Lc1),
135            2 => Some(Self::Lc2),
136            3 => Some(Self::Lc3),
137            4 => Some(Self::Lc4),
138            5 => Some(Self::Lc5),
139            6 => Some(Self::Lc6),
140            7 => Some(Self::Lc7),
141            _ => None,
142        }
143    }
144}
145
146/// Encoded ein `@mutable`-Member mit **LC4** (Default-Universal-Code).
147///
148/// # Errors
149/// Body-Fehler oder Member-ID > 0x0FFF_FFFF.
150pub fn encode_mutable_member<F>(
151    writer: &mut BufferWriter,
152    member_id: u32,
153    must_understand: bool,
154    body: F,
155) -> Result<(), EncodeError>
156where
157    F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
158{
159    encode_mutable_member_lc(writer, member_id, must_understand, LengthCode::Lc4, body)
160}
161
162/// Encoded ein `@mutable`-Member mit explizitem Length-Code.
163///
164/// # Errors
165/// `ValueOutOfRange` bei Member-ID-Overflow oder Body-Length-Mismatch
166/// fuer den gewaehlten LC.
167pub fn encode_mutable_member_lc<F>(
168    writer: &mut BufferWriter,
169    member_id: u32,
170    must_understand: bool,
171    lc: LengthCode,
172    body: F,
173) -> Result<(), EncodeError>
174where
175    F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
176{
177    if member_id > 0x0FFF_FFFF {
178        return Err(EncodeError::ValueOutOfRange {
179            message: "EMHEADER member_id exceeds 28-bit field",
180        });
181    }
182    let mut inner = BufferWriter::new(writer.endianness());
183    body(&mut inner)?;
184    let body_bytes = inner.into_bytes();
185    let body_len = body_bytes.len();
186
187    let nextint: Option<u32> = match lc {
188        LengthCode::Lc0 => {
189            if body_len != 1 {
190                return Err(EncodeError::ValueOutOfRange {
191                    message: "LC0 requires exactly 1 byte body",
192                });
193            }
194            None
195        }
196        LengthCode::Lc1 => {
197            if body_len != 2 {
198                return Err(EncodeError::ValueOutOfRange {
199                    message: "LC1 requires exactly 2 bytes body",
200                });
201            }
202            None
203        }
204        LengthCode::Lc2 => {
205            if body_len != 4 {
206                return Err(EncodeError::ValueOutOfRange {
207                    message: "LC2 requires exactly 4 bytes body",
208                });
209            }
210            None
211        }
212        LengthCode::Lc3 => {
213            if body_len != 8 {
214                return Err(EncodeError::ValueOutOfRange {
215                    message: "LC3 requires exactly 8 bytes body",
216                });
217            }
218            None
219        }
220        LengthCode::Lc4 | LengthCode::Lc5 => {
221            let n = u32::try_from(body_len).map_err(|_| EncodeError::ValueOutOfRange {
222                message: "LC4/LC5 body exceeds u32::MAX",
223            })?;
224            Some(n)
225        }
226        LengthCode::Lc6 => {
227            // `body_len % 4 != 0` ist aequivalent zu `(body_len - 4) % 4 != 0`
228            // fuer body_len >= 4 (nach dem ersten Check). Eliminiert eine
229            // mathematisch-aequivalente `-4`/`+4`-Mutation.
230            if body_len < 4 || body_len % 4 != 0 {
231                return Err(EncodeError::ValueOutOfRange {
232                    message: "LC6 body must be DHEADER + 4n bytes",
233                });
234            }
235            let n =
236                u32::try_from((body_len - 4) / 4).map_err(|_| EncodeError::ValueOutOfRange {
237                    message: "LC6 element count exceeds u32::MAX",
238                })?;
239            Some(n)
240        }
241        LengthCode::Lc7 => {
242            // body_len < 4: Reject. body_len in {4,12,20,...}: Pass.
243            // body_len in {5,6,7}: Pass body_len % 8 ≠ 0 → Reject. Aber
244            // Boundary-Check muss aufpassen: body_len=8 hat body_len%8=0,
245            // aber (8-4)/8=0.5 ist kein gueltiges n. Wir brauchen
246            // `(body_len - 4) % 8 == 0`, was aequivalent zu
247            // `body_len % 8 == 4` ist.
248            if body_len < 4 || body_len % 8 != 4 {
249                return Err(EncodeError::ValueOutOfRange {
250                    message: "LC7 body must be DHEADER + 8n bytes",
251                });
252            }
253            let n =
254                u32::try_from((body_len - 4) / 8).map_err(|_| EncodeError::ValueOutOfRange {
255                    message: "LC7 element count exceeds u32::MAX",
256                })?;
257            Some(n)
258        }
259    };
260
261    let m_bit = u32::from(must_understand) << 31;
262    let lc_bits = (lc as u32) << 28;
263    // Arithmetic form statt OR: bit-positions ueberlappen nicht
264    // (m_bit=Bit31, lc_bits=Bits 28-30, member_id<=Bits 0-27).
265    // Mathematisch identisch zu `m_bit | lc_bits | member_id`,
266    // aber mutation-detection-freundlicher: `+` vs `^`/`-`/`*`
267    // sind nicht aequivalent zueinander.
268    let emheader = m_bit + lc_bits + member_id;
269    writer.write_u32(emheader)?;
270    if let Some(ni) = nextint {
271        writer.write_u32(ni)?;
272    }
273    writer.write_bytes(&body_bytes)?;
274    Ok(())
275}
276
277/// Encoder fuer einen `@mutable`-Struct mit Validierung der
278/// non-optional-Member-Vollstaendigkeit (XTypes 1.3 §7.4.1.2.3).
279///
280/// Vor jedem Member-Encode wird `member_id` als "emitted" gemerkt; beim
281/// `finish` muss jede in `required_ids` aufgelistete Member-ID emittiert
282/// worden sein, sonst wird `EncodeError::MissingNonOptionalMember`
283/// zurueckgegeben.
284///
285/// Spec-Hintergrund: Eine `EXTENSIBLE` (final/appendable/mutable)-Encode
286/// MUSS alle non-optional Member enthalten. Dieser Validator schliesst
287/// das Encoder-Loch fuer @mutable, weil bei MUTABLE die EMHEADER-Reihenfolge
288/// nicht festliegt und Encoder-Bugs sonst stillschweigend passierten.
289pub struct MutableStructEncoder<'a> {
290    writer: &'a mut BufferWriter,
291    required_ids: Vec<u32>,
292    emitted_ids: Vec<u32>,
293}
294
295impl<'a> MutableStructEncoder<'a> {
296    /// Neuer Encoder. `required_ids` ist die Liste der Member-IDs,
297    /// die spec-konform alle emittiert werden MUESSEN (= alle
298    /// non-optional Member des Structs).
299    pub fn new(writer: &'a mut BufferWriter, required_ids: Vec<u32>) -> Self {
300        Self {
301            writer,
302            required_ids,
303            emitted_ids: Vec::new(),
304        }
305    }
306
307    /// Member encoden. Verhalten wie `encode_mutable_member`, plus
308    /// Tracking der emittierten ID.
309    ///
310    /// # Errors
311    /// Wie `encode_mutable_member`.
312    pub fn encode_member<F>(
313        &mut self,
314        member_id: u32,
315        must_understand: bool,
316        body: F,
317    ) -> Result<(), EncodeError>
318    where
319        F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
320    {
321        encode_mutable_member(self.writer, member_id, must_understand, body)?;
322        self.emitted_ids.push(member_id);
323        Ok(())
324    }
325
326    /// Member mit explizitem Length-Code.
327    ///
328    /// # Errors
329    /// Wie `encode_mutable_member_lc`.
330    pub fn encode_member_lc<F>(
331        &mut self,
332        member_id: u32,
333        must_understand: bool,
334        lc: LengthCode,
335        body: F,
336    ) -> Result<(), EncodeError>
337    where
338        F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
339    {
340        encode_mutable_member_lc(self.writer, member_id, must_understand, lc, body)?;
341        self.emitted_ids.push(member_id);
342        Ok(())
343    }
344
345    /// Schliesst die Mutable-Sequenz ab und prueft, dass jede in
346    /// `required_ids` aufgelistete Member-ID auch emittiert wurde.
347    ///
348    /// # Errors
349    /// `MissingNonOptionalMember { member_id }` mit der ersten
350    /// fehlenden ID (deterministisch in Reihenfolge der `required_ids`-
351    /// Liste).
352    pub fn finish(self) -> Result<(), EncodeError> {
353        for required in &self.required_ids {
354            if !self.emitted_ids.contains(required) {
355                return Err(EncodeError::MissingNonOptionalMember {
356                    member_id: *required,
357                });
358            }
359        }
360        Ok(())
361    }
362}
363
364/// Geparstes EMHEADER + Body-Slice eines `@mutable`-Members.
365#[derive(Debug, Clone)]
366pub struct MutableMember<'a> {
367    /// 28-bit Member-ID.
368    pub member_id: u32,
369    /// `must_understand`-Flag.
370    pub must_understand: bool,
371    /// Length-Code.
372    pub length_code: LengthCode,
373    /// Body als unverbrauchter Slice.
374    pub body: &'a [u8],
375}
376
377/// Liest einen `@mutable`-Member-Eintrag (EMHEADER + NEXTINT + Body).
378///
379/// # Errors
380/// `UnexpectedEof` / `LengthExceeded` bei truncated/oversize Body.
381pub fn read_mutable_member<'a>(
382    reader: &mut BufferReader<'a>,
383) -> Result<Option<MutableMember<'a>>, DecodeError> {
384    if reader.remaining() == 0 {
385        return Ok(None);
386    }
387    let emheader = reader.read_u32()?;
388    let must_understand = (emheader >> 31) & 1 == 1;
389    let lc_raw = ((emheader >> 28) & 0b0111) as u8;
390    let member_id = emheader & 0x0FFF_FFFF;
391    let length_code = LengthCode::from_wire(lc_raw).ok_or_else(|| DecodeError::LengthExceeded {
392        announced: usize::from(lc_raw),
393        remaining: 0,
394        offset: reader.position(),
395    })?;
396
397    let nextint = if length_code.has_nextint() {
398        reader.read_u32()?
399    } else {
400        0
401    };
402
403    let body_len_u64 = length_code.body_len(nextint);
404    let body_len = usize::try_from(body_len_u64).map_err(|_| DecodeError::LengthExceeded {
405        announced: usize::MAX,
406        remaining: reader.remaining(),
407        offset: reader.position(),
408    })?;
409    if body_len > reader.remaining() {
410        return Err(DecodeError::LengthExceeded {
411            announced: body_len,
412            remaining: reader.remaining(),
413            offset: reader.position(),
414        });
415    }
416    let body = reader.read_bytes(body_len)?;
417    Ok(Some(MutableMember {
418        member_id,
419        must_understand,
420        length_code,
421        body,
422    }))
423}
424
425/// Aller-Members-eines-`@mutable`-Structs in eine Map sammeln. Erlaubt
426/// dem Caller, Members nach ID zu suchen statt sequenziell zu lesen.
427///
428/// # Errors
429/// Wie [`read_mutable_member`].
430pub fn read_all_mutable_members<'a>(
431    reader: &mut BufferReader<'a>,
432) -> Result<Vec<MutableMember<'a>>, DecodeError> {
433    let mut out = Vec::new();
434    while let Some(m) = read_mutable_member(reader)? {
435        out.push(m);
436    }
437    Ok(out)
438}
439
440// ============================================================================
441// @final (no-op-Wrapper)
442// ============================================================================
443
444/// `@final`-Struct: tight-packed, kein Header. Diese Funktion ist ein
445/// reiner Convenience-Wrapper, damit die 3 Extensibility-Modi
446/// uniforme Aufruf-Sites haben.
447///
448/// # Errors
449/// Body-Fehler.
450pub fn encode_final<F>(writer: &mut BufferWriter, body: F) -> Result<(), EncodeError>
451where
452    F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
453{
454    body(writer)
455}
456
457/// Decoder-Pendant: einfach den Body aufrufen.
458///
459/// # Errors
460/// Body-Fehler.
461pub fn decode_final<T, F>(reader: &mut BufferReader<'_>, body: F) -> Result<T, DecodeError>
462where
463    F: FnOnce(&mut BufferReader<'_>) -> Result<T, DecodeError>,
464{
465    body(reader)
466}
467
468#[cfg(test)]
469mod tests {
470    #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
471    use super::*;
472    use crate::Endianness;
473    use crate::encode::{CdrDecode, CdrEncode};
474    use alloc::vec;
475
476    // ---- @final ----
477
478    #[test]
479    fn final_struct_two_u32_members() {
480        let mut w = BufferWriter::new(Endianness::Little);
481        encode_final(&mut w, |w| {
482            42u32.encode(w)?;
483            100u32.encode(w)?;
484            Ok(())
485        })
486        .unwrap();
487        let bytes = w.into_bytes();
488        // Tight-packed: 2 * 4 byte u32 = 8 byte total.
489        assert_eq!(bytes.len(), 8);
490
491        let mut r = BufferReader::new(&bytes, Endianness::Little);
492        let (a, b) = decode_final(&mut r, |r| {
493            Ok::<_, DecodeError>((u32::decode(r)?, u32::decode(r)?))
494        })
495        .unwrap();
496        assert_eq!((a, b), (42, 100));
497    }
498
499    // ---- @appendable ----
500
501    #[test]
502    fn appendable_struct_writes_dheader() {
503        let mut w = BufferWriter::new(Endianness::Little);
504        encode_appendable(&mut w, |w| {
505            42u32.encode(w)?;
506            7u8.encode(w)?;
507            Ok(())
508        })
509        .unwrap();
510        let bytes = w.into_bytes();
511        // DHEADER (4 byte u32 = body length) + body.
512        // Body: u32 (4) + u8 (1) = 5 byte.
513        assert_eq!(&bytes[0..4], &[5, 0, 0, 0]); // DHEADER LE
514        assert_eq!(&bytes[4..8], &[42, 0, 0, 0]); // u32 = 42
515        assert_eq!(bytes[8], 7);
516        assert_eq!(bytes.len(), 9);
517    }
518
519    #[test]
520    fn appendable_struct_roundtrip() {
521        let mut w = BufferWriter::new(Endianness::Little);
522        encode_appendable(&mut w, |w| {
523            42u32.encode(w)?;
524            7u8.encode(w)?;
525            Ok(())
526        })
527        .unwrap();
528        let bytes = w.into_bytes();
529
530        let mut r = BufferReader::new(&bytes, Endianness::Little);
531        let (a, b) = decode_appendable(&mut r, |r| {
532            Ok::<_, DecodeError>((u32::decode(r)?, u8::decode(r)?))
533        })
534        .unwrap();
535        assert_eq!((a, b), (42, 7));
536    }
537
538    #[test]
539    fn appendable_decoder_skips_extra_trailing_bytes() {
540        // Schreibe Encoder-Struktur mit 2 Members, aber Decoder liest
541        // nur das erste — der Sub-Reader-Trick wirft die zweiten Bytes
542        // weg ohne Fehler.
543        let mut w = BufferWriter::new(Endianness::Little);
544        encode_appendable(&mut w, |w| {
545            42u32.encode(w)?;
546            99u8.encode(w)?;
547            Ok(())
548        })
549        .unwrap();
550        let bytes = w.into_bytes();
551
552        let mut r = BufferReader::new(&bytes, Endianness::Little);
553        let only_first = decode_appendable(&mut r, u32::decode).unwrap();
554        assert_eq!(only_first, 42);
555        // Outer-Reader hat alles konsumiert (DHEADER + komplettem Body).
556        assert_eq!(r.remaining(), 0);
557    }
558
559    #[test]
560    fn appendable_decoder_rejects_announced_overrun() {
561        let bytes = [0xFFu8, 0xFF, 0xFF, 0xFF, 1, 2, 3];
562        let mut r = BufferReader::new(&bytes, Endianness::Little);
563        let res = decode_appendable(&mut r, u32::decode);
564        assert!(matches!(res, Err(DecodeError::LengthExceeded { .. })));
565    }
566
567    // ---- @mutable ----
568
569    // ---- MutableStructEncoder (XTypes 1.3 §7.4.1.2.3) ----
570
571    #[test]
572    fn mutable_struct_encoder_succeeds_when_all_required_emitted() {
573        let mut w = BufferWriter::new(Endianness::Little);
574        let mut enc = MutableStructEncoder::new(&mut w, vec![1, 2, 3]);
575        enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
576        enc.encode_member(2, false, |w| 7u8.encode(w)).unwrap();
577        enc.encode_member(3, false, |w| 99u16.encode(w)).unwrap();
578        enc.finish().unwrap();
579    }
580
581    #[test]
582    fn mutable_encode_omitting_non_optional_member_errors() {
583        let mut w = BufferWriter::new(Endianness::Little);
584        let mut enc = MutableStructEncoder::new(&mut w, vec![1, 2, 3]);
585        enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
586        // Member 2 wird nicht emittiert — Spec-Verletzung.
587        enc.encode_member(3, false, |w| 99u16.encode(w)).unwrap();
588        let err = enc.finish().unwrap_err();
589        assert_eq!(err, EncodeError::MissingNonOptionalMember { member_id: 2 });
590    }
591
592    #[test]
593    fn mutable_encode_first_missing_id_is_reported() {
594        let mut w = BufferWriter::new(Endianness::Little);
595        let mut enc = MutableStructEncoder::new(&mut w, vec![10, 20, 30]);
596        enc.encode_member(20, false, |w| 5u32.encode(w)).unwrap();
597        // 10 und 30 fehlen — Encoder meldet 10 zuerst.
598        let err = enc.finish().unwrap_err();
599        assert_eq!(err, EncodeError::MissingNonOptionalMember { member_id: 10 });
600    }
601
602    #[test]
603    fn mutable_encode_optional_only_with_no_required_succeeds() {
604        // Wenn alle Member optional sind, ist required_ids leer und der
605        // Encoder darf auch null Member emittieren.
606        let mut w = BufferWriter::new(Endianness::Little);
607        let enc = MutableStructEncoder::new(&mut w, vec![]);
608        enc.finish().unwrap();
609    }
610
611    #[test]
612    fn mutable_encode_extra_optional_emitted_does_not_break_finish() {
613        // required = [1]; emitted = [1, 99]; OK — 99 ist optional.
614        let mut w = BufferWriter::new(Endianness::Little);
615        let mut enc = MutableStructEncoder::new(&mut w, vec![1]);
616        enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
617        enc.encode_member(99, false, |w| 0u8.encode(w)).unwrap();
618        enc.finish().unwrap();
619    }
620
621    #[test]
622    fn mutable_encode_with_lc_variant_tracks_id() {
623        let mut w = BufferWriter::new(Endianness::Little);
624        let mut enc = MutableStructEncoder::new(&mut w, vec![5]);
625        enc.encode_member_lc(5, false, LengthCode::Lc0, |w| 0x42u8.encode(w))
626            .unwrap();
627        enc.finish().unwrap();
628    }
629
630    #[test]
631    fn mutable_member_emheader_layout() {
632        let mut w = BufferWriter::new(Endianness::Little);
633        encode_mutable_member(&mut w, 0x1234, false, |w| 42u32.encode(w)).unwrap();
634        let bytes = w.into_bytes();
635        // EMHEADER LE: m_bit=0, lc=4 (bits 30-28 = 100), member_id=0x1234
636        // → 0x4000_1234
637        assert_eq!(&bytes[0..4], &[0x34, 0x12, 0x00, 0x40]);
638        // NEXTINT = body length = 4
639        assert_eq!(&bytes[4..8], &[4, 0, 0, 0]);
640        // body = u32 LE 42
641        assert_eq!(&bytes[8..12], &[42, 0, 0, 0]);
642    }
643
644    #[test]
645    fn mutable_member_must_understand_sets_high_bit() {
646        let mut w = BufferWriter::new(Endianness::Little);
647        encode_mutable_member(&mut w, 1, true, |w| 0u8.encode(w)).unwrap();
648        let bytes = w.into_bytes();
649        // EMHEADER LE: m_bit=1, lc=4, id=1 → 0xC000_0001
650        assert_eq!(&bytes[0..4], &[0x01, 0x00, 0x00, 0xC0]);
651    }
652
653    #[test]
654    fn mutable_member_rejects_oversized_id() {
655        let mut w = BufferWriter::new(Endianness::Little);
656        let res = encode_mutable_member(&mut w, 0xFFFF_FFFF, false, |w| 0u8.encode(w));
657        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
658    }
659
660    #[test]
661    fn mutable_struct_roundtrip_two_members() {
662        let mut w = BufferWriter::new(Endianness::Little);
663        encode_mutable_member(&mut w, 1, false, |w| 42u32.encode(w)).unwrap();
664        encode_mutable_member(&mut w, 2, true, |w| 7u8.encode(w)).unwrap();
665        let bytes = w.into_bytes();
666
667        let mut r = BufferReader::new(&bytes, Endianness::Little);
668        let members = read_all_mutable_members(&mut r).unwrap();
669        assert_eq!(members.len(), 2);
670        assert_eq!(members[0].member_id, 1);
671        assert!(!members[0].must_understand);
672        assert_eq!(members[1].member_id, 2);
673        assert!(members[1].must_understand);
674
675        let mut sub = BufferReader::new(members[0].body, Endianness::Little);
676        assert_eq!(u32::decode(&mut sub).unwrap(), 42);
677        let mut sub = BufferReader::new(members[1].body, Endianness::Little);
678        assert_eq!(u8::decode(&mut sub).unwrap(), 7);
679    }
680
681    #[test]
682    fn mutable_member_reads_none_on_eof() {
683        let bytes: [u8; 0] = [];
684        let mut r = BufferReader::new(&bytes, Endianness::Little);
685        let res = read_mutable_member(&mut r).unwrap();
686        assert!(res.is_none());
687    }
688
689    // ---- WP 1.A: LC0..7 Encoder/Decoder ----
690
691    #[test]
692    fn lc0_encode_decode_one_byte_body() {
693        let mut w = BufferWriter::new(Endianness::Little);
694        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc0, |w| 0x42u8.encode(w)).unwrap();
695        let bytes = w.into_bytes();
696        assert_eq!(&bytes[0..4], &[0x01, 0x00, 0x00, 0x00]);
697        assert_eq!(bytes[4], 0x42);
698        assert_eq!(bytes.len(), 5);
699        let mut r = BufferReader::new(&bytes, Endianness::Little);
700        let m = read_mutable_member(&mut r).unwrap().unwrap();
701        assert_eq!(m.length_code, LengthCode::Lc0);
702        assert_eq!(m.body, &[0x42]);
703    }
704
705    #[test]
706    fn lc1_encode_decode_two_byte_body() {
707        let mut w = BufferWriter::new(Endianness::Little);
708        encode_mutable_member_lc(&mut w, 7, false, LengthCode::Lc1, |w| 0x1234u16.encode(w))
709            .unwrap();
710        let bytes = w.into_bytes();
711        assert_eq!(bytes.len(), 4 + 2);
712        let mut r = BufferReader::new(&bytes, Endianness::Little);
713        let m = read_mutable_member(&mut r).unwrap().unwrap();
714        assert_eq!(m.length_code, LengthCode::Lc1);
715        assert_eq!(m.body, &[0x34, 0x12]);
716    }
717
718    #[test]
719    fn lc2_encode_decode_four_byte_body() {
720        let mut w = BufferWriter::new(Endianness::Little);
721        encode_mutable_member_lc(&mut w, 9, true, LengthCode::Lc2, |w| 42u32.encode(w)).unwrap();
722        let bytes = w.into_bytes();
723        // m=1, lc=2, id=9 → 0xA000_0009 LE
724        assert_eq!(&bytes[0..4], &[0x09, 0x00, 0x00, 0xA0]);
725        assert_eq!(bytes.len(), 4 + 4);
726        let mut r = BufferReader::new(&bytes, Endianness::Little);
727        let m = read_mutable_member(&mut r).unwrap().unwrap();
728        assert_eq!(m.length_code, LengthCode::Lc2);
729        assert!(m.must_understand);
730    }
731
732    #[test]
733    fn lc3_encode_decode_eight_byte_body() {
734        let mut w = BufferWriter::new(Endianness::Little);
735        encode_mutable_member_lc(&mut w, 11, false, LengthCode::Lc3, |w| {
736            0xDEADBEEF_CAFEBABEu64.encode(w)
737        })
738        .unwrap();
739        let bytes = w.into_bytes();
740        assert_eq!(bytes.len(), 4 + 8);
741        let mut r = BufferReader::new(&bytes, Endianness::Little);
742        let m = read_mutable_member(&mut r).unwrap().unwrap();
743        assert_eq!(m.length_code, LengthCode::Lc3);
744        assert_eq!(m.body.len(), 8);
745    }
746
747    #[test]
748    fn lc4_default_path_unchanged() {
749        let mut w = BufferWriter::new(Endianness::Little);
750        encode_mutable_member(&mut w, 1, false, |w| 42u32.encode(w)).unwrap();
751        let bytes = w.into_bytes();
752        assert_eq!(bytes.len(), 4 + 4 + 4);
753        let mut r = BufferReader::new(&bytes, Endianness::Little);
754        let m = read_mutable_member(&mut r).unwrap().unwrap();
755        assert_eq!(m.length_code, LengthCode::Lc4);
756    }
757
758    #[test]
759    fn lc5_aggregate_with_dheader() {
760        let mut w = BufferWriter::new(Endianness::Little);
761        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc5, |w| {
762            8u32.encode(w)?;
763            42u32.encode(w)?;
764            7u32.encode(w)?;
765            Ok(())
766        })
767        .unwrap();
768        let bytes = w.into_bytes();
769        assert_eq!(bytes.len(), 20);
770        assert_eq!(&bytes[4..8], &[12, 0, 0, 0]);
771        let mut r = BufferReader::new(&bytes, Endianness::Little);
772        let m = read_mutable_member(&mut r).unwrap().unwrap();
773        assert_eq!(m.length_code, LengthCode::Lc5);
774        assert_eq!(m.body.len(), 12);
775    }
776
777    #[test]
778    fn lc6_array_of_4byte_primitives() {
779        let mut w = BufferWriter::new(Endianness::Little);
780        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |w| {
781            12u32.encode(w)?;
782            10u32.encode(w)?;
783            20u32.encode(w)?;
784            30u32.encode(w)?;
785            Ok(())
786        })
787        .unwrap();
788        let bytes = w.into_bytes();
789        assert_eq!(&bytes[4..8], &[3, 0, 0, 0]);
790        assert_eq!(bytes.len(), 4 + 4 + 16);
791        let mut r = BufferReader::new(&bytes, Endianness::Little);
792        let m = read_mutable_member(&mut r).unwrap().unwrap();
793        assert_eq!(m.length_code, LengthCode::Lc6);
794        assert_eq!(m.body.len(), 16);
795    }
796
797    #[test]
798    fn lc6_lc7_roundtrip_against_cyclone_sample() {
799        // Spec §7.4.3.4.2: LC=6 fuer 4-byte-Element-Arrays; LC=7 fuer
800        // 8-byte-Element-Arrays. Beide Encoder erzeugen dasselbe
801        // Wire-Layout, das Cyclone DDS und FastDDS dekodieren koennen.
802        // Wir verifizieren byte-genau drei Stellen:
803        //   - LC=6 EMHEADER (Bits 30-28 = 110)
804        //   - NEXTINT = element-count (4 byte)
805        //   - DHEADER + payload
806        let mut w = BufferWriter::new(Endianness::Little);
807        // LC=6 Body-Layout: DHEADER (4) + 4n element-bytes.
808        // 100 u32 Elemente = 400 byte → body_len = 404.
809        encode_mutable_member_lc(&mut w, 0xABCD, false, LengthCode::Lc6, |w| {
810            // DHEADER: gibt die Anzahl der Element-Bytes an (400).
811            400u32.encode(w)?;
812            for i in 0..100u32 {
813                i.encode(w)?;
814            }
815            Ok(())
816        })
817        .unwrap();
818        let bytes = w.into_bytes();
819
820        // EMHEADER: must_understand=0, lc=6, member_id=0xABCD.
821        // → 0x6000_ABCD LE = [0xCD, 0xAB, 0x00, 0x60].
822        assert_eq!(&bytes[0..4], &[0xCD, 0xAB, 0x00, 0x60]);
823        // NEXTINT = element-count = 100 LE.
824        assert_eq!(&bytes[4..8], &[100, 0, 0, 0]);
825        // Payload: DHEADER 4 + 100 * 4 = 404 byte ab Offset 8.
826        assert_eq!(bytes.len(), 8 + 404);
827
828        // Decoder akzeptiert.
829        let mut r = BufferReader::new(&bytes, Endianness::Little);
830        let m = read_mutable_member(&mut r).unwrap().unwrap();
831        assert_eq!(m.length_code, LengthCode::Lc6);
832        assert_eq!(m.member_id, 0xABCD);
833        assert_eq!(m.body.len(), 404);
834    }
835
836    #[test]
837    fn lc6_with_many_elements_decodes_correctly() {
838        // 70_000 Elemente — Encoder schreibt LC=6 mit grossem NEXTINT.
839        // Verifiziert, dass der Decoder den >= u16 NEXTINT korrekt
840        // liest (kein silent-truncate).
841        let mut w = BufferWriter::new(Endianness::Little);
842        encode_mutable_member_lc(&mut w, 5, false, LengthCode::Lc6, |w| {
843            // DHEADER = element-bytes-count (70_000 * 4 = 280_000).
844            280_000u32.encode(w)?;
845            for i in 0..70_000u32 {
846                i.encode(w)?;
847            }
848            Ok(())
849        })
850        .unwrap();
851        let bytes = w.into_bytes();
852        let nextint = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
853        assert_eq!(nextint, 70_000);
854        let mut r = BufferReader::new(&bytes, Endianness::Little);
855        let m = read_mutable_member(&mut r).unwrap().unwrap();
856        // body inkl. DHEADER = 4 + 280_000.
857        assert_eq!(m.body.len(), 4 + 70_000 * 4);
858    }
859
860    #[test]
861    fn lc7_array_of_8byte_primitives() {
862        let mut w = BufferWriter::new(Endianness::Little);
863        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |w| {
864            16u32.encode(w)?;
865            w.write_bytes(&100u64.to_le_bytes())?;
866            w.write_bytes(&200u64.to_le_bytes())?;
867            Ok(())
868        })
869        .unwrap();
870        let bytes = w.into_bytes();
871        assert_eq!(&bytes[4..8], &[2, 0, 0, 0]);
872        assert_eq!(bytes.len(), 4 + 4 + 20);
873        let mut r = BufferReader::new(&bytes, Endianness::Little);
874        let m = read_mutable_member(&mut r).unwrap().unwrap();
875        assert_eq!(m.length_code, LengthCode::Lc7);
876        assert_eq!(m.body.len(), 20);
877    }
878
879    #[test]
880    fn lc0_rejects_wrong_body_size() {
881        let mut w = BufferWriter::new(Endianness::Little);
882        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc0, |w| 42u32.encode(w));
883        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
884    }
885
886    #[test]
887    fn lc6_rejects_misaligned_body() {
888        let mut w = BufferWriter::new(Endianness::Little);
889        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |w| {
890            0u32.encode(w)?;
891            0u8.encode(w)?;
892            0u8.encode(w)?;
893            0u8.encode(w)?;
894            Ok(())
895        });
896        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
897    }
898
899    #[test]
900    fn length_code_body_len_calculation() {
901        assert_eq!(LengthCode::Lc0.body_len(0), 1);
902        assert_eq!(LengthCode::Lc1.body_len(0), 2);
903        assert_eq!(LengthCode::Lc2.body_len(0), 4);
904        assert_eq!(LengthCode::Lc3.body_len(0), 8);
905        assert_eq!(LengthCode::Lc4.body_len(100), 100);
906        assert_eq!(LengthCode::Lc5.body_len(20), 20);
907        assert_eq!(LengthCode::Lc6.body_len(3), 16);
908        assert_eq!(LengthCode::Lc7.body_len(2), 20);
909    }
910
911    #[test]
912    fn length_code_has_nextint_flag() {
913        assert!(!LengthCode::Lc0.has_nextint());
914        assert!(!LengthCode::Lc3.has_nextint());
915        assert!(LengthCode::Lc4.has_nextint());
916        assert!(LengthCode::Lc7.has_nextint());
917    }
918
919    #[test]
920    fn length_code_from_wire_roundtrip() {
921        for v in 0..=7u8 {
922            let lc = LengthCode::from_wire(v).expect("valid");
923            assert_eq!(lc as u8, v);
924        }
925        assert!(LengthCode::from_wire(8).is_none());
926    }
927
928    // ---- Mixed nesting ----
929
930    #[test]
931    fn appendable_in_mutable_member() {
932        let mut w = BufferWriter::new(Endianness::Little);
933        encode_mutable_member(&mut w, 5, false, |w| {
934            encode_appendable(w, |w| {
935                42u32.encode(w)?;
936                100u32.encode(w)?;
937                Ok(())
938            })
939        })
940        .unwrap();
941        let bytes = w.into_bytes();
942        let mut r = BufferReader::new(&bytes, Endianness::Little);
943        let m = read_mutable_member(&mut r).unwrap().unwrap();
944        assert_eq!(m.member_id, 5);
945        let mut sub = BufferReader::new(m.body, Endianness::Little);
946        let (a, b) = decode_appendable(&mut sub, |r| {
947            Ok::<_, DecodeError>((u32::decode(r)?, u32::decode(r)?))
948        })
949        .unwrap();
950        assert_eq!((a, b), (42, 100));
951    }
952
953    // ---- Mutation-Killer fuer encode_mutable_member_lc ----
954
955    /// Faengt Mutation `>` -> `>=` auf member_id-Boundary (Zeile 176).
956    /// member_id == 0x0FFFFFFF (= 28-bit-MAX) muss DURCHGEHEN.
957    #[test]
958    fn mutable_member_id_at_28bit_max_accepted() {
959        let mut w = BufferWriter::new(Endianness::Little);
960        let res = encode_mutable_member_lc(&mut w, 0x0FFF_FFFF, false, LengthCode::Lc2, |inner| {
961            u32::encode(&0u32, inner)
962        });
963        assert!(
964            res.is_ok(),
965            "member_id=0x0FFFFFFF must succeed, got {res:?}"
966        );
967    }
968
969    /// Member_id ueber 28-Bit muss ABGELEHNT werden.
970    /// Faengt `>` -> `==` Mutation auf der gleichen Zeile (=> nur exact
971    /// match wuerde erroren, alle hoeheren wuerden Pass werden).
972    #[test]
973    fn mutable_member_id_29bit_rejected() {
974        let mut w = BufferWriter::new(Endianness::Little);
975        let res = encode_mutable_member_lc(&mut w, 0x1000_0000, false, LengthCode::Lc2, |inner| {
976            u32::encode(&0u32, inner)
977        });
978        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
979    }
980
981    /// Faengt Mutation `<` -> `==` auf body_len < 4 in Lc6 (Zeile 226).
982    /// body_len < 4 muss erroren — egal welcher Wert.
983    #[test]
984    fn lc6_body_len_less_than_4_rejected() {
985        for short_len in [0usize, 1, 2, 3] {
986            let mut w = BufferWriter::new(Endianness::Little);
987            let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
988                inner.write_bytes(&vec![0u8; short_len])
989            });
990            assert!(
991                matches!(res, Err(EncodeError::ValueOutOfRange { .. })),
992                "Lc6 with body_len={short_len} must error, got {res:?}"
993            );
994        }
995    }
996
997    /// Faengt `<` -> `<=` Mutation: body_len == 4 (DHEADER allein, n=0)
998    /// muss DURCHGEHEN bei Lc6 (4-4=0, 0%4=0).
999    #[test]
1000    fn lc6_body_len_exactly_4_accepted() {
1001        let mut w = BufferWriter::new(Endianness::Little);
1002        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1003            inner.write_bytes(&[0u8; 4])
1004        });
1005        assert!(res.is_ok(), "Lc6 body_len=4 must succeed, got {res:?}");
1006    }
1007
1008    /// Faengt Mutation `-` -> `+` auf `(body_len - 4) / 4` fuer Lc6.
1009    /// nextint muss EXAKT (body_len - 4) / 4 sein, nicht (body_len + 4) / 4.
1010    /// body_len=12 → original n=2, mutiert n=4.
1011    #[test]
1012    fn lc6_nextint_value_is_minus_4_div_4() {
1013        let mut w = BufferWriter::new(Endianness::Little);
1014        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1015            inner.write_bytes(&[0u8; 12])
1016        })
1017        .unwrap();
1018        let bytes = w.into_bytes();
1019        // Layout: 4-byte EMHEADER + 4-byte nextint + 12-byte body = 20.
1020        assert_eq!(bytes.len(), 20);
1021        // nextint = bytes[4..8] LE
1022        let mut ni = [0u8; 4];
1023        ni.copy_from_slice(&bytes[4..8]);
1024        let nextint = u32::from_le_bytes(ni);
1025        assert_eq!(nextint, 2, "nextint must be (12-4)/4=2, not (12+4)/4=4");
1026    }
1027
1028    /// Lc7-Variante: gleiche Mutationen wie Lc6 mit `% 8` und `/ 8`.
1029    /// body_len < 4 muss erroren.
1030    #[test]
1031    fn lc7_body_len_less_than_4_rejected() {
1032        for short_len in [0usize, 1, 2, 3] {
1033            let mut w = BufferWriter::new(Endianness::Little);
1034            let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1035                inner.write_bytes(&vec![0u8; short_len])
1036            });
1037            assert!(
1038                matches!(res, Err(EncodeError::ValueOutOfRange { .. })),
1039                "Lc7 with body_len={short_len} must error"
1040            );
1041        }
1042    }
1043
1044    /// Lc7 body_len==4 (DHEADER + 0 elements) muss durchgehen.
1045    /// Faengt `<` -> `<=` Mutation.
1046    #[test]
1047    fn lc7_body_len_exactly_4_accepted() {
1048        let mut w = BufferWriter::new(Endianness::Little);
1049        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1050            inner.write_bytes(&[0u8; 4])
1051        });
1052        assert!(res.is_ok());
1053    }
1054
1055    /// Lc7 nextint = (body_len - 4) / 8. body_len=20 → n=2 original,
1056    /// n=3 mit `+`-Mutation.
1057    #[test]
1058    fn lc7_nextint_value_is_minus_4_div_8() {
1059        let mut w = BufferWriter::new(Endianness::Little);
1060        encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1061            inner.write_bytes(&[0u8; 20])
1062        })
1063        .unwrap();
1064        let bytes = w.into_bytes();
1065        // 4 EMHEADER + 4 nextint + 20 body = 28
1066        assert_eq!(bytes.len(), 28);
1067        let mut ni = [0u8; 4];
1068        ni.copy_from_slice(&bytes[4..8]);
1069        let nextint = u32::from_le_bytes(ni);
1070        assert_eq!(nextint, 2, "nextint must be (20-4)/8=2, not (20+4)/8=3");
1071    }
1072
1073    /// Lc6 body_len=8 muss erroren ((8-4)%4=0 ok, also Pass — kein
1074    /// Boundary-Fail). Hier Test fuer Lc6 body_len=6: (6-4)%4=2 ≠ 0 → Error.
1075    /// Faengt `||` -> `&&` (Zeile 226 wurde nicht direkt missed, aber
1076    /// Test fuer Vollstaendigkeit).
1077    #[test]
1078    fn lc6_misaligned_body_len_rejected() {
1079        let mut w = BufferWriter::new(Endianness::Little);
1080        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1081            inner.write_bytes(&[0u8; 6])
1082        });
1083        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
1084    }
1085
1086    /// Lc7 misaligned body. Faengt `||` -> `&&` (Zeile 238).
1087    /// body_len=12: (12-4)%8 = 8%8 = 0 ok. Need body_len=10: (10-4)%8 = 6.
1088    #[test]
1089    fn lc7_misaligned_body_len_rejected() {
1090        let mut w = BufferWriter::new(Endianness::Little);
1091        let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1092            inner.write_bytes(&[0u8; 10])
1093        });
1094        assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
1095    }
1096
1097    /// EMHEADER must_understand-Bit + LC-Bits werden korrekt gesetzt.
1098    /// Faengt `|` -> `^`/`-`/`*` Mutationen auf der EMHEADER-
1099    /// Konstruktion (nach Refactor zu `+`).
1100    #[test]
1101    fn emheader_combines_must_understand_lc_and_member_id() {
1102        let mut w = BufferWriter::new(Endianness::Little);
1103        encode_mutable_member_lc(&mut w, 0x123_4567, true, LengthCode::Lc6, |inner| {
1104            inner.write_bytes(&[0u8; 4])
1105        })
1106        .unwrap();
1107        let bytes = w.into_bytes();
1108        let mut h = [0u8; 4];
1109        h.copy_from_slice(&bytes[..4]);
1110        let emheader = u32::from_le_bytes(h);
1111        // m_bit (Bit 31) = 0x8000_0000
1112        // lc_bits (Lc6 = 6 << 28) = 0x6000_0000
1113        // member_id = 0x0123_4567
1114        // Sum = 0x8000_0000 + 0x6000_0000 + 0x0123_4567 = 0xE123_4567
1115        assert_eq!(emheader, 0xE123_4567);
1116    }
1117}