pub enum UnionLabelMatch {
SignExtended,
NegativeLabelUnmatchedBelow32,
}Expand description
Whether a case label of a narrow signed discriminator can be negative.
IDL 4.2 §7.4.1.4.4.2.4 makes a case label a value of the
discriminator’s type, and XTypes 1.3 §7.4.3.5.3 rule (26) writes
the discriminator and then the member the labels select. Nothing in
either clause is width-dependent: case -13: on an int8 selects
its branch for _d = -13 exactly as case -1234567: on a long
does.
§What one implementation does instead
Cyclone DDS 11.0.1 compares the label against the discriminator’s
octets read unsigned, so f3 is 243 and not -13, and cfc7 is
53191 and not -12345. No label matches, and what happens next is
whether the union declared a default::
| the union | what Cyclone writes | measured on |
|---|---|---|
no default: | the discriminator and no branch at all | union-discriminator-{int8,short}, four octets shorter |
a default: | the discriminator and the union’s storage read through the default branch | combined-everything, four and eight octets longer |
It is the label’s width and its sign together. union-discriminator-long
carries case -1234567: and agrees byte for byte;
union-discriminator-{octet,uint8,ushort} carry unsigned narrow
labels and agree byte for byte. Only the pair diverges.
§The storage, and why it is not the member zero-padded
combined-everything-unannotated, case -1: long whole; default: double fraction;, whole = 0x7fffffff, XCDR1:
le cyclone ffff 0000 00000000 ffffff7f 00000000
be cyclone ffff 0000 00000000 00000000 7fffffffThe discriminator, padding to the double’s eight-alignment, and
then eight octets. The big-endian row is the same eight octets
reversed whole: a member zero-padded to the branch’s width would
give 7fffffff 00000000 there. So what is written is the union’s
storage — the selected member’s octets in the host’s order at offset
zero, zeros above them — emitted as one value of the default
branch’s width in the stream’s byte order.
The zero octets are guaranteed rather than lucky:
wireprobe-zerodds-c-gen declares the sample as
static {root_type} zd_sample;, and §6.7.9 of the C standard
zero-initialises a static object.
This is a host-storage dependence, the same class as
WstringUnitOrder::HostUnswapped, and it is recorded here for the
same reason: the corpus was measured on x86-64 and “little-endian”
is what was measured, not “the host’s order”. The C column reaches
it through a real C union and would follow a big-endian host; the
Rust and Python columns synthesise the little-endian octets. The two
coincide on every host this project measures on and would part on a
big-endian one — unmeasured, and stated rather than left to be
discovered.
§No dialect selects the second value
Not even Dialect::Cyclone, which is this project’s default. A
default has to be the form a conforming reader gets right, and this
one drops the selected branch off the wire: a peer reading
union-discriminator-short takes the member after the union for
the branch. That is a silent wrong value, and shipping it to every
caller who wrote --dialect cyclone would be shipping the defect.
--union-labels negative-unmatched-below-32 reaches it, and so does
the configuration of that name — the same construction
EnumHolderWidth::BitBoundDroppedByFrontEnd and
PidFlagPolicy::ImplExtensionOnMembers are reached by.
§What it reaches, and what it does not
crate::union_negative_matches_nothing is the one crossing, and it
answers two questions.
The discriminator has to be a signed integer primitive narrower
than 32 bits — int8 and short. Not octet, char or boolean,
none of which can carry a negative label; not long or long long,
which were measured to agree; and not an enumeration
discriminator: union-discriminator-enum declares no negative
@value, so nothing measured says what Cyclone does with one and
answering would be a claim about octets nobody has.
Every branch has to be a primitive of one, two, four or eight
octets, because the storage above is what a default: branch is read
through and a string or sequence branch is read as a pointer.
union-member-sequence is
switch (short) { case -1: sequence<long> ids; default: string text; },
its -1 is the union’s only label, and
tests/interop-results/sources/vendor-corpus.tsv records Cyclone on
it and on its twin — both encoding versions, both byte orders — as
refused — the probe was killed: Segmentation fault. There are no
octets to reproduce, so the setting does not reach the shape and this
project writes the clause’s frame there: a complete frame a
conforming reader can use.
It is not a planner setting. A union’s extent is the
discriminator plus the widest branch under both values — with a
default: Cyclone writes the widest branch’s storage, and without
one the extent still bounds what any value of the discriminator could
produce. Nothing here reaches an offset, so it stays out of
LayoutKey::offset_key, and that is a claim rather than an
omission.
Variants§
SignExtended
A case label is compared against the discriminator’s own signed value.
The clause, and what RTI, Fast DDS and this project write.
NegativeLabelUnmatchedBelow32
A negative label on a signed discriminator narrower than 32 bits matches nothing.
What Cyclone DDS writes. Named for the behaviour and not for the vendor, so that a reader does not take it for a reading of §7.4.1.4.4.2.4.
Implementations§
Source§impl UnionLabelMatch
impl UnionLabelMatch
Sourcepub const MATCHING_WIDTH: u16 = 32
pub const MATCHING_WIDTH: u16 = 32
The width at or above which a signed label still matches.
Thirty-two: union-discriminator-long’s case -1234567: was
measured to match and union-discriminator-short’s case -12345: was measured not to.
Sourcepub const fn is_default(self) -> bool
pub const fn is_default(self) -> bool
Whether this is the default.
Sourcepub const fn label_matches(self, label: i128, signed: bool, bits: u16) -> bool
pub const fn label_matches(self, label: i128, signed: bool, bits: u16) -> bool
Whether a label of the given value, on a discriminator of the given signedness and width, selects its branch.
The arithmetic behind crate::union_negative_matches_nothing,
separated so that the crossing can be tested without an Ast.