Spangle.LusterBits 0.1.1

dotnet add package Spangle.LusterBits --version 0.1.1                
NuGet\Install-Package Spangle.LusterBits -Version 0.1.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Spangle.LusterBits" Version="0.1.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Spangle.LusterBits --version 0.1.1                
#r "nuget: Spangle.LusterBits, 0.1.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Spangle.LusterBits as a Cake Addin
#addin nuget:?package=Spangle.LusterBits&version=0.1.1

// Install Spangle.LusterBits as a Cake Tool
#tool nuget:?package=Spangle.LusterBits&version=0.1.1                

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:

  • type: Type of the output parameter. Calculated value is casted to this type. The type MUST be a primitive type or enum.
  • name: Name of the output parameter. This is used for the name of the accessor method.
  • length: Length of the field in bits. If length is over the type, higher bits are ignored.
  • position: Position of the field in bits. This value can be omitted if the fields are specified in order without alignment.
  • description: Description of the field. When this value is specified, the accessor method is also documented with comment.

Types

  • Almost all primitive types
    • byte
    • sbyte
    • ushort
    • short
    • uint
    • int
    • ulong
    • long
    • decimal
    • float
    • double
    • bool
      • Evaluated as fieldVal != 0
  • Enum types
  • String types (Experimental)
    • UTF8String
    • UTF16String
    • UTF16BigEndianString
    • string (treated as UTF8String)

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

  • Support for fields whose position depends on a condition specified by other fields
  • Support for fields that can be present or absent depending on conditions
  • Support reading from IStream and PipeReader directly for GenerateType.Deserialized
  • Support changing of accessibility for FieldAsSpan (currently, always internal)
  • Support giving the size of fixed array field explicitly for FieldAsSpan
  • Support for explicitly specifying the size of a fixed array field to allow FieldAsSpan to have a free data field at the end.

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.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.1 183 7/18/2023
0.1.0 163 7/18/2023