GenPack is a library that uses the .NET source generator to automatically generate packets as classes once you define a schema for the packets. It's easy to use and the results are useful.
GenPack also works well with Native AOT. You can take advantage of the benefits of Native AOT.
[GenPackable]
public partial record PeoplePacket
{
public readonly static PacketSchema Schema = PacketSchemaBuilder.Create()
.@short("Age", "Age description")
.@string("Name", "Name description")
.Build();
}The following code is automatically generated by the schema information.
public partial record PeoplePacket : GenPack.IGenPackable
{
/// <summary>
/// Age description
/// </summary>
public short Age { get; set; }
/// <summary>
/// Name description
/// </summary>
public string Name { get; set; } = string.Empty;
public byte[] ToPacket()
{
using var ms = new System.IO.MemoryStream();
ToPacket(ms);
return ms.ToArray();
}
public void ToPacket(System.IO.Stream stream)
{
using var writer = new GenPack.IO.EndianAwareBinaryWriter(stream, GenPack.UnitEndian.Little, GenPack.StringEncoding.UTF8);
writer.Write(Age);
writer.Write(Name);
}
public static PeoplePacket FromPacket(byte[] data)
{
using var ms = new System.IO.MemoryStream(data);
return FromPacket(ms);
}
public static PeoplePacket FromPacket(System.IO.Stream stream)
{
PeoplePacket result = new PeoplePacket();
using var reader = new GenPack.IO.EndianAwareBinaryReader(stream, GenPack.UnitEndian.Little, GenPack.StringEncoding.UTF8);
int size = 0;
byte[] buffer = null;
result.Age = reader.ReadInt16();
result.Name = reader.ReadString();
return result;
}
}It's simple to use. You can binary serialize with ToPacket() and deserialize with FromPacket().
var p = new PeoplePacket()
{
Age = 10,
Name = "John"
};
var data = p.ToPacket();
var newP = PeoplePacket.FromPacket(data);
Console.WriteLine(newP);Output:
PeoplePacket { Age = 10, Name = John }
Decorate the attribute of class or record with GenPackable. At this point, the target must be given partial.
GenPack's packet schema is represented by creating a PacketSchema using the PacketSchemaBuilder.
[GenPackable]
public partial record PeoplePacket
{
public readonly static PacketSchema Schema = PacketSchemaBuilder.Create()
.@short("Age", "Age description")
.@string("Name", "Name description")
.Build();
}The format beginning with @ means the schema property to be created. For example, @short("Age", "Age description") gives the Age property the type short and the description Age description.
This translates to the following:
/// <summary>
/// Age description
/// </summary>
public short Age { get; set; }You can then use the auto-generated properties.
var p = new PeoplePacket();
p.Age = 32;| Property | Description | Bits | Arguments |
|---|---|---|---|
| @byte | byte | 8 | property name, description |
| @sbyte | signed byte | 8 | property name, description |
| @short | short int | 16 | property name, description |
| @ushort | unsigned short int | 16 | property name, description |
| @int | int | 32 | property name, description |
| @uint | unsigned int | 32 | property name, description |
| @long | long int | 64 | property name, description |
| @ulong | unsigned long int | 64 | property name, description |
| @float | single float | 32 | property name, description |
| @double | double float | 64 | property name, description |
| @string | string | N | property name, description |
| @object<type> | genpackable object | N | property name, description |
| @list<type> | variable list | N | property name, [sizeMode], description |
| @dict<type> | variable dictionary | N | property name, [sizeMode], description |
| @array<type> | fixed array | N | property name, size, description |
GenPack supports multiple size encoding modes for @list and @dict to optimize for different use cases:
[GenPackable]
public partial record SizeModeExamplePacket
{
public readonly static PacketSchema Schema = PacketSchemaBuilder.Create()
// Default: Variable 7-bit encoding (most space efficient for small sizes)
.@list<int>("DefaultList", "Uses variable 7-bit encoding")
// Fixed 8-bit encoding (max 255 items)
.@list<string>("SmallList", SizeMode.Fixed8Bit, "Max 255 items")
// Fixed 16-bit encoding (max 65,535 items)
.@dict<int>("MediumDict", SizeMode.Fixed16Bit, "Max 65,535 items")
// Fixed 32-bit encoding (max 2,147,483,647 items)
.@list<byte>("LargeList", SizeMode.Fixed32Bit, "Very large lists")
.Build();
}| Size Mode | Bytes | Maximum Items | Use Case |
|---|---|---|---|
| Variable7Bit | 1-5 | 2,147,483,647 | Default, space efficient for small sizes |
| Fixed8Bit | 1 | 255 | Small collections, embedded systems |
| Fixed16Bit | 2 | 65,535 | Medium collections, protocol compatibility |
| Fixed32Bit | 4 | 2,147,483,647 | Large collections, performance critical |
- Variable7Bit: Most space efficient for collections with < 128 items
- Fixed8Bit: Consistent 1-byte overhead, ideal for embedded systems
- Fixed16Bit: Common in network protocols, predictable size
- Fixed32Bit: Better performance for large collections, no variable decoding
- Support for Endian, string Encoding.
- Support for checksums.
- Support 8-bit, 16-bit, 32-bit, or variable 7-bit sizes for
@listand@dict. - Add
@verproperty to allow revision control of packets. - Automatically select and deserialize target structures based on packet command(identification code).
- Generate JSON and gRPC schema with
PacketSchema. - Process device packets with uncomplicated packet structures.
- Process structures with complex packets, such as PLCs.
- Process packets that require speed, such as
MemoryPack.