Home

Awesome

Spangle.LusterBits

Spangle.LusterBits is a C# Source Generator library that generates accessor methods for struct that has fixed byte array fields (e.g. private fixed byte _data[SomeFixedLength];). It is useful for accessing binary data such as network packets, especially, when only a few fields of the data are needed.

Spangle.LusterBits targets a struct that has Network-Ordered (Big-Endian) fixed byte array that has no alignment. So you do not need to unmarshal the network packets, only just do MemoryMarshal.Ref<TargetStruct>(...) for it.

Getting Started

Spangle.LusterBits is available as a NuGet package. You can install it from the NuGet Package Manager Console:

nuget install Spangle.LusterBits

Or via the .NET Core command-line interface:

dotnet add package Spangle.LusterBits

Usage

Spangle.LusterBits searches marker attribute that is specified by LusterCharmAttribute. The marker attribute must be specified on a struct that has fixed byte array fields and the fields also have the setting attribute that is specified by BitFieldAttribute. Spangle.LusterBits searches for the marker attribute specified by LusterCharmAttribute. The marker attribute must be specified on a struct that has fixed byte array fields and the fields also have the field setting attributes specified by BitFieldAttribute. Of course, the struct must also be a partial.

// All target structures would have such a signature:
// `accessibility unsafe partial struct StructName`
[LusterCharm]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe partial struct SomePacket
{
    public const int PacketLength = 8; // 64 bits

    [
        BitField(typeof(byte), "SomeByte", 2),
        BitField(typeof(uint), "SomeNotAlignedUInt32", 30),
        BitField(typeof(SomeEnum), "SomeEnumCastedValue", 4),
        BitField(typeof(ushort), "SomeUShort", 15),
        BitField(typeof(bool), "SomeBoolean", 1),
        BitField(typeof(byte), "SomeByte2", 2),
        BitField(typeof(ushort), "SomeUShort2", 10),
    ]
    private fixed byte _data[PacketLength];
}
public enum SomeEnum : byte
{
    Value0 = 0b0000,
    Value1 = 0b0001,
    Value2 = 0b0010,
    // ... can use 4 bits for value
}

Then, the following accessor methods are generated:

public unsafe partial struct SomePacket
{
    public byte SomeByte => (byte)(_data[0] >>> 6);

    public uint SomeNotAlignedUInt32 => (uint)(((uint)(_data[0] & 0x3F) << 24) + ((uint)_data[1] << 16) + ((uint)_data[2] << 8) + ((uint)_data[3]));

    public Spangle.LusterBits.Tests.Learning.SomeEnum SomeEnumCastedValue => (Spangle.LusterBits.Tests.Learning.SomeEnum)(_data[4] >>> 4);

    public ushort SomeUShort => (ushort)(((_data[4] & 0x0F) << 11) + (_data[5] << 3) + (_data[6] >>> 5));

    public bool SomeBoolean => 0 != ((_data[6] & 0x10) >>> 4);

    public byte SomeByte2 => (byte)((_data[6] & 0x0C) >>> 2);

    public ushort SomeUShort2 => (ushort)(((_data[6] & 0x03) << 8) + (_data[7]));

}

You can use the generated accessor methods as follows:

ref readonly var packet = ref MemoryMarshal.Read<SomePacket>(buffer);
return packet.SomeByte; // The value is first 2bit of the byte sequence

Field Settings

The field setting attributes specified by LusterCharmAttribute have the following parameters:

Types

String types are experimental, because they can only be used in the rare case of fixed-length strings.

There may be cases where an enum-like (e.g. A1234 | B5678) fixed length ASCII string is needed, in such cases, you can use UTF8String and assume that each character is a single byte, and the string will be treated normally as an ASCII string.

Nested Structs

Spangle.LusterBits does not support nested struct out of the box. If you want to use nested struct, you should separate its own fixed byte array and child struct that has BitFieldAttribute.

[LusterCharm]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe partial struct SomePacket
{
    public const int PacketLength = 8; // 64 bits

    [
        BitField(typeof(byte), "SomeByte", 2),
        // its own field settings...
    ]
    private fixed byte _data[PacketLength];

    public readonly SomeNestedPacket SomeNestedPacket;
}

Request for Comments

If this project is interesting to you and you have a real use for it, please let us know if there are any missing features. We may not implement everything, but we may respond to make this project better.

Roadmap

Disclaimer

This library is still in its early development stage. The APIs are not stable and may change in future releases.

License

This library is under the MIT License. See the LICENSE file for the full license text.