Skip to main content

CodecTarget

Trait CodecTarget 

Source
pub trait CodecTarget {
    type Expr: Clone;
    type Stmt: Clone;
    type Block: Clone;
    type Type: Clone;
    type Decl;

Show 101 methods // Required methods fn scalar(kind: Scalar) -> Self::Type; fn named(name: &str) -> Self::Type; fn handle(kind: Handle) -> Self::Type; fn constant(which: Constant) -> Self::Expr; fn code_point(value: Self::Expr, narrow: bool) -> Self::Expr; fn unsigned_holder(bits: u16) -> Self::Type; fn runtime_static(method: &str, args: Vec<Self::Expr>) -> Self::Expr; fn nothing() -> Self::Type; fn name(text: &str) -> Self::Expr; fn int(value: i128) -> Self::Expr; fn literal(value: Literal) -> Self::Expr; fn call(callee: &str, args: Vec<Self::Expr>) -> Self::Expr; fn runtime( receiver: &str, method: &str, args: Vec<Self::Expr>, ) -> Self::Expr; fn member(base: &str, field: &str) -> Self::Expr; fn whole(base: &str) -> Self::Expr; fn wide_int(value: i128) -> Self::Expr; fn field_of(value: Self::Expr, field: &str) -> Self::Expr; fn index(value: Self::Expr, at: Self::Expr) -> Self::Expr; fn count_of(base: &str, fixed: Option<u64>) -> Self::Expr; fn binary(op: &'static str, lhs: Self::Expr, rhs: Self::Expr) -> Self::Expr; fn unary(op: &'static str, value: Self::Expr) -> Self::Expr; fn paren(value: Self::Expr) -> Self::Expr; fn cast(type_: Self::Type, value: Self::Expr) -> Self::Expr; fn handoff(value: Self::Expr, form: Handoff) -> Self::Expr; fn declare( type_: Self::Type, name: &str, value: Option<Self::Expr>, ) -> Self::Stmt; fn perform(value: Self::Expr) -> Self::Stmt; fn give_back(value: Option<Self::Expr>) -> Self::Stmt; fn when( condition: Self::Expr, then: Self::Block, otherwise: Option<Self::Block>, ) -> Self::Stmt; fn count_up( counter: &str, bound: Self::Expr, body: Self::Block, ) -> Self::Stmt; fn until(condition: Self::Expr, body: Self::Block) -> Self::Stmt; fn leave() -> Self::Stmt; fn note(doc: Doc) -> Self::Stmt; fn make_room( base: &str, element: Self::Type, count: Self::Expr, assigned_whole: bool, fill: Vec<Self::Stmt>, ) -> Vec<Self::Stmt>; fn sequence_element( base: &str, index: Self::Expr, element: Self::Type, ) -> (Vec<Self::Stmt>, Self::Expr, Vec<Self::Stmt>); fn keep_first(base: &str, count: Self::Expr) -> Vec<Self::Stmt>; fn present(base: &str, field: &str) -> Self::Expr; fn set_present(base: &str, field: &str, present: bool) -> Vec<Self::Stmt>; fn case_member(base: &str, field: &str) -> Self::Expr; fn entry_half(entry: Self::Expr, key: bool) -> Self::Expr; fn optional_destination( base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr); fn optional_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr; fn elements(base: &str) -> Self::Expr; fn materialised(value: Self::Expr) -> Option<Self::Expr>; fn absent() -> Self::Expr; fn block(statements: Vec<Self::Stmt>) -> Self::Block; fn into_statements(block: Self::Block) -> Vec<Self::Stmt>; fn routine(spec: Routine<Self>) -> Option<Self::Decl>; fn value_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type; fn default_into( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> Result<Self::Stmt, EncodeError>; fn member_default( ast: &Ast, node: &MemberNode, type_: Id<TypeNode>, place: Self::Expr, ) -> Result<Vec<Self::Stmt>, EncodeError>; fn escape(name: &str) -> String; fn map_entry_name(ast: &Ast, id: Id<TypeNode>) -> String; fn module_prefix(ast: &Ast, id: Id<TypeNode>) -> String; fn type_name(ast: &Ast, id: Id<TypeNode>) -> String; // Provided methods fn unsigned_less( octets: u8, left: Self::Expr, right: Self::Expr, ) -> Self::Expr { ... } fn bool_less(left: Self::Expr, right: Self::Expr) -> Self::Expr { ... } fn bit_pattern(source: BitPatternSource, value: Self::Expr) -> Self::Expr { ... } fn carries_hdds_axes() -> bool { ... } fn unsigned_int(value: i128) -> Self::Expr { ... } fn wide_unsigned(value: u64) -> Self::Expr { ... } fn wide_signed(value: i128) -> Self::Expr { ... } fn bulk_octets_write(base: &str, count: Self::Expr) -> Option<Self::Stmt> { ... } fn bulk_octets_read(base: &str, count: Self::Expr) -> Option<Self::Stmt> { ... } fn drop_value(value: Self::Expr) -> Self::Stmt { ... } fn mark_used(name: &str) -> Vec<Self::Stmt> { ... } fn read_call( callee: &str, place: Self::Expr, form: Handoff, behaviour: Self::Expr, ) -> Self::Stmt { ... } fn read_forward(callee: &str) -> Self::Stmt { ... } fn make_room_of( _ast: &Ast, _element_id: Id<TypeNode>, base: &str, element: Self::Type, count: Self::Expr, assigned_whole: bool, fill: Vec<Self::Stmt>, ) -> Vec<Self::Stmt> { ... } fn store( place: Self::Expr, type_: Self::Type, value: Self::Expr, ) -> Self::Stmt { ... } fn storage(type_: Self::Type, name: &str) -> Self::Stmt { ... } fn header_field(value: Self::Expr, field: &str) -> Self::Expr { ... } fn clear_union_cases(base: &str, fields: &[String]) -> Vec<Self::Stmt> { ... } fn open_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt> { ... } fn close_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt> { ... } fn discriminant(base: &str) -> Self::Expr { ... } fn enumerator_ordinal( ast: &Ast, id: Id<TypeNode>, value: Self::Expr, ) -> Self::Expr { ... } fn case_label( ast: &Ast, discriminator: Id<TypeNode>, value: i128, ) -> Self::Expr { ... } fn behind_indirection( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> Self::Expr { ... } fn make_indirection( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> (Vec<Self::Stmt>, Self::Expr) { ... } fn holds_by_reference( ast: &Ast, aggregate: Id<TypeNode>, member_type: Id<TypeNode>, optional: bool, external: bool, ) -> bool { ... } fn reference_present(base: &str, field: &str) -> Self::Expr { ... } fn reference_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr { ... } fn reference_destination( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr) { ... } fn reference_absent(base: &str, field: &str) -> Vec<Self::Stmt> { ... } fn holds_a_cycle_externally() -> bool { ... } fn externals_are_pointers() -> bool { ... } fn optionals_are_pointers() -> bool { ... } fn value_is_pointer(ast: &Ast, id: Id<TypeNode>) -> bool { ... } fn refuse_unset_external(present: Self::Expr) -> Vec<Self::Stmt> { ... } fn external_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, optional: bool, ) -> Self::Expr { ... } fn external_destination( base: &str, field: &str, optional: bool, ) -> (Vec<Self::Stmt>, Self::Expr) { ... } fn external_present(base: &str, field: &str) -> Self::Expr { ... } fn external_set_present( base: &str, field: &str, present: bool, ) -> Vec<Self::Stmt> { ... } fn external_case_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr { ... } fn external_case_destination( base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr) { ... } fn published_name(name: String) -> String { ... } fn initialized_reader( _spec: Routine<Self>, _initializer: Self::Stmt, ) -> Option<Self::Decl> { ... } fn bitmask_value( ast: &Ast, id: Id<TypeNode>, value: Self::Expr, ) -> Self::Expr { ... } fn inherited_path(depth: usize) -> Option<String> { ... } fn map_entry_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type { ... } fn enumerator_constant( ast: &Ast, id: Id<TypeNode>, enumerator: &str, ) -> Self::Expr { ... }
}
Expand description

The syntax a codec walk builds.

Every method is a constructor: the trait holds no state and a backend is a unit type. What varies between backends is spelling, not order — a walk calls these in the sequence the layout plan dictates and the backend never reorders.

Required Associated Types§

Source

type Expr: Clone

An expression.

Source

type Stmt: Clone

A statement.

Source

type Block: Clone

A sequence of statements.

Source

type Type: Clone

A type, as written in a declaration.

Source

type Decl

A top-level declaration — in C a function, in Java a method.

Required Methods§

Source

fn scalar(kind: Scalar) -> Self::Type

A scalar local’s type.

Source

fn named(name: &str) -> Self::Type

A type named by the materialiser.

Source

fn handle(kind: Handle) -> Self::Type

The type of a handle the runtime hands back. See Handle.

Source

fn constant(which: Constant) -> Self::Expr

A policy value the runtime publishes. See Constant.

Source

fn code_point(value: Self::Expr, narrow: bool) -> Self::Expr

A character’s code point, as the runtime’s parameter wants it.

narrow is a char rather than a wchar: C truncates to an octet and widens again, which is what its runtime is handed today and what 17 220 recorded slots agree on. A target whose character type is already a code unit hands it over and lets the runtime keep the low byte — the same octet, decided one step later.

A seam and not a cast, because the width is the backend’s: Scalar::UInt32 is uint32_t in C and long in Java, and writeChar takes an int.

Source

fn unsigned_holder(bits: u16) -> Self::Type

The unsigned type wide enough to hold bits of a bit set.

A @bit_bound(12) set is assembled in something at least twelve bits wide, and which type that is belongs to the target: C picks the smallest uintN_t that fits, and a language with no unsigned types picks the next signed one up and lets the runtime read the bits. The walk asks for room, not for a spelling.

Source

fn runtime_static(method: &str, args: Vec<Self::Expr>) -> Self::Expr

An operation on the runtime that has no receiver.

no_dheader is the only one: the plan says a value takes no delimiter and the walk still needs a handle to hand the close, so the runtime supplies one that finishes nothing. C exports it as a free function and Java as a static method on the writer, which is the same difference as CodecTarget::runtime and not the same call.

Source

fn nothing() -> Self::Type

What a codec function returns when it returns nothing.

Source

fn name(text: &str) -> Self::Expr

A bare name.

Source

fn int(value: i128) -> Self::Expr

An integer literal.

Source

fn literal(value: Literal) -> Self::Expr

Any other literal the common tree can hold.

Source

fn call(callee: &str, args: Vec<Self::Expr>) -> Self::Expr

A call to a free function or, in a target that has no free functions, to the static method standing in for one.

Source

fn runtime(receiver: &str, method: &str, args: Vec<Self::Expr>) -> Self::Expr

An operation on the runtime’s writer or reader.

method is the operation’s bare name — write_u32, read_pid, finish_dheader — and the backend spells it: C prefixes the runtime’s single namespace and passes the receiver first, zdw_write_u32(w, x); a target with methods writes w.writeU32(x). Separate from CodecTarget::call because the two differ in more than the name, and a walk that built the C form and let a renderer guess at the other would be deciding a language question in the walk.

receiver is the local the body holds the writer or reader in, which is w and r in every backend so far.

Source

fn member(base: &str, field: &str) -> Self::Expr

A member of a value the codec was handed.

v->x where the parameter is a pointer, v.x where it is a reference. Not CodecTarget::field_of, which reaches into a value the body already owns.

Source

fn whole(base: &str) -> Self::Expr

The whole value the codec was handed, rather than one member.

*v where the parameter is a pointer, v where it is a reference. What a primitive’s codec writes.

Source

fn wide_int(value: i128) -> Self::Expr

An integer literal that has to survive its own width.

A union label the size of 16045690984833335998 is not an int in any of these languages, and a bare decimal is a constant whose type the compiler picks — with a warning where the reader is lucky and a quietly different comparison where they are not. C writes UINT64_C(..), Java an L suffix. Separate from CodecTarget::int because most literals are small and the suffixed form would be noise on all of them.

Source

fn field_of(value: Self::Expr, field: &str) -> Self::Expr

A member of an expression.

Source

fn index(value: Self::Expr, at: Self::Expr) -> Self::Expr

a[i]

Source

fn count_of(base: &str, fixed: Option<u64>) -> Self::Expr

How many elements the collection in base holds.

_length on the C sequence struct, .length on a Java array, and for a fixed-size array the bound the materialiser fixed. The base name and not an expression, for the reason CodecTarget::elements takes one: how a target reaches into its own collection is the target’s.

Source

fn binary(op: &'static str, lhs: Self::Expr, rhs: Self::Expr) -> Self::Expr

a op b, written as given.

Source

fn unary(op: &'static str, value: Self::Expr) -> Self::Expr

op a, written as given.

Source

fn paren(value: Self::Expr) -> Self::Expr

Parentheses written on purpose.

The trees carry no precedence, so a walk that needs a bracket says so and every backend honours it.

Source

fn cast(type_: Self::Type, value: Self::Expr) -> Self::Expr

A conversion the target requires and the walk knows is exact.

Source

fn handoff(value: Self::Expr, form: Handoff) -> Self::Expr

A value on its way into a nested codec.

The abstraction of &v->x against v.x; see Handoff.

Source

fn declare( type_: Self::Type, name: &str, value: Option<Self::Expr>, ) -> Self::Stmt

A local declaration with an optional initialiser.

Source

fn perform(value: Self::Expr) -> Self::Stmt

An expression evaluated for its effect.

Source

fn give_back(value: Option<Self::Expr>) -> Self::Stmt

return, with or without a value.

Source

fn when( condition: Self::Expr, then: Self::Block, otherwise: Option<Self::Block>, ) -> Self::Stmt

if (cond) { .. } else { .. }

Source

fn count_up(counter: &str, bound: Self::Expr, body: Self::Block) -> Self::Stmt

A counted loop from zero to bound, exclusive.

Given rather than assembled from the pieces because the counter’s type is the target’s business: C declares size_t, Java int, and a walk that wrote the declaration itself would be choosing for both.

Source

fn until(condition: Self::Expr, body: Self::Block) -> Self::Stmt

A loop with no counter.

A @mutable aggregate’s headers run until the delimiter is used up and a PL_CDR1 list until its sentinel; neither has a bound, and a counted loop with the clauses left empty would claim an induction variable somebody forgot.

Source

fn leave() -> Self::Stmt

Leave the enclosing loop.

Source

fn note(doc: Doc) -> Self::Stmt

A comment on its own line.

Source

fn make_room( base: &str, element: Self::Type, count: Self::Expr, assigned_whole: bool, fill: Vec<Self::Stmt>, ) -> Vec<Self::Stmt>

Room for count values of element, with fill inside it.

The one operation in the reader that is not a spelling. C sets the length, nulls the buffer, allocates count * sizeof(element) out of the reader’s arena, returns if that fails, and runs the fill inside the guard — five statements. A target whose allocation throws rather than returning nothing writes one and has no guard to put the fill in.

base is the local the codec holds the sequence in, not a finished expression: how a target reaches into it is the target’s own — C’s sequence keeps a _length and a _buffer behind a pointer, and a language with a list type has neither. fill is the statements that fill it, already built; a backend may drop the guard but must not drop the fill.

Shaped from the C column, which is the only caller so far. A second reader backend is what will say whether the shape holds — a target that appends rather than writing into a slot will want the element’s place from here too, and it is better to widen this when that caller exists than to guess at it now. assigned_whole says what fill does with each slot: assign it outright, as a sequence’s element decoder does, or write into it, as a map’s two halves do. It is the difference between a target whose slot is storage and one whose slot is a reference that has to point at some — C ignores it and Java does not.

Source

fn sequence_element( base: &str, index: Self::Expr, element: Self::Type, ) -> (Vec<Self::Stmt>, Self::Expr, Vec<Self::Stmt>)

Where a sequence’s element goes while it is being read, and what it takes to get it there: the statements before, the place, and the statements after.

Almost always (nothing, base[index], nothing), and it is a seam because of one container. std::vector<bool> is specialised — [vector.bool] — and its operator[] returns a proxy object, not a bool &. A codec that takes its destination by reference has nowhere to point, so the C++ column reads into a local and assigns it back. Every other element of every other target is a real object and answers with the subscript.

Only a sequence needs it. T a[2] is a std::array, which is not specialised for any element, and a map’s halves are a std::pair’s members, which are real objects too.

The write direction has no such problem: reading a proxy is what a proxy is for, and write_bool(v[i]) converts.

Source

fn keep_first(base: &str, count: Self::Expr) -> Vec<Self::Stmt>

Keep the first count elements of the collection in base and drop the rest.

§7.3.1.2.1.12.3’s TRIM for a bounded collection, which the reader applies after every element has been read: stopping at the bound would leave the rest of the sequence in the stream and put every following member at the wrong offset.

A statement and not an assignment to CodecTarget::count_of, which is what this used to be. C’s sequence is a _length beside a _buffer and the length is the answer — the elements past the bound stay in the buffer, unreferenced. A std::vector knows its own size and v.size() = 5u is not an assignment; a List shortens differently again. The walk says how many to keep and the target says how.

Source

fn present(base: &str, field: &str) -> Self::Expr

Whether an @optional member is present.

The last structural assumption C made and no other target shares. C declares a boolean _has_m beside the member and this reads it; adapter/java.rs declares one boxed field and lets null mean absent, “because a boolean _has_m and an m are two things that can disagree, and nothing in the type system stops them”. A backend that inherited C’s flag would reach a field the materialiser never emitted.

Source

fn set_present(base: &str, field: &str, present: bool) -> Vec<Self::Stmt>

Say that an @optional member is present, or is not.

C assigns the parallel flag. A target where absence is the value assigns nothing on the way in — writing the member is what makes it present — and assigns CodecTarget::absent on the way out. Returning no statement is a legitimate answer.

Source

fn case_member(base: &str, field: &str) -> Self::Expr

A union’s selected case member.

C declares the cases in an anonymous union _u inside the struct, so a member is v->_u.x; adapter/java.rs declares one field per case directly on the class, so it is v.x.

Source

fn entry_half(entry: Self::Expr, key: bool) -> Self::Expr

One half of a map’s entry.

C generates a struct with key and value; C++’s entry is a std::pair, whose halves are first and second. The walk asks for the half and the backend names it.

Source

fn optional_destination( base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr)

Where a reader puts an @optional member’s value, and what it takes to have somewhere to put it.

The write direction has CodecTarget::optional_value, which reaches a value that is already there. Reading is the other way round and the two targets differ in a way no expression covers: C’s optional member is a plain T beside a _has_ flag, so the destination is the member and nothing has to happen first; C++’s is a std::optional<T> and the member’s decoder takes a T&, so the optional has to be engaged before there is a T to write into at all.

Returns the statements that make room, followed by the place. The statements go immediately before the read; a backend that needs none returns an empty vector, and the generated source is then exactly what it was before this seam existed.

Not folded into CodecTarget::set_present: that is called after the read at two of the five sites and before it at the other three, and moving it would reorder the C column’s output for a difference that is not C’s.

Source

fn optional_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr

The value an @optional member holds, given that it holds one.

C’s optional member is a plain T beside a _has_ flag, so this is the member. C++’s is a std::optional<T> and the codec takes a T, so it is .value(). Java’s boxed field is already the value.

The member’s type is a parameter because one target’s answer depends on it and no other’s does. adapter/csharp.rs wraps a value type in Nullable<T> and leaves a reference type alone, so the unwrapping is .Value for the first and nothing for the second — and string?.Value does not compile any more than passing an int? where an int is wanted does. A backend that answered from the name alone would be right for one half of its own mapping.

Only ever reached inside a branch the presence test guarded, so a target whose accessor throws on an absent value is safe here.

Source

fn elements(base: &str) -> Self::Expr

Where a sequence keeps the elements themselves.

C’s sequence is a _length and a _buffer and this is the buffer; a target whose sequence is the array answers the value.

Source

fn materialised(value: Self::Expr) -> Option<Self::Expr>

Whether a value the reader had to materialise is there at all.

C’s zdw_read_string_alloc answers NULL where the reader’s heap is exhausted, and every use of what it produced is guarded against that — a decoder that measured a null string would read through the pointer. A target whose string type owns its own storage has no such state and answers None, and the guard is then not written at all rather than written against a value that is always there: std::string != std::nullopt is not an expression.

Distinct from CodecTarget::present, which asks about an @optional member — a property of the sample. This asks whether the read produced anything, which is a property of the runtime.

Source

fn absent() -> Self::Expr

The value that says a target holds nothing.

NULL in C, null in Java and C#, None in Python. What a decoder writes into a pointer before it has allocated, and what it compares against to find out whether the allocation worked.

Source

fn block(statements: Vec<Self::Stmt>) -> Self::Block

Statements gathered into a body.

Source

fn into_statements(block: Self::Block) -> Vec<Self::Stmt>

A body taken apart again.

A walk that has built a nested block and then finds it belongs inline — an array’s innermost loop under a DHEADER, a union’s if-chain after the last arm — would otherwise have to carry a Vec and a Block in parallel and keep them agreeing.

Source

fn routine(spec: Routine<Self>) -> Option<Self::Decl>

A codec function or method.

body absent asks for a forward declaration, which C needs ahead of the bodies so a recursive or mutually recursive type does not depend on the order the walk visited its types in. A target that has no such thing returns None and the caller emits nothing.

Routine::tolerate_unused marks a codec that may legitimately have no caller. Generated code is compiled with an uncalled function as an error, and that is worth keeping: normally it means the generator emitted the wrong thing. The exception is a codec reached from one position and written inline at another, where having a caller is a property of the dialect rather than of the type graph.

Source

fn value_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type

How this target spells the type of id where a codec holds it.

The materialiser’s answer, not the walk’s: a sequence<long> is a struct with a buffer and a length in C, a std::vector in C++ and a List<Integer> in Java, and only the target knows which.

Source

fn default_into( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> Result<Self::Stmt, EncodeError>

Table 9’s value for id, written into place.

What a reader puts where a member the stream did not carry belongs — DDS-XTypes 1.3 Table 9. The value is the specification’s and the same in every column; what differs is where a target keeps it. C calls a generated defaulter because a struct-valued default is not an expression it can assign; a target with literals for its aggregates assigns one.

§Errors

When the target has no form for Table 9’s value. Fallible because a backend under construction has to be able to refuse here: a reader generated with a hole in it and no error is a reader that fails where it runs rather than where it was built, and the two are a long way apart.

Source

fn member_default( ast: &Ast, node: &MemberNode, type_: Id<TypeNode>, place: Self::Expr, ) -> Result<Vec<Self::Stmt>, EncodeError>

The declared @default of node, written into place, or Table 9’s value where the member declares none.

Fallible on purpose: a @default a target cannot spell is reported rather than quietly replaced by Table 9’s zero, which is a different sample from the one the designer declared.

§Errors

When the declared default has no form in this target.

Source

fn escape(name: &str) -> String

An IDL identifier as this target spells a member or a field.

Every language escapes the identifiers that collide with its own keywords, and each escapes them differently — C prefixes, Java prefixes with a different mark, C# writes @. Both halves of a column have to agree: a member the materialiser declared escaped and the codec reaches unescaped is a field no compiler finds.

Source

fn map_entry_name(ast: &Ast, id: Id<TypeNode>) -> String

The name this target gives a map’s entry type.

A map<K, V> has no direct form in most targets and becomes a collection of a generated pair; what that pair is called is the materialiser’s, and the codec has to call it the same thing.

Source

fn module_prefix(ast: &Ast, id: Id<TypeNode>) -> String

The prefix this target puts before a module’s declarations.

C has no namespaces and flattens module a { module b { … } } into a prefix; a target with namespaces answers the empty string and lets its own scoping do the work.

Source

fn type_name(ast: &Ast, id: Id<TypeNode>) -> String

The name this target gives the type of id.

What a generated codec function is named after, and the reason a codec name is built from this rather than from the IDL: two IDL types that collapse to one name in a target must not end up with one codec, and a target that escapes a keyword has to escape it the same way in both halves.

Provided Methods§

Source

fn unsigned_less(octets: u8, left: Self::Expr, right: Self::Expr) -> Self::Expr

Whether left is less than right as unsigned integers of this width.

The default is the target’s own <, which is right wherever the held type is unsigned or strictly wider than the value. Five columns are not in that position at eight octets, and each overrides: Java and Kotlin hold an IDL unsigned long long as the bit pattern in a long and reach Long.compareUnsigned, Lua reaches math.ult, OCaml’s Int64 reaches its runtime’s u64_less, and Ada 83 — which has no modular type at all, only Long_Integer — reaches Zd_Wire83.U64_Less. Every one of them sorted 16045690984833335998 ahead of 7 in a map’s ascending-key order before the override existed (measured against Fast DDS on map-key-ulonglong; Ada 83 against Fast DDS and OpenDDS, all four encodings, 2026-09-04). A target in that position overrides.

The narrower widths need no override anywhere: an unsigned long tops out at 4294967295, which every column above holds zero-extended in a signed type at least 64 bits wide, so the signed order and the unsigned order are the same order.

Source

fn bool_less(left: Self::Expr, right: Self::Expr) -> Self::Expr

Whether left is less than right as boolean map keys — false before true, the ascending-key order’s reading of the one-octet wire value.

The default is the target’s own <, which C, C++, Rust and Python all give two booleans. A language whose < refuses the pair — Java, C# and TypeScript name a type error — overrides with a spelling of the same order.

Source

fn bit_pattern(source: BitPatternSource, value: Self::Expr) -> Self::Expr

The value’s storage bits, zero-extended into the runtime’s 64-bit pattern argument. See BitPatternSource; consumed by the runtime’s write_union_overlay, which writes the low octets of the pattern as one integer of that width in wire endianness — the Cyclone union overlay.

The default spells each form as a free function of the runtime — bit_pattern_int(value, octets), bit_pattern_bool, bit_pattern_f32, bit_pattern_f64 — which fits every target whose CodecTarget::runtime_static names free functions. A target that routes runtime_static elsewhere, or that has a native spelling for a form, overrides.

Source

fn carries_hdds_axes() -> bool

Whether this target’s runtime carries the three axes zerodds_ast_layout::Dialect::Hdds selects.

The axes are zerodds_ast_layout::key::ElementPadding::Packed, zerodds_ast_layout::key::SequenceElementDelimiter::BeforeEachNonFinalElement and zerodds_ast_layout::key::WstringCodeUnits::Utf8. The reference is zerodds-wire-codec; a target whose runtime has the packed-element origin, the element length in DHEADER form and a UTF-8 wstring answers true and may be generated under that dialect. The default is false, so a runtime that has not been given them is refused with the axis named rather than generated in the specification’s form — a different octet string wearing this key’s name.

Source

fn unsigned_int(value: i128) -> Self::Expr

A count, in whatever spelling keeps the target’s conversions quiet.

A sequence bound, an element count and a member id are all unsigned on the wire, and C writes them with the u suffix so that a comparison against a uint32_t does not trip -Wsign-compare. Java has no unsigned literal and 5u is not a token there at all. The walk asks for a count and each backend spells it.

Source

fn wide_unsigned(value: u64) -> Self::Expr

A 64-bit pattern, in a spelling wide enough to survive itself.

A bitmask’s declared-bit mask is sixty-four bits and a bare decimal is a constant whose type the compiler picks. C writes UINT64_C(..); Java has no unsigned integer and writes the signed pattern with an L.

Source

fn wide_signed(value: i128) -> Self::Expr

A signed 64-bit constant, as CodecTarget::wide_unsigned.

Source

fn bulk_octets_write(base: &str, count: Self::Expr) -> Option<Self::Stmt>

All of count octet elements of base at once, in place of the element loop of a sequence<octet> or a one-dimensional octet[N] (and the same over uint8); None, the default, keeps the loop. The octets are the loop’s: an octet has alignment 1 and no byte order (XTypes 1.3 §7.4.3.4.1), so a run of them is the same bytes however it is copied. What a target gains is the per-element call — measured in the C++ column: sequence<octet, 8192> element by element cost ~100 µs per roundtrip of four codec passes.

Source

fn bulk_octets_read(base: &str, count: Self::Expr) -> Option<Self::Stmt>

The read side of Self::bulk_octets_write: count octets into base, which already holds room for them.

Source

fn drop_value(value: Self::Expr) -> Self::Stmt

A value read for its effect and not kept.

(void)zdw_read_dheader(r); in C, where an ignored return is a -Wunused-result away from a warning and the cast is how a caller says it meant it. A target where a call is already a statement writes the call: (void) expr; is not a Java statement at all.

Source

fn mark_used(name: &str) -> Vec<Self::Stmt>

How this target says a parameter is deliberately not read.

Most decoders have nothing to do with the try-construct behaviour — a long has no bound to miss — and the C probe is built with -Wunused-parameter -Werror, where that fails the build. (void)zd_tc; is C’s way of saying so. A target whose compiler does not warn on an unread parameter answers with no statement, which is a legitimate answer and not an omission: javac has no such warning, and zd_tc; is not a Java statement at all.

Source

fn read_call( callee: &str, place: Self::Expr, form: Handoff, behaviour: Self::Expr, ) -> Self::Stmt

zd_dec_<T>(r, <destination>, <behaviour>); — one nested decoder run, with its answer put where place says.

The read direction’s counterpart of Handoff, and a whole method rather than an argument form because the two targets do not put the destination in the same syntactic position. C passes its address and the callee writes through it. Java has no address to pass and no out-parameter to pass it in, so the decoder answers with the value and the destination is the assignment’s left-hand side — the same decision, one step later.

A backend that overrides this is saying its decoders return their value; CodecTarget::routine has to agree with it, and so does CodecTarget::give_back.

Source

fn read_forward(callee: &str) -> Self::Stmt

The whole sample handed straight on to another type’s decoder.

What a typedef reads as: transparent on the wire, transparent to §7.2.2.7, and in C transparent in storage too — the destination and the behaviour go through untouched, and neither an address nor a dereference is taken because v is already the one the caller gave. Separate from CodecTarget::read_call because there is no place here to take: the destination is the parameter itself.

Source

fn make_room_of( _ast: &Ast, _element_id: Id<TypeNode>, base: &str, element: Self::Type, count: Self::Expr, assigned_whole: bool, fill: Vec<Self::Stmt>, ) -> Vec<Self::Stmt>

CodecTarget::make_room for a sequence whose element is element_id. A target that cannot spell a slot’s start value from element alone overrides it: Kotlin’s Array(n) { T() } has no constructor to call where T is a typealias of Int.

Source

fn store(place: Self::Expr, type_: Self::Type, value: Self::Expr) -> Self::Stmt

One scalar, as it comes off the runtime, into a place of type_.

C’s runtime hands back the member’s own width and the assignment is the whole of it. Java has no unsigned type and no integer narrower than int in an expression: readI8 answers an int because that is what a number in [-128, 127] is once it has been widened, and a byte field does not take one without being told to. Which of its holders need that is the backend’s, and a walk that cast unconditionally would put a redundant cast in the C column, where -Wall names it.

Source

fn storage(type_: Self::Type, name: &str) -> Self::Stmt

A local the body writes into rather than assigns.

A C local of a struct type is storage the moment it is declared. A Java local of a class type is a reference to none, and a body that assigns its fields would be dereferencing a null. Separate from CodecTarget::declare, which is for a local the body assigns before it reads — those need no storage of their own and a target that made some would be allocating for nothing.

Source

fn header_field(value: Self::Expr, field: &str) -> Self::Expr

One field of a header the runtime handed back.

zd_h.member_id in C. A target whose runtime publishes its headers behind accessors answers with the call — zd_h.memberId() — and the walk names the field rather than the spelling. Not CodecTarget::field_of, which reaches into a value of a generated type, where both columns write a field.

Source

fn clear_union_cases(base: &str, fields: &[String]) -> Vec<Self::Stmt>

Statements that leave every case member of a union unset, before the member the discriminator selects is read.

The decoder starts from the union’s Table 9 default, which carries the member the default discriminator selects. A column whose union has a “not set” for a case field — Python’s and Julia’s None and nothing, Swift’s nil, Nim’s and OCaml’s Option, a missing key or property in Lua, Node and TypeScript, Elixir’s nil — and promises that at most one case is live has to clear that member, or a decoded sample carries the default branch’s value beside the one read (measured: idl-conformance/_interop mapenum, a Sel read with _d = 2 came back with n = 9 and z = 0). Nothing by default: in C the cases share storage, and where a case field has no “not set” (C++, C#, Java, Go, D, Zig, F#, Ada, Kotlin) its default value is what an unselected field holds anyway.

Source

fn open_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt>

Statements that make an array’s Table 9 default writable, before its elements are read into it; lengths are its dimensions, outermost first.

Nothing by default: every column but one holds an array in something its elements can be assigned into. Python declares the member tuple[T, …] (adapter/python.rs) and a tuple cannot be written into, so its default is a tuple and the reader turns it into lists here and back in CodecTarget::close_array.

Source

fn close_array(base: &str, lengths: &[i128]) -> Vec<Self::Stmt>

Statements that give a read array the type its member declares, after its last element is read. Nothing by default; see CodecTarget::open_array.

Source

fn discriminant(base: &str) -> Self::Expr

A union’s discriminant.

_d in every column whose materialiser names it that, which is all of them but one: DDS-TS 1.0 §7.5 names the property discriminator, and the TypeScript materialiser declares it under that name. The default is the name the other columns share, so a column that does not override generates exactly what it generated before this seam existed.

Source

fn enumerator_ordinal( ast: &Ast, id: Id<TypeNode>, value: Self::Expr, ) -> Self::Expr

The integer an enumeration’s value carries, given the value.

The identity in every column but one, and deliberately so: an enumeration is a numeric constant at the language surface there, so the value in the variable is the integer — under zerodds_ast_layout::EnumeratorValue::Declared it is even the integer that goes on the wire, which is why encode::enumeration_write emits no chain for that setting.

DDS-TS 1.0 §7.6 makes an enumeration a string-literal union, so that column has no integer in the variable at all and answers with a lookup in the generated <Enum>Ordinal table. Measured: without this seam enum-sparse-values wrote 70 79 87 — the first code unit of each enumerator’s name — where the ledger has 65535 1 100.

Source

fn case_label(ast: &Ast, discriminator: Id<TypeNode>, value: i128) -> Self::Expr

The value a union case label is compared against, in this target’s spelling of the discriminator’s type.

The default is the label folded to an integer and narrowed to the discriminator’s own type, which is what every column with a numeric enumeration needs. A column whose enumeration is not numeric at the language surface — DDS-TS 1.0 §7.6 makes it a string-literal union — cannot compare a discriminant against a number at all, and answers with its own constant.

Source

fn behind_indirection( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> Self::Expr

The place a member is reached at, given that the column could not put the member’s value inside its own aggregate.

zerodds_ast_layout::member_held_inline_closes_a_cycle is what says so: the member’s type reaches the aggregate that declares it over nothing but inline edges, so a column whose member is the value has no size for it. place is the place the seams below would have reached — the member, the @optional accessor, the @external one, or a union branch — and the answer is that place as the column keeps it once the indirection is there.

The default is the place unchanged, and it is the right answer for fifteen of the seventeen columns: a member that is already a reference — Python’s Optional[T], Rust’s Option<Box<T>>, Java’s boxed field — has a size whatever it points at, so nothing about it changes when it closes a cycle. C is the column that asks: T next; beside a _has_next flag is storage that contains itself, and the member has to become T *next.

Not folded into CodecTarget::optional_value: that seam is handed the member’s type and not the aggregate that declares it, and the cycle is a fact about the pair. The call sites hold both and ask this only where the pair closes one, so a column that takes the default emits exactly the source it emitted before this seam existed.

Source

fn make_indirection( ast: &Ast, id: Id<TypeNode>, place: Self::Expr, ) -> (Vec<Self::Stmt>, Self::Expr)

The reading counterpart of CodecTarget::behind_indirection: the same place, and what has to happen before a reader can write through it.

A column that keeps an indirection has nothing to write into until the indirection points somewhere, so this is where the storage is made. id is the member’s type, which is what an allocation needs to know the size of.

Returns the statements that make room, followed by the place, in the shape CodecTarget::optional_destination uses and for the same reason.

Source

fn holds_by_reference( ast: &Ast, aggregate: Id<TypeNode>, member_type: Id<TypeNode>, optional: bool, external: bool, ) -> bool

Whether this column holds an @optional or @external member of aggregate, of type member_type, behind a reference of its own — where the shared walk would reach it through CodecTarget::present, CodecTarget::optional_value and their external_ counterparts. A member this answers for goes through CodecTarget::reference_present, CodecTarget::reference_value, CodecTarget::reference_destination and CodecTarget::reference_absent instead.

false by default, which leaves every column’s output as it was. Ada answers from its adapter (crate::adapter::ada::held_by_reference): a member whose type is still incomplete where the aggregate is declared is an access type there (RM 3.10.1), which depends on the order Ada declares in and on nothing the layout plans. The generated Ada for recursive-optional-external asked such a member for Present and did not compile (44 matrix cells, 2026-10-01).

Source

fn reference_present(base: &str, field: &str) -> Self::Expr

Whether a member CodecTarget::holds_by_reference answers for is present. Reached only where that answered true.

Source

fn reference_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr

The value behind such a member, given that it is present.

Source

fn reference_destination( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr)

Where a reader puts such a member’s value, and what it takes to have somewhere to put it; id is the member’s type.

Source

fn reference_absent(base: &str, field: &str) -> Vec<Self::Stmt>

The statements that leave such a member absent.

Source

fn holds_a_cycle_externally() -> bool

Whether this column keeps a member that closes a type cycle the way it keeps an @external one — through the external_ seams below, whether or not the member carries the annotation.

false by default, which leaves a column’s output as it was. The columns that answer true are the ones whose @external member is an indirection of their own (C++’s std::shared_ptr, D’s and Zig’s pointer, Nim’s ref, Swift’s ZdExternal) and whose plain member is the value, so a member on a cycle needs the same holder; crate::adapter::held_externally is the rule, and their adapters and defaulters ask it too. Ada decides by its declaration order (CodecTarget::holds_by_reference) and C by the pointer it declares (behind_pointer), so neither takes this.

Source

fn externals_are_pointers() -> bool

Whether this column holds an @external member behind a pointer of its own, where the member’s value is not a pointer already.

XTypes 1.3 §7.5.1.2.3.1 maps an external member of C to “pointer-to-X”. The member is then reached through CodecTarget::behind_indirection and a reader makes storage for it with CodecTarget::make_indirection, the way both do for a member that closes a cycle (behind_pointer asks both questions at once). false by default; the columns that keep an @external member behind a holder of their own go through the external_ seams instead.

Source

fn optionals_are_pointers() -> bool

Whether this column holds an @optional member behind a pointer of its own, where the member’s value is not a pointer already.

XTypes 1.3 §7.5.1.2.4.1 maps an optional member of C to “pointer-to-X” and says “A NULL pointer shall indicate an omitted value”; §7.5.1.2.4.2 does the same for C++. The member is then reached through CodecTarget::behind_indirection and a reader makes storage for it with CodecTarget::make_indirection, exactly as for an @external one; CodecTarget::present asks whether the pointer is not NULL. false by default: every other column holds an absent value in its own way.

Source

fn value_is_pointer(ast: &Ast, id: Id<TypeNode>) -> bool

Whether the column’s value of id is a pointer already, so that an @external or @optional member of that type needs no pointer of its own. C’s string, wstring and fixed are const char * (XTypes 1.3 §7.5.1.2.3.1); a C++ std::string is a value.

Source

fn refuse_unset_external(present: Self::Expr) -> Vec<Self::Stmt>

Statements that stop a writer from going on when an @external member that is not @optional has no value at the time of the write.

XTypes 1.3 §7.5.1.2.3.1.1 (C) and §7.5.1.2.3.2.1 (C++): “If the member is only external but not optional, then it is not allowed for the member to be NULL at the time of a write.” present says whether the member has a value. Nothing by default: a column that holds an external member inline has no unset state to refuse.

Source

fn external_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, optional: bool, ) -> Self::Expr

The value of an @external member, as the member’s codec takes it.

DDS-XTypes 1.3 Table 21: @external changes no octet — it says where the binding keeps the value. Most columns keep it inline, and the default is then exactly what the walk wrote before this seam existed: the member, or the optional accessor where the member is also @optional. A column that keeps it behind an indirection — D’s T*, which is also what gives a self-recursive type a size at all — answers with the dereference, for both. A walk that reached the member directly would hand a pointer to a codec that takes the value.

Source

fn external_destination( base: &str, field: &str, optional: bool, ) -> (Vec<Self::Stmt>, Self::Expr)

Where a reader puts an @external member’s value, and what it takes to have somewhere to put it. The reading counterpart of CodecTarget::external_value, shaped like CodecTarget::optional_destination and for the same reason: a column whose member is an indirection has to make storage before there is a value to write into. The default is the walk’s old answer for both combinations.

Source

fn external_present(base: &str, field: &str) -> Self::Expr

Whether an @optional @external member is present.

Separate from CodecTarget::present because the two annotations can answer differently in one column: D’s plain @optional is Nullable!(T) and its isNull, while @optional @external is one pointer and its null — a wrapper over the pointer would carry a present-but-null state the wire form has no encoding for.

Source

fn external_set_present( base: &str, field: &str, present: bool, ) -> Vec<Self::Stmt>

Say that an @optional @external member is present, or is not. The counterpart of CodecTarget::set_present, split for the reason CodecTarget::external_present gives.

Source

fn external_case_value( ast: &Ast, id: Id<TypeNode>, base: &str, field: &str, ) -> Self::Expr

The value of an @external union branch, as the branch’s codec takes it. The union counterpart of CodecTarget::external_value, reached through CodecTarget::case_member because that is the accessor a branch has; there is no optional parameter because no union member is @optional (DDS-XTypes 1.3 §7.2.2.4.4.4.7). The default is the accessor itself — the columns that keep the value inline — and a column that keeps an indirection answers with the dereference, exactly as it does for a structure member.

Source

fn external_case_destination( base: &str, field: &str, ) -> (Vec<Self::Stmt>, Self::Expr)

Where a reader puts an @external union branch’s value, and what it takes to have somewhere to put it. The union counterpart of CodecTarget::external_destination, shaped the same way and defaulted the same way.

Source

fn published_name(name: String) -> String

One generated name, in the spelling this target publishes it under.

The identity everywhere but Go, and it exists because Go has no visibility keyword: an identifier is exported if and only if its first character is upper case (Go specification, “Exported identifiers”), so a name is the only place a Go column can put the decision. Applied inside crate::emit::encode::codec_name and crate::emit::decode::decoder_name, which is what makes it a single decision: every caller of either — the walk’s own nested calls, wireprobe-zerodds-go-gen’s entry point, a test naming a function — gets the published spelling without asking.

A target that overrides this owes the transform an inverse in name only: it must be injective, or two codecs collide under one name and no compiler reports that as a collision.

Source

fn initialized_reader( _spec: Routine<Self>, _initializer: Self::Stmt, ) -> Option<Self::Decl>

A new reader entry with an explicit, target-derived initializer. Targets that cannot preserve this contract refuse the new entry only; existing data routines and their initialization remain unchanged.

Source

fn bitmask_value(ast: &Ast, id: Id<TypeNode>, value: Self::Expr) -> Self::Expr

The 64-bit pattern a bitmask arrives as, in the bitmask’s own declared type.

One cast to Self::value_type by default, which every target with a real conversion operator answers with — (Flags)zd_bits in C, (int) zd_bits in Java. TypeScript overrides it: its declared bitmask type is an alias whose holder is a number up to 32 bits, its as is erased and refuses bigint to number outright (TS2352), so the narrowing has to be a runtime conversion through the holder before the alias names it.

Source

fn inherited_path(depth: usize) -> Option<String>

The path from a derived structure to the base members depth levels up, for a column that binds inheritance as a first member parent (XTypes 1.3 §7.5.1.4.1.3), ending in the separator: parent. for the base, parent.parent. for its base. None, the default, is a column that declares the base’s members in the derived structure.

Source

fn map_entry_type(ast: &Ast, id: Id<TypeNode>) -> Self::Type

The type a map’s entry is declared with — the reader’s scratch entry and make_room’s element.

The default names CodecTarget::map_entry_name, which is right wherever the materialiser declares a type of that name. It is not right in C++, whose materialiser spells the entry as std::pair<K, V> and declares nothing under the tag — map_string_long zd_scratch; was a name no compiler finds (20 zerodds-cpp read cells across the map family).

Source

fn enumerator_constant( ast: &Ast, id: Id<TypeNode>, enumerator: &str, ) -> Self::Expr

The constant one enumerator of id is named by.

The materialiser’s spelling. C flattens the modules into a prefix and writes a bare identifier; Java declares a class of public static final int and every use is written from the outer class down — ZdTypes.common.Colour.BLUE. A walk that built the name from CodecTarget::module_prefix alone would emit an unqualified BLUE, which in Java names nothing.

The default is C’s and not the general answer: it is CodecTarget::module_prefix and CodecTarget::escape put together, which works wherever an enumerator sits in the enclosing scope. C++ declares a scoped enumeration, so the same enumerator is ::a::b::Colour::red and carries the type’s own name in the middle, which no prefix supplies; that column overrides.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl CodecTarget for Ada

Source§

impl CodecTarget for Ada83

Source§

impl CodecTarget for C

Source§

impl CodecTarget for CSharp

Source§

impl CodecTarget for Cpp

Source§

impl CodecTarget for D

Source§

impl CodecTarget for Elixir

Source§

impl CodecTarget for Fsharp

Source§

impl CodecTarget for Go

Source§

impl CodecTarget for Java

Source§

impl CodecTarget for Julia

Source§

impl CodecTarget for Kotlin

Source§

impl CodecTarget for Lua

Source§

impl CodecTarget for Nim

Source§

impl CodecTarget for Node

Source§

impl CodecTarget for OCaml

Source§

impl CodecTarget for Python

Source§

impl CodecTarget for Swift

Source§

impl CodecTarget for TypeScript

Source§

impl CodecTarget for Zig