{"id":13629476,"url":"https://github.com/MeltyPlayer/Schema","last_synced_at":"2025-04-17T09:34:10.036Z","repository":{"id":164852538,"uuid":"640278532","full_name":"MeltyPlayer/Schema","owner":"MeltyPlayer","description":"Library for serializing C# types to and from binary. Provides a Roslyn generator that automatically implements read/write logic.","archived":false,"fork":false,"pushed_at":"2024-04-13T06:19:05.000Z","size":704,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-13T20:53:10.646Z","etag":null,"topics":["binary","c-sharp","dotnet","serialization","serializer"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MeltyPlayer.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,"dei":null}},"created_at":"2023-05-13T15:03:59.000Z","updated_at":"2024-04-14T21:25:43.695Z","dependencies_parsed_at":null,"dependency_job_id":"64ab8b26-f84a-4292-9c78-f1832c78c0b2","html_url":"https://github.com/MeltyPlayer/Schema","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeltyPlayer%2FSchema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeltyPlayer%2FSchema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeltyPlayer%2FSchema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MeltyPlayer%2FSchema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MeltyPlayer","download_url":"https://codeload.github.com/MeltyPlayer/Schema/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249331808,"owners_count":21252645,"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":["binary","c-sharp","dotnet","serialization","serializer"],"created_at":"2024-08-01T22:01:11.615Z","updated_at":"2025-04-17T09:34:09.607Z","avatar_url":"https://github.com/MeltyPlayer.png","language":"C#","funding_links":[],"categories":["Content"],"sub_categories":["225. [Schema](https://ignatandrei.github.io/RSCG_Examples/v2/docs/Schema) , in the [Serializer](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#serializer) category"],"readme":"# Schema\n\n![GitHub](https://img.shields.io/github/license/MeltyPlayer/Schema)\n[![Nuget](https://img.shields.io/nuget/v/schema)](https://www.nuget.org/packages/schema)\n![Nuget](https://img.shields.io/nuget/dt/schema)\n![Unit tests](https://github.com/MeltyPlayer/Schema/actions/workflows/dotnet.yml/badge.svg)\n[![Coverage Status](https://coveralls.io/repos/github/MeltyPlayer/Schema/badge.svg?service=github)](https://coveralls.io/github/MeltyPlayer/Schema)\n\n## Overview\n\nLibrary for serializing C# types to/from binary. Provides a Roslyn generator that automatically implements read/write logic.\n\n**Warning: The design of this library is still in flux, so anticipate making changes when upgrading to future versions.**\n\n## Credits\n\n- [@connorhaigh](https://github.com/connorhaigh), whose [SubstreamSharp](https://github.com/connorhaigh/SubstreamSharp) library was pulled in for reading substreams.\n- [@jefffhaynes](https://github.com/jefffhaynes), whose [BinarySerializer](https://github.com/jefffhaynes/BinarySerializer) attribute library inspired the schema attributes for configuring how binary data is read.\n- [@Kermalis](https://github.com/Kermalis), whose [EndianBinaryIO](https://github.com/Kermalis/EndianBinaryIO) library inspired [Span](https://learn.microsoft.com/en-us/archive/msdn-magazine/2018/january/csharp-all-about-span-exploring-a-new-net-mainstay)-based performance improvements.\n- [@Sergio0694](https://github.com/Sergio0694), whose [BinaryPack](https://github.com/Sergio0694/BinaryPack) generator inspired the schema source generator used to generate read/write methods.\n\n## Usage\n\n### Implementing binary schema classes\n\nTo write a binary schema class, you must first have it implement the `IBinarySerializable` or `IBinaryDeserializable` interfaces (or `IBinaryConvertible` if you need both).\n\nThen, based on how complicated your schema class is, you can either choose to automatically or manually implement `Read()`/`Write()` methods.\n\n#### Automatically\n\nFor most schema classes, you should be able to use the automatic code generator.\n\nAll you have to do is annotate the schema class with the `[BinarySchema]` attribute and mark it as partial; this will flag to the generator that it should implement read/write methods for this class.\nIt will then look into all fields/properties in the schema class, and attempt to implement read/write logic in the same order that the fields/properties appear.\n\nAny nested schema classes will be automatically read/written as expected.\n\nSome types require additional attributes in order to clarify any ambiguity.\nFor example, booleans require a `[IntegerFormat(SchemaIntegerType.###)]` attribute to know what type of integer to read, which it will then compare to 0.\n\nAny readonly primitives will treated as assertions, which is useful for validating things like magic text or padding.\n\n#### Manually\n\nFor complicated schema classes, such as ones that use decompression logic or pointers, you'll need to implement the read/write logic manually.\n\nSpecifically, you'll need to implement both a `Read(IBinaryReader br)` and `Write(IBinaryWriter bw)` method.\nThe `SchemaBinaryReader` and `SchemaBinaryWriter` classes provide many helpful methods for reading/writing a number of different primitive formats, including basic ones such as `byte`/`int`/`float`, but also more complex/unique ones such as `Half` (two-byte float) and `un16` (unsigned normalized 16-bit float).\n\nSimilar to the automatic process, you can nest schema classes and manually read/write them by calling their `Read()`/`Write()` methods. \nThis can allow you to automatically generate subsections, so only the most complex logic needs to be manually written.\n\n### How to use a binary schema class\n\nTo convert a given schema class to or from binary, simply instantiate an `SchemaBinaryReader` or `SchemaBinaryWriter` and pass it into the corresponding `Read()` or `Write()` methods in the schema class.\n\n### Supported Attributes\n\nThe following attributes are currently supported in this library **when automatically generating code**. Some attributes are only used at read or write time—these are prefixed with an R or W respectively.\n\n**Warning: These names are not final, so they may change in future versions.**\n\n#### Align\n\nSpecifies how a field or property's offset (relative to the start of the stream) should be aligned when reading/writing. If misaligned, the `SchemaBinaryReader`/`SchemaBinaryWriter` will automatically insert the remaining bytes of padding. For example, `[Align(4)]` would force a field/property's starting offset to be a multiple of 4 (0, 4, 8, 12, 16, etc.).\n```cs\n[Align(4)]\npublic int alignedField;\n\n[Align(4)]\npublic int AlignedProperty { get; set; }\n```\n\n#### Endianness\n\nForces a type, field, or property to be read/written with a given [endianness](https://en.wikipedia.org/wiki/Endianness) (big-endian or little-endian). Tracked via a stack within the `SchemaBinaryReader`/`SchemaBinaryWriter`. If unspecified, will use whatever endianness was last specified in the stack (or the system endianness by default).\n```cs\n[BinarySchema]\n[Endianness(Endianness.BigEndian)]\npublic partial class BigEndianType : IBinaryConvertible {\n  ...\n  \n  [Endianness(Endianness.LittleEndian)]\n  public int LittleEndianProperty { get; set; }\n  \n  ...\n}\n```\n\n#### IfBoolean/RIfBoolean\n\nMarks that a nullable field or property will only be read/written if some other boolean field or property is true.\n```cs\n[IntegerFormat(SchemaIntegerType.BYTE)]\npublic bool HasValue { get; set; }\n\n[RIfBoolean(nameof(this.HasValue))]\npublic int? Value { get; set; }\n```\n\n#### IChildOf\u0026lt;TParent\u0026gt;\n\nThis pseudo-attribute marks a type as a \"child\" of some \"parent\" type—that it is contained as one of the members of the \"parent type\"—and passes the parent down to the child so it can be referenced in Schema logic.\n\nUsed by having the child type implement the `IChildOf\u003cTParent\u003e` interface, where `TParent` stores the child type in a field/property or as a member of a sequence (array/list):\n```cs\n[BinarySchema]\npublic partial class ChildType : IBinaryConvertible, IChildOf\u003cParentType\u003e {\n  public ParentType Parent { get; set; }\n  \n  ...\n}\n```\n\nBelow is a simple example where a boolean from the parent is used to decide when to read a value in the child:\n```cs\n[BinarySchema]\npublic partial class ParentType : IBinaryConvertible {\n  [IntegerFormat(SchemaIntegerType.BYTE)]\n  public bool ChildHasSomeField { get; set; }\n\n  public ChildType Child { get; } = new();\n}\n\n[BinarySchema]\npublic partial class ChildType : IBinaryConvertible, IChildOf\u003cParentType\u003e {\n  // This is automatically skipped while reading/writing.\n  public ParentType Parent { get; set; }\n\n  [Skip]\n  private bool HasSomeField =\u003e Parent.ChildHasSomeField;\n\n  [RIfBoolean(nameof(HasSomeField))]\n  public int? SomeField { get; set; }\n}\n```\n\n#### Skip\n\nDesignates that a field or property should be skipped while reading/writing.\n\n*Note: `IChildOf\u003cTParent\u003e.Parent` is automatically skipped.*\n```cs\n[Skip]\npublic int skippedField;\n\n[Skip]\npublic int SkippedProperty { get; set; }\n```\n\nThis can be used to encapsulate logic within properties, such as in the following examples:\n\n1) **Value conversion**\n```cs\n[StringLengthSource(4)]\npublic string Magic { get; set; }\n\n[Skip]\npublic MagicType Type =\u003e this.Magic switch {\n  \"IMGE\" =\u003e MagicType.IMAGE,\n  \"SOND\" =\u003e MagicType.SOUND,\n  \"TEXT\" =\u003e MagicType.TEXT,\n};\n```\n\n2) **\"Switch\" cases**\n```cs\n[NullTerminatedString]\npublic string Magic { get; set; }\n\n[Skip]\npublic ISection? Section =\u003e this.imageSection_ ?? this.soundSection_ ?? this.textSection_;\n\n[Skip]\nprivate bool IsImage_ =\u003e this.Magic == \"IMAGE\";\n[Skip]\nprivate bool IsSound_ =\u003e this.Magic == \"SOUND\";\n[Skip]\nprivate bool IsText_ =\u003e this.Magic == \"TEXT\";\n\n[RIfBoolean(nameof(this.IsImage))]\nprivate ImageSection? imageSection_ { get; set; }\n\n[RIfBoolean(nameof(this.IsSound_))]\nprivate SoundSection? soundSection_ { get; set; }\n\n[RIfBoolean(nameof(this.IsText_))]\nprivate TextSection? textSection_ { get; set; }\n```\n\n#### Numbers/Enums\n\n##### NumberFormat\n\nTODO\n\n##### IntegerFormat\n\nTODO\n\n\n#### Strings\n\n*Note: At the moment, only ASCII is fully supported.*\n\n##### StringLengthSource/RStringLengthSource\n\nDesignates the length of a string field or property via one of three cases. \n\n*Note: Any trailing null terminators will be ignored at read time.*\n\n1) **Constant length**\n\nIf a constant is passed into `StringLengthSource`, that many characters will be read/written.\n```cs\n[StringLengthSource(8)]\npublic string Text { get; set; }\n```\n\n2) **Preceding value**\n\nIf a `SchemaIntegerType` is passed into `StringLengthSource`, an integer of that type will first be read and used as the length of the string, or the length of the string will first be written before writing the string itself.\n```cs\n[StringLengthSource(SchemaIntegerType.BYTE)]\npublic string TextWithByteLength { get; set; }\n```\n\n3) **Another field or property**\n\nIf the name of another field or property is passed into `RStringLengthSource`, that other value will be used as the length of the string when reading.\n```cs\npublic byte TextLength { get; set; }\n\n[RStringLengthSource(nameof(this.TextLength))]\npublic string Text { get; set; }\n```\n\n##### NullTerminatedString\n\nDesignates that a string field or property will be read until a null terminator is reached, and written with a null terminator affixed to the end.\n```cs\n[NullTerminatedString]\npublic string Text { get; set; }\n```\n\n#### Sequences\n\n*Note: \"Sequence\" is the term used within Schema to refer to an array/list of elements.*\n\n##### SequenceLengthSource/RSequenceLengthSource\n\nTODO\n\n##### RSequenceUntilEndOfStreamAttribute\n\nTODO\n\n\n#### Pointers/Memory\n\nTODO\n\n##### WPointerTo\n\nTODO\n\n##### WSizeOfMemberInBytes\n\nTODO\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeltyPlayer%2FSchema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMeltyPlayer%2FSchema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeltyPlayer%2FSchema/lists"}