Skip to main content

WstringLengthPolicy

Struct WstringLengthPolicy 

Source
pub struct WstringLengthPolicy {
    pub counts: WstringLengthCounts,
    pub terminator: WstringTerminator,
    pub unit: WstringUnitWidth,
}
Expand description

How a wstring’s length is written and how wide its code units are — C1 × C2 × C3, a product space and not a list.

§Why this is a struct and no longer an enum

It was an enum of five named forms, and the enum was the defect. The five names mixed three independent questions — what the length counts, whether a terminator is included, how wide a code unit is — and a sixth question, byte order, which had no business here at all and has moved to WstringUnitOrder.

A list of named forms can only hold the combinations somebody has already written down. Measured 2026-08-12 by reconstruction, over wstring-unbounded-utf8, wstring-bounded-utf8, wstring-unbounded-empty and array-1d-wstring: every combination of (C1, C2, C3, C4) was encoded and compared against the recorded octets, and exactly one reproduces each row.

implementationC1 countsC2 terminatorC3 unitC4 order
Cycloneoctetsabsent2host, unswapped
OpenDDSoctetsabsent2stream
Fast DDScharactersabsent2stream
RTI, XCDR1characterspresent4stream
ros-cyclonecharactersabsent4stream
ros-fastrtpscharactersabsent4host, unswapped
omniORBoctets, mark countedabsent2byte-order mark
TAOoctetsabsent4stream
JacORBoctetsabsent2big-endian always

The row that forced the rebuild is either ROS one: characters, no terminator, four-octet units. The old enum could not reach it. CharacterCount was that length with two-octet units and SpecificationWideUnits was four-octet units under the other length; the cell between them had no name, so two implementations wrote a form this project had no way to express. That is what an enumeration standing in for a product space costs, and Self::ROS_TYPE_SUPPORT is the cell.

tests::every_cell_of_the_product_is_reachable fails if any of the eight cells stops being constructible or stops round-tripping through Self::parse.

§Position

Every method here takes a WirePosition, for the reason WcharFraming’s methods do: a caller must not be able to reach a member’s answer for an element by not asking. On this axis the two positions are measured to agree — all nine implementations answer the member and the collection element identically, checked 2026-08-12 and guarded by wstring_length_policy.rs::the_two_positions_still_agree. That is a checked non-existence, not an assumption, and the argument is here so that the day an implementation parts them there is a place to put it rather than a signature to change.

§Width, offsets, and the scalar wchar

RTI’s four-octet XCDR1 wchar is the same four-octet character its XCDR1 wstring writes, and its two-octet XCDR2 wchar the same as its XCDR2 wstring. So the scalar takes C3 and there is no second setting: a policy saying “four-octet wstring characters, two-octet wchar” would name no implementation.

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

A code point above the BMP has no wchar in any cell (§7.2.2.2.1.2.5) — the four-octet form zero-extends a code unit, it does not carry a code point.

Fields§

§counts: WstringLengthCounts

C1 — what the length prefix counts.

§terminator: WstringTerminator

C2 — whether a terminating NUL is written and counted.

§unit: WstringUnitWidth

C3 — how wide one code unit is.

Implementations§

Source§

impl WstringLengthPolicy

Source

pub const SPECIFICATION: Self

Octets, no terminator, two-octet units.

XTypes 1.3 §7.4.1.1.2 and CORBA’s GIOP 1.2 wstring over a two-octet code set. The default. Cyclone and OpenDDS under both versions, RTI under XCDR2, omniORB and JacORB.

Source

pub const CHARACTER_COUNT: Self

Characters, no terminator, two-octet units.

What Fast DDS writes under both versions. The writer refuses a code point outside the BMP in this cell rather than truncating it the way Fast CDR does: the truncation is undetectable at the far end, so emitting it would be this project putting a different character on the wire and calling it interoperability.

Source

pub const CHARACTER_COUNT_TERMINATED: Self

Characters including the terminator, four-octet units.

CORBA 3.3 part 2 §9.3.1.6’s GIOP 1.1 wstring over a four-octet code set, and what RTI Connext 7.7 writes under XCDR1 — measured 2026-08-12 at both the member and the collection-element position, byte for byte, with no RTI-specific rule anywhere. Its XCDR2 output is Self::SPECIFICATION.

Source

pub const SPECIFICATION_WIDE_UNITS: Self

Octets, no terminator, four-octet units.

CORBA §9.3.1.6’s GIOP 1.2 wstring over a four-octet transmission code set — TAO 2.5.24.

Source

pub const ROS_TYPE_SUPPORT: Self

Characters, no terminator, four-octet units.

The cell the old enumeration could not reach. Both ROS 2 type supports write it: rosidl_typesupport_fastrtps counts characters like Fast CDR and hands each one over as a four-octet wchar_t, and rmw_cyclonedds_cpp walking the introspection type support arrives at the same octets.

Measured 2026-08-12 from the recorded corpus. wstring-unbounded-utf8, plain-cdr1 little endian, "äöü ሴ Z":

ros-fastrtps  07000000 e4000000 f6000000 fc000000 20000000 34120000 20000000 5a000000
ros-cyclone   07000000 e4000000 f6000000 fc000000 20000000 34120000 20000000 5a000000

Seven, for seven characters, over four-octet units and no terminator. The two are byte-identical here, and they part only on C4 in a big-endian stream.

Source

pub const MEASURED: [Self; 5]

The cells an implementation has been measured in, for a caller that wants to sweep the named forms.

Not the set of legal values — that is the eight-cell product, and Self::ALL_CELLS enumerates it. A caller that sweeps this list is sweeping what has been seen, which is the right set for a vendor comparison and the wrong one for a completeness check.

Source

pub const ALL_CELLS: [Self; 8]

Every cell of C1 × C2 × C3, measured or not.

Eight, and the three that no implementation writes are as constructible and as nameable as the five that are. A completeness check runs over this; a vendor sweep runs over Self::MEASURED.

Source

pub const COORDINATE_FORM_HELP: &'static str = "octets|characters / unterminated|terminated / 2|4"

The coordinate form’s grammar, for a help text next to Self::MEASURED’s five named cells.

Kept here, beside Self::parse, because a caller building a help text out of the named cells alone would leave the three unnamed ones unreachable from anything it printed — the defect Self::ALL_CELLS’s own documentation describes for the old enum. One constant next to the grammar it describes, rather than a copy at each call site that could drift from what Self::parse actually accepts.

Source

pub const fn new( counts: WstringLengthCounts, terminator: WstringTerminator, unit: WstringUnitWidth, ) -> Self

Build a cell of the product directly.

Public and const so that a cell nobody has named is as easy to reach as one that is named. The named constants below are conveniences for the cells an implementation was measured in; they are not the set of legal values.

Source

pub const fn is_default(self) -> bool

Whether this is the default.

Source

pub const fn as_str(self) -> &'static str

The name a configuration uses.

Every one of the eight cells has one, and every one round-trips through Self::parse. A cell an implementation was measured in renders under the name it shipped with; the three nobody writes render as their coordinates, counts/terminator/width. That a cell nobody has named is still writable on a command line and still readable in a diagnostic is the property the enumeration did not have, and it is why the ROS form was unreachable for as long as the names were the values.

Source

pub fn parse(value: &str) -> Option<Self>

Parse a configuration value.

A named cell, or the three coordinates separated by /. Every value Self::as_str produces parses back to the same cell, and the coordinate form reaches all eight whether or not they carry a name. An unknown value returns None rather than falling back to the default: a typo in a wire-affecting setting must be loud.

Source

pub const fn counts_at(self, _position: WirePosition) -> WstringLengthCounts

C1 at a position — what the length prefix counts.

An accessor and not the field, for the reason the type’s documentation gives: a caller that reads policy.counts directly has answered for every position by not asking about any, which is exactly the shape that let a member’s wchar framing stand in for an element’s through five emitters. The nine implementations answer both positions alike, measured 2026-08-12, so the argument is unused — and it is the argument that keeps that a measurement rather than an assumption.

Source

pub const fn terminator_at(self, _position: WirePosition) -> WstringTerminator

C2 at a position — whether a terminating NUL is written.

Source

pub const fn unit_at(self, _position: WirePosition) -> WstringUnitWidth

C3 at a position — how wide one code unit is.

Source

pub const fn character_width(self, _position: WirePosition) -> usize

How many octets one Char16 takes, in a wstring and as a scalar wchar alike.

Takes the position for the reason the type’s documentation gives. The two positions are measured to agree, so the argument is unused in the body and named _position rather than dropped from the signature: a caller still cannot reach this without saying where it is asking from.

Source

pub const fn char16_octets(self, _position: WirePosition) -> u8

The same width as a byte count, for the one caller that needs it narrow: WcharFraming::scalar_layout, whose alignment answer has to be a u8 and must not be produced by a fallible conversion.

Source

pub const fn char16_size(self, _position: WirePosition) -> u32

The same width as the planner wants it.

A separate signature and not a cast at the call site: a plan that narrowed a usize into a u32 with a fallback would answer 2 for a width it did not recognise, and a wrong width is a wrong offset for every member behind the member.

Trait Implementations§

Source§

impl Clone for WstringLengthPolicy

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 WstringLengthPolicy

Source§

impl Debug for WstringLengthPolicy

Source§

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

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

impl Default for WstringLengthPolicy

Source§

fn default() -> Self

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

impl Eq for WstringLengthPolicy

Source§

impl Hash for WstringLengthPolicy

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 WstringLengthPolicy

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 WstringLengthPolicy

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 WstringLengthPolicy

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 WstringLengthPolicy

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.