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
@list
and@dict
. - Add
@ver
property 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
.