Skip to main content

WstringLengthCounts

Enum WstringLengthCounts 

Source
pub enum WstringLengthCounts {
    Octets,
    Characters,
}
Expand description

How a wstring’s length is counted, and how wide one character is.

XTypes 1.3 is not ambiguous here. §7.4.1.1.2: “The serialized length of an object of type String<Char16> shall be the number of bytes in the CDR buffer taken by the String<Char16> characters. This is twice the number of characters in the string”, and “The representation shall also not include any terminating NUL character(s).” §7.4.3.5.3 rule (4) writes O.ssize as a UInt32 and then the Char16 units, annotated “No NUL”. The three implementations write three different things anyway.

§The three implementations disagree

Measured on 2026-08-09 from @final struct WStr { wstring w; octet tail; }, little endian, the w member’s bytes only:

w = "ab", XCDR2w = "ab", XCDR1
Cyclone04000000 6100620004000000 61006200
RTI 7.704000000 6100620003000000 61000000 62000000 00000000
Fast DDS02000000 6100620002000000 61006200
w = "𝄞" (U+1D11E), XCDR2XCDR1
Cyclone04000000 34d81edd04000000 34d81edd
RTI 7.704000000 34d81edd03000000 34d80000 1edd0000 00000000
Fast DDS01000000 1ed101000000 1ed1

The runs are in tests/fixtures/vendor_bytes.md. Fast DDS’s shape is in its own source rather than in the harness: Fast CDR 2.3.5 src/cpp/Cdr.cpp:884 computes wstrlen * 2 for the capacity check and then serialises wstrlen, and writes each wchar_t as two bytes. wchar_t is four bytes wide on Linux, so U+1D11E leaves as its low sixteen bits, d11e. Past the BMP that stops being a length convention and becomes a lost character.

U+D11E is a Hangul syllable and a perfectly well-formed code point. Nothing downstream can see that it is the wrong one: the truncation happened in the sender and left no trace in the bytes. Only where the low sixteen bits land in D800..=DFFF does the result fail to be UTF-16 and get refused, and that is luck rather than a check.

§The reader takes the configured form and never infers one

The forms are not separable from the bytes. Fast DDS’s encoding of @final struct { wstring w; long n; } with w = "ab", n = 7 is 02000000 61006200 07000000; the specification’s encoding of the same type with w = "a", n = 7 is 02000000 6100, two bytes of padding, 07000000. Twelve bytes each, differing only in the content of padding that no clause fixes and that RTI is measured leaving uninitialised. A reader choosing between those two samples would not be resolving an ambiguity, it would be inventing an answer, and it would be taking it from the length field — the field a hostile peer controls, and the one a reader must never let decide how much it reads. So this is a setting, not a detection.

What the reader owes in exchange is that every mismatch it can see is an error and not a shorter string:

  • a length the configured form cannot have produced is refused, and the error names the form the length does fit and the setting that selects it. RTI’s XCDR1 count of 3 and Fast DDS’s odd character counts are both odd byte counts under WstringLengthPolicy::SPECIFICATION;
  • code units that are not well-formed UTF-16 are refused rather than replaced. Substituting U+FFFD would hand the caller a character the sender never had;
  • whatever is left at the end of the frame has to be padding that alignment accounts for, so a short read that shifts every later member ends as an error rather than as a plausible sample.

Two cases survive all three. An even character count of BMP text from Fast DDS, where the next member’s alignment absorbs the bytes the short read left — the twelve bytes above are that case. And a non-BMP code point Fast DDS truncated into a valid BMP one, which arrives as a well-formed string of the right length holding the wrong character. Both are undecidable from the stream, and they are the reason this is a setting at all.

§The width reaches a scalar wchar as well

WstringLengthPolicy::character_width is not a wstring detail. A Char16 is one type and each implementation writes it at one width wherever it appears. Measured 2026-08-09, case wchar_member, @final struct WCh { wchar c; octet tail; } with c = U+0061:

XCDR1 LEXCDR1 BEXCDR2 LEXCDR2 BE
Cyclone6100 ab0061 ab6100 ab0061 ab
RTI 7.761000000 ab00000061 ab6100 ab0061 ab
Fast DDS6100 ab0061 ab6100 ab0061 ab

RTI’s four-byte XCDR1 wchar is the same four-byte character its XCDR1 wstring writes, and its two-byte XCDR2 wchar is the same two-byte character its XCDR2 wstring writes. So the scalar takes WstringLengthPolicy::character_width and there is no second setting: a policy that said “four-byte wstring characters, two-byte wchar” would name no implementation.

This is why LayoutKey::offset_key carries the policy. A wstring plans as crate::Extent::Unbounded under every variant and moves no offset, but a wchar is a sized primitive: at four bytes it aligns to four and shifts every member after it. A cache that served a two-byte plan to a four-byte request would put them all in the wrong place.

A code point above the BMP has no wchar in any variant (§7.2.2.2.1.2.5) — the four-byte form zero-extends a code unit, it does not carry a code point. C1 — what a wstring’s length prefix counts.

One of the three questions WstringLengthPolicy used to answer at once. Separated because implementations answer them independently: see that type’s measured table, where two implementations agree on C1 and part on C3.

Variants§

§

Octets

The number of octets the code units occupy.

XTypes 1.3 §7.4.1.1.2, and CORBA 3.3 part 2 §9.3.1.6’s GIOP 1.2 wstring. Cyclone, OpenDDS, omniORB, TAO and JacORB.

§

Characters

The number of code units, whatever each one weighs.

CORBA §9.3.1.6’s GIOP 1.1 wstring, and Fast CDR’s wcslen. RTI under XCDR1, Fast DDS, and both ROS type supports.

Trait Implementations§

Source§

impl Clone for WstringLengthCounts

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for WstringLengthCounts

Source§

impl Debug for WstringLengthCounts

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for WstringLengthCounts

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Eq for WstringLengthCounts

Source§

impl Hash for WstringLengthCounts

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for WstringLengthCounts

Source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

fn clamp_to<R>(self, range: R) -> Self
where Self: Sized, R: ClampBounds<Self>,

🔬This is a nightly-only experimental API. (clamp_to)
Restrict a value to a certain range. Read more
Source§

impl PartialEq for WstringLengthCounts

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl PartialOrd for WstringLengthCounts

Source§

fn partial_cmp(&self, other: &Self) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl StructuralPartialEq for WstringLengthCounts

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.