{"id":13629694,"url":"https://github.com/RudolfKurkaMs/StructPacker","last_synced_at":"2025-04-17T09:35:56.644Z","repository":{"id":179827365,"uuid":"287993120","full_name":"RudolfKurkaMs/StructPacker","owner":"RudolfKurkaMs","description":"Low-level, lightweight and performance-focused serializer for C# struct types that uses Source Generators technology.","archived":false,"fork":false,"pushed_at":"2022-06-25T15:09:34.000Z","size":2100,"stargazers_count":75,"open_issues_count":1,"forks_count":7,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-05-23T07:52:25.205Z","etag":null,"topics":["csharp-serializer","csharp-sourcegenerator"],"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/RudolfKurkaMs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-08-16T17:35:59.000Z","updated_at":"2024-01-20T03:59:26.000Z","dependencies_parsed_at":"2024-01-02T22:59:20.505Z","dependency_job_id":null,"html_url":"https://github.com/RudolfKurkaMs/StructPacker","commit_stats":null,"previous_names":["rudolfkurka/structpacker"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RudolfKurkaMs%2FStructPacker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RudolfKurkaMs%2FStructPacker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RudolfKurkaMs%2FStructPacker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RudolfKurkaMs%2FStructPacker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RudolfKurkaMs","download_url":"https://codeload.github.com/RudolfKurkaMs/StructPacker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223751379,"owners_count":17196627,"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":["csharp-serializer","csharp-sourcegenerator"],"created_at":"2024-08-01T22:01:16.736Z","updated_at":"2024-11-08T20:31:46.178Z","avatar_url":"https://github.com/RudolfKurkaMs.png","language":"C#","funding_links":[],"categories":["Do not want to test 113 ( old ISourceGenerator )"],"sub_categories":["1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category"],"readme":"[![Nuget](https://img.shields.io/nuget/v/StructPacker?style=for-the-badge)](https://www.nuget.org/packages/StructPacker/)\n\n## Overview\n\nStructPacker is binary serializer that auto-generates C# serialization code to achieve peak runtime performance and efficiency.\n\n## Features\n\n* One of the fastest binary serialization you can get under .NET without going native.\n* Very little to none collateral memory allocations resulting in low GC pressure. Well suited for devices with low memory and/or processing power like mobile phones, IoT or performance-critical apps like games.\n* Supports C# language for generated output. Other .NET languages like VB.NET of F# could be added in the future.\n* Output is fully managed code with single 20KB DLL dependency (added automatically as part of nuget package).\n* No post-build steps, no runtime type inspections, no dynamic IL generation or native libraries which means high portability and compatibility.\n* No fancy requirements. Can be installed in any C# .NET project that satisfies at least .NET Standard 1.0 api surface.\n* Optimized serialization to/from byte array, stream or pooled byte array (using array pooling further increases efficiency).\n* Serializable types:\n  * Primitive types (bool, byte, sbyte, char, short, ushort, int, uint, long, ulong, float, double, decimal).\n  * String, DateTime and TimeSpan.\n  * Array (one-dimensional) version of all the above.\n\n## How it works\n\nStructPacker uses brand new feature of Visual Studio called [Source Generators](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/).\nWhenever you build your project (or just type something) StructPacker's component inspects your code, collects all structures that are marked with the attribute and emits tailor-made code that \"knows\" how to serialize and deserialize them.\nThis generated code is then automatically compiled into the project output as internal class.\n\nThis means StructPacker does not need to do any runtime type inspections or IL generation resulting in highly performant and highly portable code.\n\n## Prerequisites\n\nVisual Studio version 16.8 and above is required as its first version to support source generators.\n\n## Installation\n\nSimply install the [nuget package](https://www.nuget.org/packages/StructPacker/) and you're good to go.\n\n## Usage\n\n```csharp\nusing System;\nusing System.IO;\nusing StructPacker;\n```\n\n```csharp\n[Pack]\ninternal struct ChatMsg\n{\n    public int ID { get; set; }\n    public bool IsOutgoing { get; set; }\n    public string Text { get; set; }\n    public DateTime TimePosted { get; set; }\n\n    [SkipPack]\n    public string ExampleIgnoredProperty { get; set; }\n}\n```\n\n```csharp\n//create a test message\nvar sourceMsg = new ChatMsg\n{\n    ID = 5,\n    IsOutgoing = true,\n    Text = \"Test\",\n    TimePosted = DateTime.MaxValue\n};\n\n//save it to byte array\nbyte[] byteArr = sourceMsg.Pack();\n\n//or save it to a pooled buffer (managed by StructPacker), once this instance is disposed its internal byte buffer is reclaimed and can be used again elsewhere\nusing PooledBuffer pooled = sourceMsg.PackToBuffer(); \n\n//byte data is available through Data property, number of valid bytes is in the Size property. Important: do not read past the Size property (it can be lower than actual length of the byte array)!\nusing var memStr = new MemoryStream(pooled.Data, 0, pooled.Size);\n\n//declare message for deserializing\nvar unpackedMsg = new ChatMsg();\n\n//load content from a stream\nunpackedMsg.Unpack(memStr);\n\n//or alternatively load it from a byte array (notice you can reuse the same structure for multiple uses)\nunpackedMsg.Unpack(byteArr);\n```\n\n## Benchmarks\n\nTest PC configuration:\n\n``` ini\nBenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041\nIntel Core i5-9600K CPU 3.70GHz (Coffee Lake), 1 CPU, 6 logical and 6 physical cores\n.NET Core SDK=5.0.100\n  [Host]     : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n  DefaultJob : .NET Core 3.1.9 (CoreCLR 4.700.20.47201, CoreFX 4.700.20.47203), X64 RyuJIT\n```\n\n\\\nTest with small structure (395B):\n\n|               Method |        Mean |     Error |    StdDev | Ratio | RatioSD |  Gen 0 |  Gen 1 | Gen 2 | Allocated |\n|--------------------- |------------:|----------:|----------:|------:|--------:|-------:|-------:|------:|----------:|\n|         StructPacker |    294.0 ns |   1.38 ns |   1.30 ns |  1.00 |    0.00 | 0.1817 |      - |     - |     856 B |\n|           [BinaryPack](https://github.com/Sergio0694/BinaryPack) |    316.6 ns |   1.89 ns |   1.77 ns |  1.08 |    0.01 | 0.2346 |      - |     - |    1104 B |\n| [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp) |  1,015.9 ns |   1.40 ns |   1.24 ns |  3.46 |    0.02 | 0.2346 |      - |     - |    1104 B |\n|      [BinaryFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=netcore-3.1) | 15,235.9 ns | 153.14 ns | 143.25 ns | 51.82 |    0.50 | 3.0212 | 0.0610 |     - |   14229 B |\n|      [Newtonsoft.Json](https://www.newtonsoft.com/json) | 13,127.6 ns |  71.22 ns |  63.13 ns | 44.66 |    0.21 | 2.5940 | 0.0305 |     - |   12232 B |\n\n\\\nTest with large structure (~2MB):\n\n|               Method |         Mean |       Error |      StdDev |  Ratio | RatioSD |      Gen 0 |     Gen 1 |     Gen 2 | Allocated |\n|--------------------- |-------------:|------------:|------------:|-------:|--------:|-----------:|----------:|----------:|----------:|\n|         StructPacker |     765.3 us |     4.14 us |     3.67 us |   1.00 |    0.00 |   483.3984 |  408.2031 |  286.1328 |   2.59 MB |\n|           [BinaryPack](https://github.com/Sergio0694/BinaryPack) |   2,273.2 us |    45.36 us |   131.59 us |   2.93 |    0.21 |  1085.9375 | 1023.4375 |  886.7188 |  10.66 MB |\n| [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp) |   8,290.1 us |    10.19 us |     9.53 us |  10.83 |    0.05 |  1093.7500 | 1000.0000 |  875.0000 |   4.63 MB |\n|      [BinaryFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=netcore-3.1) |   6,381.4 us |   151.39 us |   446.38 us |   8.44 |    0.75 |  1421.8750 | 1171.8750 |  843.7500 |   9.01 MB |\n|      [Newtonsoft.Json](https://www.newtonsoft.com/json) | 181,518.3 us | 1,290.44 us | 1,207.08 us | 237.05 |    2.10 | 46000.0000 | 3000.0000 | 1000.0000 | 234.34 MB |\n\n\\\n**Conclusions:**\n\nStructPacker is faster and uses less memory than any other tested component.\n\nHowever please note that in cases like Newtonsoft.Json the comparison only tells you that StructPacker is better at what it can do but Newtonsoft.Json can do much more things (trading off performance for convenience). It is also a text format (not a binary one).\n\nEntire benchmark app is included in the source.\n\n## FAQ\n\n**Why use only structures and not classes?**\n\nIt's for performance optimization (not having to allocate objects on the heap). Also, it's easier to inspect structures as they cannot inherit from other types making the source generator's job easier.\n\n**Are types marked as \"partial\" supported?**\n\nAt the moment no. Maybe in the future.\n\n**Can StructPacker resolve type conflicts? Is there type-less api etc.?**\n\nNo, these features are considered outside of the scope of this project (in favor of performance and runtime efficiency).\nAlso note there is no type information in the output sequence meaning you have to know which structure to read next and if that structure isn't what has been serialized the stream gets corrupted.\nIt's up to the coder to prevent this situation.\n\n**If I make code changes to the input structure, can it still be deserialized from bytes created with it's earlier version?**\n\nDepends. StructPacker exactly follows the structure's inner layout as it is defined in code (think of it as if you're \"overlaying\" the input data with a structure ala C++). This means it is resistant against member renames (member names don't matter as they are not serialized at all).\nHowever you must avoid changing the order (and obviously the underlying type) of members or the structure will not be deserialized correctly from it's older image. This includes ignoring or un-ignoring members.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRudolfKurkaMs%2FStructPacker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRudolfKurkaMs%2FStructPacker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRudolfKurkaMs%2FStructPacker/lists"}