Skip to main content

LengthCodePolicy

Enum LengthCodePolicy 

Source
pub enum LengthCodePolicy {
    Compact,
    ReuseNestedDelimiter,
    ShortWhereItFits,
    UniversalNextInt,
    SizedOrNextInt,
}
Expand description

Which length code a @mutable member’s EMHEADER gets.

XTypes 1.3 §7.4.3.4.2 gives eight codes and constrains almost nothing about which one a writer picks. LC0 to LC3 state a length of 1, 2, 4 or 8 in the header itself; LC4 states it in a following NEXTINT; LC5 to LC7 say the member’s own leading word is the NEXTINT, times one, four or eight. Several are legal for the same member and they are not the same bytes, so which one is a configuration point.

§The three implementations take three positions

Measured 2026-08-09 through wire-oracle/gate, cases mut_lengthcodes and mut_lc_nested, XCDR2 little endian. The EMHEADER each member of a @mutable struct gets:

memberCycloneRTI 7.7Fast DDS
string s = "abc"00000050 LC5LC5LC5
sequence<octet> of 301000050 LC5LC5LC5
sequence<short> of 202000040 LC4LC402000030 LC3
sequence<long> of 203000060 LC6LC6LC6
sequence<double> of 104000070 LC7LC7LC7
sequence<Cell> of 105000050 LC505000040 LC4LC5
Cell arr[2]06000050 LC5LC4LC5
long ap[2]07000040 LC4LC407000030 LC3
Cell nested08000040 LC4LC408000030 LC3
long tail09000020 LC2LC2LC2
ACell app (@appendable)00000040 LC400000050 LC5LC5
MCell mnest (@mutable)01000040 LC4LC5LC5

The sequence<Cell> and Cell arr[2] rows are the collection delimiter — CollectionDelimiterPolicy — seen from the length-code side: RTI writes no collection DHEADER, so there is no leading word to reuse and LC4 is the only code left. What is left over after that is two independent choices, and each implementation makes a different pair of them:

  1. Does a nested aggregate’s own DHEADER count as a reusable leading word? An @appendable or @mutable member begins with a DHEADER, which is exactly the byte length LC5 would state. RTI and Fast DDS reuse it. Cyclone does not, and says why in dds_cdrstream.c: case DDS_OP_VAL_UNI: case DDS_OP_VAL_EXT: return LENGTH_CODE_NEXTINT; /* FIXME: may be optimized for specific cases, e.g. when EXT type is appendable */.
  2. Does a member with no reusable word take a short code when its serialized size happens to be 1, 2, 4 or 8? Fast DDS does. Cyclone and RTI take a short code only where the member’s kind fixes the width — a primitive, an enumeration, a bitmask — and write LC4 for an eight-byte array or an eight-byte nested structure alike. Cyclone’s get_length_code switches on the op kind and never on a size.

The two nest: Cyclone makes neither choice, RTI the first, Fast DDS both. So one enum in that order rather than two booleans, which would have four combinations and three witnesses.

§The runtime half of Fast DDS’s rule

Fast DDS’s sequence<short> of two elements is eight bytes and takes LC3. Three elements would be ten and take LC4. A layout plan cannot make that choice — the count is in the value, not in the type — so Self::ShortWhereItFits is answered in two places: the plan answers for every member whose size the type fixes, and the writer answers for the rest by patching the header once the value’s length is known. Writer::finish_emheader does the patching, which is the same thing it already does to the NEXTINT and is safe for the same reason: under XCDR2 MAXALIGN is four, the value starts four-aligned with the NEXTINT and four-aligned without it, so removing the placeholder moves the value without changing a byte of it.

§Every code decodes

A reader that implements §7.4.3.4.2 handles all eight, because the code is in the header. This is a question about which bytes go out, not about which can be read — unlike CollectionDelimiterPolicy, where nothing in the stream says whether the four bytes in front are a delimiter or the first element.

Variants§

§

Compact

LC5, LC6 and LC7 where a string’s or a sequence’s own leading word can be reused, LC0 to LC3 where the member’s kind fixes its width, and LC4 everywhere else.

The default, and what Cyclone DDS writes. A nested aggregate’s DHEADER is not reused and an eight-byte array is not shortened, which is where Cyclone parts from the other two.

§

ReuseNestedDelimiter

Self::Compact, and a nested aggregate that begins with its own DHEADER reuses it as the NEXTINT.

What RTI Connext 7.7 writes. Four bytes shorter than Self::Compact for every @appendable or @mutable member of a @mutable type.

§

ShortWhereItFits

Self::ReuseNestedDelimiter, and any member with no reusable leading word takes LC0 to LC3 whenever its serialized size turns out to be 1, 2, 4 or 8 bytes.

What Fast DDS writes. The reuse is tried first: Fast DDS frames an eight-byte string as LC5 and not LC3, so the leading word wins where there is one.

Half of this is a writer-time decision — see the type’s documentation.

§

UniversalNextInt

LC4 with an explicit NEXTINT throughout, whatever the member’s size or shape. Four bytes more per member than any of the three above, and the form the pre-rebuild ZeroDDS emitters produced.

No measured implementation writes it. Reach for it when a peer expects that form — the OMG wire vector V-10 at e1f3af895 is pinned in exactly that shape — or when reproducing a recorded capture byte for byte.

§

SizedOrNextInt

LC0 to LC3 wherever the member’s serialized length is exactly 1, 2, 4 or 8 octets, LC4 everywhere else, and never a reused leading word.

What OpenDDS writes, and the fourth of the four positions: it answers the second question of the pair above yes and the first no, which is the combination none of the three others is. Measured on struct-mutable-length-codes, XCDR2, against this project’s Self::Compact:

member                    ours              OpenDDS
string s = "abc"          LC5               LC3
sequence<octet> of 3      LC5               LC4, NEXTINT 7
sequence<short> of 2      LC4, NEXTINT 8    LC3
sequence<long> of 2       LC6               LC4, NEXTINT 12
sequence<double> of 1     LC7               LC4, NEXTINT 12
sequence<Cell> of 1       LC5               LC4
Cell pair[2]              LC5               LC4
long plain[2]             LC4               LC3
Cell nested               LC4               LC3

Every code on both sides is legal for the member it is on — §7.4.3.4.2 permits several for the same member — so this is a reading of the clause and not a defect, and it is the one of the four that never spends a code on a word the member already carries.

Half of it is a writer-time decision, for the same reason Self::ShortWhereItFits’s is: string s is eight octets because the sample says "abc", and no plan knows that. The plan answers where the type fixes the size — long plain[2] and Cell nested — and Writer::finish_emheader patches the rest.

Implementations§

Source§

impl LengthCodePolicy

Source

pub const ALL: [Self; 5]

Every policy.

Source

pub const fn is_default(self) -> bool

Whether this is the default.

Source

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

Short name, for configuration and diagnostics.

Source

pub const fn reuses_a_leading_word(self) -> bool

Whether a member’s own leading word — a string’s length, a sequence’s count, a nested aggregate’s DHEADER — is ever spent as the NEXTINT.

Three of the five do it in some form; Self::UniversalNextInt and Self::SizedOrNextInt never do, which is what leaves them with LC0 to LC4 only.

Source

pub const fn reuses_nested_delimiter(self) -> bool

Whether a nested aggregate’s own DHEADER is reused as the NEXTINT, which is LC5 over the member rather than LC4 in front of it.

Source

pub const fn short_code_where_it_fits(self) -> bool

Whether a member with no reusable leading word takes LC0 to LC3 when its serialized size is 1, 2, 4 or 8.

The plan applies it where the type fixes the size; the writer applies it to the rest.

Source

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

Parse a configuration value.

Accepts the names LengthCodePolicy::as_str produces. An unknown value returns None rather than falling back to the default: a typo in a wire-affecting setting must be loud.

Trait Implementations§

Source§

impl Clone for LengthCodePolicy

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 LengthCodePolicy

Source§

impl Debug for LengthCodePolicy

Source§

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

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

impl Default for LengthCodePolicy

Source§

fn default() -> Self

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

impl Eq for LengthCodePolicy

Source§

impl Hash for LengthCodePolicy

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 LengthCodePolicy

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 LengthCodePolicy

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 LengthCodePolicy

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 LengthCodePolicy

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.