A high-performance, source-generated binary serialization library for .NET with support for polymorphism, cyclic references, and efficient memory management. Part of the HexaEngine game engine ecosystem.
HexaEngine.Sappho is a specialized serialization library designed for game development scenarios where performance is critical. Originally developed as a submodule for the HexaEngine game engine, it provides efficient binary serialization suitable for:
- Game State Persistence: Save/load game states with complex object graphs
- Network Communication: Fast serialization for multiplayer game data
- Asset Management: Efficient binary formats for game assets
- Scene Serialization: Handling complex game object hierarchies with circular references
While designed for game engines, Sappho is a standalone library suitable for any .NET application requiring high-performance binary serialization.
- π High Performance: Optimized for speed with unsafe code and minimal allocations
- π Cyclic References: Automatic handling of circular object references
- π Polymorphism: First-class support for polymorphic serialization and deserialization
- βοΈ Source Generation: Uses C# source generators for compile-time serialization code generation
- π§ Flexible: Support for primitives, strings, enums, structs, and complex object graphs
- πΎ Efficient: Binary format with endian-aware operations
- π― Type-Safe: Strongly-typed serialization with compile-time validation
dotnet add package HexaEngine.SapphoUse the [SapphoObject] attribute to mark classes or structs for serialization:
using HexaEngine.Sappho;
[SapphoObject]
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public float Height { get; set; }
}Note: Classes must be marked as
partialto allow the source generator to add serialization code.
// Create a serialization context
var context = new SapphoSerializationContext();
// Create an object
var person = new Person
{
Name = "John Doe",
Age = 30,
Height = 5.9f
};
// Serialize
using var writer = new SapphoWriter(context);
writer.WriteObject(person);
// Deserialize
var reader = new SapphoReader(writer.Buffer, writer.Count, context);
var deserializedPerson = reader.ReadObject<Person>();Use the [SapphoPolymorphic] attribute to enable polymorphic serialization:
[SapphoObject]
public partial class Animal
{
public string Name { get; set; }
}
[SapphoObject]
public partial class Dog : Animal
{
public string Breed { get; set; }
}
[SapphoObject]
public partial class Cat : Animal
{
public int LivesRemaining { get; set; }
}
[SapphoObject]
public partial class Zoo
{
[SapphoPolymorphic]
public Animal[] Animals { get; set; }
}When using polymorphic types, register them with the serialization context:
context.RegisterType<Animal>(Animal.SapphoTypeId);
context.RegisterType<Dog>(Dog.SapphoTypeId);
context.RegisterType<Cat>(Cat.SapphoTypeId);
var zoo = new Zoo
{
Animals = new Animal[]
{
new Dog { Name = "Buddy", Breed = "Golden Retriever" },
new Cat { Name = "Whiskers", LivesRemaining = 9 }
}
};The library automatically handles cyclic references:
[SapphoObject]
public partial class Node
{
public string Name { get; set; }
public Node? Next { get; set; }
}
var node1 = new Node { Name = "Node 1" };
var node2 = new Node { Name = "Node 2" };
node1.Next = node2;
node2.Next = node1; // Cyclic reference
// Serialization will handle this correctlyUse [SapphoIgnore] to exclude properties from serialization:
[SapphoObject]
public partial class CachedData
{
public string Data { get; set; }
[SapphoIgnore]
public string CachedValue { get; set; } // Not serialized
}Use [SapphoMember] for fine-grained control over serialization:
[SapphoObject]
public partial class CustomClass
{
[SapphoMember]
public string ImportantField { get; set; }
}- Primitives:
byte,sbyte,short,ushort,int,uint,long,ulong,float,double,bool,char - Strings: UTF-8 encoded with automatic null handling
- Enums: All enum types with any underlying integral type
- Structs: Value types that implement
ISapphoSerializable<T> - Classes: Reference types with automatic reference tracking
- Arrays: Single-dimensional arrays of supported types
- Nullable Types: Full support for nullable reference and value types
HexaEngine.Sappho is designed for high-performance scenarios:
// From the Example project:
// Average serialization time: ~XXX ns (depends on object complexity)The library uses:
- Unsafe code for direct memory access
- Stack allocations for small buffers
- Efficient buffer management with dynamic resizing
- Endian-aware binary format
- SapphoWriter: Handles serialization to a binary buffer
- SapphoReader: Handles deserialization from a binary buffer
- SapphoSerializationContext: Manages type registration and reference tracking
- Source Generator: Automatically generates serialization code at compile time
ISapphoSerializable<T>: Base interface for serializable typesISapphoSerializer<T>: Custom serializer interfaceISapphoInstanceId: Optional interface for custom instance ID tracking
HexaEngine.Sappho/
βββ HexaEngine.Sappho/ # Core serialization library
βββ HexaEngine.Sappho.Analyzer/ # Source generator
βββ HexaEngine.Sappho.Tests/ # Unit tests
βββ Example/ # Example usage
- .NET 9.0 or later (core library)
- .NET Standard 2.0 or later (analyzer)
- C# 12 or later (for source generation features)
dotnet builddotnet testThe test suite includes comprehensive tests for:
- Primitive type serialization
- String handling (including Unicode and empty strings)
- Enum serialization
- Struct serialization
- Polymorphic serialization
- Cyclic reference handling
- Null value handling
Contributions are welcome! Please ensure:
- All tests pass
- Code follows existing conventions
- New features include appropriate tests
This project is licensed under the MIT License. See the LICENSE file for more details.
Part of the HexaEngine ecosystem.
Developed as a submodule for HexaEngine - a modern game engine for .NET.
- HexaEngine - The main game engine project
- Hexa.NET.Utilities - Utility library used by Sappho
Do you listen to girl in red?