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}