BinaryBundle 2.2.4
dotnet add package BinaryBundle --version 2.2.4
NuGet\Install-Package BinaryBundle -Version 2.2.4
<PackageReference Include="BinaryBundle" Version="2.2.4" />
paket add BinaryBundle --version 2.2.4
#r "nuget: BinaryBundle, 2.2.4"
// Install BinaryBundle as a Cake Addin
#addin nuget:?package=BinaryBundle&version=2.2.4
// Install BinaryBundle as a Cake Tool
#tool nuget:?package=BinaryBundle&version=2.2.4
BinaryBundle
BinaryBundle allows you to generate serialization and deserialization methods for your C# classes and structs using source generators.
Because the serialization methods are created at compile time, it is highly portable and efficient, as it does not rely on any reflection or any sort of runtime generation. If used correctly, BinaryBundle will not generate any additional garbage.
Good use cases for this library includes network packets, where you know both sides are guaranteed to be on the same version, like a multiplayer game. This library is not recommended for use cases where you need forwards or backwards compatibility as the serialized data is highly dependent on the field layout of the classes.
Currently supported types:
- All .NET primitive types
- Arrays, both jagged and multidimensional
- Enums
- Properties and fields
- Lists and dictionaries
- Tuples
Getting started
Importing
Import the package to your project using NuGet.
Marking a type for serialization
To inform BinaryBundle that it should add Serialize()
and Deserialize()
methods to a class, record or struct, mark it with the [BinaryBundle]
attribute. The type also needs to have a partial
modifier so that code can be added to it.
[BinaryBundle]
public partial class SimpleClass {
public int IntField;
}
BinaryBundle will create a Serialize()
and Deserialize()
method for this type, as well make the type implement IBundleSerializable
. Since these fields are set from normal C# methods, they can not be marked readonly
.
Writing a serializable type to a binary format
You can now write the class to a binary format using the generated methods with the included BundleDefaultWriter
and BundleDefaultReader
classes. These are small wrappers around the standard library BinaryWriter
and BinaryReader
classes. If you want to use your own custom reader/writer, that's outlined here: Using your own Reader, Writer and Interface
var bytes = new byte[0xFF];
var @class = new SimpleClass() {
IntField = 42,
};
var bundleWriter = new BundleDefaultWriter(bytes);
@class.Serialize(buffer);
var deserializedClass = new SimpleClass();
var bundleReader = new BundleDefaultReader(bytes);
deserializedClass.Deserialize(buffer);
Console.WriteLine(@class.IntField); // 42
Console.WriteLine(deserializedClass.IntField); // 42
Serializable fields
If any fields or properties have types that implement the IBundleSerializable, either manually or from the generator, the serialization methods will be called on them as well. The IBundleSerializable
type will not be instantiated by the deserialization method, make sure it's created before Deserialize
is called!
[BinaryBundle]
public partial class NestedClass {
// Deserialize() will be called on InnerClass, make sure it's instantiated before Deserialization!
public InnerClass ClassField = new InnerClass();
// For structs you don't need to worry about this, since they always have a valid default value
public InnerStruct StructField;
}
[BinaryBundle]
public partial class InnerClass {
public int IntField;
}
[BinaryBundle]
public partial struct InnerStruct {
public int IntField;
}
Extending serialization support to any type
Often you want to be able to serialize types outside your own project. BinaryBundle allows you to define TypeExtension methods that can be used to serialize any type. To mark a pair of methods as TypeExtensions add the [BundleSerializeTypeExtension]
and [BundleDeserializeTypeExtension]
attributes to them respectively.
static class VectorSerializeExtension {
[BundleSerializeTypeExtension]
public static void WriteVector2(this BundleDefaultWriter writer, Vector2 vector) {
writer.WriteFloat(vector.X);
writer.WriteFloat(vector.Y);
}
[BundleDeserializeTypeExtension]
public static Vector2 ReadVector2(this BundleDefaultReader reader) {
float x = reader.ReadFloat();
float y = reader.ReadFloat();
return new Vector2(x, y);
}
}
Defining these methods as extension methods isn't necessary but it looks nice.
Attributes that can be used on fields
There are some attributes that BinaryBundle will recognize and will generate different code for.
BundleIgnore
Fields with the BundleIgnore
attribute will be skipped entirely.
BundleLimit
Collections with the BundleLimit
attribute will be limited to the size defined in the first parameter. This can be useful to prevent huge allocations when receiving untrusted data.
The second parameter is optional and decides the behavior on collections that exceed the size to either clamp the size down when sent, or throw an exception. Deserializing a size that is read as too big will always throw an exception as this would be invalid data.
Using your own Reader, Writer and Interface
While the provided BundleDefaultWriter
and BundleDefaultReader
classes are fairly fast and flexible, they are not perfect for every project. For that reason you can specify custom Writer and Reader classes to use for the serialization methods. This is done by specifying the classes used by the serializable interface.
Defining types used by the entire project
To indicate that an interface should be the one used for serializable classes you need to mark it with the [BundleDefaultInterface]
attribute, as well as implement IBundleSerializableBase<TWriter, TReader>
. The types used by TWriter and TReader are the ones that will be used for serialization.
[BundleDefaultInterface]
interface IMySerializable: IBundleSerializableBase<MyWriter, MyReader> { }
Your custom Reader and Writer types have to implement IBundleReader
and IBundleWriter
respectively.
How Reader and Writer is used
To use a custom Reader and Writer, they have to implement the corresponding interfaces, IBundleReader
and IBundleWriter
. These interfaces require you to define methods for writing and reading all .NET primitive types. These primitive calls are used by BinaryBundle to serialize more complex types like arrays, dictionaries and the serializable objects themselves.
For example the code generated for serializing an array looks like this:
writer.WriteByte((byte)array.Length); // (For representative purposes writes a byte here, in reality it uses 1-4 bytes depending on the size of the array)
for (int i = 0;i < array.Length; i++) {
writer.WriteInt32(array[i]);
}
BinaryBundle is made with binary data formats in mind, for this reason it's not possible to serialize to something like JSON with this. In the above code you'll notice there's no callback to close the array which would be necessary to serialize to a JSON list.
Considerations and limitations
Reference types
BinaryBundle does not know how to instantiate types, and can therefore not create reference types. In practice this means reference types can only exist on the top level where they can be instantiated in the constructor you write.
Lists and Arrays are excepted from this, who have been manually made to support being nested inside other things for convenience. However this will allocate new objects and create garbage.
Properties
BinaryBundle will serialize properties only if they are auto properties. If the property has a get or set implementation it will not be serialized.
[BinaryBundle]
partial class MyClass {
// This property will be serialized
public int AutoProperty { get; set; }
// This property will also be serialized
public int AutoPrivateProperty { get; private set; }
// This field will be serialized
private int backedProperty;
// This property is not serialized, since it's data already exists in backedProperty
public int BackedProperty { get => backedProperty; set => backedProperty = value; }
}
Strings
By default strings are written in a null terminated UTF8 format. You can change this by overriding the BundleDefaultWriter/Reader. In the default implementation null strings are not supported, and will be interpreted as an empty string.
Enums
Enums are serialized using their underlying C# type.
// Serialized with an int
enum MyEnum {
Field0,
Field1
}
// Serialized with a byte
enum MyByteEnum : byte {
Field0,
Field1
}
Product | Versions 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 is compatible. 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 is compatible. 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 | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- 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 |
---|---|---|
2.2.4 | 65 | 6/6/2024 |
2.2.3 | 60 | 6/6/2024 |
2.2.2 | 47 | 6/6/2024 |
2.2.1 | 49 | 6/6/2024 |
2.2.0 | 93 | 6/4/2024 |
2.1.0 | 84 | 6/4/2024 |
2.0.0 | 189 | 12/17/2023 |
1.2.0 | 132 | 10/5/2023 |
1.1.5 | 147 | 7/2/2023 |
1.1.4 | 140 | 6/6/2023 |
1.1.3 | 379 | 10/9/2022 |
1.1.2 | 379 | 10/9/2022 |
1.1.1 | 383 | 3/21/2022 |
1.1.0 | 379 | 3/21/2022 |
1.0.0 | 411 | 3/14/2022 |