Skip to main content

Module walk

Module walk 

Source
Expand description

phase-421 W5 — the schema-driven serialization strategy (RFC-0088 D7).

A provider that declares impl = "schema" in its nros-serdes.toml writes ONE implementation and gets every message, with no codegen plugin and no generated code per type. This module is the machinery that makes that true: the walk over crate::schema::Field lives here, once, and a provider supplies only the primitive encode/decode operations of its own wire.

§The signature RFC-0088 D7 sketched cannot be implemented

D7 wrote:

fn serialize(msg: *const u8, schema: &'static [Field], out: &mut [u8]) -> …

That takes the HOST STRUCT as a pointer and reads fields at Field::offset. Measured against the schemas codegen actually emits, it stops at the first variable-length field, and it does so for three independent reasons:

  • A String field’s host type is heapless::String<N> and the schema does not carry N. nros generate-rust emits FieldType::String — the IDL type — for a member whose Rust storage is a fixed-capacity buffer. BoundedString(n) is the IDL bound, not the host capacity, and the two are different numbers.
  • heapless::String / heapless::Vec are repr(Rust). Their field order and padding are unspecified, so “the length is at offset 0” is not a fact this crate is allowed to assume. Field::offset is well defined — offset_of! works on a repr(Rust) struct — but it only gets you to the START of the container, and the container’s own interior is opaque.
  • A nested type’s SIZE is not in the schema. crate::schema::NestedType carries type_name and fields; striding an Array(N, Nested(..)) or a sequence of structs needs size_of of the element, and the largest offset in a repr(Rust) child does not determine it.

Extending the schema to carry host layout would change what codegen must emit for every committed generated message, which is a different change from this one. So the pivot moves: the value access a schema-driven provider has today is the CDR byte stream, which nano-ros already produces for every message from generated code. impl = "schema" is therefore a TRANSCODER strategy in v1 — CDR in, foreign wire out, and back — and that is precisely why the walk belongs here rather than in each provider.

The cost is the one D7 already accepted: schema-driven is slower than the per-type serializer we emit for CDR, and impl = "codegen" is the answer when someone hits the wall.

§What the walk does not cover

FieldType::WString / FieldType::BoundedWString reach SchemaError::Unsupported, because crate::cdr::CdrReader has no wide-string primitive to read them WITH — there is no read_wstring, in either direction. No message in packages/interfaces/* uses one, so this is a hole in the CDR codec that the schema walk inherits rather than one it introduces.

Enums§

SchemaError
What can go wrong in a schema-driven encode or decode.

Traits§

SchemaSerializer
A serialization format implemented once, by walking the schema (RFC-0088 D7).
SchemaSink
The encode half of a schema-driven format: the walk pushes typed values in, the implementor writes its wire.
SchemaSource
The decode half: the walk pulls typed values out, in schema order.

Functions§

decode_to_cdr
Walk schema, pulling each value from source and writing CDR.
encode_from_cdr
Walk schema, reading CDR and pushing each value into sink.