Katera is a source generator for deterministic bitfield layouts in C#. Its goal is to add support for bitfield-style structs without inheriting the undefined or host-dependent behavior of C/C++ bitfields. This also means that no implicit alignment happens, all alignment must be added manually.
All bitfield layouts support LSB-first and MSB-first ordering, explicit padding, and memory-backed overlays for interpreting raw bytes.
- Partial
struct/record structsource generation for compact bit layouts - Support for
bool, integer types, and enums as bit fields - Explicit layout control with
Size,Mode,AllowOverlap,BitOrder, andOffset - Automatic layout size inference when
Sizeis omitted - Explicit member-level padding with
PadAttribute - Support for get-only and init-only bit properties
- Blob-backed, register-backed, and expanded storage modes with optional overlay views
- Analyzer diagnostics for invalid field usage, size mismatches, overlaps, and gaps
Katera generates bitfield logic for partial struct and partial record struct types annotated with BitLayoutAttribute. Declare each bit field property as partial with BitFieldAttribute and optionally use PadAttribute or Offset to control exact placement.
using Katera;
[BitLayout]
public partial struct PacketHeader
{
[BitField(3)] public partial byte Version { get; init; }
[BitField(5)] public partial byte Flags { get; set; }
[BitField(16)] public partial ushort Length { get; set; }
[BitField(8)] public partial byte Checksum { get; set; }
}Katera will generate the backing storage and property accessors so you can treat PacketHeader like a compact value type.
Apply BitLayoutAttribute to a partial struct/partial record struct to configure the layout.
Properties:
Size(bits): total layout size.0(default) means automatically compute size from fields.Mode: one ofStorageMode.Auto(default),Register,Blob, orExpanded.AllowOverlap: set totrueto allow overlapping fields (defaultfalse).BitOrder: chooseBitOrder.LSBFirst(default) orBitOrder.MSBFirst.
Example:
[BitLayout(Size = 32, Mode = StorageMode.Register, BitOrder = BitOrder.MSBFirst)]
public partial struct Flags32
{
[BitField(1)] public partial bool Enabled { get; set; }
[BitField(7)] public partial byte Type { get; set; }
[BitField(24)] public partial uint Value { get; set; }
}Use BitFieldAttribute(length) to declare a field's bit width. Supported bit field types are:
boolbyte,sbyteshort,ushortint,uintlong,ulong- enums (backed by a supported integral type)
Example with explicit offset:
[BitLayout]
public partial struct ExplicitOffsets
{
[BitField(4, Offset = 0)] public partial byte A { get; set; }
[BitField(4, Offset = 4)] public partial byte B { get; set; }
[BitField(8, Offset = 8)] public partial byte C { get; set; }
}StorageMode.Auto is the default. It chooses Register for layouts up to 8 bytes and Blob for larger layouts.
StorageMode.Register- Uses a single primitive backing storage value (
byte,ushort,uint, orulong) - Valid only for layouts of 8 bytes or smaller
- Generates low-level raw helpers such as
SetRaw(TBacking)and conversion operators for the backing value
- Uses a single primitive backing storage value (
StorageMode.Blob- Uses one or more
ulongvalues internally - Valid only for layouts larger than 8 bytes
- Uses one or more
StorageMode.Expanded- Uses separate generated fields and properties without unified backing storage
Use PadAttribute on a field or property declaration to reserve unnamed bits between fields.
[BitLayout]
public partial struct PaddedLayout
{
[BitField(5)] public partial byte A { get; set; }
[Pad(3)]
[BitField(8)] public partial byte B { get; set; }
}This is useful when you want explicit gaps or alignment without creating a named field. If gaps are created implicitly via Offset, the analyzer will emit a KATERA006 warning, so it is recommended to use this attribute.
PadAttribute is currently consumed from members in declaration order. Although the attribute type allows broader targets, generator layout padding is defined by member-level usage.
Katera supports read-only bit fields and init-only bit fields.
[BitLayout]
public partial struct ReadOnlyLayout
{
[BitField(8)] public partial byte ReadOnlyValue { get; }
[BitField(8)] public partial byte InitValue { get; init; }
}The generator omits setters for get-only fields and generates init setters when the property is declared with init.
These are most useful if the bitfield primarily targets overlay usage, but can also have uses in regular owned types.
Enums are supported as long as their underlying type is one of the supported integral types.
public enum Color : byte { Red, Green, Blue }
[BitLayout]
public partial struct EnumLayout
{
[BitField(2)] public partial Color Color { get; set; }
[BitField(1)] public partial bool IsVisible { get; set; }
}bool fields are stored as a single bit and will not allow larger sizes.
Signed integral fields are masked to the declared bit width and sign-extended on read. In practice, that means a signed field behaves like a fixed-width two's-complement value inside the declared bit range.
For example, a 4-bit sbyte field stores values in the range -8 to 7. Writing a value outside the representable range truncates to the low bits before later reads sign-extend that stored pattern.
[BitLayout]
public partial struct SignedNibble
{
[BitField(4)] public partial sbyte Delta { get; set; }
}
SignedNibble value = new();
value.Delta = -2;
Console.WriteLine(value.Delta); // -2Fields may cross underlying storage boundaries in blob-backed layouts. Katera handles reads and writes across 64-bit boundaries automatically.
[BitLayout(Mode = StorageMode.Blob)]
public partial struct WideLayout
{
[BitField(62)] public partial ulong First { get; set; }
[BitField(4)] public partial uint Second { get; set; }
}Set AllowOverlap = true to permit overlapping fields. Overlap is not allowed by default, and the option is only honored for register-backed and blob-backed layouts. Note: Expanded layouts do not support overlapping fields regardless of setting.
[BitLayout(AllowOverlap = true)]
public partial struct OverlapLayout
{
[BitField(8)]
public partial byte A { get; set; }
[BitField(8, Offset = 0)]
public partial byte AliasOfA { get; set; }
}Katera generates an overlay view type named {TypeName}View for a given layout.
[BitLayout(Mode = StorageMode.Blob)]
public partial struct Packet
{
[BitField(16)] public partial ushort ID { get; set; }
[BitField(16)] public partial ushort Checksum { get; set; }
}The generated overlay type exposes:
static PacketView Over(Span<byte> span)static PacketView Over(ref byte reference)Span<byte> AsSpan()
Overlay helpers require a buffer that contains at least the full layout size in bytes, which is the declared Size in bits rounded up to the next whole byte. The returned view covers exactly that many bytes.
Use overlays when you need to interpret or mutate a raw byte buffer without copying the entire bitfield struct.
Register-backed and blob-backed generated types offer helpers for raw byte conversion.
Raw helpers such as From(ReadOnlySpan<byte>), TryFrom(ReadOnlySpan<byte>, out T), and WriteTo(Span<byte>) all require a buffer at least as large as the layout's full backing size in bytes. That is:
- the declared
Sizein bits, rounded up to the nearest whole byte - or the computed size when
Sizeis omitted
For Register-backed layouts, this is the same size as the generated backing primitive (byte, ushort, uint, or ulong). Register mode also generates raw backing helpers like SetRaw(...) and implicit/explicit conversions to the primitive backing type.
For Blob-backed layouts, this is the full number of bytes covered by the generated blob storage.
StorageMode.Expanded does not generate From(...), TryFrom(...), or WriteTo(...). Instead, it emits internal raw helpers for the expanded representation and still generates the overlay view type.
Example:
var data = new byte[4];
var header = PacketHeader.From(data);
header.Flags = 3;
header.WriteTo(data);
if (PacketHeader.TryFrom(data, out var parsed))
{
Console.WriteLine(parsed.Length);
}For StorageMode.Register, Katera generates extra helpers on the owned type. These include SetRaw(<backing-type>), an implicit conversion from the bitfield type to the backing primitive, and an explicit conversion from the backing primitive back to the bitfield type.
[BitLayout(Mode = StorageMode.Register, Size = 8)]
public partial struct Test
{
[BitField(1)] public partial bool A { get; set; }
}
Test test = new();
test.SetRaw(0x3);
byte raw = test; // implicit conversion
Test other = (Test)0x2; // explicit conversionThis is useful when you want a very small register-backed layout and need direct access to the underlying primitive without invoking individual field setters.
Katera reports analyzer diagnostics for invalid layouts.
| Rule ID | Category | Severity | Notes |
|---|---|---|---|
| KATERA001 | Katera.BitLayout | Error | Fields exceed declared size. |
| KATERA002 | Katera.BitLayout | Error | Property type cannot hold declared bit length. |
| KATERA003 | Katera.BitLayout | Error | Storage mode/size combination is unsupported. |
| KATERA004 | Katera.BitLayout | Error | Invalid BitField or Pad target usage. |
| KATERA005 | Katera.BitLayout | Error | Overlapping fields without overlap allowance. |
| KATERA006 | Katera.BitLayout | Warning | Implicit gap, use Pad to make it explicit. |
| KATERA007 | Katera.BitLayout | Error | BitField length must be greater than zero. |
When overlaps occur without AllowOverlap = true, KATERA005 is currently reported once per overlapping bit position.
BitLayoutAttributemust be applied to apartial structorpartial record struct.- Bit field properties must be instance properties and must be
partial. PadAttributelayout padding is defined by member-level usage.- Explicit offsets and
Padmay be used together to control layout precisely. Sizeis in bits; if omitted, Katera computes the smallest byte size necessary for the declared fields.