{"id":13629399,"url":"https://github.com/Cysharp/MemoryPack","last_synced_at":"2025-04-17T09:33:32.814Z","repository":{"id":60286818,"uuid":"532163204","full_name":"Cysharp/MemoryPack","owner":"Cysharp","description":"Zero encoding extreme performance binary serializer for C# and Unity.","archived":false,"fork":false,"pushed_at":"2024-09-24T12:21:58.000Z","size":3101,"stargazers_count":3303,"open_issues_count":34,"forks_count":193,"subscribers_count":45,"default_branch":"main","last_synced_at":"2024-10-29T15:06:40.804Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cysharp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["neuecc"]}},"created_at":"2022-09-03T05:19:15.000Z","updated_at":"2024-10-29T14:07:47.000Z","dependencies_parsed_at":"2022-09-27T16:50:28.624Z","dependency_job_id":"a7994a63-116d-45bf-adc8-a5f16c721af5","html_url":"https://github.com/Cysharp/MemoryPack","commit_stats":{"total_commits":377,"total_committers":20,"mean_commits":18.85,"dds":0.2625994694960212,"last_synced_commit":"6050a49d413355a9314e916d27a8bfea8ae7d3ba"},"previous_names":[],"tags_count":69,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FMemoryPack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FMemoryPack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FMemoryPack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FMemoryPack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cysharp","download_url":"https://codeload.github.com/Cysharp/MemoryPack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222530509,"owners_count":16998475,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T22:01:09.423Z","updated_at":"2024-11-08T20:30:49.936Z","avatar_url":"https://github.com/Cysharp.png","language":"C#","funding_links":["https://github.com/sponsors/neuecc"],"categories":["Content","Uncategorized","C# #"],"sub_categories":["34. [MemoryPack](https://ignatandrei.github.io/RSCG_Examples/v2/docs/MemoryPack) , in the [EnhancementClass](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementclass) category","Uncategorized"],"readme":"# MemoryPack\n\n[![NuGet](https://img.shields.io/nuget/v/MemoryPack.svg)](https://www.nuget.org/packages/MemoryPack)\n[![GitHub Actions](https://github.com/Cysharp/MemoryPack/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/MemoryPack/actions)\n[![Releases](https://img.shields.io/github/release/Cysharp/MemoryPack.svg)](https://github.com/Cysharp/MemoryPack/releases)\n\nZero encoding extreme performance binary serializer for C# and Unity.\n\n![image](https://user-images.githubusercontent.com/46207/200979655-63ed38ae-dad2-4ca0-bbb7-9e0aa98914af.png)\n\n\u003e Compared with [System.Text.Json](https://learn.microsoft.com/ja-jp/dotnet/api/system.text.json), [protobuf-net](https://github.com/protobuf-net/protobuf-net), [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp), [Orleans.Serialization](https://github.com/dotnet/orleans/). Measured by .NET 7 / Ryzen 9 5950X machine. These serializers have `IBufferWriter\u003cbyte\u003e` method, serialized using `ArrayBufferWriter\u003cbyte\u003e` and reused to avoid measure buffer copy. \n\nFor standard objects, MemoryPack is x10 faster and x2 ~ x5 faster than other binary serializers. For struct array, MemoryPack is even more powerful, with speeds up to x50 ~ x200 greater than other serializers.\n\nMemoryPack is my 4th serializer, previously I've created well known serializers, ~~[ZeroFormatter](https://github.com/neuecc/ZeroFormatter)~~, ~~[Utf8Json](https://github.com/neuecc/Utf8Json)~~, [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp). The reason for MemoryPack's speed is due to its C#-specific, C#-optimized binary format and a well tuned implementation based on my past experience. It is also a completely new design utilizing .NET 7 and C# 11 and the Incremental Source Generator (.NET Standard 2.1 (.NET 5, 6) and there is also Unity support).\n\nOther serializers perform many encoding operations such as VarInt encoding, tag, string, etc. MemoryPack format uses a zero-encoding design that copies as much C# memory as possible. Zero-encoding is similar to FlatBuffers, but it doesn't need a special type, MemoryPack's serialization target is POCO.\n\nOther than performance, MemoryPack has these features.\n\n* Support modern I/O APIs (`IBufferWriter\u003cbyte\u003e`, `ReadOnlySpan\u003cbyte\u003e`, `ReadOnlySequence\u003cbyte\u003e`)\n* Native AOT friendly Source Generator based code generation, no Dynamic CodeGen (IL.Emit)\n* Reflectionless non-generics APIs\n* Deserialize into existing instance\n* Polymorphism (Union) serialization\n* Limited version-tolerant (fast/default) and full version-tolerant support\n* Circular reference serialization\n* PipeWriter/Reader based streaming serialization\n* TypeScript code generation and ASP.NET Core Formatter\n* Unity (2021.3) IL2CPP Support via .NET Source Generator\n\nInstallation\n---\nThis library is distributed via NuGet. For best performance, recommend to use `.NET 7`. Minimum requirement is `.NET Standard 2.1`.\n\n\u003e PM\u003e Install-Package [MemoryPack](https://www.nuget.org/packages/MemoryPack)\n\nAnd also a code editor requires Roslyn 4.3.1 support, for example Visual Studio 2022 version 17.3, .NET SDK 6.0.401. For details, see the [Roslyn Version Support](https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-version-support) document.\n\nFor Unity, the requirements and installation process are completely different. See the [Unity](#unity) section for details.\n\nQuick Start\n---\nDefine a struct or class to be serialized and annotate it with the `[MemoryPackable]` attribute and the `partial` keyword.\n\n```csharp\nusing MemoryPack;\n\n[MemoryPackable]\npublic partial class Person\n{\n    public int Age { get; set; }\n    public string Name { get; set; }\n}\n```\n\nSerialization code is generated by the C# source generator feature which implements the `IMemoryPackable\u003cT\u003e` interface. In Visual Studio you can check a generated code by using a shortcut `Ctrl+K, R` on the class name and select `*.MemoryPackFormatter.g.cs`.\n\nCall `MemoryPackSerializer.Serialize\u003cT\u003e/Deserialize\u003cT\u003e` to serialize/deserialize an object instance.\n\n```csharp\nvar v = new Person { Age = 40, Name = \"John\" };\n\nvar bin = MemoryPackSerializer.Serialize(v);\nvar val = MemoryPackSerializer.Deserialize\u003cPerson\u003e(bin);\n```\n\n`Serialize` method supports a return type of `byte[]` as well as it can serialize to `IBufferWriter\u003cbyte\u003e` or `Stream`. `Deserialize` method supports `ReadOnlySpan\u003cbyte\u003e`, `ReadOnlySequence\u003cbyte\u003e` and `Stream`. And there are alse non-generics versions.\n\nBuilt-in supported types\n---\nThese types can be serialized by default:\n\n* .NET primitives (`byte`, `int`, `bool`, `char`, `double`, etc.)\n* Unmanaged types (Any `enum`, Any user-defined `struct` which doesn't contain reference types)\n* `string`, `decimal`, `Half`, `Int128`, `UInt128`, `Guid`, `Rune`, `BigInteger`\n* `TimeSpan`,  `DateTime`, `DateTimeOffset`, `TimeOnly`, `DateOnly`, `TimeZoneInfo`\n* `Complex`, `Plane`, `Quaternion` `Matrix3x2`, `Matrix4x4`, `Vector2`, `Vector3`, `Vector4`\n* `Uri`, `Version`, `StringBuilder`, `Type`, `BitArray`, `CultureInfo`\n* `T[]`, `T[,]`, `T[,,]`, `T[,,,]`, `Memory\u003c\u003e`, `ReadOnlyMemory\u003c\u003e`, `ArraySegment\u003c\u003e`, `ReadOnlySequence\u003c\u003e`\n* `Nullable\u003c\u003e`, `Lazy\u003c\u003e`, `KeyValuePair\u003c,\u003e`, `Tuple\u003c,...\u003e`, `ValueTuple\u003c,...\u003e`\n* `List\u003c\u003e`, `LinkedList\u003c\u003e`, `Queue\u003c\u003e`, `Stack\u003c\u003e`, `HashSet\u003c\u003e`, `SortedSet\u003c\u003e`, `PriorityQueue\u003c,\u003e`\n* `Dictionary\u003c,\u003e`, `SortedList\u003c,\u003e`, `SortedDictionary\u003c,\u003e`,  `ReadOnlyDictionary\u003c,\u003e` \n* `Collection\u003c\u003e`, `ReadOnlyCollection\u003c\u003e`, `ObservableCollection\u003c\u003e`, `ReadOnlyObservableCollection\u003c\u003e`\n* `IEnumerable\u003c\u003e`, `ICollection\u003c\u003e`, `IList\u003c\u003e`, `IReadOnlyCollection\u003c\u003e`, `IReadOnlyList\u003c\u003e`, `ISet\u003c\u003e`\n* `IDictionary\u003c,\u003e`, `IReadOnlyDictionary\u003c,\u003e`, `ILookup\u003c,\u003e`, `IGrouping\u003c,\u003e`,\n* `ConcurrentBag\u003c\u003e`, `ConcurrentQueue\u003c\u003e`, `ConcurrentStack\u003c\u003e`, `ConcurrentDictionary\u003c,\u003e`, `BlockingCollection\u003c\u003e`\n* Immutable collections (`ImmutableList\u003c\u003e`, etc.) and interfaces (`IImmutableList\u003c\u003e`, etc.)\n\nDefine `[MemoryPackable]` `class` / `struct` / `record` / `record struct`\n---\n`[MemoryPackable]` can annotate to any `class`, `struct`, `record`, `record struct` and `interface`. If a type is `struct` or `record struct` which contains no reference types ([C# Unmanaged types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types)) any additional annotation (ignore, include, constructor, callbacks) is not used, that serialize/deserialize directly from the memory.\n\nOtherwise, by default, `[MemoryPackable]` serializes public instance properties or fields. You can use `[MemoryPackIgnore]` to remove serialization target, `[MemoryPackInclude]` promotes a private member to serialization target.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    // these types are serialized by default\n    public int PublicField;\n    public readonly int PublicReadOnlyField;\n    public int PublicProperty { get; set; }\n    public int PrivateSetPublicProperty { get; private set; }\n    public int ReadOnlyPublicProperty { get; }\n    public int InitProperty { get; init; }\n    public required int RequiredInitProperty { get; init; }\n\n    // these types are not serialized by default\n    int privateProperty { get; set; }\n    int privateField;\n    readonly int privateReadOnlyField;\n\n    // use [MemoryPackIgnore] to remove target of a public member\n    [MemoryPackIgnore]\n    public int PublicProperty2 =\u003e PublicProperty + PublicField;\n\n    // use [MemoryPackInclude] to promote a private member to serialization target\n    [MemoryPackInclude]\n    int privateField2;\n    [MemoryPackInclude]\n    int privateProperty2 { get; set; }\n}\n```\n\n`MemoryPack`'s code generator adds information about what members are serialized to the `\u003cremarks /\u003e` section. This can be viewed by hovering over the type with Intellisense.\n\n![image](https://user-images.githubusercontent.com/46207/192393984-9af01fcb-872e-46fb-b08f-4783e8cef4ae.png)\n\nAll members must be memorypack-serializable, if not the code generator will emit an error.\n\n![image](https://user-images.githubusercontent.com/46207/192413557-8a47d668-5339-46c5-a3da-a77841666f81.png)\n\nMemoryPack has 35 diagnostics rules (`MEMPACK001` to `MEMPACK035`) to be defined comfortably.\n\nIf target type is defined MemoryPack serialization externally and registered, use `[MemoryPackAllowSerialize]` to silent diagnostics.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample2\n{\n    [MemoryPackAllowSerialize]\n    public NotSerializableType? NotSerializableProperty { get; set; }\n}\n```\n\nMember order is **important**, MemoryPack does not serialize the member-name or other information, instead serializing fields in the order they are declared. If a type is inherited, serialization is performed in the order of parent → child. The order of members can not change for the deserialization. For the schema evolution, see the [Version tolerant](#version-tolerant) section.\n\nThe default order is sequential, but you can choose the explicit layout with `[MemoryPackable(SerializeLayout.Explicit)]` and `[MemoryPackOrder()]`.\n\n```csharp\n// serialize Prop0 -\u003e Prop1\n[MemoryPackable(SerializeLayout.Explicit)]\npublic partial class SampleExplicitOrder\n{\n    [MemoryPackOrder(1)]\n    public int Prop1 { get; set; }\n    [MemoryPackOrder(0)]\n    public int Prop0 { get; set; }\n}\n```\n\n### Constructor selection\n\nMemoryPack supports both parameterized and parameterless constructors. The selection of the constructor follows these rules. (Applies to classes and structs).\n\n* If there is `[MemoryPackConstructor]`, use it.\n* If there is no explicit constructor (including private), use a parameterless one.\n* If there is one parameterless/parameterized constructor (including private), use it.\n* If there are multiple constructors, then the `[MemoryPackConstructor]` attribute must be applied to the desired constructor (the generator will not automatically choose one), otherwise the generator will emit an error.\n* If using a parameterized constructor, all parameter names must match corresponding member names (case-insensitive).\n\n```csharp\n[MemoryPackable]\npublic partial class Person\n{\n    public readonly int Age;\n    public readonly string Name;\n\n    // You can use a parameterized constructor - parameter names must match corresponding members name (case-insensitive)\n    public Person(int age, string name)\n    {\n        this.Age = age;\n        this.Name = name;\n    }\n}\n\n// also supports record primary constructor\n[MemoryPackable]\npublic partial record Person2(int Age, string Name);\n\npublic partial class Person3\n{\n    public int Age { get; set; }\n    public string Name { get; set; }\n\n    public Person3()\n    {\n    }\n\n    // If there are multiple constructors, then [MemoryPackConstructor] should be used\n    [MemoryPackConstructor]\n    public Person3(int age, string name)\n    {\n        this.Age = age;\n        this.Name = name;\n    }\n}\n```\n\n### Serialization callbacks\n\nWhen serializing/deserializing, MemoryPack can invoke a before/after event using the `[MemoryPackOnSerializing]`, `[MemoryPackOnSerialized]`, `[MemoryPackOnDeserializing]`, `[MemoryPackOnDeserialized]` attributes. It can annotate both static and instance (non-static) methods, and public and private methods. \n\n```csharp\n[MemoryPackable]\npublic partial class MethodCallSample\n{\n    // method call order is static -\u003e instance\n    [MemoryPackOnSerializing]\n    public static void OnSerializing1()\n    {\n        Console.WriteLine(nameof(OnSerializing1));\n    }\n\n    // also allows private method\n    [MemoryPackOnSerializing]\n    void OnSerializing2()\n    {\n        Console.WriteLine(nameof(OnSerializing2));\n    }\n\n    // serializing -\u003e /* serialize */ -\u003e serialized\n    [MemoryPackOnSerialized]\n    static void OnSerialized1()\n    {\n        Console.WriteLine(nameof(OnSerialized1));\n    }\n\n    [MemoryPackOnSerialized]\n    public void OnSerialized2()\n    {\n        Console.WriteLine(nameof(OnSerialized2));\n    }\n\n    [MemoryPackOnDeserializing]\n    public static void OnDeserializing1()\n    {\n        Console.WriteLine(nameof(OnDeserializing1));\n    }\n\n    // Note: instance method with MemoryPackOnDeserializing, that not called if instance is not passed by `ref`\n    [MemoryPackOnDeserializing]\n    public void OnDeserializing2()\n    {\n        Console.WriteLine(nameof(OnDeserializing2));\n    }\n\n    [MemoryPackOnDeserialized]\n    public static void OnDeserialized1()\n    {\n        Console.WriteLine(nameof(OnDeserialized1));\n    }\n\n    [MemoryPackOnDeserialized]\n    public void OnDeserialized2()\n    {\n        Console.WriteLine(nameof(OnDeserialized2));\n    }\n}\n```\n\nCallbacks allows parameterless method and `ref reader/writer, ref T value` method. For example, ref callbacks can write/read custom header before serialization process.\n\n```csharp\n[MemoryPackable]\npublic partial class EmitIdData\n{\n    public int MyProperty { get; set; }\n\n    [MemoryPackOnSerializing]\n    static void WriteId\u003cTBufferWriter\u003e(ref MemoryPackWriter\u003cTBufferWriter\u003e writer, ref EmitIdData? value)\n        where TBufferWriter : IBufferWriter\u003cbyte\u003e // .NET Standard 2.1, use where TBufferWriter : class, IBufferWriter\u003cbyte\u003e\n    {\n        writer.WriteUnmanaged(Guid.NewGuid()); // emit GUID in header.\n    }\n\n    [MemoryPackOnDeserializing]\n    static void ReadId(ref MemoryPackReader reader, ref EmitIdData? value)\n    {\n        // read custom header before deserialize\n        var guid = reader.ReadUnmanaged\u003cGuid\u003e();\n        Console.WriteLine(guid);\n    }\n}\n```\n\nIf set a value to `ref value`, you can change the value used for serialization/deserialization. For example, instantiate from ServiceProvider.\n\n```csharp\n// before using this formatter, set ServiceProvider\n// var options = MemoryPackSerializerOptions.Default with { ServiceProvider = provider };\n// MemoryPackSerializer.Deserialize(value, options);\n\n[MemoryPackable]\npublic partial class InstantiateFromServiceProvider\n{\n    static IServiceProvider serviceProvider = default!;\n\n    public int MyProperty { get; private set; }\n\n    [MemoryPackOnDeserializing]\n    static void OnDeserializing(ref MemoryPackReader reader, ref InstantiateFromServiceProvider value)\n    {\n        if (value != null) return;\n        value = reader.Options.ServiceProvider!.GetRequiredService\u003cInstantiateFromServiceProvider\u003e();\n    }\n}\n```\n\nDefine custom collection\n---\nBy default, annotated `[MemoryPackObject]` type try to serialize its members. However, if a type is a collection (`ICollection\u003c\u003e`, `ISet\u003c\u003e`, `IDictionary\u003c,\u003e`), use `GenerateType.Collection` to serialize it correctly.\n\n```csharp\n[MemoryPackable(GenerateType.Collection)]\npublic partial class MyList\u003cT\u003e : List\u003cT\u003e\n{\n}\n\n[MemoryPackable(GenerateType.Collection)]\npublic partial class MyStringDictionary\u003cTValue\u003e : Dictionary\u003cstring, TValue\u003e\n{\n\n}\n```\n\nStatic constructor\n---\nMemoryPackable class can not define static constructor because generated partial class uses it. Instead, you can define a `static partial void StaticConstructor()` to do the same thing.\n\n```csharp\n[MemoryPackable]\npublic partial class CctorSample\n{\n    static partial void StaticConstructor()\n    {\n    }\n}\n```\n\nPolymorphism (Union)\n---\nMemoryPack supports serializing interface and abstract class objects for polymorphism serialization. In MemoryPack this feature is called Union. Only interfaces and abstracts classes are allowed to be annotated with `[MemoryPackUnion]` attributes. Unique union tags are required.\n\n```csharp\n// Annotate [MemoryPackable] and inheritance types with [MemoryPackUnion]\n// Union also supports abstract class\n[MemoryPackable]\n[MemoryPackUnion(0, typeof(FooClass))]\n[MemoryPackUnion(1, typeof(BarClass))]\npublic partial interface IUnionSample\n{\n}\n\n[MemoryPackable]\npublic partial class FooClass : IUnionSample\n{\n    public int XYZ { get; set; }\n}\n\n[MemoryPackable]\npublic partial class BarClass : IUnionSample\n{\n    public string? OPQ { get; set; }\n}\n// ---\n\nIUnionSample data = new FooClass() { XYZ = 999 };\n\n// Serialize as interface type.\nvar bin = MemoryPackSerializer.Serialize(data);\n\n// Deserialize as interface type.\nvar reData = MemoryPackSerializer.Deserialize\u003cIUnionSample\u003e(bin);\n\nswitch (reData)\n{\n    case FooClass x:\n        Console.WriteLine(x.XYZ);\n        break;\n    case BarClass x:\n        Console.WriteLine(x.OPQ);\n        break;\n    default:\n        break;\n}\n```\n\n`tag` allows `0` ~ `65535`, it is especially efficient for less than `250`.\n\nIf an interface and derived types are in different assemblies, you can use `MemoryPackUnionFormatterAttribute` instead. Formatters are generated the way that they are automatically registered via `ModuleInitializer` in C# 9.0 and above.\n\n\u003e Note that `ModuleInitializer` is not supported in Unity, so the formatter must be manually registered. To register your union formatter invoke `{name of your union formatter}Initializer.RegisterFormatter()` manually in Startup. For example `UnionSampleFormatterInitializer.RegisterFormatter()`.\n\n```csharp\n// AssemblyA\n[MemoryPackable(GenerateType.NoGenerate)]\npublic partial interface IUnionSample\n{\n}\n\n// AssemblyB define definition outside of target type\n[MemoryPackUnionFormatter(typeof(IUnionSample))]\n[MemoryPackUnion(0, typeof(FooClass))]\n[MemoryPackUnion(1, typeof(BarClass))]\npublic partial class UnionSampleFormatter\n{\n}\n```\n\nUnion can be assembled in code via `DynamicUnionFormatter\u003cT\u003e`.\n\n```csharp\n// (ushort, Type)[]\nvar formatter = new DynamicUnionFormatter\u003cIFooBarBaz\u003e(\n    (0, typeof(Foo)),\n    (1, typeof(Bar)),\n    (2, typeof(Baz))\n);\n\nMemoryPackFormatterProvider.Register(formatter);\n```\n\nSerialize API\n---\n`Serialize` has three overloads.\n\n```csharp\n// Non generic API also available, these version is first argument is Type and value is object?\nbyte[] Serialize\u003cT\u003e(in T? value, MemoryPackSerializerOptions? options = default)\nvoid Serialize\u003cT, TBufferWriter\u003e(in TBufferWriter bufferWriter, in T? value, MemoryPackSerializerOptions? options = default)\nasync ValueTask SerializeAsync\u003cT\u003e(Stream stream, T? value, MemoryPackSerializerOptions? options = default, CancellationToken cancellationToken = default)\n```\n\nFor performance, the recommended API uses `BufferWriter`. This serializes directly into the buffer. It can be applied to `PipeWriter` in `System.IO.Pipelines`, `BodyWriter` in ASP .NET Core, etc.\n\nIf a `byte[]` is required (e.g. `RedisValue` in [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis)), the return `byte[]` API is simple and almost as fast.\n\nNote that `SerializeAsync` for `Stream` is asynchronous only for Flush; it serializes everything once into MemoryPack's internal pool buffer and then writes using `WriteAsync`. Therefore, the `BufferWriter` overload, which separates and controls buffer and flush, is better.\n\nIf you want to do a complete streaming write, see the [Streaming Serialization](#streaming-serialization) section.\n\n### MemoryPackSerializerOptions\n\n`MemoryPackSerializerOptions` configures whether strings are serialized as UTF16 or UTF8. This can be configured by passing `MemoryPackSerializerOptions.Utf8` for UTF8 encoding, `MemoryPackSerializerOptions.Utf16` for UTF16 encoding or `MemoryPackSerializerOptions.Default` which defaults to UTF8. Passing null or using the default parameter results in UTF8 encoding.\n\nSince C#'s internal string representation is UTF16, UTF16 performs better. However, the payload tends to be larger; in UTF8, an ASCII string is one byte, while in UTF16 it is two bytes. Because the difference in size of this payload is so large, UTF8 is set by default.\n\nIf the data is non-ASCII (e.g. Japanese, which can be more than 3 bytes, and UTF8 is larger), or if you have to compress it separately, UTF16 may give better results.\n\nWhile UTF8 or UTF16 can be selected during serialization, it is not necessary to specify it during deserialization. It will be automatically detected and deserialized normally.\n\nAdditionaly you can get/set `IServiceProvider? ServiceProvider { get; init; }` from options. It is useful to get DI object(such as `ILogger\u003cT\u003e`) from serialization process(`MemoryPackReader/MemoryPackWriter` has .Options property).\n\nDeserialize API\n---\n`Deserialize` has `ReadOnlySpan\u003cbyte\u003e` and `ReadOnlySequence\u003cbyte\u003e`, `Stream` overload and `ref` support.\n\n```csharp\nT? Deserialize\u003cT\u003e(ReadOnlySpan\u003cbyte\u003e buffer)\nint Deserialize\u003cT\u003e(ReadOnlySpan\u003cbyte\u003e buffer, ref T? value)\nT? Deserialize\u003cT\u003e(in ReadOnlySequence\u003cbyte\u003e buffer)\nint Deserialize\u003cT\u003e(in ReadOnlySequence\u003cbyte\u003e buffer, ref T? value)\nasync ValueTask\u003cT?\u003e DeserializeAsync\u003cT\u003e(Stream stream)\n```\n\n`ref` overload overwrites an existing instance, for details see the [Overwrite](#overwrite) section.\n\n`DeserializeAsync(Stream)` is not a complete streaming read operation, first it reads into MemoryPack's internal pool up to the end-of-stream, then it deserializes.\n\nIf you want to do a complete streaming read operation, see the [Streaming Serialization](#streaming-serialization) section.\n\nOverwrite\n---\nTo reduce allocations, MemoryPack supports deserializing to an existing instance, overwriting it. This can be used with the `Deserialize(ref T? value)` overload.\n\n```csharp\nvar person = new Person();\nvar bin = MemoryPackSerializer.Serialize(person);\n\n// overwrite data to existing instance.\nMemoryPackSerializer.Deserialize(bin, ref person);\n```\n\nMemoryPack will attempt to overwrite as much as possible, but if the following conditions do not match, it will create a new instance (as in normal deserialization).\n\n* ref value (includes members in object graph) is null, set new instance\n* only allows parameterless constructor, if parameterized constructor is used, create new instance\n* if value is `T[]`, reuse only if the length is the same, otherwise create new instance\n* if value is collection that has `.Clear()` method(`List\u003c\u003e`, `Stack\u003c\u003e`, `Queue\u003c\u003e`, `LinkedList\u003c\u003e`, `HashSet\u003c\u003e`, `PriorityQueue\u003c,\u003e`, `ObservableCollection`, `Collection`, `ConcurrentQueue\u003c\u003e`, `ConcurrentStack\u003c\u003e`, `ConcurrentBag\u003c\u003e`, `Dictionary\u003c,\u003e`, `SortedDictionary\u003c,\u003e`, `SortedList\u003c,\u003e`, `ConcurrentDictionary\u003c,\u003e`) call Clear() and reuse it, otherwise create new instance\n\nVersion tolerant\n---\nIn default(`GenerateType.Object`), MemoryPack supports limited schema evolution.\n\n* unmanaged struct can't be changed anymore\n* members can be added, but can not be deleted\n* can change member name\n* can't change member order\n* can't change member type\n\n```csharp\n[MemoryPackable]\npublic partial class VersionCheck\n{\n    public int Prop1 { get; set; }\n    public long Prop2 { get; set; }\n}\n\n// Add is OK.\n[MemoryPackable]\npublic partial class VersionCheck\n{\n    public int Prop1 { get; set; }\n    public long Prop2 { get; set; }\n    public int? AddedProp { get; set; }\n}\n\n// Remove is NG.\n[MemoryPackable]\npublic partial class VersionCheck\n{\n    // public int Prop1 { get; set; }\n    public long Prop2 { get; set; }\n}\n\n// Change order is NG.\n[MemoryPackable]\npublic partial class VersionCheck\n{\n    public long Prop2 { get; set; }\n    public int Prop1 { get; set; }\n}\n```\n\nIn use-case, store old data (to file, to redis, etc...) and read to new schema is always ok. In the RPC scenario, schema exists both on the client and the server side, the client must be updated before the server. An updated client has no problem connecting to the old server but an old client can not connect to a new server.\n\n\nBy default, when the old data read to new schema, any members not on the data side are initialized with the `default` literal.\nIf you want to avoid this and use initial values of field/properties, you can use `[SuppressDefaultInitialization]`.\n\n```cs\n[MemoryPackable]\npublic partial class DefaultValue\n{\n    public string Prop1 { get; set; }\n\n    [SuppressDefaultInitialization]\n    public int Prop2 { get; set; } = 111; // \u003c if old data is missing, set `111`.\n    \n    public int Prop3 { get; set; } = 222; // \u003c if old data is missing, set `default`.\n}\n```\n\n `[SuppressDefaultInitialization]` has following limitation:\n- Cannot be used with readonly, init-only, and required modifier.\n\nThe next [Serialization info](#serialization-info) section shows how to check for schema changes, e.g., by CI, to prevent accidents.\n\nWhen using `GenerateType.VersionTolerant`, it supports full version-tolerant.\n\n* unmanaged struct can't change any more\n* all members must add `[MemoryPackOrder]` explicitly(except annotate `SerializeLayout.Sequential`)\n* members can add, can delete but not reuse order (can use missing order)\n* can change member name\n* can't change member order\n* can't change member type\n\n```csharp\n// Ok to serialize/deserialize both \n// VersionTolerantObject1 -\u003e VersionTolerantObject2 and \n// VersionTolerantObject2 -\u003e VersionTolerantObject1\n\n[MemoryPackable(GenerateType.VersionTolerant)]\npublic partial class VersionTolerantObject1\n{\n    [MemoryPackOrder(0)]\n    public int MyProperty0 { get; set; } = default;\n\n    [MemoryPackOrder(1)]\n    public long MyProperty1 { get; set; } = default;\n\n    [MemoryPackOrder(2)]\n    public short MyProperty2 { get; set; } = default;\n}\n\n[MemoryPackable(GenerateType.VersionTolerant)]\npublic partial class VersionTolerantObject2\n{\n    [MemoryPackOrder(0)]\n    public int MyProperty0 { get; set; } = default;\n\n    // deleted\n    //[MemoryPackOrder(1)]\n    //public long MyProperty1 { get; set; } = default;\n\n    [MemoryPackOrder(2)]\n    public short MyProperty2 { get; set; } = default;\n\n    // added\n    [MemoryPackOrder(3)]\n    public short MyProperty3 { get; set; } = default;\n}\n```\n\n```csharp\n// If set SerializeLayout.Sequential explicitly, allows automatically order.\n// But it can not remove any member for versoin-tolerant.\n[MemoryPackable(GenerateType.VersionTolerant, SerializeLayout.Sequential)]\npublic partial class VersionTolerantObject3\n{\n    public int MyProperty0 { get; set; } = default;\n    public long MyProperty1 { get; set; } = default;\n    public short MyProperty2 { get; set; } = default;\n}\n```\n\n`GenerateType.VersionTolerant` is slower than `GenerateType.Object` in serializing. Also, the payload size will be slightly larger.\n\nSerialization info\n----\nYou can check IntelliSense in type what members are serialized. There is an option to write that information to a file at compile time. Set `MemoryPackGenerator_SerializationInfoOutputDirectory` as follows.\n\n```xml\n\u003c!-- output memorypack serialization info to directory --\u003e\n\u003cItemGroup\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_SerializationInfoOutputDirectory\" /\u003e\n\u003c/ItemGroup\u003e\n\u003cPropertyGroup\u003e\n    \u003cMemoryPackGenerator_SerializationInfoOutputDirectory\u003e$(MSBuildProjectDirectory)\\MemoryPackLogs\u003c/MemoryPackGenerator_SerializationInfoOutputDirectory\u003e\n\u003c/PropertyGroup\u003e\n```\n\nThe following info is written to the file.\n\n![image](https://user-images.githubusercontent.com/46207/192460684-c2fd8bcb-375e-41dd-9960-58205d5b1b7a.png)\n\nIf the type is unmanaged, showed `unmanaged` before type name.\n\n```txt\nunmanaged FooStruct\n---\nint x\nint y\n```\n\nBy checking the differences in this file, dangerous schema changes can be prevented. For example, you may want to use CI to detect the following rules\n\n* modify unmanaged type\n* member order change\n* member deletion\n\nCircular Reference\n---\nMemoryPack also supports circular reference. This allows the tree objects to be serialized as is.\n\n```csharp\n// to enable circular-reference, use GenerateType.CircularReference\n[MemoryPackable(GenerateType.CircularReference)]\npublic partial class Node\n{\n    [MemoryPackOrder(0)]\n    public Node? Parent { get; set; }\n    [MemoryPackOrder(1)]\n    public Node[]? Children { get; set; }\n}\n```\n\n For example, [System.Text.Json preserve-references](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/preserve-references) code will become like here.\n\n```csharp\n// https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/preserve-references?pivots=dotnet-7-0\nEmployee tyler = new()\n{\n    Name = \"Tyler Stein\"\n};\n\nEmployee adrian = new()\n{\n    Name = \"Adrian King\"\n};\n\ntyler.DirectReports = new List\u003cEmployee\u003e { adrian };\nadrian.Manager = tyler;\n\nvar bin = MemoryPackSerializer.Serialize(tyler);\nEmployee? tylerDeserialized = MemoryPackSerializer.Deserialize\u003cEmployee\u003e(bin);\n\nConsole.WriteLine(tylerDeserialized?.DirectReports?[0].Manager == tylerDeserialized); // true\n\n[MemoryPackable(GenerateType.CircularReference)]\npublic partial class Employee\n{\n    [MemoryPackOrder(0)]\n    public string? Name { get; set; }\n    [MemoryPackOrder(1)]\n    public Employee? Manager { get; set; }\n    [MemoryPackOrder(2)]\n    public List\u003cEmployee\u003e? DirectReports { get; set; }\n}\n```\n\n`GenerateType.CircularReference` has the same characteristics as version-tolerant. However, as an additional constraint, only parameterless constructors are allowed. Also, object reference tracking is only done for objects marked with `GenerateType.CircularReference`. If you want to track any other object, wrap it.\n\nCustomFormatter\n---\nIf implements `MemoryPackCustomFormatterAttribute\u003cT\u003e` or `MemoryPackCustomFormatterAttribute\u003cTFormatter, T\u003e`(more performant, but complex), you can configure to use custom formatter to MemoryPackObject's member.\n\n```csharp\n[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]\npublic abstract class MemoryPackCustomFormatterAttribute\u003cT\u003e : Attribute\n{\n    public abstract IMemoryPackFormatter\u003cT\u003e GetFormatter();\n}\n```\n\nMemoryPack provides the following formatting attributes: `Utf8StringFormatterAttribute`, `Utf16StringFormatterAttribute`, `InternStringFormatterAttribute`, `OrdinalIgnoreCaseStringDictionaryFormatterAttribute\u003cTValue\u003e`, `BitPackFormatterAttribute`, `BrotliFormatter`, `BrotliStringFormatter`, `BrotliFormatter\u003cT\u003e`, `MemoryPoolFormatter\u003cT\u003e`, `ReadOnlyMemoryPoolFormatter\u003cT\u003e`.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    // serialize this member as UTF16 String, it is performant than UTF8 but in ASCII, size is larger(but non ASCII, sometimes smaller).\n    [Utf16StringFormatter]\n    public string? Text { get; set; }\n\n    // In deserialize, Dictionary is initialized with StringComparer.OrdinalIgnoreCase.\n    [OrdinalIgnoreCaseStringDictionaryFormatter\u003cint\u003e]\n    public Dictionary\u003cstring, int\u003e? Ids { get; set; }\n    \n    // In deserialize time, all string is interned(see: String.Intern). If similar values come repeatedly, it saves memory.\n    [InternStringFormatter]\n    public string? Flag { get; set; }\n}\n```\n\nIn order to configure a set/dictionary's equality comparer, all built-in formatters have a comparer constructor overload. You can easily create custom equality-comparer formatters.\n\n```csharp\npublic sealed class OrdinalIgnoreCaseStringDictionaryFormatter\u003cTValue\u003e : MemoryPackCustomFormatterAttribute\u003cDictionary\u003cstring, TValue?\u003e\u003e\n{\n    static readonly DictionaryFormatter\u003cstring, TValue?\u003e formatter = new DictionaryFormatter\u003cstring, TValue?\u003e(StringComparer.OrdinalIgnoreCase);\n\n    public override IMemoryPackFormatter\u003cDictionary\u003cstring, TValue?\u003e\u003e GetFormatter()\n    {\n        return formatter;\n    }\n}\n```\n\n`BitPackFormatter` compresses `bool[]` types only. `bool[]` is normally serialized as 1 byte per boolean value, however `BitPackFormatter` serializes `bool[]` like a `BitArray` storing each bool as 1 bit. Using `BitPackFormatter`, 8 bools become 1 byte where they would normally be 8 bytes, resulting in a 8x smaller size.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    public int Id { get; set; }\n\n    [BitPackFormatter]\n    public bool[]? Data { get; set; }\n}\n```\n\n`BrotliFormatter` is for `byte[]`, for example you can compress large payload by Brotli.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    public int Id { get; set; }\n\n    [BrotliFormatter]\n    public byte[]? Payload { get; set; }\n}\n```\n\n`BrotliStringFormatter` is for `string`, serialize compressed string (UTF16) by Brotli.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    public int Id { get; set; }\n\n    [BrotliStringFormatter]\n    public string? LargeText { get; set; }\n}\n```\n\n`BrotliFormatter\u003cT\u003e` is for any type, serialized data compressed by Brotli. If a type is `byte[]` or `string`, you should use `BrotliFormatter` or `BrotliStringFormatter` for performance.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    public int Id { get; set; }\n\n    [BrotliFormatter\u003cChildType\u003e]\n    public ChildType? Child { get; set; }\n}\n```\n\nDeserialize array pooling\n---\nIn order to deserialize a large array (any `T`), MemoryPack offers multiple efficient pooling methods. The most effective way is to use the [#Overwrite](#overwrite) function. In particular `List\u003cT\u003e` is always reused.\n\n```csharp\n[MemoryPackable]\npublic partial class ListBytesSample\n{\n    public int Id { get; set; }\n    public List\u003cbyte\u003e Payload { get; set; }\n}\n\n// ----\n\n// List\u003cbyte\u003e is reused, no allocation in deserialize.\nMemoryPackSerializer.Deserialize\u003cListBytesSample\u003e(bin, ref reuseObject);\n\n// for efficient operation, you can get Span\u003cT\u003e by CollectionsMarshal\nvar span = CollectionsMarshal.AsSpan(value.Payload);\n```\n\nA convenient way is to deserialize to an ArrayPool at deserialization time. MemoryPack provides `MemoryPoolFormatter\u003cT\u003e` and `ReadOnlyMemoryPoolFormatter\u003cT\u003e`.\n\n```csharp\n[MemoryPackable]\npublic partial class PoolModelSample : IDisposable\n{\n    public int Id { get; }\n\n    [MemoryPoolFormatter\u003cbyte\u003e]\n    public Memory\u003cbyte\u003e Payload { get; private set; }\n\n    public PoolModelSample(int id, Memory\u003cbyte\u003e payload)\n    {\n        Id = id;\n        Payload = payload;\n    }\n\n    // You must write the return code yourself, here is snippet.\n\n    bool usePool;\n\n    [MemoryPackOnDeserialized]\n    void OnDeserialized()\n    {\n        usePool = true;\n    }\n\n    public void Dispose()\n    {\n        if (!usePool) return;\n\n        Return(Payload); Payload = default;\n    }\n\n    static void Return\u003cT\u003e(Memory\u003cT\u003e memory) =\u003e Return((ReadOnlyMemory\u003cT\u003e)memory);\n\n    static void Return\u003cT\u003e(ReadOnlyMemory\u003cT\u003e memory)\n    {\n        if (MemoryMarshal.TryGetArray(memory, out var segment) \u0026\u0026 segment.Array is { Length: \u003e 0 })\n        {\n            ArrayPool\u003cT\u003e.Shared.Return(segment.Array, clearArray: RuntimeHelpers.IsReferenceOrContainsReferences\u003cT\u003e());\n        }\n    }\n}\n\n// ---\n\nusing(var value = MemoryPackSerializer.Deserialize\u003cPoolModelSample\u003e(bin))\n{\n    // do anything...\n}   // return to ArrayPool\n```\n\nPerformance\n---\nSee the my blog post [How to make the fastest .NET Serializer with .NET 7 / C# 11, case of MemoryPack](https://medium.com/@neuecc/how-to-make-the-fastest-net-serializer-with-net-7-c-11-case-of-memorypack-ad28c0366516)\n\nPayload size and compression\n---\nPayload size depends on the target value; unlike JSON, there are no keys and it is a binary format, so the payload size is likely to be smaller than JSON.\n\nFor those with varint encoding, such as MessagePack and Protobuf, MemoryPack tends to be larger if ints are used a lot (in MemoryPack, ints are always 4 bytes due to fixed size encoding, while MessagePack is 1~5 bytes).\n\nfloat and double are 4 bytes and 8 bytes in MemoryPack, but 5 bytes and 9 bytes in MessagePack. So MemoryPack is smaller, for example, for Vector3 (float, float, float) arrays.\n\nString is UTF8 by default, which is similar to other serializers, but if the UTF16 option is chosen, it will be of a different nature.\n\nIn any case, if the payload size is large, compression should be considered. LZ4, ZStandard and Brotli are recommended.\n\n### Compression\n\nMemoryPack provides an efficient helper for [Brotli](https://github.com/google/brotli) compression via [BrotliEncoder](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.brotliencoder) and [BrotliDecoder](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.brotlidecoder). MemoryPack's `BrotliCompressor` and `BrotliDecompressor` provide compression/decompression optimized for MemoryPack's internal behavior.\n\n```csharp\nusing MemoryPack.Compression;\n\n// Compression(require using)\nusing var compressor = new BrotliCompressor();\nMemoryPackSerializer.Serialize(compressor, value);\n\n// Get compressed byte[]\nvar compressedBytes = compressor.ToArray();\n\n// Or write to other IBufferWriter\u003cbyte\u003e(for example PipeWriter)\ncompressor.CopyTo(response.BodyWriter);\n```\n\n```csharp\nusing MemoryPack.Compression;\n\n// Decompression(require using)\nusing var decompressor = new BrotliDecompressor();\n\n// Get decompressed ReadOnlySequence\u003cbyte\u003e from ReadOnlySpan\u003cbyte\u003e or ReadOnlySequence\u003cbyte\u003e\nvar decompressedBuffer = decompressor.Decompress(buffer);\n\nvar value = MemoryPackSerializer.Deserialize\u003cT\u003e(decompressedBuffer);\n```\n\nBoth `BrotliCompressor` and `BrotliDecompressor` are struct, it does not allocate memory on heap. Both store compressed or decompressed data in an internal memory pool for Serialize/Deserialize. Therefore, it is necessary to release the memory pooling, don't forget to use `using`.\n\nCompression level is very important. The default is set to quality-1 (CompressionLevel.Fastest), which is different from the .NET default (CompressionLevel.Optimal, quality-4).\n\nFastest (quality-1) will be close to the speed of [LZ4](https://github.com/lz4/lz4), but 4 is much slower. This was determined to be critical in the serializer use scenario. Be careful when using the standard `BrotliStream` (quality-4 is the default). In any case, compression/decompression speeds and sizes will result in very different results for different data. Please prepare the data to be handled by your application and test it yourself.\n\nNote that there is a several-fold speed penalty between MemoryPack's uncompressed and Brotli's added compression.\n\nBrotli is also suppored in a custom formatter. `BrotliFormatter` can compress a specific member.\n\n```csharp\n[MemoryPackable]\npublic partial class Sample\n{\n    public int Id { get; set; }\n\n    [BrotliFormatter]\n    public byte[]? Payload { get; set; }\n}\n```\n\nSerialize external types\n---\nIf you want to serialize external types, you can make a custom formatter and register it to provider, see [Formatter/Provider API](#formatterprovider-api) for details. However, creating a custom formatter is difficult. Therefore, we recommend making a wrapper type. For example, if you want to serialize an external type called `AnimationCurve`.\n\n```csharp\n// Keyframe: (float time, float inTangent, float outTangent, int tangentMode, int weightedMode, float inWeight, float outWeight)\n[MemoryPackable]\npublic readonly partial struct SerializableAnimationCurve\n{\n    [MemoryPackIgnore]\n    public readonly AnimationCurve AnimationCurve;\n\n    [MemoryPackInclude]\n    WrapMode preWrapMode =\u003e AnimationCurve.preWrapMode;\n    [MemoryPackInclude]\n    WrapMode postWrapMode =\u003e AnimationCurve.postWrapMode;\n    [MemoryPackInclude]\n    Keyframe[] keys =\u003e AnimationCurve.keys;\n\n    [MemoryPackConstructor]\n    SerializableAnimationCurve(WrapMode preWrapMode, WrapMode postWrapMode, Keyframe[] keys)\n    {\n        var curve = new AnimationCurve(keys);\n        curve.preWrapMode = preWrapMode;\n        curve.postWrapMode = postWrapMode;\n        this.AnimationCurve = curve;\n    }\n\n    public SerializableAnimationCurve(AnimationCurve animationCurve)\n    {\n        this.AnimationCurve = animationCurve;\n    }\n}\n```\n\nThe type to wrap is public, but excluded from serialization (`MemoryPackIgnore`). The properties you want to serialize are private, but included (`MemoryPackInclude`). Two patterns of constructors should also be prepared. The constructor used by the serializer should be private.\n\nAs it is, it must be wrapped every time, which is inconvenient. And also strcut wrapper can not represents null. So let's create a custom formatter.\n\n```csharp\npublic class AnimationCurveFormatter : MemoryPackFormatter\u003cAnimationCurve\u003e\n{\n    // Unity does not support scoped and TBufferWriter so change signature to `Serialize(ref MemoryPackWriter writer, ref AnimationCurve value)`\n    public override void Serialize\u003cTBufferWriter\u003e(ref MemoryPackWriter\u003cTBufferWriter\u003e writer, scoped ref AnimationCurve? value)\n    {\n        if (value == null)\n        {\n            writer.WriteNullObjectHeader();\n            return;\n        }\n\n        writer.WritePackable(new SerializableAnimationCurve(value));\n    }\n\n    public override void Deserialize(ref MemoryPackReader reader, scoped ref AnimationCurve? value)\n    {\n        if (reader.PeekIsNull())\n        {\n            reader.Advance(1); // skip null block\n            value = null;\n            return;\n        }\n        \n        var wrapped = reader.ReadPackable\u003cSerializableAnimationCurve\u003e();\n        value = wrapped.AnimationCurve;\n    }\n}\n```\n\nFinally, register the formatter in startup.\n\n```csharp\nMemoryPackFormatterProvider.Register\u003cAnimationCurve\u003e(new AnimationCurveFormatter());\n```\n\u003e Note: Unity's AnimationCurve can serializable by default so does not needs this custom formatter for AnimationCurve\n\nPackages\n---\nMemoryPack has these packages.\n\n* MemoryPack\n* MemoryPack.Core\n* MemoryPack.Generator\n* MemoryPack.Streaming\n* MemoryPack.AspNetCoreMvcFormatter\n* MemoryPack.UnityShims\n\n`MemoryPack` is the main library, it provides full support for high performance serialization and deserialization of binary objects. It depends on `MemoryPack.Core` for the core base libraries and `MemoryPack.Generator` for code generation. `MemoryPack.Streaming` adds additional extensions for [Streaming Serialization](#streaming-serialization).  `MemoryPack.AspNetCoreMvcFormatter` adds input/output formatters for ASP.NET Core. `MemoryPack.UnityShims` adds Unity shim types and formatters for share type between .NET and Unity.\n\nTypeScript and ASP.NET Core Formatter\n---\nMemoryPack supports TypeScript code generation. It generates class and serialization code from C#, In other words, you can share types with the Browser without using OpenAPI, proto, etc.\n\nCode generation is integrated with Source Generator, the following options(`MemoryPackGenerator_TypeScriptOutputDirectory`) set the output directory for TypeScript code. Runtime code is output at the same time, so no additional dependencies are required.\n\n```xml\n\u003c!-- output memorypack TypeScript code to directory --\u003e\n\u003cItemGroup\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_TypeScriptOutputDirectory\" /\u003e\n\u003c/ItemGroup\u003e\n\u003cPropertyGroup\u003e\n    \u003cMemoryPackGenerator_TypeScriptOutputDirectory\u003e$(MSBuildProjectDirectory)\\wwwroot\\js\\memorypack\u003c/MemoryPackGenerator_TypeScriptOutputDirectory\u003e\n\u003c/PropertyGroup\u003e\n```\n\nA C# MemoryPackable type must be annotated with `[GenerateTypeScript]`.\n\n```csharp\n[MemoryPackable]\n[GenerateTypeScript]\npublic partial class Person\n{\n    public required Guid Id { get; init; }\n    public required int Age { get; init; }\n    public required string FirstName { get; init; }\n    public required string LastName { get; init; }\n    public required DateTime DateOfBirth { get; init; }\n    public required Gender Gender { get; init; }\n    public required string[] Emails { get; init; }\n}\n\npublic enum Gender\n{\n    Male, Female, Other\n}\n```\n\nRuntime code and TypeScript type will be generated in the target directory.\n\n![image](https://user-images.githubusercontent.com/46207/194916544-1b6bb5ed-966b-43c3-a378-3eac297c2b40.png)\n\nThe generated code is as follows, with simple fields and static methods for serialize/serializeArray and deserialize/deserializeArray.\n\n```typescript\nimport { MemoryPackWriter } from \"./MemoryPackWriter.js\";\nimport { MemoryPackReader } from \"./MemoryPackReader.js\";\nimport { Gender } from \"./Gender.js\"; \n\nexport class Person {\n    id: string;\n    age: number;\n    firstName: string | null;\n    lastName: string | null;\n    dateOfBirth: Date;\n    gender: Gender;\n    emails: (string | null)[] | null;\n\n    constructor() {\n        // snip...\n    }\n\n    static serialize(value: Person | null): Uint8Array {\n        // snip...\n    }\n\n    static serializeCore(writer: MemoryPackWriter, value: Person | null): void {\n        // snip...\n    }\n\n    static serializeArray(value: (Person | null)[] | null): Uint8Array {\n        // snip...\n    }\n\n    static serializeArrayCore(writer: MemoryPackWriter, value: (Person | null)[] | null): void {\n        // snip...\n    }\n    static deserialize(buffer: ArrayBuffer): Person | null {\n        // snip...\n    }\n\n    static deserializeCore(reader: MemoryPackReader): Person | null {\n        // snip...\n    }\n\n    static deserializeArray(buffer: ArrayBuffer): (Person | null)[] | null {\n        // snip...\n    }\n\n    static deserializeArrayCore(reader: MemoryPackReader): (Person | null)[] | null {\n        // snip...\n    }\n}\n```\n\nYou can use this type like following.\n\n```typescript\nlet person = new Person();\nperson.id = crypto.randomUUID();\nperson.age = 30;\nperson.firstName = \"foo\";\nperson.lastName = \"bar\";\nperson.dateOfBirth = new Date(1999, 12, 31, 0, 0, 0);\nperson.gender = Gender.Other;\nperson.emails = [\"foo@bar.com\", \"zoo@bar.net\"];\n\n// serialize to Uint8Array\nlet bin = Person.serialize(person);\n\nlet blob = new Blob([bin.buffer], { type: \"application/x-memorypack\" })\n\nlet response = await fetch(\"http://localhost:5260/api\",\n    { method: \"POST\", body: blob, headers: { \"Content-Type\": \"application/x-memorypack\" } });\n\nlet buffer = await response.arrayBuffer();\n\n// deserialize from ArrayBuffer \nlet person2 = Person.deserialize(buffer);\n```\n\nThe `MemoryPack.AspNetCoreMvcFormatter` package adds `MemoryPack` input and output formatters for ASP.NET Core MVC. You can add `MemoryPackInputFormatter`, `MemoryPackOutputFormatter` to ASP.NET Core MVC with the following code.\n\n```csharp\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddRazorPages();\n\nbuilder.Services.AddControllers(options =\u003e\n{\n    options.InputFormatters.Insert(0, new MemoryPackInputFormatter());\n    // If checkContentType: true then can output multiple format(JSON/MemoryPack, etc...). default is false.\n    options.OutputFormatters.Insert(0, new MemoryPackOutputFormatter(checkContentType: false));\n});\n```\n\nIf you call from HttpClient, you can set `application/x-memorypack` to content-header.\n\n```csharp\nvar content = new ByteArrayContent(bin)\ncontent.Headers.ContentType = new MediaTypeHeaderValue(\"application/x-memorypack\");\n```\n\n### TypeScript Type Mapping\n\nThere are a few restrictions on the types that can be generated. Among the primitives, `char` and `decimal` are not supported. Also, OpenGenerics type cannot be used.\n\n|  C#  |  TypeScript  | Description |\n| ---- | ---- | ---- |\n| `bool` |  `boolean`  |\n| `byte` |  `number`  |\n| `sbyte` |  `number`  |\n| `int` |  `number` |\n| `uint` |  `number` |\n| `short` |  `number` |\n| `ushort` |  `number` |\n| `long` |  `bigint` |\n| `ulong` |  `bigint` |\n| `float` |  `number` |\n| `double` |  `number` |\n| `string` |  `string \\| null`  | \n| `Guid` |  `string`  | In TypeScript, represents as string but serialize/deserialize as 16byte binary\n| `DateTime` | `Date` | DateTimeKind will be ignored\n| `enum` | `const enum` | `long` and `ulong` underlying type is not supported\n| `T?` | `T \\| null` |\n| `T[]` | `T[] \\| null` |\n| `byte[]` | `Uint8Array \\| null` |\n| `: ICollection\u003cT\u003e` | `T[] \\| null` | Supports all `ICollection\u003cT\u003e` implemented type like `List\u003cT\u003e`\n| `: ISet\u003cT\u003e` | `Set\u003cT\u003e \\| null` | Supports all `ISet\u003cT\u003e` implemented type like `HashSet\u003cT\u003e`\n| `: IDictionary\u003cK,V\u003e` | `Map\u003cK, V\u003e \\| null` | Supports all `IDictionary\u003cK,V\u003e` implemented type like `Dictionary\u003cK,V\u003e`.\n| `[MemoryPackable]` | `class` | Supports class only\n| `[MemoryPackUnion]` | `abstract class` |\n\n`[GenerateTypeScript]` can only be applied to classes and is currently not supported by struct.\n\n### Configure import file extension and member name casing\n\nIn default, MemoryPack generates file extension as `.js` like `import { MemoryPackWriter } from \"./MemoryPackWriter.js\";`. If you want to change other extension or empty, use `MemoryPackGenerator_TypeScriptImportExtension` to configure it.\nAlso the member name is automatically converted to camelCase. If you want to use original name, use `MemoryPackGenerator_TypeScriptConvertPropertyName` to `false`.\n\n```xml\n\u003cItemGroup\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_TypeScriptOutputDirectory\" /\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_TypeScriptImportExtension\" /\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_TypeScriptConvertPropertyName\" /\u003e\n    \u003cCompilerVisibleProperty Include=\"MemoryPackGenerator_TypeScriptEnableNullableTypes\" /\u003e\n\u003c/ItemGroup\u003e\n\u003cPropertyGroup\u003e\n    \u003cMemoryPackGenerator_TypeScriptOutputDirectory\u003e$(MSBuildProjectDirectory)\\wwwroot\\js\\memorypack\u003c/MemoryPackGenerator_TypeScriptOutputDirectory\u003e\n    \u003c!-- allows empty --\u003e\n    \u003cMemoryPackGenerator_TypeScriptImportExtension\u003e\u003c/MemoryPackGenerator_TypeScriptImportExtension\u003e\n    \u003c!-- default is true --\u003e\n    \u003cMemoryPackGenerator_TypeScriptConvertPropertyName\u003efalse\u003c/MemoryPackGenerator_TypeScriptConvertPropertyName\u003e\n    \u003c!-- default is false --\u003e\n    \u003cMemoryPackGenerator_TypeScriptEnableNullableTypes\u003etrue\u003c/MemoryPackGenerator_TypeScriptEnableNullableTypes\u003e\n\u003c/PropertyGroup\u003e\n```\n\n`MemoryPackGenerator_TypeScriptEnableNullableTypes` allows C# nullable annotations to be reflected in TypeScript code. The default is false, making everything nullable.\n\nStreaming Serialization\n---\n`MemoryPack.Streaming` provides `MemoryPackStreamingSerializer`, which adds additional support for serializing and deserializing collections with streams.\n\n```csharp\npublic static class MemoryPackStreamingSerializer\n{\n    public static async ValueTask SerializeAsync\u003cT\u003e(PipeWriter pipeWriter, int count, IEnumerable\u003cT\u003e source, int flushRate = 4096, CancellationToken cancellationToken = default)\n    public static async ValueTask SerializeAsync\u003cT\u003e(Stream stream, int count, IEnumerable\u003cT\u003e source, int flushRate = 4096, CancellationToken cancellationToken = default)\n    public static async IAsyncEnumerable\u003cT?\u003e DeserializeAsync\u003cT\u003e(PipeReader pipeReader, int bufferAtLeast = 4096, int readMinimumSize = 8192, [EnumeratorCancellation] CancellationToken cancellationToken = default)\n    public static IAsyncEnumerable\u003cT?\u003e DeserializeAsync\u003cT\u003e(Stream stream, int bufferAtLeast = 4096, int readMinimumSize = 8192, CancellationToken cancellationToken = default)\n}\n```\n\nFormatter/Provider API\n---\nIf you want to implement formatter manually, inherit `MemoryPackFormatter\u003cT\u003e` and override the `Serialize` and `Deserialize` methods.\n\n```csharp\npublic class SkeltonFormatter : MemoryPackFormatter\u003cSkelton\u003e\n{\n    public override void Serialize\u003cTBufferWriter\u003e(ref MemoryPackWriter\u003cTBufferWriter\u003e writer, scoped ref Skelton? value)\n    {\n        if (value == null)\n        {\n            writer.WriteNullObjectHeader();\n            return;\n        }\n\n        // use writer method.\n    }\n\n    public override void Deserialize(ref MemoryPackReader reader, scoped ref Skelton? value)\n    {\n        if (!reader.TryReadObjectHeader(out var count))\n        {\n            value = null;\n            return;\n        }\n\n        // use reader method.\n    }\n}\n```\nThe created formatter is registered with `MemoryPackFormatterProvider`.\n\n```csharp\nMemoryPackFormatterProvider.Register(new SkeltonFormatter());\n```\n\nNote: `unmanged struct`(doesn't contain reference types) can not use custom formatter, it always serializes native memory layout.\n\nMemoryPackWriter/ReaderOptionalState\n---\nInitializing `MemoryPackWriter`/`MemoryPackReader` requires OptionalState. It is wrapper of `MemoryPackSerializerOptions`, it can create form `MemoryPackWriterOptionalStatePool`.\n\n```csharp\n// when disposed, OptionalState will return to pool.\nusing(var state = MemoryPackWriterOptionalStatePool.Rent(MemoryPackSerializerOptions.Default))\n{\n    var writer = new MemoryPackWriter\u003cT\u003e(ref t, state);\n}\n\n// for Reader\nusing (var state = MemoryPackReaderOptionalStatePool.Rent(MemoryPackSerializerOptions.Default))\n{\n    var reader = new MemoryPackReader(buffer, state);\n}\n```\n\nTarget framework dependency\n---\nMemoryPack provides `netstandard2.1` and `net7.0` but both are not compatible. For example, MemoryPackable types under `netstandard2.1` project and use it from `net7.0` project, throws runtime exception like this\n\n\u003e Unhandled exception. System.TypeLoadException: Virtual static method '*' is not implemented on type '*' from assembly '*'.\n\nSince net7.0 uses static abstract members (`Virtual static method`), that does not support netstandard2.1, this behavior is a specification.\n\n.NET 7 project shouldn't use the netstandard 2.1 dll. In other words, if the Application is a .NET 7 Project, all the dependencies that use MemoryPack must support .NET 7. So if a library developer has a dependency on MemoryPack, you need to configure dual target framework.\n\n```xml\n\u003cTargetFrameworks\u003enetstandard2.1;net7.0\u003c/TargetFrameworks\u003e\n```\n\nRPC\n---\n[Cysharp/MagicOnion](https://github.com/Cysharp/MagicOnion) is a code-first grpc-dotnet framework using MessagePack instead of protobuf. MagicOnion now supports MemoryPack as a serialization layer via `MagicOnion.Serialization.MemoryPack` package(preview). See details: [MagicOnion#MemoryPack support](https://github.com/Cysharp/MagicOnion#memorypack-support)\n\nUnity\n---\n\nMinimum supported Unity version is `2022.3.12f1`.\n\nThe `MemoryPack` core package is provided by nuget. It is also available in Unity. If you want to get Unity built-in type support, we additionally provide MemoryPack.Unity extension.\n\n1. Install `MemoryPack` from NuGet using [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)\n\n* Open Window from NuGet -\u003e Manage NuGet Packages, Search \"MemoryPack\" and Press Install.\n![screenshot](https://github.com/Cysharp/MemoryPack/assets/727159/599ff1ed-6cca-4724-be67-3edddb5e62ee)\n\n* If you encount version conflicts error, please disable version validation in Player Settings(Edit -\u003e Project Settings -\u003e Player -\u003e Scroll down and expand \"Other Settings\" than uncheck \"Assembly Version Validation\" under the \"Configuration\" section).\n\n2. Install the `MemoryPack.Unity` package by referencing the git URL\n\n* `https://github.com/Cysharp/MemoryPack.git?path=src/MemoryPack.Unity/Assets/MemoryPack.Unity`\n![screenshot](https://github.com/Cysharp/ZLogger/assets/46207/7325d266-05b4-47c9-b06a-a67a40368dd2)\n![screenshot](https://github.com/Cysharp/MemoryPack/assets/727159/9a4af1df-ce07-49d7-9420-922dfb139b55)\n\n\nMemoryPack uses the *.*.* release tag, so you can specify a version like #1.0.0. For example: `https://github.com/Cysharp/MemoryPack.git?path=src/MemoryPack.Unity/Assets/MemoryPack.Unity#1.0.0`\n\n\nAs with the .NET version, the code is generated by a code generator (`MemoryPack.Generator.dll`). Reflection-free implementation also provides the best performance in IL2CPP.\n\nFor more information on Unity and Source Generator, please refer to the [Unity documentation](https://docs.unity3d.com/Manual/roslyn-analyzers.html).\n\nSource Generator is also used officially by Unity by [com.unity.properties](https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/index.html) and [com.unity.entities](https://docs.unity3d.com/Packages/com.unity.properties@2.0/changelog/CHANGELOG.html). In other words, it is the standard for code generation in the next generation of Unity.\n\nYou can serialize all unmanaged types (such as `Vector3`, `Rect`, etc...) and some classes(`AnimationCurve`, `Gradient`, `RectOffset`). If you want to serialize other Unity-specific types, see [Serialize external types](#serialize-external-types) section.\n\nIn Unity performance, MemoryPack is x3~x10 faster than JsonUtility.\n\n![image](https://user-images.githubusercontent.com/46207/209254561-79ec18fe-c421-4d8c-9c86-b55276dd1a45.png)\n\nIf shared code has Unity's type(`Vector2`, etc...), MemoryPack provides `MemoryPack.UnityShims` package in NuGet.\n\nThe `MemoryPack.UnityShims` package provides shims for Unity's standard structs (`Vector2`, `Vector3`, `Vector4`, `Quaternion`, `Color`, `Bounds`, `Rect`, `Keyframe`, `WrapMode`, `Matrix4x4`, `GradientColorKey`, `GradientAlphaKey`, `GradientMode`, `Color32`, `LayerMask`, `Vector2Int`, `Vector3Int`, `RangeInt`, `RectInt`, `BoundsInt`) and some classes(`AnimationCurve`, `Gradient`, `RectOffset`).\n\n\u003e [!WARNING]\n\u003e Currently, the following limitations exist for use in Unity\n\n\n1. Unity version does not support CustomFormatter.\n2. If you are using .NET7 or later, MemoryPack binary format is not fully compatible with Unity.\n    - This problem occurs with value types that `[StructLayout(LayoutKind.Auto)]` is explicitly specified. (The default for struct is `LayoutKind.Sequencil`.) For such types, binaries serialized in .NET cannot be deserialized in Untiy. Similarly, a binary serialized in Unity cannot be serialized in .NET side.\n    - The affected types typically include the following types.\n        - `DateTimeOffset`\n        - `ValueTuple`\n    - Currently, the simple solution is to not use these types.\n\n\nNative AOT\n---\nUnfortunately, .NET 7 Native AOT causes crash (`Generic virtual method pointer lookup failure`) when use MemoryPack due to a runtime bug. It \nis going to be fixed in .NET 8. Using ``Microsoft.DotNet.ILCompiler` preview version, will fix it in .NET 7. Please see [issue's comment](https://github.com/Cysharp/MemoryPack/issues/75#issuecomment-1386884611) how setup it.\n\nBinary wire format specification\n---\nThe type of `T` defined in `Serialize\u003cT\u003e` and `Deserialize\u003cT\u003e` is called C# schema. MemoryPack format is not self-described format. Deserialize requires the corresponding C# schema. These types exist as internal representations of binaries, but types cannot be determined without a C# schema.\n\nEndian must be `Little Endian`. However, reference C# implementation does not care about endianness so can not use on big-endian machine. However, modern computers are usually little-endian.\n\nThere are eight types of format.\n\n* Unmanaged struct\n* Object\n* Version Tolerant Object\n* Circular Reference Object\n* Tuple\n* Collection\n* String\n* Union\n\n### Unmanaged struct\n\nUnmanaged struct is C# struct that doesn't contain reference types, similar constraint of [C# Unmanaged types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types). Serializing struct layout as it is, includes padding.\n\n### Object\n\n`(byte memberCount, [values...])`\n\nObject has 1byte unsigned byte as member count in header. Member count allows `0` to `249`, `255` represents object is `null`. Values store memorypack value for the number of member count.\n\n### Version Tolerant Object\n\n`(byte memberCount, [varint byte-length-of-values...], [values...])`\n\nVersion Tolerant Object is similar as Object but has byte length of values in header. varint follows these spec, first sbyte is value or typeCode and next X byte is value. 0 to 127 = unsigned byte value, -1 to -120 = signed byte value, -121 = byte, -122 = sbyte, -123 = ushort, -124 = short, -125 = uint, -126 = int, -127 = ulong, -128 = long.\n\n### Circular Reference Object\n\n`(byte memberCount, [varint byte-length-of-values...], varint referenceId, [values...])`  \n`(250, varint referenceId)`\n\nCircular Reference Object is similar as Version Tolerant Object but if memberCount is 250, next varint(unsigned-int32) is referenceId. If not, after byte-length-of-values, varint referenceId is written.\n\n### Tuple\n\n`(values...)`\n\nTuple is fixed-size, non-nullable value collection. In .NET, `KeyValuePair\u003cTKey, TValue\u003e` and `ValueTuple\u003cT,...\u003e` are serialized as Tuple.\n\n### Collection\n\n`(int length, [values...])`\n\nCollection has 4 byte signed integer as data count in header, `-1` represents `null`. Values store memorypack value for the number of length.\n\n### String\n\n`(int utf16-length, utf16-value)`  \n`(int ~utf8-byte-count, int utf16-length, utf8-bytes)`\n\nString has two-forms, UTF16 and UTF8. If first 4byte signed integer is `-1`, represents null. `0`, represents empty. UTF16 is same as collection(serialize as `ReadOnlySpan\u003cchar\u003e`, utf16-value's byte count is utf16-length * 2). If first signed integer \u003c= `-2`, value is encoded by UTF8. utf8-byte-count is encoded in complement, `~utf8-byte-count` to retrieve count of bytes. Next signed integer is utf16-length, it allows `-1` that represents unknown length. utf8-bytes store bytes for the number of utf8-byte-count.\n\n### Union\n\n`(byte tag, value)`  \n`(250, ushort tag, value)`\n\nFirst unsigned byte is tag that for discriminated value type or flag, `0` to `249` represents tag, `250` represents next unsigned short is tag, `255` represents union is `null`.\n\nLicense\n---\nThis library is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FMemoryPack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCysharp%2FMemoryPack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FMemoryPack/lists"}