Skip to main content

zerodds_wire/
reader.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Taking bytes off the wire.
4//!
5//! Every read that cannot be satisfied is an error naming what was
6//! expected and where. A reader that returned a zero for a truncated
7//! stream would hand the caller a value that was never sent.
8
9use crate::writer::{
10    BYTE_ORDER_MARK, E_FLAG_MUST_UNDERSTAND, E_MEMBER_ID_MASK, FixedDigits, ParameterRole,
11    PlCdr1Framing, PlCdr1Reading, SWAPPED_BYTE_ORDER_MARK, WcharFraming, WcharPosition, WcharWidth,
12    WirePosition, WstringLengthCounts, WstringLengthPolicy, WstringTerminator, WstringUnitOrder,
13};
14use crate::writer::{
15    E_FLAG_IMPL_EXTENSION, PID_EXTENDED, PID_IGNORE, PID_IMPL_EXTENSION, PID_LIST_END,
16};
17use crate::{Endianness, WireError};
18use alloc::collections::BTreeMap;
19use alloc::format;
20use alloc::string::{String, ToString};
21use alloc::vec::Vec;
22use zerodds_float128::Float128;
23
24/// The widest `fixed` IDL 4.2 §7.4.1.4.4.3.4 admits, in octets:
25/// `fixed<31, S>` is thirty-two nibbles. A value-precision field that presents
26/// no sign nibble within it is a stream this reader refuses rather than one it
27/// keeps reading.
28const MAX_FIXED_OCTETS: usize = 16;
29
30/// The lowest nibble value that is a sign and not a digit.
31///
32/// CORBA 3.3 part 2 §9.3.2.8: a digit is `0` to `9`, and every nibble at or
33/// above this is a sign. It is what makes the value-precision form
34/// self-delimiting. `zerodds_ast_layout::key::FixedDigitCount::FIRST_SIGN_NIBBLE`
35/// is the planner's half of the same constant, and
36/// `crates/wire-codec/tests/fixed_digits_agree.rs` holds the two together.
37const FIRST_SIGN_NIBBLE: u8 = 0x0a;
38
39/// Reads one sample's bytes.
40#[derive(Debug, Clone)]
41pub struct Reader<'a> {
42    bytes: &'a [u8],
43    at: usize,
44    endianness: Endianness,
45    /// Where alignment is counted from.
46    ///
47    /// Zero for a sample, and zero inside a DHEADER as well, because
48    /// XCDR counts alignment from the encapsulation and not from a
49    /// nested value's own start. A PL_CDR1 parameter is the exception:
50    /// §7.4.1.2 makes each one "a CDR-encapsulated block of data", so
51    /// its value counts from the block. `Writer` has the same field for
52    /// the same clause.
53    origin: usize,
54    /// The encoding's `MAXALIGN`. The writer's counterpart — a reader
55    /// that skipped padding the writer never wrote reads the wrong
56    /// bytes from the second member on.
57    max_align: usize,
58    /// How the peer is expected to have framed a PL_CDR1 parameter list.
59    ///
60    /// The same type the writer takes, and set from the same policy, so
61    /// the two cannot drift: a reader that accepted any framing would
62    /// pass every wrong-dialect run silently, and one that carried its
63    /// own copy of the rules would disagree with the writer the first
64    /// time either changed.
65    ///
66    /// Deliberately `PlCdr1Framing` and not a dialect. `zerodds-wire`
67    /// has no dependencies and is not going to gain one — it executes a
68    /// plan and does not know what a dialect is. The settings that *are*
69    /// dialect-level reach the read path the other way, through the
70    /// layout plan the reader walks, which is computed from the same
71    /// `LayoutKey` the writer used.
72    pl_cdr1: PlCdr1Framing,
73    /// How wide a `Char16` the peer wrote is. The writer's counterpart:
74    /// a reader two bytes narrower than the stream returns the right
75    /// character and then reads every later member two bytes early.
76    wchar_width: WcharWidth,
77    /// How the peer framed a scalar `Char16`. See
78    /// [`crate::writer::WcharFraming`]: an ORB writes an octet count and
79    /// then the octets, and a reader that expected a bare code unit
80    /// takes the count for half a character.
81    wchar_framing: WcharFraming,
82    /// In what byte order the peer put wide code units. See
83    /// [`crate::writer::WstringUnitOrder`].
84    wstring_unit_order: WstringUnitOrder,
85    /// Whether a PL_CDR1 stream in another framing than `pl_cdr1` is
86    /// read or reported. See [`PlCdr1Reading`].
87    pl_cdr1_reading: PlCdr1Reading,
88}
89
90impl<'a> Reader<'a> {
91    /// A reader over `bytes` in the given byte order.
92    #[must_use]
93    pub const fn new(bytes: &'a [u8], endianness: Endianness) -> Self {
94        Self {
95            bytes,
96            at: 0,
97            endianness,
98            origin: 0,
99            max_align: 8,
100            pl_cdr1: PlCdr1Framing::DEFAULT,
101            wchar_width: WcharWidth::Two,
102            wchar_framing: WcharFraming::Fixed,
103            wstring_unit_order: WstringUnitOrder::Stream,
104            pl_cdr1_reading: PlCdr1Reading::Strict,
105        }
106    }
107
108    /// A reader whose alignment is capped at `max_align`. Four for
109    /// XCDR2, eight for XCDR1.
110    #[must_use]
111    pub const fn with_max_align(bytes: &'a [u8], endianness: Endianness, max_align: usize) -> Self {
112        Self {
113            bytes,
114            at: 0,
115            endianness,
116            origin: 0,
117            max_align: if max_align < 1 { 1 } else { max_align },
118            pl_cdr1: PlCdr1Framing::DEFAULT,
119            wchar_width: WcharWidth::Two,
120            wchar_framing: WcharFraming::Fixed,
121            wstring_unit_order: WstringUnitOrder::Stream,
122            pl_cdr1_reading: PlCdr1Reading::Strict,
123        }
124    }
125
126    /// The same reader expecting a `Char16` at another width.
127    ///
128    /// Set from the same
129    /// `zerodds_ast_layout::WstringLengthPolicy::character_width` the
130    /// writer was set from, which is what keeps the two agreeing.
131    #[must_use]
132    pub const fn with_wchar_width(mut self, width: WcharWidth) -> Self {
133        self.wchar_width = width;
134        self
135    }
136
137    /// The `Char16` width in force.
138    #[must_use]
139    pub const fn wchar_width(&self) -> WcharWidth {
140        self.wchar_width
141    }
142
143    /// The same reader expecting another scalar `Char16` framing.
144    ///
145    /// Set from the same `zerodds_ast_layout::WcharFraming` the writer
146    /// and the layout plan were set from.
147    #[must_use]
148    pub const fn with_wchar_framing(mut self, framing: WcharFraming) -> Self {
149        self.wchar_framing = framing;
150        self
151    }
152
153    /// The `Char16` framing in force.
154    #[must_use]
155    pub const fn wchar_framing(&self) -> WcharFraming {
156        self.wchar_framing
157    }
158
159    /// The same reader expecting another wide code-unit order.
160    #[must_use]
161    pub const fn with_wstring_unit_order(mut self, order: WstringUnitOrder) -> Self {
162        self.wstring_unit_order = order;
163        self
164    }
165
166    /// The wide code-unit order in force.
167    #[must_use]
168    pub const fn wstring_unit_order(&self) -> WstringUnitOrder {
169        self.wstring_unit_order
170    }
171
172    /// The same reader expecting another PL_CDR1 framing.
173    #[must_use]
174    pub const fn with_pl_cdr1_framing(mut self, framing: PlCdr1Framing) -> Self {
175        self.pl_cdr1 = framing;
176        self
177    }
178
179    /// The framing in force.
180    #[must_use]
181    pub const fn pl_cdr1_framing(&self) -> PlCdr1Framing {
182        self.pl_cdr1
183    }
184
185    /// The same reader taking, or reporting, a PL_CDR1 stream in another
186    /// framing than the one it was given.
187    ///
188    /// [`PlCdr1Reading::Strict`] is the default. Under
189    /// [`PlCdr1Reading::Tolerant`] the reader accepts any flags on
190    /// `PID_EXTENDED` and `PID_LIST_END` (XTypes 1.3 §7.4.1.2.1), the
191    /// short and the extended header of a parameter whichever form the
192    /// framing names (§7.4.1.2.2), and a declared length that is the
193    /// value's own length or that length rounded up to four (§7.4.1.2).
194    #[must_use]
195    pub const fn with_pl_cdr1_reading(mut self, reading: PlCdr1Reading) -> Self {
196        self.pl_cdr1_reading = reading;
197        self
198    }
199
200    /// The PL_CDR1 reading in force.
201    #[must_use]
202    pub const fn pl_cdr1_reading(&self) -> PlCdr1Reading {
203        self.pl_cdr1_reading
204    }
205
206    /// The alignment cap in force.
207    #[must_use]
208    pub const fn max_align(&self) -> usize {
209        self.max_align
210    }
211
212    /// The byte order this reader was given.
213    ///
214    /// A caller that has to decide something the stream's order settles
215    /// and that this reader does not read for it — a CORBA `wstring`
216    /// without a byte-order mark takes the message's order, CORBA 3.3
217    /// part 2 §9.3.1.6 — has to be able to ask.
218    #[must_use]
219    pub const fn endianness(&self) -> Endianness {
220        self.endianness
221    }
222
223    /// Where it is.
224    #[must_use]
225    pub const fn position(&self) -> usize {
226        self.at
227    }
228
229    /// How many bytes are left.
230    #[must_use]
231    pub const fn remaining(&self) -> usize {
232        self.bytes.len().saturating_sub(self.at)
233    }
234
235    /// Move to `target`, which is a position this reader has already
236    /// been told about.
237    ///
238    /// The one way a header-driven decoder steps over a member it does
239    /// not declare: XTypes 1.3 §7.4.3.4.2 gives every `MMEMBER` an
240    /// extent, and §7.4.1.2 gives every PL_CDR1 parameter a length, so
241    /// where the next header starts is always known even when what sits
242    /// between is not.
243    ///
244    /// # Errors
245    /// [`WireError::Truncated`] for a target past the end of the stream
246    /// and [`WireError::OutOfRange`] for one behind the current
247    /// position. Neither is silently clamped: a target behind the cursor
248    /// means the extent and the value disagree, and a reader that
249    /// stayed put would read the *value's* bytes as the next header.
250    pub fn skip_to(&mut self, target: usize) -> Result<(), WireError> {
251        if target < self.at {
252            return Err(WireError::OutOfRange {
253                at: self.at,
254                expected: alloc::format!(
255                    "a member extent ending at or after {}, and this one ends at {target}",
256                    self.at
257                ),
258            });
259        }
260        if target > self.bytes.len() {
261            return Err(WireError::Truncated {
262                at: self.at,
263                needed: target - self.at,
264                available: self.remaining(),
265            });
266        }
267        self.at = target;
268        Ok(())
269    }
270
271    /// A string this reader has just taken off the wire, against the
272    /// bound of the member it is going into.
273    ///
274    /// A method rather than a free call so the offset in a
275    /// [`WireError::Discarded`] is this reader's own. The narrowing
276    /// itself is [`crate::try_construct::fit_string`], which is where
277    /// the clause quotations are.
278    ///
279    /// # Errors
280    /// As [`crate::try_construct::fit_string`].
281    pub fn fit_string(
282        &self,
283        value: String,
284        bound: usize,
285        behaviour: crate::TryConstruct,
286    ) -> Result<crate::Narrowed<String>, WireError> {
287        crate::try_construct::fit_string(self.at, value, bound, behaviour)
288    }
289
290    /// A `wstring` this reader has just taken off the wire, against the
291    /// bound of the member it is going into.
292    ///
293    /// The bound counts **UTF-16 code units** and not octets, which is
294    /// why this is its own method and not [`Reader::fit_string`] with
295    /// another argument. The narrowing itself is
296    /// [`crate::try_construct::fit_wstring`].
297    ///
298    /// # Errors
299    /// As [`crate::try_construct::fit_wstring`].
300    pub fn fit_wstring(
301        &self,
302        value: String,
303        bound: usize,
304        behaviour: crate::TryConstruct,
305    ) -> Result<crate::Narrowed<String>, WireError> {
306        crate::try_construct::fit_wstring(self.at, value, bound, behaviour)
307    }
308
309    /// A sequence this reader has just taken off the wire, against the
310    /// bound of the member it is going into.
311    ///
312    /// # Errors
313    /// As [`crate::try_construct::fit_sequence`].
314    pub fn fit_sequence<T>(
315        &self,
316        value: Vec<T>,
317        bound: usize,
318        behaviour: crate::TryConstruct,
319    ) -> Result<crate::Narrowed<Vec<T>>, WireError> {
320        crate::try_construct::fit_sequence(self.at, value, bound, behaviour)
321    }
322
323    /// A map this reader has just taken off the wire, against the bound
324    /// of the member it is going into.
325    ///
326    /// The bound counts **entries**, which is the unit Table 17's
327    /// `O2.length <= T1.length` is in for a `map<K, V, N>`.
328    ///
329    /// # Errors
330    /// As [`crate::try_construct::fit_map`].
331    pub fn fit_map<K: Ord, V>(
332        &self,
333        value: BTreeMap<K, V>,
334        bound: usize,
335        behaviour: crate::TryConstruct,
336    ) -> Result<crate::Narrowed<BTreeMap<K, V>>, WireError> {
337        crate::try_construct::fit_map(self.at, value, bound, behaviour)
338    }
339
340    /// A map held as its entries (XTypes 1.3 §7.5.1.3.6), against the bound of the member it
341    /// is going into.
342    ///
343    /// # Errors
344    /// As [`crate::try_construct::fit_map_entries`].
345    pub fn fit_map_entries<K, V>(
346        &self,
347        value: Vec<crate::MapEntry<K, V>>,
348        bound: usize,
349        behaviour: crate::TryConstruct,
350    ) -> Result<crate::Narrowed<Vec<crate::MapEntry<K, V>>>, WireError> {
351        crate::try_construct::fit_map_entries(self.at, value, bound, behaviour)
352    }
353
354    /// Skip padding to `align`.
355    ///
356    /// # Errors
357    /// [`WireError::Truncated`] when the padding the writer must have
358    /// written is not there. The offset reported is where the padding
359    /// began, which is where the stream stopped agreeing with itself —
360    /// letting the position run past the buffer instead put every later
361    /// error at an offset the stream does not have.
362    pub fn align_to(&mut self, align: usize) -> Result<(), WireError> {
363        let align = if align < self.max_align {
364            align
365        } else {
366            self.max_align
367        };
368        if align <= 1 {
369            return Ok(());
370        }
371        let offset = self.at - self.origin;
372        let padding = (align - (offset % align)) % align;
373        if padding > self.remaining() {
374            return Err(WireError::Truncated {
375                at: self.at,
376                needed: padding,
377                available: self.remaining(),
378            });
379        }
380        self.at += padding;
381        Ok(())
382    }
383
384    /// Begin a collection element whose alignment is counted from its
385    /// own first octet, returning the origin it replaced.
386    ///
387    /// The read-side pair of [`Writer::begin_packed_element`](crate::Writer::begin_packed_element), and it
388    /// has to move with it: a reader that aligned the departure from the
389    /// encapsulation would consume the padding its writer did not emit
390    /// and misread every element after the first.
391    pub fn begin_packed_element(&mut self) -> usize {
392        let saved = self.origin;
393        self.origin = self.at;
394        saved
395    }
396
397    /// Restore the stream origin a packed element displaced. The pair of
398    /// [`Self::begin_packed_element`].
399    pub fn finish_packed_element(&mut self, saved: usize) {
400        self.origin = saved;
401    }
402
403    // -- primitives ------------------------------------------------------
404
405    /// `boolean`.
406    ///
407    /// # Errors
408    /// [`WireError::Truncated`], or [`WireError::OutOfRange`] for a byte
409    /// that is neither 0 nor 1 — a boolean that is 2 is a stream that
410    /// disagrees with its own type.
411    pub fn read_bool(&mut self) -> Result<bool, WireError> {
412        let at = self.at;
413        match self.read_u8()? {
414            0 => Ok(false),
415            1 => Ok(true),
416            _ => Err(WireError::OutOfRange {
417                at,
418                expected: String::from("a boolean of 0 or 1"),
419            }),
420        }
421    }
422
423    /// `octet` and `uint8`.
424    ///
425    /// # Errors
426    /// [`WireError::Truncated`].
427    pub fn read_u8(&mut self) -> Result<u8, WireError> {
428        let byte = *self.bytes.get(self.at).ok_or(WireError::Truncated {
429            at: self.at,
430            needed: 1,
431            available: self.remaining(),
432        })?;
433        self.at += 1;
434        Ok(byte)
435    }
436
437    /// `int8`.
438    ///
439    /// # Errors
440    /// As [`Reader::read_u8`].
441    pub fn read_i8(&mut self) -> Result<i8, WireError> {
442        #[allow(clippy::cast_possible_wrap)]
443        Ok(self.read_u8()? as i8)
444    }
445
446    /// `char`.
447    ///
448    /// # Errors
449    /// As [`Reader::read_u8`].
450    pub fn read_char(&mut self) -> Result<char, WireError> {
451        Ok(char::from(self.read_u8()?))
452    }
453
454    /// `wchar`, at the width [`Reader::wchar_width`] names.
455    ///
456    /// The four-byte form is RTI's XCDR1 one, where the high sixteen
457    /// bits are zero padding around a code unit. A value that uses them
458    /// is refused rather than truncated: narrowing it here would return
459    /// a character the peer did not send.
460    ///
461    /// `position` selects the framing exactly as it does on the writing
462    /// side, and for the same reason: under
463    /// [`WcharFraming::GiopLengthPrefixedMemberOnly`] a member carries a
464    /// count octet and an element does not, so a reader given the wrong
465    /// position takes the first half of a code unit for a count and
466    /// refuses — or, at the one width where the count is a plausible
467    /// unit, returns a character nobody sent.
468    ///
469    /// # Errors
470    /// [`WireError::Truncated`], or [`WireError::OutOfRange`] for a code
471    /// point that is not a character, or for a four-byte character
472    /// holding more than one UTF-16 code unit.
473    pub fn read_wchar(&mut self, position: WcharPosition) -> Result<char, WireError> {
474        let width = self.wchar_width;
475        let at = self.at;
476        let value = if self.wchar_framing.has_length_octet(position) {
477            self.read_giop_wchar()?
478        } else {
479            self.align_to(width.bytes())?;
480            match width {
481                WcharWidth::Two => u32::from(self.read_u16()?),
482                WcharWidth::Four => self.read_u32()?,
483            }
484        };
485        if value > 0xFFFF {
486            return Err(WireError::OutOfRange {
487                at,
488                expected: String::from("a four-byte wchar holding one UTF-16 code unit"),
489            });
490        }
491        char::from_u32(value).ok_or(WireError::OutOfRange {
492            at,
493            expected: String::from("a Unicode code point"),
494        })
495    }
496
497    /// [`Reader::read_wchar`] at the member position.
498    ///
499    /// The reading counterpart of `Writer::write_wchar_member`, and
500    /// there for the same reason: a collection combinator takes a path
501    /// of one argument.
502    ///
503    /// # Errors
504    /// As [`Reader::read_wchar`].
505    pub fn read_wchar_member(&mut self) -> Result<char, WireError> {
506        self.read_wchar(WcharPosition::Member)
507    }
508
509    /// [`Reader::read_wchar`] at the collection-element position.
510    ///
511    /// # Errors
512    /// As [`Reader::read_wchar`].
513    pub fn read_wchar_element(&mut self) -> Result<char, WireError> {
514        self.read_wchar(WcharPosition::Element)
515    }
516
517    /// CORBA §9.3.1.6's GIOP 1.2 `wchar`: an octet count, then that many
518    /// octets, aligned to one.
519    ///
520    /// The count is checked against the configured code-set width rather
521    /// than believed. A stream whose first octet says `0xE4` — which is
522    /// what a bare two-octet `wchar` looks like read this way — would
523    /// otherwise consume 228 octets of whatever followed and return a
524    /// character assembled out of the next four members. The count is
525    /// the one field a peer controls and the one a reader must never let
526    /// decide how much it reads.
527    ///
528    /// # Errors
529    /// [`WireError::OutOfRange`] for a count that is not the configured
530    /// width, [`WireError::Truncated`] for a stream that ends inside the
531    /// octets.
532    fn read_giop_wchar(&mut self) -> Result<u32, WireError> {
533        let at = self.at;
534        let order = self.wstring_unit_order;
535        let expected = self.wchar_width.bytes();
536        let declared = usize::from(self.read_u8()?);
537        if declared != expected {
538            return Err(WireError::OutOfRange {
539                at,
540                expected: format!(
541                    "a GIOP 1.2 wchar of {expected} octets, the configured transmission code set, \
542                     and not {declared}"
543                ),
544            });
545        }
546        let octets = self.take(expected)?;
547        let mut value: u32 = 0;
548        // A scalar cannot carry a byte-order mark, so `ByteOrderMark`
549        // means CORBA's default here — big-endian — exactly as
550        // `BigEndianAlways` does. `Writer::write_wchar` has the run.
551        let little = matches!(order, WstringUnitOrder::Stream) && self.endianness.is_little();
552        if little {
553            for (shift, octet) in octets.iter().enumerate() {
554                value |= u32::from(*octet) << (8 * shift);
555            }
556        } else {
557            for octet in octets {
558                value = (value << 8) | u32::from(*octet);
559            }
560        }
561        Ok(value)
562    }
563
564    /// A bitmask, read from the holder its `@bit_bound` names, reduced to
565    /// the bits the type declares.
566    ///
567    /// `declared` is the set of positions the bitmask names, one bit per
568    /// position, and it is an argument rather than a runtime table
569    /// because the type is the generated code's and the wire is this
570    /// crate's.
571    ///
572    /// DDS-XTypes 1.3 §7.4.1.1.3.2: "the states of the remaining
573    /// serialized bits are not specified, and those bits are not
574    /// considered to be part of the bitmask", and Table 18: "Preserve bit
575    /// values by index for all bits identified in both T1 and T2." A bit
576    /// the stream sets and the type does not name is therefore not part
577    /// of the value this type reads, and it is dropped; it is not a
578    /// reason to refuse the sample, which would make a reader refuse a
579    /// writer whose type Table 18 declares assignable.
580    ///
581    /// # Errors
582    /// [`WireError::Truncated`] for a short stream, and
583    /// [`WireError::OutOfRange`] for a bound outside 1..=64.
584    pub fn read_bitmask(&mut self, bit_bound: u16, declared: u64) -> Result<u64, WireError> {
585        if bit_bound == 0 || bit_bound > 64 {
586            return Err(WireError::OutOfRange {
587                at: self.at,
588                expected: alloc::format!(
589                    "a bitmask bound between 1 and 64, and this one is {bit_bound}"
590                ),
591            });
592        }
593        let bits = match bit_bound {
594            0..=8 => u64::from(self.read_u8()?),
595            9..=16 => u64::from(self.read_u16()?),
596            17..=32 => u64::from(self.read_u32()?),
597            _ => self.read_u64()?,
598        };
599        Ok(bits & declared)
600    }
601
602    /// A bitset's anchor integer, of the width the plan sized it at.
603    ///
604    /// The counterpart of `Writer::write_bitset`; the unpacking of the
605    /// fields out of the anchor is the generated code's, because the
606    /// positions belong to the type and not to the wire.
607    ///
608    /// # Errors
609    /// [`WireError::Truncated`] for a short stream, and
610    /// [`WireError::OutOfRange`] for an anchor that is not one, two,
611    /// four or eight octets.
612    pub fn read_bitset(&mut self, anchor_bytes: u32) -> Result<u64, WireError> {
613        match anchor_bytes {
614            1 => Ok(u64::from(self.read_u8()?)),
615            2 => Ok(u64::from(self.read_u16()?)),
616            4 => Ok(u64::from(self.read_u32()?)),
617            8 => self.read_u64(),
618            other => Err(WireError::OutOfRange {
619                at: self.at,
620                expected: alloc::format!(
621                    "a bitset anchor of 1, 2, 4 or 8 octets, and this one is {other}"
622                ),
623            }),
624        }
625    }
626
627    /// `unsigned short`.
628    ///
629    /// # Errors
630    /// [`WireError::Truncated`].
631    pub fn read_u16(&mut self) -> Result<u16, WireError> {
632        self.align_to(2)?;
633        let bytes: [u8; 2] = self.take(2)?.try_into().map_err(|_| WireError::Truncated {
634            at: self.at,
635            needed: 2,
636            available: self.remaining(),
637        })?;
638        Ok(if self.endianness.is_little() {
639            u16::from_le_bytes(bytes)
640        } else {
641            u16::from_be_bytes(bytes)
642        })
643    }
644
645    /// A 16-bit value in **little-endian** order whatever the stream's
646    /// order is. The counterpart of `Writer::write_u16_little`, and see
647    /// there for the one caller it has.
648    ///
649    /// # Errors
650    /// [`WireError::Truncated`].
651    pub fn read_u16_little(&mut self) -> Result<u16, WireError> {
652        self.align_to(2)?;
653        let bytes: [u8; 2] = self.take(2)?.try_into().map_err(|_| WireError::Truncated {
654            at: self.at,
655            needed: 2,
656            available: self.remaining(),
657        })?;
658        Ok(u16::from_le_bytes(bytes))
659    }
660
661    /// A 16-bit value in **big-endian** order whatever the stream's
662    /// order is. The counterpart of `Writer::write_u16_big`.
663    ///
664    /// # Errors
665    /// [`WireError::Truncated`].
666    pub fn read_u16_big(&mut self) -> Result<u16, WireError> {
667        self.align_to(2)?;
668        let bytes: [u8; 2] = self.take(2)?.try_into().map_err(|_| WireError::Truncated {
669            at: self.at,
670            needed: 2,
671            available: self.remaining(),
672        })?;
673        Ok(u16::from_be_bytes(bytes))
674    }
675
676    /// A 32-bit value in **little-endian** order whatever the stream's
677    /// order is. The counterpart of `Writer::write_u32_little`.
678    ///
679    /// # Errors
680    /// [`WireError::Truncated`].
681    pub fn read_u32_little(&mut self) -> Result<u32, WireError> {
682        self.align_to(4)?;
683        let bytes: [u8; 4] = self.take(4)?.try_into().map_err(|_| WireError::Truncated {
684            at: self.at,
685            needed: 4,
686            available: self.remaining(),
687        })?;
688        Ok(u32::from_le_bytes(bytes))
689    }
690
691    /// A 32-bit value in **big-endian** order whatever the stream's
692    /// order is. The counterpart of `Writer::write_u32_big`.
693    ///
694    /// # Errors
695    /// [`WireError::Truncated`].
696    pub fn read_u32_big(&mut self) -> Result<u32, WireError> {
697        self.align_to(4)?;
698        let bytes: [u8; 4] = self.take(4)?.try_into().map_err(|_| WireError::Truncated {
699            at: self.at,
700            needed: 4,
701            available: self.remaining(),
702        })?;
703        Ok(u32::from_be_bytes(bytes))
704    }
705
706    /// `short`.
707    ///
708    /// # Errors
709    /// As [`Reader::read_u16`].
710    pub fn read_i16(&mut self) -> Result<i16, WireError> {
711        #[allow(clippy::cast_possible_wrap)]
712        Ok(self.read_u16()? as i16)
713    }
714
715    /// `unsigned long`.
716    ///
717    /// # Errors
718    /// [`WireError::Truncated`].
719    pub fn read_u32(&mut self) -> Result<u32, WireError> {
720        self.align_to(4)?;
721        let bytes: [u8; 4] = self.take(4)?.try_into().map_err(|_| WireError::Truncated {
722            at: self.at,
723            needed: 4,
724            available: self.remaining(),
725        })?;
726        Ok(if self.endianness.is_little() {
727            u32::from_le_bytes(bytes)
728        } else {
729            u32::from_be_bytes(bytes)
730        })
731    }
732
733    /// `long`.
734    ///
735    /// # Errors
736    /// As [`Reader::read_u32`].
737    pub fn read_i32(&mut self) -> Result<i32, WireError> {
738        #[allow(clippy::cast_possible_wrap)]
739        Ok(self.read_u32()? as i32)
740    }
741
742    /// `unsigned long long`.
743    ///
744    /// # Errors
745    /// [`WireError::Truncated`].
746    pub fn read_u64(&mut self) -> Result<u64, WireError> {
747        self.align_to(8)?;
748        let bytes: [u8; 8] = self.take(8)?.try_into().map_err(|_| WireError::Truncated {
749            at: self.at,
750            needed: 8,
751            available: self.remaining(),
752        })?;
753        Ok(if self.endianness.is_little() {
754            u64::from_le_bytes(bytes)
755        } else {
756            u64::from_be_bytes(bytes)
757        })
758    }
759
760    /// `long long`.
761    ///
762    /// # Errors
763    /// As [`Reader::read_u64`].
764    pub fn read_i64(&mut self) -> Result<i64, WireError> {
765        #[allow(clippy::cast_possible_wrap)]
766        Ok(self.read_u64()? as i64)
767    }
768
769    /// `float`.
770    ///
771    /// # Errors
772    /// As [`Reader::read_u32`].
773    pub fn read_f32(&mut self) -> Result<f32, WireError> {
774        Ok(f32::from_bits(self.read_u32()?))
775    }
776
777    /// `double`.
778    ///
779    /// # Errors
780    /// As [`Reader::read_u64`].
781    pub fn read_f64(&mut self) -> Result<f64, WireError> {
782        Ok(f64::from_bits(self.read_u64()?))
783    }
784
785    /// `long double`: all sixteen bytes, as IEEE 754 binary128.
786    ///
787    /// The counterpart of [`Writer::write_f128`](crate::Writer::write_f128). This used to read the
788    /// first eight bytes as a `double` and skip the rest, which decoded
789    /// every value a `double` cannot hold as a different number and said
790    /// nothing.
791    ///
792    /// The bytes are taken whole and reversed as a whole for a
793    /// big-endian stream, per CORBA 3.3 part 2 Figure 9.2. Alignment is
794    /// eight, capped to four under XCDR2 by
795    /// [`Reader::align_to`]'s `MAXALIGN`.
796    ///
797    /// # Errors
798    /// [`WireError::Truncated`].
799    pub fn read_f128(&mut self) -> Result<Float128, WireError> {
800        self.align_to(8)?;
801        let bytes = self.take(16)?;
802        let mut sixteen = [0u8; 16];
803        sixteen.copy_from_slice(bytes);
804        Ok(if self.endianness.is_little() {
805            Float128::from_le_bytes(sixteen)
806        } else {
807            Float128::from_be_bytes(sixteen)
808        })
809    }
810
811    // -- strings ---------------------------------------------------------
812
813    /// `fixed<P, S>`: packed BCD, CORBA 3.3 part 2 §9.3.2.8, as a decimal
814    /// string.
815    ///
816    /// A string and not a number, for the reason the twenty other language
817    /// columns of this project carry it as text too: `fixed<31, 2>` fits no
818    /// integer Rust has, and a binary float would be the wrong answer rather
819    /// than an approximate one, because `fixed` exists to be exact.
820    ///
821    /// The text that comes back is the one spelling the declaration admits —
822    /// no leading zeros past a single one before the point, exactly `S`
823    /// after it, and a `-` only on a value that is neither zero nor
824    /// positive. `1.5` and `0001.500` are the same sample under
825    /// `fixed<7, 3>` and only one string can come out of it.
826    ///
827    /// # Errors
828    /// [`WireError::Truncated`] for a stream that ends inside the field.
829    /// [`WireError::OutOfRange`] for a declaration outside IDL 4.2 rule
830    /// (42), a nibble that is neither a digit nor a sign, or — under
831    /// [`FixedDigits::ValuePrecision`] or [`FixedDigits::ValueStringDigits`] —
832    /// a field that presents no sign nibble within the widest `fixed` IDL
833    /// admits.
834    pub fn read_fixed(
835        &mut self,
836        digits: u8,
837        scale: u8,
838        policy: FixedDigits,
839    ) -> Result<String, WireError> {
840        // The same bound as the writer's, on this side too: rule (42) caps
841        // the BCD form at 31 digits, and a reader asked for a wider field
842        // would take octets no declaration can name.
843        if digits == 0 || digits > 31 || scale > digits {
844            return Err(WireError::refused(
845                self.position(),
846                "a fixed whose digit count and scale can describe a value",
847            ));
848        }
849        let mut read = Vec::with_capacity(usize::from(digits) + 2);
850        let nibbles = match policy {
851            FixedDigits::Declared => {
852                // The declaration decides how wide the field is, so the
853                // octet count is known before a byte is read.
854                let nibbles = usize::from(digits) + 1;
855                for _ in 0..nibbles.div_ceil(2) {
856                    let byte = self.read_u8()?;
857                    read.push(byte >> 4);
858                    read.push(byte & 0x0f);
859                }
860                nibbles
861            }
862            FixedDigits::ValuePrecision | FixedDigits::ValueStringDigits => {
863                // The value decides, so the field's end is read out of the
864                // octets: §9.3.2.8 makes a digit `0` to `9` and a sign `0xa`
865                // to `0xf`, and the nibble count is even by construction, so
866                // the field ends at the first octet whose **low** nibble is
867                // a sign. That is what makes this form decodable at all. A
868                // field wider than `fixed<31, S>`'s sixteen octets is a
869                // stream that never presents a sign nibble, and is refused
870                // rather than read on.
871                let mut octets = 0usize;
872                loop {
873                    if octets >= MAX_FIXED_OCTETS {
874                        return Err(WireError::refused(
875                            self.position(),
876                            "a sign nibble within the widest fixed IDL admits",
877                        ));
878                    }
879                    let byte = self.read_u8()?;
880                    read.push(byte >> 4);
881                    read.push(byte & 0x0f);
882                    octets += 1;
883                    if byte & 0x0f >= FIRST_SIGN_NIBBLE {
884                        break;
885                    }
886                }
887                read.len()
888            }
889        };
890        // A leading zero nibble pads the pair when the nibble count is odd;
891        // it is not a digit and the writer put it there for the packing
892        // alone. Under the value's count the pad is already inside what was
893        // read, and stripping a leading zero changes no value.
894        let body = read.get(read.len() - nibbles..).unwrap_or(&[]);
895        let Some((sign, value)) = body.split_last() else {
896            return Err(WireError::refused(
897                self.position(),
898                "at least a sign nibble",
899            ));
900        };
901        let negative = match sign {
902            0x0d | 0x0b => true,
903            0x0c | 0x0a | 0x0f | 0x0e => false,
904            _ => {
905                return Err(WireError::refused(
906                    self.position(),
907                    "a sign nibble, 0xc or 0xd",
908                ));
909            }
910        };
911        // The point sits `scale` digits from the **right** and not `P - S`
912        // from the left. Under the declared count the two are the same
913        // arithmetic, because the field carries exactly `P` digits; under
914        // the value's count it carries fewer, and a value with fewer digits
915        // than the scale — `1c` under `fixed<5, 2>` — needs its integer part
916        // written as a single zero. Enough leading zeros are prepended for
917        // the integer part to have at least one digit, which makes the split
918        // below unconditional.
919        let mut spelled = String::with_capacity(value.len() + usize::from(scale) + 1);
920        for _ in value.len()..usize::from(scale) + 1 {
921            spelled.push('0');
922        }
923        for digit in value {
924            if *digit > 9 {
925                return Err(WireError::refused(
926                    self.position(),
927                    "a decimal digit nibble",
928                ));
929            }
930            spelled.push(char::from(b'0' + digit));
931        }
932        // The one spelling the declaration admits: leading zeros beyond a
933        // single one go, and a negative zero comes back positive because
934        // §9.3.2.8 has one zero and gives it the positive sign nibble.
935        let split = spelled.len() - usize::from(scale);
936        let whole = spelled[..split].trim_start_matches('0');
937        let fraction = &spelled[split..];
938        let mut text = String::with_capacity(spelled.len() + 2);
939        if negative && !(whole.is_empty() && fraction.bytes().all(|byte| byte == b'0')) {
940            text.push('-');
941        }
942        text.push_str(if whole.is_empty() { "0" } else { whole });
943        if scale > 0 {
944            text.push('.');
945            text.push_str(fraction);
946        }
947        // A terminated BCD field can still exceed this declaration's bound
948        // under ValuePrecision. Apply the writer's shared rule before
949        // returning a value that the same type could never write back.
950        crate::writer::parse_fixed(&text, digits, scale, self.position())?;
951        Ok(text)
952    }
953
954    /// A string: a length including the terminator, the bytes, the NUL.
955    ///
956    /// # Errors
957    /// [`WireError::ImpossibleLength`] for a length the stream cannot
958    /// hold — allocating for it is how a reader turns a bad message into
959    /// an out-of-memory. [`WireError::NotUtf8`] for bytes that are not.
960    pub fn read_string(&mut self) -> Result<String, WireError> {
961        // Aligned before the offset is taken, so `at` names the length
962        // field and not the padding in front of it. An error whose
963        // offset points at padding cannot be matched to a length field
964        // by anyone reading a hex dump.
965        self.align_to(4)?;
966        let at = self.at;
967        let length = self.read_u32()? as usize;
968        if length == 0 || length > self.remaining() + 1 {
969            return Err(WireError::ImpossibleLength {
970                at,
971                claimed: length,
972                available: self.remaining(),
973            });
974        }
975        // The length counts the terminator; the text is one byte less.
976        let text_at = self.at;
977        let text = self.take(length - 1)?;
978        let out = core::str::from_utf8(text)
979            .map_err(|_| WireError::NotUtf8 { at })?
980            .to_string();
981        // §7.4.1.1.2 puts a NUL at the end and the length counts it. A
982        // last byte that is not one is a frame that disagrees with
983        // itself, and reading past it takes the next member's first
984        // byte for a terminator.
985        let terminator_at = self.at;
986        if self.read_u8()? != 0 {
987            return Err(WireError::OutOfRange {
988                at: terminator_at,
989                expected: String::from("a NUL terminating the string, as its length counts"),
990            });
991        }
992        // A NUL inside the text is worse than a wrong terminator: every
993        // C-based peer stops at it, so the value this reader returns and
994        // the value the peer returns differ for bytes both accepted.
995        if let Some(offset) = out.bytes().position(|byte| byte == 0) {
996            return Err(WireError::OutOfRange {
997                at: text_at + offset,
998                expected: String::from("no NUL inside a string a C peer would stop at"),
999            });
1000        }
1001        Ok(out)
1002    }
1003
1004    /// A `wstring`, in the cell `policy` and `order` name.
1005    ///
1006    /// The default is XTypes 1.3 §7.4.1.1.2 — an octet count, "twice
1007    /// the number of characters in the string", no terminator and no
1008    /// byte-order mark to strip. Eight of the nine measured
1009    /// implementations write something else in at least one axis, and
1010    /// [`WstringLengthPolicy`] carries the octets and the argument.
1011    ///
1012    /// # Nothing here looks at the length to decide which cell it is
1013    ///
1014    /// The length is the field a hostile peer controls, and the cells
1015    /// are not separable anyway: Fast DDS's `@final struct { wstring w;
1016    /// long n; }` with `w = "ab", n = 7` is `02000000 61006200
1017    /// 07000000`, and the specification's encoding of the same type with
1018    /// `w = "a", n = 7` is `02000000 6100`, two octets of padding,
1019    /// `07000000`. Twelve octets each, differing only in the content of
1020    /// padding that no clause fixes. A reader choosing between those two
1021    /// samples would not be resolving an ambiguity, it would be taking
1022    /// its answer from the length field.
1023    ///
1024    /// So the configured cell is read, and a length that cell cannot
1025    /// have produced is refused with the cell it *does* fit named in
1026    /// the error — a diagnosis after the refusal, not a parse decided
1027    /// by the stream.
1028    ///
1029    /// # Errors
1030    ///
1031    /// [`WireError::WrongFraming`] for a length the configured cell
1032    /// cannot have produced: an octet count the unit width does not
1033    /// divide, or a count of zero where C2 says the count includes a
1034    /// terminator. Rounding an odd octet count down would leave the
1035    /// remainder in the stream and put every later member at the wrong
1036    /// offset.
1037    ///
1038    /// [`WireError::ImpossibleLength`] for a length past the end of the
1039    /// stream — allocating for it is how a reader turns a bad message
1040    /// into an out-of-memory.
1041    ///
1042    /// [`WireError::OutOfRange`] for a four-octet character above
1043    /// `0xFFFF` (the wide form zero-extends a code *unit*, so a value
1044    /// that does not fit one is not a form this reader knows how to
1045    /// narrow), for a terminator that is not a NUL where one is
1046    /// expected, for a NUL inside the text where a terminator is
1047    /// expected — every C peer stops at it, so this reader's value and
1048    /// the peer's would differ for octets both accepted — and for units
1049    /// that are not well-formed UTF-16. An unpaired surrogate is
1050    /// refused rather than replaced: U+FFFD in its place would be a
1051    /// character the sender never had.
1052    ///
1053    /// That last refusal catches less than it looks. Fast CDR's
1054    /// truncation of `U+1D11E` is `U+D11E`, a Hangul syllable and
1055    /// well-formed UTF-16, so the wrong character arrives with nothing
1056    /// to object to. Only a code point whose low sixteen bits land in
1057    /// `D800..=DFFF` fails here — which is why
1058    /// [`Writer::write_wstring`](crate::Writer::write_wstring) refuses to produce the truncation in
1059    /// the first place.
1060    pub fn read_wstring(
1061        &mut self,
1062        policy: WstringLengthPolicy,
1063        order: WstringUnitOrder,
1064        position: WirePosition,
1065    ) -> Result<String, WireError> {
1066        // Aligned before the offset is taken, so `at` names the length
1067        // field and not the padding in front of it.
1068        self.align_to(4)?;
1069        let at = self.at;
1070        let declared = self.read_u32()? as usize;
1071        let width = policy.character_width(position);
1072        // The three coordinates, not a list of named forms. C1 says
1073        // whether the count is of units or of the octets they occupy,
1074        // C3 says what an octet count divides by, and C2 is handled
1075        // after the run because a terminator occupies a unit on the
1076        // wire whichever way it is counted.
1077        let count = match policy.counts_at(position) {
1078            WstringLengthCounts::Characters => declared,
1079            WstringLengthCounts::Octets => {
1080                if declared % width != 0 {
1081                    return Err(wstring_form_mismatch(policy, declared, at));
1082                }
1083                declared / width
1084            }
1085        };
1086        let mut units = self.read_wstring_units(count, width, order, at)?;
1087        if matches!(policy.terminator_at(position), WstringTerminator::Present) {
1088            // The count includes the NUL, so a count of zero is a frame
1089            // that does not even hold its own terminator.
1090            if declared < 1 {
1091                return Err(wstring_form_mismatch(policy, declared, at));
1092            }
1093            let terminator = units.pop();
1094            if terminator != Some(0) {
1095                return Err(WireError::OutOfRange {
1096                    at,
1097                    expected: String::from(
1098                        "a NUL character ending the wstring, as its count includes one",
1099                    ),
1100                });
1101            }
1102            // A NUL inside the text is the narrow string's problem in
1103            // wide form. Only where a terminator is expected — a cell
1104            // without one is entitled to carry U+0000 as a character.
1105            if let Some(offset) = units.iter().position(|unit| *unit == 0) {
1106                return Err(WireError::OutOfRange {
1107                    at,
1108                    expected: format!(
1109                        "no NUL inside a wstring a C peer would stop at, and this one has \
1110                         a NUL at character {offset} of {}",
1111                        units.len()
1112                    ),
1113                });
1114            }
1115        }
1116        // The mark is not a character of the string, and under
1117        // `ByteOrderMark` it also decides the order of everything behind
1118        // it — which `read_wstring_units` could not know until it had
1119        // read it.
1120        if order.has_mark() {
1121            strip_byte_order_mark(&mut units);
1122        }
1123        String::from_utf16(&units).map_err(|_| WireError::OutOfRange {
1124            at,
1125            expected: String::from("well-formed UTF-16, not an unpaired surrogate"),
1126        })
1127    }
1128
1129    /// A `wstring` whose characters arrive as their own UTF-8 octets.
1130    ///
1131    /// The read side of `Writer::write_wstring_utf8` and of
1132    /// `WstringCodeUnits::Utf8`: the declared length is the UTF-8 octet
1133    /// count, and the terminator, when the cell carries one, is the last
1134    /// of those octets. A count the stream cannot hold is refused before
1135    /// a byte is read, for the reason [`Reader::read_wstring`] gives.
1136    ///
1137    /// # Errors
1138    /// [`WireError::ImpossibleLength`] for a length larger than the
1139    /// stream can hold, [`WireError::OutOfRange`] for a terminator cell
1140    /// whose last octet is not a NUL or whose run carries one earlier,
1141    /// and [`WireError::NotUtf8`] for octets that are not well-formed
1142    /// UTF-8.
1143    pub fn read_wstring_utf8(
1144        &mut self,
1145        policy: WstringLengthPolicy,
1146        position: WirePosition,
1147    ) -> Result<String, WireError> {
1148        // `read_u32` aligns to four, so `at` names the length field and
1149        // not the padding in front of it.
1150        let at = self.at;
1151        let declared = self.read_u32()? as usize;
1152        if declared > self.remaining() {
1153            return Err(WireError::ImpossibleLength {
1154                at,
1155                claimed: declared,
1156                available: self.remaining(),
1157            });
1158        }
1159        let mut bytes = Vec::with_capacity(declared);
1160        for _ in 0..declared {
1161            bytes.push(self.read_u8()?);
1162        }
1163        if matches!(policy.terminator_at(position), WstringTerminator::Present) {
1164            if bytes.pop() != Some(0) {
1165                return Err(WireError::OutOfRange {
1166                    at,
1167                    expected: String::from(
1168                        "a NUL octet ending the wstring, as its count includes one",
1169                    ),
1170                });
1171            }
1172            if bytes.contains(&0) {
1173                return Err(WireError::OutOfRange {
1174                    at,
1175                    expected: String::from(
1176                        "no NUL inside a wstring a C peer would stop at, and this one has one",
1177                    ),
1178                });
1179            }
1180        }
1181        String::from_utf8(bytes).map_err(|_| WireError::NotUtf8 { at })
1182    }
1183
1184    /// `count` characters of `width` octets each, as UTF-16 code units.
1185    ///
1186    /// The four-octet form is RTI's XCDR1 one and TAO's, where a
1187    /// character is a code unit zero-extended; a value above `0xFFFF`
1188    /// there is not a form this reader knows how to narrow, so it is
1189    /// refused rather than truncated — truncating is the defect the
1190    /// whole policy exists because of.
1191    fn read_wstring_units(
1192        &mut self,
1193        count: usize,
1194        width: usize,
1195        order: WstringUnitOrder,
1196        at: usize,
1197    ) -> Result<Vec<u16>, WireError> {
1198        let octets = count.checked_mul(width).unwrap_or(usize::MAX);
1199        if octets > self.remaining() {
1200            return Err(WireError::ImpossibleLength {
1201                at,
1202                claimed: octets,
1203                available: self.remaining(),
1204            });
1205        }
1206        let mut units = Vec::with_capacity(count);
1207        for _ in 0..count {
1208            if width == 4 {
1209                // `ByteOrderMark` reads little-endian here and the mark
1210                // is re-examined once the whole run is in: a mark that
1211                // says `FFFE` means every unit has to be swapped back,
1212                // which is `strip_byte_order_mark`'s second job.
1213                let wide = match order {
1214                    WstringUnitOrder::Stream => self.read_u32()?,
1215                    WstringUnitOrder::BigEndianAlways => self.read_u32_big()?,
1216                    WstringUnitOrder::ByteOrderMark | WstringUnitOrder::HostUnswapped => {
1217                        self.read_u32_little()?
1218                    }
1219                };
1220                units.push(u16::try_from(wide).map_err(|_| WireError::OutOfRange {
1221                    at,
1222                    expected: format!(
1223                        "a four-octet wstring character holding one UTF-16 code unit, \
1224                         not {wide:08x}"
1225                    ),
1226                })?);
1227            } else {
1228                units.push(match order {
1229                    WstringUnitOrder::Stream => self.read_u16()?,
1230                    WstringUnitOrder::BigEndianAlways => self.read_u16_big()?,
1231                    WstringUnitOrder::ByteOrderMark | WstringUnitOrder::HostUnswapped => {
1232                        self.read_u16_little()?
1233                    }
1234                });
1235            }
1236        }
1237        Ok(units)
1238    }
1239
1240    // -- framing ---------------------------------------------------------
1241
1242    /// A DHEADER: the length the members that follow occupy.
1243    ///
1244    /// # Errors
1245    /// [`WireError::ImpossibleLength`] when it claims more than is left.
1246    pub fn read_dheader(&mut self) -> Result<usize, WireError> {
1247        self.align_to(4)?;
1248        let at = self.at;
1249        let length = self.read_u32()? as usize;
1250        if length > self.remaining() {
1251            return Err(WireError::ImpossibleLength {
1252                at,
1253                claimed: length,
1254                available: self.remaining(),
1255            });
1256        }
1257        Ok(length)
1258    }
1259
1260    /// An EMHEADER, unpacked into its parts, its NEXTINT and the extent
1261    /// of the member it introduces.
1262    ///
1263    /// # The NEXTINT of length codes 5 to 7 is not consumed
1264    ///
1265    /// XTypes 1.3 §7.4.3.4.2 gives the NEXTINT of codes 5, 6 and 7 two
1266    /// jobs: it "shall be reused also as part of the serialized member
1267    /// ... because the serialization of certain members also starts
1268    /// with an integer length, which would take exactly the same value
1269    /// as NEXTINT. Therefore the use of length codes 5 to 7 saves 4
1270    /// bytes in the serialization."
1271    ///
1272    /// `Writer::write_emheader` reserves a placeholder for code 4 only,
1273    /// for that reason. This read it for every code from 4 up and left
1274    /// the position four bytes into the member — so the sequence that
1275    /// followed lost its own length word and every byte after it was
1276    /// read at the wrong offset. It is peeked now, and the member is
1277    /// read from where it starts.
1278    ///
1279    /// # The extent
1280    ///
1281    /// `extent` is how many bytes the member occupies counted from the
1282    /// position this call leaves the reader at, which is the number a
1283    /// caller needs whichever code it turned out to be:
1284    ///
1285    /// | code | extent |
1286    /// |---|---|
1287    /// | 0, 1, 2, 3 | 1, 2, 4, 8 |
1288    /// | 4 | the NEXTINT, which this call consumed |
1289    /// | 5 | 4 + NEXTINT |
1290    /// | 6 | 4 + 4 × NEXTINT |
1291    /// | 7 | 4 + 8 × NEXTINT |
1292    ///
1293    /// # Errors
1294    /// [`WireError::Truncated`], or [`WireError::ImpossibleLength`] for
1295    /// a NEXTINT claiming more than the stream holds. Allocating on an
1296    /// unchecked NEXTINT is how a reader turns a malformed message into
1297    /// an out-of-memory; this returned `Ok` for `ffffffff`.
1298    pub fn read_emheader(&mut self) -> Result<EmHeaderRead, WireError> {
1299        let word = self.read_u32()?;
1300        #[allow(clippy::cast_possible_truncation)]
1301        let length_code = ((word >> 28) & 0x7) as u8;
1302        let member_id = word & 0x0FFF_FFFF;
1303        let must_understand = (word >> 31) != 0;
1304
1305        let at = self.at;
1306        let (next_int, extent) = match length_code {
1307            0 => (None, 1),
1308            1 => (None, 2),
1309            2 => (None, 4),
1310            3 => (None, 8),
1311            4 => {
1312                let next = self.read_u32()? as usize;
1313                (Some(next), next)
1314            }
1315            // Peeked, not consumed: the member's own serialization
1316            // begins at this word.
1317            code => {
1318                let next = self.peek_u32()? as usize;
1319                let scale = match code {
1320                    5 => 1,
1321                    6 => 4,
1322                    _ => 8,
1323                };
1324                let body = next.checked_mul(scale).ok_or(WireError::ImpossibleLength {
1325                    at,
1326                    claimed: next,
1327                    available: self.remaining(),
1328                })?;
1329                let extent = body.checked_add(4).ok_or(WireError::ImpossibleLength {
1330                    at,
1331                    claimed: next,
1332                    available: self.remaining(),
1333                })?;
1334                (Some(next), extent)
1335            }
1336        };
1337        if extent > self.remaining() {
1338            return Err(WireError::ImpossibleLength {
1339                at,
1340                claimed: extent,
1341                available: self.remaining(),
1342            });
1343        }
1344        Ok(EmHeaderRead {
1345            member_id,
1346            length_code,
1347            must_understand,
1348            next_int,
1349            extent,
1350        })
1351    }
1352
1353    /// A PL_CDR1 parameter header, in whichever form it was written.
1354    ///
1355    /// # The flag bits are not part of the id
1356    ///
1357    /// XTypes 1.3 §7.4.1.2 splits the sixteen-bit field: the member id
1358    /// is the low fourteen bits, bit 14 is must-understand and bit 15 is
1359    /// implementation-specific. Cyclone DDS sets must-understand on
1360    /// every parameter it writes, so its `PID_EXTENDED` goes out as
1361    /// `0x7f01` and its sentinel as `0x7f02` —
1362    /// `crates/ast-layout/tests/fixtures/vendor_bytes.md` §2 records
1363    /// both, and RTI Connext writes the same sentinel.
1364    ///
1365    /// Comparing the whole word against `0x3f01` and `0x3f02` therefore
1366    /// recognised neither. A Cyclone parameter list read that way takes
1367    /// the extended header's id and length for eight bytes of value, and
1368    /// runs on past the sentinel into whatever follows it, with no error
1369    /// anywhere. Two of the three implementations this project is
1370    /// measured against write those bytes.
1371    ///
1372    /// # Errors
1373    /// [`WireError::Truncated`]; [`WireError::ImpossibleLength`] for a
1374    /// declared length longer than the stream holds;
1375    /// [`WireError::OutOfRange`] for an extended header whose short
1376    /// length is not the 8 §7.4.1.2 fixes it at.
1377    pub fn read_pid(&mut self) -> Result<PidRead, WireError> {
1378        self.read_pid_as(ParameterRole::Member)
1379    }
1380
1381    /// A PL_CDR1 parameter header in a named role.
1382    ///
1383    /// The bytes are the same bytes; the role decides which length
1384    /// convention [`Reader::finish_pid`] checks the declared length
1385    /// against, because one implementation pads a `@mutable` member's
1386    /// and not an `@optional` member's. See [`ParameterRole`].
1387    ///
1388    /// # Errors
1389    /// As [`Reader::read_pid`].
1390    pub fn read_pid_as(&mut self, role: ParameterRole) -> Result<PidRead, WireError> {
1391        self.align_to(4)?;
1392        let pid = self.read_u16()?;
1393        let at = self.at;
1394        let length = self.read_u16()?;
1395        let must_understand = pid & PID_MUST_UNDERSTAND != 0;
1396        let id = pid & PID_ID_MASK;
1397
1398        if id == PID_EXTENDED & PID_ID_MASK {
1399            // The marker's flags are asked at
1400            // `WirePosition::ExtendedMarker` and nowhere else. They are
1401            // not a property of the framing form: Cyclone and RTI write
1402            // different forms and the same `7f01`, while Fast DDS and
1403            // OpenDDS write the same form and `3f01` against `ff01`.
1404            let expected = self
1405                .pl_cdr1
1406                .pid_flags
1407                .flags_at(WirePosition::ExtendedMarker);
1408            let found = pid & !PID_ID_MASK;
1409            if found != expected && !self.pl_cdr1_reading.is_tolerant() {
1410                return Err(self.wrong_framing(at, "PID_EXTENDED", expected, found));
1411            }
1412            if length != 8 {
1413                return Err(WireError::OutOfRange {
1414                    at,
1415                    expected: String::from("a PID_EXTENDED header of 8 bytes, per §7.4.1.2"),
1416                });
1417            }
1418            let member_header = self.read_u32()?;
1419            let at = self.at;
1420            let length = self.read_u32()? as usize;
1421            if length > self.remaining() {
1422                return Err(WireError::ImpossibleLength {
1423                    at,
1424                    claimed: length,
1425                    available: self.remaining(),
1426                });
1427            }
1428            // The four flag bits are not part of the id. XTypes 1.3
1429            // §7.4.1.2.1 splits the `eMemberHeader` into `FLAG_1` to
1430            // `FLAG_4` and a 28-bit member id, and `FLAG_2` is the
1431            // member's must-understand.
1432            //
1433            // Taking the whole word for the id gave a Cyclone member
1434            // declared `@must_understand` an id of `0x40000001`, which
1435            // matches nothing the model declares, and the member was
1436            // **silently dropped**. Cyclone writes `01000040` for
1437            // `@id(1) @must_understand` and `02000000` for the plain
1438            // member beside it — `vendor_bytes.md` §2.
1439            //
1440            // The marker's own must-understand bit is a different bit
1441            // in a different word and is checked above; Cyclone sets it
1442            // on every parameter, so reading the member's property off
1443            // it would make the whole list must-understand.
1444            return Ok(PidRead {
1445                pid: member_header & E_MEMBER_ID_MASK,
1446                length,
1447                end: false,
1448                must_understand: member_header & E_FLAG_MUST_UNDERSTAND != 0,
1449                // `FLAG_1` is `FLAG_IMPL_EXTENSION` in the extended
1450                // header exactly as bit 15 is in the short one.
1451                impl_extension: member_header & E_FLAG_IMPL_EXTENSION != 0,
1452                // The extended header carries a 28-bit id and has no
1453                // reserved range: 0x3F01 upwards are ordinary member ids
1454                // there, which is why the short form needs the extended
1455                // one for them at all.
1456                reserved: false,
1457                ignore: false,
1458                role,
1459                saved_origin: self.begin_parameter_value(),
1460            });
1461        }
1462
1463        let length = length as usize;
1464        let end = id == PID_LIST_END & PID_ID_MASK;
1465        if !end && self.pl_cdr1.require_extended && !self.pl_cdr1_reading.is_tolerant() {
1466            return Err(WireError::WrongFraming {
1467                at,
1468                expected: alloc::string::String::from(
1469                    "PID_EXTENDED for every parameter, as Cyclone DDS writes",
1470                ),
1471                found: alloc::format!("the short header for id {id}"),
1472                fits: Some("--pl-cdr1-headers specification or short-true-length"),
1473            });
1474        }
1475        // The sentinel's flags are asked at `WirePosition::Sentinel` and
1476        // nowhere else. They are not the member's: OpenDDS sets both
1477        // flags on every member id and closes with a bare `3f02`, while
1478        // Cyclone and RTI leave the member bare and close with `7f02`.
1479        let sentinel_flags = self.pl_cdr1.pid_flags.flags_at(WirePosition::Sentinel);
1480        if end && pid & !PID_ID_MASK != sentinel_flags && !self.pl_cdr1_reading.is_tolerant() {
1481            return Err(self.wrong_framing(at, "PID_LIST_END", sentinel_flags, pid & !PID_ID_MASK));
1482        }
1483        if !end && length > self.remaining() {
1484            return Err(WireError::ImpossibleLength {
1485                at,
1486                claimed: length,
1487                available: self.remaining(),
1488            });
1489        }
1490        let saved_origin = if end {
1491            self.origin
1492        } else {
1493            self.begin_parameter_value()
1494        };
1495        Ok(PidRead {
1496            pid: u32::from(id),
1497            length,
1498            end,
1499            must_understand,
1500            // Under a framing that puts `FLAG_IMPL_EXTENSION` on every
1501            // member id the bit no longer tells a vendor-private
1502            // parameter from a user member — OpenDDS writes `c0010004`
1503            // for `@id(1) long a`. It is reported absent there because
1504            // under that framing it carries no information, and
1505            // reporting it would send every member down the
1506            // unknown-parameter path. The loss is the writing
1507            // implementation's, not this reader's: a stream in which
1508            // every id sets the bit has no way left to say "vendor
1509            // private".
1510            impl_extension: pid & PID_IMPL_EXTENSION != 0
1511                && self.pl_cdr1.pid_flags.flags_at(WirePosition::Member) & PID_IMPL_EXTENSION == 0,
1512            // 0x3F01 and 0x3F02 never reach here — both are handled
1513            // above — so what this catches is `PID_IGNORE` and the rest
1514            // of §7.4.1.2.2's reserved range.
1515            // The threshold is the framing's and not a constant: one
1516            // implementation carries the whole low fourteen bits, and
1517            // reading `@id(16383)` as a reserved parameter there drops
1518            // the member without a word.
1519            reserved: !end && id > self.pl_cdr1.highest_short_id,
1520            ignore: id == PID_IGNORE,
1521            role,
1522            saved_origin,
1523        })
1524    }
1525
1526    /// Start a parameter's value, returning the origin it replaces.
1527    ///
1528    /// The mirror of `Writer::begin_parameter_value`, and it has to
1529    /// exist for the same clause. XTypes 1.3 §7.4.1.2: "Each element, or
1530    /// parameter, within a parameter list data structure is simply a
1531    /// CDR-encapsulated block of data." A block counts alignment from
1532    /// its own start, so an eight-byte member at the head of one is
1533    /// already aligned.
1534    ///
1535    /// The writer was given this and the reader was not. Cyclone,
1536    /// RTI and Fast DDS all put `Ext`'s `double` directly behind its
1537    /// header — `crates/ast-layout/tests/fixtures/vendor_bytes.md` §2
1538    /// records the header as `01000800`, an eight-byte value with no
1539    /// padding in front of it. Counting from the stream instead put four
1540    /// bytes of padding there that no vendor wrote, so the double came
1541    /// out of the following bytes and every member after it was read at
1542    /// the wrong offset. No error anywhere: eight bytes were available,
1543    /// and eight bytes are a double.
1544    fn begin_parameter_value(&mut self) -> usize {
1545        let saved = self.origin;
1546        self.origin = self.at;
1547        saved
1548    }
1549
1550    /// End a parameter's value and step to the next header.
1551    ///
1552    /// The padding to four follows the value and is outside the
1553    /// declared length, so the next parameter is found by alignment and
1554    /// not by adding the length — which is what lets one reader take
1555    /// RTI's rounded-up length and Fast DDS's exact one.
1556    ///
1557    /// The padding is consumed here only under a framing that writes it
1558    /// (`PlCdr1Framing::pad_after_value`), mirroring
1559    /// `Writer::finish_pid`: under Fast DDS's and OpenDDS's framing a
1560    /// parameter writes no padding of its own — the next header aligns
1561    /// itself in `read_pid_as` — and a *final* `@optional` parameter of
1562    /// a non-mutable type ends the stream where its value does, so an
1563    /// unconditional alignment here read two bytes past the end of a
1564    /// stream the writer had closed correctly (measured:
1565    /// `zerodds-rust/opendds-xcdr1-*/annotation-must-understand-unannotated`).
1566    ///
1567    /// # Errors
1568    /// [`WireError::Truncated`] where the framing's padding is not
1569    /// there.
1570    pub fn finish_pid(&mut self, header: PidRead) -> Result<(), WireError> {
1571        if !header.end {
1572            self.check_declared_length(&header)?;
1573        }
1574        self.origin = header.saved_origin;
1575        if self.pl_cdr1.pad_after_value {
1576            return self.align_to(4);
1577        }
1578        Ok(())
1579    }
1580
1581    /// End a parameter whose member this reader does not declare.
1582    ///
1583    /// [`Reader::finish_pid`] finds the next header by alignment from
1584    /// wherever the value's read left the cursor, which is the right
1585    /// answer when the value *was* read; it also checks the declared
1586    /// length against how far the cursor moved, which for a member
1587    /// nobody read has moved nowhere. This one steps over the declared
1588    /// length instead. §7.4.1.2 gives every parameter one, which is what
1589    /// makes an unknown member skippable at all.
1590    ///
1591    /// # Errors
1592    /// [`WireError::Truncated`] where the declared length runs past the
1593    /// stream, and whatever the alignment to the next header returns.
1594    pub fn skip_parameter(&mut self, header: PidRead) -> Result<(), WireError> {
1595        if !header.end {
1596            // `origin` is the value's own start until it is put back,
1597            // so the value ends a declared length after it.
1598            let end = self.origin.saturating_add(header.length);
1599            self.skip_to(end)?;
1600        }
1601        self.origin = header.saved_origin;
1602        // The same mirror as `finish_pid`: only a framing that wrote
1603        // the padding has it to skip, and the next header aligns
1604        // itself.
1605        if self.pl_cdr1.pad_after_value {
1606            return self.align_to(4);
1607        }
1608        Ok(())
1609    }
1610
1611    /// The declared length against what the configured framing would
1612    /// have declared for the value that was just read.
1613    ///
1614    /// `overran` in the probe already refuses a value longer than the
1615    /// header allows, which is the malformed case. This is the other
1616    /// one: a length that is well formed and belongs to a different
1617    /// implementation. `short c` of `vendor_bytes.md`'s `ext_mutable`
1618    /// is the whole difference — two bytes of value, declared as four
1619    /// by Cyclone and RTI and as two by Fast DDS — so a member whose
1620    /// value already fills its four-byte slot reveals nothing and is
1621    /// silently accepted either way. That is a property of the bytes
1622    /// and not a gap here.
1623    fn check_declared_length(&self, header: &PidRead) -> Result<(), WireError> {
1624        // `origin` is still the parameter value's own start: the caller
1625        // restores it after this returns.
1626        let read = self.at.saturating_sub(self.origin);
1627        let padded = read.next_multiple_of(4);
1628        let pads = self.pl_cdr1.pads_declared_length(header.role);
1629        let expected = if pads { padded } else { read };
1630        if header.length == expected {
1631            return Ok(());
1632        }
1633        // §7.4.1.2: the length is the serialized member's own; DDSI-RTPS
1634        // 2.5 §9.4.2.11 rounds it up to four. A tolerant reader takes
1635        // either, and only those two.
1636        if self.pl_cdr1_reading.is_tolerant() && (header.length == read || header.length == padded)
1637        {
1638            return Ok(());
1639        }
1640        // The other convention, and only that one: a length matching
1641        // neither is a malformed stream and gets the plain error.
1642        let other = if pads { read } else { padded };
1643        if header.length != other {
1644            return Err(WireError::OutOfRange {
1645                at: self.at,
1646                expected: alloc::format!(
1647                    "a parameter length of {expected} for the {read} byte(s) that followed it"
1648                ),
1649            });
1650        }
1651        Err(WireError::WrongFraming {
1652            at: self.at,
1653            expected: alloc::string::String::from(if pads {
1654                "a parameter length rounded up to four (DDSI-RTPS 2.5 §9.4.2.11)"
1655            } else {
1656                "a parameter length that stops at the value (XTypes 1.3 §7.4.1.2)"
1657            }),
1658            found: alloc::format!("{} for {read} byte(s) of value", header.length),
1659            fits: Some(if pads {
1660                "--pl-cdr1-headers specification or short-true-length"
1661            } else {
1662                "--pl-cdr1-headers short-rounded-length or always-extended"
1663            }),
1664        })
1665    }
1666
1667    /// A must-understand bit that is not the one the framing calls for.
1668    fn wrong_framing(&self, at: usize, what: &str, expected: u16, found: u16) -> WireError {
1669        // Named rather than printed as a number: `0x4000` and `0xc000`
1670        // are the two flags of §7.4.1.2, and a reader who has to look
1671        // them up has been told a hexadecimal literal rather than what
1672        // is wrong.
1673        let names = |flags: u16| match (
1674            flags & PID_MUST_UNDERSTAND != 0,
1675            flags & PID_IMPL_EXTENSION != 0,
1676        ) {
1677            (false, false) => "no flags",
1678            (true, false) => "the must-understand bit",
1679            (false, true) => "the implementation-extension bit",
1680            (true, true) => "the must-understand and implementation-extension bits",
1681        };
1682        WireError::WrongFraming {
1683            at,
1684            expected: alloc::format!("{what} with {}", names(expected)),
1685            found: alloc::format!("it carries {}", names(found)),
1686            // Both positions are `PidFlagPolicy`'s now, so the flag that
1687            // fits is `--pid-flags` and not the framing form's.
1688            fits: Some(
1689                match (
1690                    found & PID_MUST_UNDERSTAND != 0,
1691                    found & PID_IMPL_EXTENSION != 0,
1692                    what,
1693                ) {
1694                    (_, true, _) => "--pid-flags impl-extension-members",
1695                    (true, false, "PID_EXTENDED") => "--pid-flags sentinel-must-understand",
1696                    (false, false, "PID_EXTENDED") => "--pid-flags bare",
1697                    (true, false, _) => "--pid-flags sentinel-must-understand",
1698                    (false, false, _) => "--pid-flags bare or impl-extension-members",
1699                },
1700            ),
1701        }
1702    }
1703
1704    // -- collections -----------------------------------------------------
1705
1706    /// A sequence: a count, then each element.
1707    ///
1708    /// # Errors
1709    /// [`WireError::ImpossibleLength`] for a count larger than the
1710    /// stream can hold — a count is one element or more, so a stream
1711    /// with fewer bytes than elements cannot be honest about it.
1712    pub fn read_sequence<T, F: Fn(&mut Self) -> Result<T, WireError>>(
1713        &mut self,
1714        element: F,
1715    ) -> Result<Vec<T>, WireError> {
1716        self.align_to(4)?;
1717        let at = self.at;
1718        let count = self.read_u32()? as usize;
1719        if count > self.remaining() {
1720            return Err(WireError::ImpossibleLength {
1721                at,
1722                claimed: count,
1723                available: self.remaining(),
1724            });
1725        }
1726        // No `with_capacity` on the claimed count. The count is checked
1727        // against the bytes left, which bounds it — but an element is
1728        // not a byte, and `Vec::<String>::with_capacity(n)` on a count
1729        // that is honest about bytes reserves twenty-four times the
1730        // message. Growing as elements arrive never exceeds twice what
1731        // was actually read.
1732        let mut out = Vec::new();
1733        for _ in 0..count {
1734            out.push(element(self)?);
1735        }
1736        Ok(out)
1737    }
1738
1739    /// An array of `count` elements, with no count on the wire.
1740    ///
1741    /// # Errors
1742    /// Whatever the element reader returns.
1743    pub fn read_array<T, F: Fn(&mut Self) -> Result<T, WireError>>(
1744        &mut self,
1745        element: F,
1746        count: usize,
1747    ) -> Result<Vec<T>, WireError> {
1748        let mut out = Vec::with_capacity(count.min(self.remaining()));
1749        for _ in 0..count {
1750            out.push(element(self)?);
1751        }
1752        Ok(out)
1753    }
1754
1755    /// A sequence behind a delimiter. XTypes 1.3 §7.4.3.5 rule (12).
1756    ///
1757    /// The delimiter is read and discarded: it exists so a reader can
1758    /// skip a collection it cannot decode, and this reader decodes it.
1759    ///
1760    /// # Errors
1761    /// As [`Reader::read_sequence`], plus a short buffer at the
1762    /// delimiter.
1763    pub fn read_delimited_sequence<T, F: Fn(&mut Self) -> Result<T, WireError>>(
1764        &mut self,
1765        element: F,
1766    ) -> Result<Vec<T>, WireError> {
1767        self.within_delimiter(|inner| inner.read_sequence(element))
1768    }
1769
1770    /// An array behind a delimiter. Rule (9), the array counterpart.
1771    ///
1772    /// # Errors
1773    /// As [`Reader::read_array`], plus a short buffer at the delimiter.
1774    pub fn read_delimited_array<T, F: Fn(&mut Self) -> Result<T, WireError>>(
1775        &mut self,
1776        element: F,
1777        count: usize,
1778    ) -> Result<Vec<T>, WireError> {
1779        self.within_delimiter(|inner| inner.read_array(element, count))
1780    }
1781
1782    /// A map: an entry count, then each entry's key followed by its
1783    /// value.
1784    ///
1785    /// The count is entries and not halves — see
1786    /// [`crate::Writer::write_map`]
1787    /// for the clause and the recorded foreign measurement it is read
1788    /// against.
1789    ///
1790    /// # A repeated key is refused
1791    ///
1792    /// [`BTreeMap::insert`] would answer a second entry under a key it
1793    /// already holds by returning the first and keeping the second: the
1794    /// sample would decode, report success, and hold fewer entries than
1795    /// the count on the wire said. That is the silent half of a bad
1796    /// decode, so it is an error here. A `map` is not supposed to carry
1797    /// one, which is exactly why a stream that does is a statement about
1798    /// the stream.
1799    ///
1800    /// # Errors
1801    /// [`WireError::ImpossibleLength`] for a count larger than the
1802    /// stream can hold, [`WireError::OutOfRange`] for a repeated key,
1803    /// and whatever either half's function returns.
1804    pub fn read_map<K, V, FK, FV>(
1805        &mut self,
1806        key: FK,
1807        value: FV,
1808    ) -> Result<BTreeMap<K, V>, WireError>
1809    where
1810        K: Ord,
1811        FK: Fn(&mut Self) -> Result<K, WireError>,
1812        FV: Fn(&mut Self) -> Result<V, WireError>,
1813    {
1814        self.align_to(4)?;
1815        let at = self.at;
1816        let count = self.read_u32()? as usize;
1817        // An entry is two values and neither is empty, so a stream with
1818        // fewer bytes left than entries cannot be honest about its
1819        // count. The same check `read_sequence` makes, and for the same
1820        // reason: it bounds the loop before anything is allocated.
1821        if count > self.remaining() {
1822            return Err(WireError::ImpossibleLength {
1823                at,
1824                claimed: count,
1825                available: self.remaining(),
1826            });
1827        }
1828        let mut out = BTreeMap::new();
1829        for _ in 0..count {
1830            let at = self.at;
1831            let k = key(self)?;
1832            let v = value(self)?;
1833            if out.insert(k, v).is_some() {
1834                return Err(WireError::OutOfRange {
1835                    at,
1836                    expected: format!(
1837                        "{count} entries under distinct keys, and this one repeats a key already \
1838                         in the map"
1839                    ),
1840                });
1841            }
1842        }
1843        Ok(out)
1844    }
1845
1846    /// A map as the sequence of its entries (XTypes 1.3 §7.5.1.3.6, §7.4.1.1.4): the entries
1847    /// in the order the stream carries them. Unlike [`Reader::read_map`] this does not refuse
1848    /// a key that repeats: the wire carries a sequence, and what a repeated key means is the
1849    /// application's.
1850    ///
1851    /// # Errors
1852    /// [`WireError::ImpossibleLength`] for a count the stream cannot hold, and whatever either
1853    /// half's function returns.
1854    pub fn read_map_entries<K, V, FK, FV>(
1855        &mut self,
1856        key: FK,
1857        value: FV,
1858    ) -> Result<Vec<crate::MapEntry<K, V>>, WireError>
1859    where
1860        FK: Fn(&mut Self) -> Result<K, WireError>,
1861        FV: Fn(&mut Self) -> Result<V, WireError>,
1862    {
1863        self.align_to(4)?;
1864        let at = self.at;
1865        let count = self.read_u32()? as usize;
1866        if count > self.remaining() {
1867            return Err(WireError::ImpossibleLength {
1868                at,
1869                claimed: count,
1870                available: self.remaining(),
1871            });
1872        }
1873        let mut out = Vec::with_capacity(count);
1874        for _ in 0..count {
1875            let k = key(self)?;
1876            let v = value(self)?;
1877            out.push(crate::MapEntry::new(k, v));
1878        }
1879        Ok(out)
1880    }
1881
1882    /// The entries of a map behind a delimiter. Rule (15).
1883    ///
1884    /// # Errors
1885    /// As [`Reader::read_map_entries`], plus a short buffer at the delimiter.
1886    pub fn read_delimited_map_entries<K, V, FK, FV>(
1887        &mut self,
1888        key: FK,
1889        value: FV,
1890    ) -> Result<Vec<crate::MapEntry<K, V>>, WireError>
1891    where
1892        FK: Fn(&mut Self) -> Result<K, WireError>,
1893        FV: Fn(&mut Self) -> Result<V, WireError>,
1894    {
1895        self.within_delimiter(|inner| inner.read_map_entries(key, value))
1896    }
1897
1898    /// A map behind a delimiter. XTypes 1.3 §7.4.3.5 rule (15).
1899    ///
1900    /// The delimiter is read and discarded, as
1901    /// [`Reader::read_delimited_sequence`] does.
1902    ///
1903    /// # Errors
1904    /// As [`Reader::read_map`], plus a short buffer at the delimiter.
1905    pub fn read_delimited_map<K, V, FK, FV>(
1906        &mut self,
1907        key: FK,
1908        value: FV,
1909    ) -> Result<BTreeMap<K, V>, WireError>
1910    where
1911        K: Ord,
1912        FK: Fn(&mut Self) -> Result<K, WireError>,
1913        FV: Fn(&mut Self) -> Result<V, WireError>,
1914    {
1915        self.within_delimiter(|inner| inner.read_map(key, value))
1916    }
1917
1918    /// An array of a length the *type* fixes, as `[T; N]`.
1919    ///
1920    /// The count is in the type — XTypes 1.3 §7.4.3.5 — so a generated
1921    /// field for `long grid[2][3]` is `[[i32; 3]; 2]` and not a `Vec`.
1922    /// [`Reader::read_array`] hands back a `Vec`, which is the right
1923    /// answer for a caller that wants one and does not fit that field:
1924    /// a `Vec` of the wrong length assigns to nothing, and one of the
1925    /// right length still has to be converted. `N` is inferred from the
1926    /// destination, so the generated call carries no turbofish.
1927    ///
1928    /// # Errors
1929    /// As [`Reader::read_array`], plus [`WireError::OutOfRange`] if the
1930    /// elements read do not number `N` — which cannot happen given the
1931    /// loop below, and is returned rather than unwrapped because a
1932    /// panic in a decoder is a denial of service a peer can send.
1933    pub fn read_array_n<T, const N: usize, F: Fn(&mut Self) -> Result<T, WireError>>(
1934        &mut self,
1935        element: F,
1936    ) -> Result<[T; N], WireError> {
1937        let at = self.at;
1938        let mut out = Vec::with_capacity(N.min(self.remaining().saturating_add(1)));
1939        for _ in 0..N {
1940            out.push(element(self)?);
1941        }
1942        out.try_into().map_err(|_| WireError::OutOfRange {
1943            at,
1944            expected: alloc::format!("an array of {N} elements"),
1945        })
1946    }
1947
1948    /// A fixed-length array behind a delimiter. Rule (9).
1949    ///
1950    /// # Errors
1951    /// As [`Reader::read_array_n`], plus a short buffer at the
1952    /// delimiter.
1953    pub fn read_delimited_array_n<T, const N: usize, F: Fn(&mut Self) -> Result<T, WireError>>(
1954        &mut self,
1955        element: F,
1956    ) -> Result<[T; N], WireError> {
1957        self.within_delimiter(|inner| inner.read_array_n(element))
1958    }
1959
1960    /// Read a delimited value, held to the extent the DHEADER declared.
1961    ///
1962    /// The delimiter was read and thrown away. A collection whose
1963    /// elements ran past it went on consuming the *next* member's bytes
1964    /// and returned them as its own — a decode that succeeds and is
1965    /// wrong, which is the harder half of the silent-drop class. One
1966    /// that stopped short left bytes inside its own extent unread, and
1967    /// said nothing about those either.
1968    ///
1969    /// The inner reader sees the stream truncated at the extent, so it
1970    /// cannot reach past it, and the position has to land exactly on the
1971    /// end. Alignment is counted from the stream's origin and not from
1972    /// the delimiter, so the inner reader keeps this one's offset:
1973    /// XTypes 1.3 §7.4.1 aligns from the encapsulation.
1974    ///
1975    /// # Errors
1976    /// [`WireError::ImpossibleLength`] for a delimiter longer than the
1977    /// stream, [`WireError::OutOfRange`] where the value did not fill
1978    /// its extent, plus whatever the value's own read returned.
1979    fn within_delimiter<T, F: FnOnce(&mut Self) -> Result<T, WireError>>(
1980        &mut self,
1981        read: F,
1982    ) -> Result<T, WireError> {
1983        let length = self.read_dheader()?;
1984        let end = self
1985            .at
1986            .checked_add(length)
1987            .ok_or(WireError::ImpossibleLength {
1988                at: self.at,
1989                claimed: length,
1990                available: self.remaining(),
1991            })?;
1992        let mut inner = Self {
1993            bytes: self.bytes.get(..end).ok_or(WireError::ImpossibleLength {
1994                at: self.at,
1995                claimed: length,
1996                available: self.remaining(),
1997            })?,
1998            at: self.at,
1999            endianness: self.endianness,
2000            origin: self.origin,
2001            max_align: self.max_align,
2002            pl_cdr1: self.pl_cdr1,
2003            wchar_width: self.wchar_width,
2004            wchar_framing: self.wchar_framing,
2005            wstring_unit_order: self.wstring_unit_order,
2006            pl_cdr1_reading: self.pl_cdr1_reading,
2007        };
2008        let value = read(&mut inner)?;
2009        if inner.at != end {
2010            return Err(WireError::OutOfRange {
2011                at: inner.at,
2012                expected: alloc::format!("a value filling the {length} bytes its DHEADER declared"),
2013            });
2014        }
2015        self.at = end;
2016        Ok(value)
2017    }
2018
2019    /// The next four bytes as a word, without moving.
2020    ///
2021    /// The one place a header has to be inspected and left where it is.
2022    fn peek_u32(&self) -> Result<u32, WireError> {
2023        let end = self.at.checked_add(4).ok_or(WireError::Truncated {
2024            at: self.at,
2025            needed: 4,
2026            available: self.remaining(),
2027        })?;
2028        let bytes: [u8; 4] = self
2029            .bytes
2030            .get(self.at..end)
2031            .and_then(|slice| slice.try_into().ok())
2032            .ok_or(WireError::Truncated {
2033                at: self.at,
2034                needed: 4,
2035                available: self.remaining(),
2036            })?;
2037        Ok(if self.endianness.is_little() {
2038            u32::from_le_bytes(bytes)
2039        } else {
2040            u32::from_be_bytes(bytes)
2041        })
2042    }
2043
2044    /// `count` bytes as they lie, with no alignment and no
2045    /// interpretation.
2046    ///
2047    /// The opaque payloads have no other reader: a GIOP request body is
2048    /// a byte string whose type the receiving skeleton knows and this
2049    /// reader does not, a CDR encapsulation carries its own byte-order
2050    /// octet and has to be read by a second reader, and an octet
2051    /// sequence is its length followed by exactly this.
2052    ///
2053    /// # Errors
2054    /// [`WireError::Truncated`] when fewer than `count` bytes remain.
2055    /// The offset reported is where the read began.
2056    pub fn read_bytes(&mut self, count: usize) -> Result<&'a [u8], WireError> {
2057        self.take(count)
2058    }
2059
2060    fn take(&mut self, count: usize) -> Result<&'a [u8], WireError> {
2061        let end = self.at.checked_add(count).ok_or(WireError::Truncated {
2062            at: self.at,
2063            needed: count,
2064            available: self.remaining(),
2065        })?;
2066        let slice = self.bytes.get(self.at..end).ok_or(WireError::Truncated {
2067            at: self.at,
2068            needed: count,
2069            available: self.remaining(),
2070        })?;
2071        self.at = end;
2072        Ok(slice)
2073    }
2074}
2075
2076/// An EMHEADER as read.
2077#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2078pub struct EmHeaderRead {
2079    /// The member id.
2080    pub member_id: u32,
2081    /// The length code.
2082    pub length_code: u8,
2083    /// Whether a reader must fail rather than skip this member.
2084    pub must_understand: bool,
2085    /// The NEXTINT, for the codes that carry one. Consumed for code 4
2086    /// and left in place for codes 5 to 7, where it is also the
2087    /// member's own first word.
2088    pub next_int: Option<usize>,
2089    /// How many bytes the member occupies from here.
2090    pub extent: usize,
2091}
2092
2093/// A parameter header as read.
2094#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2095pub struct PidRead {
2096    /// The parameter id, with the flag bits removed.
2097    pub pid: u32,
2098    /// The value's unpadded length.
2099    pub length: usize,
2100    /// Whether this is the list's end.
2101    pub end: bool,
2102    /// Whether the writer set the must-understand flag. Cyclone sets it
2103    /// on every parameter and Fast DDS on none; both are streams this
2104    /// project reads.
2105    pub must_understand: bool,
2106    /// Whether the writer set `FLAG_IMPL_EXTENSION`.
2107    ///
2108    /// XTypes 1.3 §7.4.1.2.2: the parameter is the writing
2109    /// implementation's own, and a user-defined member "will never set
2110    /// the FLAG_IMPL_EXTENSION bit". The flag used to be masked off with
2111    /// the rest, which made a vendor-private parameter indistinguishable
2112    /// from the user member sharing its low fourteen bits — and the
2113    /// vendor's payload was then read into that member with no error.
2114    pub impl_extension: bool,
2115    /// Whether the id is inside the reserved range, which no member has.
2116    ///
2117    /// §7.4.1.2.2 reserves 0x3F01 to 0x3FFF. `PID_EXTENDED` and
2118    /// `PID_LIST_END` are handled before this field is ever read; what
2119    /// is left is `PID_IGNORE` and the range above it, and none of them
2120    /// is a member id. A short parameter carrying one used to be matched
2121    /// against a member declared with that number through the *extended*
2122    /// header, where it is a legal id.
2123    pub reserved: bool,
2124    /// `PID_IGNORE`, which §7.4.1.2.2 says every consumer shall ignore.
2125    ///
2126    /// Kept apart from [`PidRead::reserved`] because it outranks
2127    /// [`PidRead::must_understand`]: the clause has no exception in it,
2128    /// so a reader that discarded the sample over the flag would refuse
2129    /// a stream the clause tells it to accept.
2130    pub ignore: bool,
2131    /// Which length convention this parameter is checked against.
2132    role: ParameterRole,
2133    /// The stream origin this parameter's block displaced.
2134    saved_origin: usize,
2135}
2136
2137impl PidRead {
2138    /// Whether this parameter can be one of the reading type's members.
2139    ///
2140    /// False for a vendor-private parameter and for one whose id is in
2141    /// the reserved range. Both are streams a conforming writer may
2142    /// send, and neither is any member of the type being read, so both
2143    /// take the unknown-parameter path — skipped, or the sample
2144    /// discarded if the writer set must-understand. XTypes 1.3
2145    /// §7.4.1.2.2, §7.2.2.4.4.4.6.
2146    #[must_use]
2147    pub const fn is_user_member(&self) -> bool {
2148        !self.impl_extension && !self.reserved && !self.ignore
2149    }
2150}
2151
2152/// The bit XTypes 1.3 §7.4.1.2 gives to must-understand.
2153const PID_MUST_UNDERSTAND: u16 = 0x4000;
2154
2155/// The low fourteen bits, which are the id itself.
2156const PID_ID_MASK: u16 = 0x3FFF;
2157
2158/// Take the byte-order mark off the front and apply what it said.
2159///
2160/// The units arrived little-endian, because that is what omniORB writes
2161/// on this host and what [`WstringUnitOrder::ByteOrderMark`] therefore
2162/// reads. Three cases, and the third is the one that matters:
2163///
2164/// - `FEFF` — the mark is the right way up, the units are as read.
2165/// - `FFFE` — the mark is byte-swapped, so every unit behind it is too,
2166///   and each one is swapped back. This is what a conforming big-endian
2167///   omniORB peer produces and what a reader that ignored the mark
2168///   would return as different characters, well formed and the right
2169///   length.
2170/// - no mark — CORBA 3.3 part 2 §9.3.1.6's rule for a GIOP 1.2
2171///   `wstring` without one is UTF-16 big-endian, so every unit read
2172///   little-endian is swapped. That rule is what makes this variant read
2173///   a JacORB peer as well as an omniORB one.
2174///
2175/// The mark is never a character of the returned string in any of the
2176/// three, which is the half §7.4.1.1.2 cares about.
2177///
2178/// The stream's own byte order is not an argument and does not need to
2179/// be: reading the units little-endian and then obeying the mark is
2180/// correct in all four combinations of writer host and stream order,
2181/// because the mark is written in the same order as the content it
2182/// introduces. That is what a byte-order mark is for.
2183fn strip_byte_order_mark(units: &mut Vec<u16>) {
2184    match units.first().copied() {
2185        Some(BYTE_ORDER_MARK) => {
2186            units.remove(0);
2187        }
2188        Some(SWAPPED_BYTE_ORDER_MARK) => {
2189            units.remove(0);
2190            for unit in units.iter_mut() {
2191                *unit = unit.swap_bytes();
2192            }
2193        }
2194        _ => {
2195            for unit in units.iter_mut() {
2196                *unit = unit.swap_bytes();
2197            }
2198        }
2199    }
2200}
2201
2202/// A length the configured cell cannot have produced.
2203///
2204/// [`WireError::WrongFraming`] and not [`WireError::OutOfRange`], for
2205/// the reason that variant exists: a reader given the wrong dialect and
2206/// a reader given a corrupt stream both stop, and only one of them is
2207/// fixed by changing a flag. The error names the cell the length *does*
2208/// fit and the setting that selects it — telling a user "an odd octet
2209/// count" and nothing else costs an afternoon to place.
2210///
2211/// Sweeps [`WstringLengthPolicy::ALL_CELLS`] and not the measured five:
2212/// a cell left out of the sweep is a cell this diagnostic can never
2213/// name, and naming the cell the writer used is the whole point of it.
2214fn wstring_form_mismatch(policy: WstringLengthPolicy, declared: usize, at: usize) -> WireError {
2215    let mut names = String::new();
2216    let mut first = None;
2217    for other in WstringLengthPolicy::ALL_CELLS {
2218        if other == policy || !other.admits_length(declared) {
2219            continue;
2220        }
2221        if first.is_none() {
2222            first = Some(other.flag());
2223        } else {
2224            names.push_str(" or ");
2225        }
2226        names.push_str(other.as_str());
2227    }
2228    let found = if first.is_none() {
2229        format!("{declared}, which fits no other wstring length policy either")
2230    } else {
2231        format!("{declared}, which fits {names}")
2232    };
2233    WireError::WrongFraming {
2234        at,
2235        expected: format!(
2236            "a wstring length the {} policy can produce",
2237            policy.as_str()
2238        ),
2239        found,
2240        fits: first,
2241    }
2242}