Parsing binary data formats is a routine task in systems programming, but it has never been particularly enjoyable. Developers typically find themselves juggling manual byte offsets, endianness conventions, and platform-dependent type widths, often stitching together a fragile chain of reader calls that becomes difficult to maintain when the format changes. A new open source project called CStructSharp, created by developer vvollers, takes a different approach by letting developers describe binary layouts in C syntax and then generating parsers, serializers, and typed accessors from that single definition.
One Definition, Multiple Targets
The core idea behind CStructSharp is straightforward. A developer writes a layout that looks like a C struct declaration, supplies a byte array or a file, and gets back named values with correct types and offsets. The same definition can drive code generation on .NET, parse binary data in Node.js, or run in a browser through WebAssembly, all from a single layout description.
This cross-platform consistency matters because binary formats do not care what language the consuming application is written in. A protocol definition that lives in a C header can be reused as-is in CStructSharp, preserving the #define constants, #ifdef conditionals, and #pragma pack directives that already exist in the original format specification. For teams that maintain binary protocols across multiple languages, that means one source of truth instead of several parallel definitions that can drift apart.
The library carries zero runtime dependencies on .NET. The core library uses only the .NET runtime itself, which keeps the dependency tree of any application that adopts it minimal. The npm package includes a prebuilt WebAssembly runtime, so browser users do not need .NET installed at all. Node.js users load the runtime from disk, and both environments expose the same parsing API.
Runtime and Compile-Time Paths
CStructSharp offers two ways to use it on .NET, and the choice depends on the project's needs. The runtime path accepts a layout string and parses bytes into a StructValue object, which supports both typed access through generic methods and dynamic field access at the cost of compile-time checking. The compiler-generated path uses a source generator: a layout attached to a static partial class produces typed classes with Parse, Serialize, and in-place setter methods at build time, along with zero-allocation ref struct views.
The source generator approach has a practical advantage. The generated code produces the same byte-level results and the same error messages as the runtime parser, checked against a parity suite over every fixture. A developer can choose between flexibility and performance per layout without worrying about divergent behavior between the two modes.
For teams using Native AOT or concerned about trimming, the generated path avoids reflection entirely. The [CStructMapped] attribute maps parsed values to custom properties by name, and the analyzer warns when a path string does not match the layout it references.
Beyond Sequential Records
What sets CStructSharp apart from simpler binary readers is the depth of the layout language. The schema supports nested structs, overlapping union views, enums with explicit integer storage, and reusable type aliases. Arrays can be sized using arithmetic and bitwise expressions, #define constants, or earlier fields. Conditional fields can be selected with if/else or switch statements, allowing the layout to describe formats where the structure depends on the values within it.
Pointers and indirection are also part of the description. Developers can describe stored pointers, pointer arrays, and multiple levels of indirection, reading targets using absolute or relative addresses or inspecting stored addresses without following them. Nested value access uses path syntax similar to packet.samples[2].value, which makes complex binary structures navigable without writing traversal code.
Byte-level control is extensive. A single record can mix little-endian and big-endian primitives using suffixes. Layouts can be packed or aligned, with alignment refined through directives and reserved bits expressed as unnamed bitfields. Expected field offsets can be asserted directly, and pointer width is configured explicitly so the interpretation of the format stays independent of the host process.
Streaming, Async, and Memory Analysis
The library supports async operations throughout. ParseAsync, WriteAsync, and UpdateAsync read and write using ReadAsync and WriteAsync, with cancellation tokens checked at every boundary. For large files, input can be a ReadOnlySequence over a PipeReader's buffer, and record files can be iterated lazily with ParseMany or generated Records methods. TryParse, TryGet, and GetOrDefault convert expected failures into values rather than exceptions.
A separate module, CStructSharp.Memory, extends the library into memory analysis. It adds unsigned address spaces, mapped regions, BTF and ISF type import, bounded traversal, and offline patching capabilities, all with the same zero-dependency runtime.
What This Replaces
CStructSharp positions itself as a replacement for several approaches that developers commonly use. Manual offset calculations with BinaryReader or BinaryPrimitives become a single layout description where every field, offset, width, and byte order is declared once. StructLayout attributes combined with MemoryMarshal become unnecessary because portable widths never depend on the host process. Other schema languages like Kaitai Struct or dissect.cstruct use different syntaxes, whereas CStructSharp keeps the familiar C language that most developers already use for binary format documentation.
The project follows semantic versioning and is currently at major version zero, which means minor releases may change the public API or the layout language. The NuGet package targets .NET 8 and .NET 10, both long-term support releases, and the npm package supports Node.js 22.14 and later along with evergreen browsers. Under the MIT license, it is open for contribution and use without restriction.
For any developer who has spent hours debugging why a binary parser fails on a big-endian field at offset 12 while the host is little-endian, CStructSharp offers a compelling alternative: declare the format once in the language the format itself uses, and let the library handle the rest.