ReadXxx/WriteXxx calls by hand for every field, you annotate your struct or class with attributes and the generator produces the implementation for you.
Source generation is spread across three components:
| Component | Role |
|---|---|
Titanis.SourceGen | The Roslyn source generator that runs at build time |
Titanis.PduStruct | A shared project that defines the attributes ([PduStruct], [PduField], etc.) |
Titanis.IO | Provides IByteSource and ByteWriter, the types the generated code reads from and writes to |
Enabling the source generator
AddTitanis.SourceGen to your project as an analyzer, not as a regular assembly reference:
project.csproj
ReferenceOutputAssembly="false" and OutputItemType="Analyzer" attributes tell MSBuild and Visual Studio to invoke the generator at build time without linking the generator assembly into your output.
Also add a reference to
Titanis.PduStruct so that the attribute types are available in your source, and to Titanis.IO so that IByteSource and ByteWriter are available.Marking a type for generation
Apply[PduStruct] to a partial class or struct to trigger code generation:
partial so the generator can add the generated methods to the same type. The generator produces implementations of IPduStruct — specifically the methods that read from an IByteSource and write to a ByteWriter.
A type marked with [PduStruct] may inherit another [PduStruct]-marked type. The generated code calls the base type’s read/write methods first before processing its own members.
Controlling which members are included
By default, all fields (regardless of access modifier) are included. Properties are excluded by default.Field alignment
The source generator does not enforce any alignment requirements by default. To enforce alignment padding for an individual field, apply[PduAlignment] to that field.
The
[PduAlignment] and [PduConditional] attributes are always applied by the generator before any custom read/write methods are invoked.Setting byte order
Use[PduByteOrder] to specify endianness. You can apply it at the assembly level, to a type, or to an individual field. More specific scopes override less specific ones.
Strings
The generator cannot infer how to serialize a string without additional information. Annotate string fields with[PduString], specifying the character set and the name of a member that returns the byte count at runtime:
byteCount consistent with the actual string content.
Arrays
Annotate array fields with[PduArraySize], providing the name of a member that returns the element count:
Conditional fields
Use[PduConditional] when a flag in the PDU controls whether a field is present:
[PduConditional] is required on Nullable<T> fields and nullable reference types. The generated code evaluates the condition both when reading and writing. If the condition is true but the field is not set, a NullReferenceException occurs at runtime — keep the value and condition consistent.
Capturing stream position
Declare along field and mark it with [PduPosition] to record the byte offset within the stream at that point:
Custom read and write methods
When none of the built-in attributes cover your scenario, you can provide custom read and write methods using[PduField] with the ReadMethod and WriteMethod named arguments:
[PduAlignment] and [PduConditional] before invoking your custom methods.
Reference
For complete working examples of every attribute, consult thePduStructSample project under samples/ in the Titanis repository.