CStructSharp 0.9.1

dotnet add package CStructSharp --version 0.9.1
                    
NuGet\Install-Package CStructSharp -Version 0.9.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CStructSharp" Version="0.9.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CStructSharp" Version="0.9.1" />
                    
Directory.Packages.props
<PackageReference Include="CStructSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CStructSharp --version 0.9.1
                    
#r "nuget: CStructSharp, 0.9.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CStructSharp@0.9.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CStructSharp&version=0.9.1
                    
Install as a Cake Addin
#tool nuget:?package=CStructSharp&version=0.9.1
                    
Install as a Cake Tool

CStructSharp

<p align="center"> <a href="LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/vvollers/cstructsharp"></a> <a href="https://www.npmjs.com/package/cstructsharp"><img alt="npm version" src="https://img.shields.io/npm/v/cstructsharp"></a> <a href="https://www.npmjs.com/package/cstructsharp"><img alt="npm unpacked size, including WASM" src="https://img.shields.io/npm/unpacked-size/cstructsharp?label=npm%20unpacked"></a> <a href="https://www.nuget.org/packages/CStructSharp"><img alt="NuGet version" src="https://img.shields.io/nuget/v/CStructSharp"></a> <a href="https://github.com/vvollers/cstructsharp/releases/latest"><img alt="NuGet package download size" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fvvollers.github.io%2Fcstructsharp%2Fbadges%2Fnuget-size.json"></a> </p> <p align="center"> <a href="https://github.com/vvollers/cstructsharp/actions/workflows/ci.yml"><img alt="Managed CI" src="https://github.com/vvollers/cstructsharp/actions/workflows/ci.yml/badge.svg?branch=main&event=push"></a> <a href="https://vvollers.github.io/cstructsharp/badges/"><img alt="C# line coverage on .NET 10" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fvvollers.github.io%2Fcstructsharp%2Fbadges%2Fline-coverage.json"></a> <a href="https://vvollers.github.io/cstructsharp/badges/"><img alt="C# branch coverage on .NET 10" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fvvollers.github.io%2Fcstructsharp%2Fbadges%2Fbranch-coverage.json"></a> <a href="https://vvollers.github.io/cstructsharp/badges/"><img alt="C# test results on .NET 10" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fvvollers.github.io%2Fcstructsharp%2Fbadges%2Ftests.json"></a> </p>

CStructSharp reads and writes binary data using a description that looks like a C struct. Give it a layout and some bytes, and it gives you named values. Give it values, and it can create bytes or change a field in existing data. Use it from C#, Node.js, or JavaScript in a browser.

Zero runtime package dependencies. The core .NET library uses only the .NET runtime, keeping integration simple and your application's dependency tree small.

Choose your starting point

Read your first value in C#

Install a stable .NET 10 SDK. These commands work in PowerShell or a Unix shell:

dotnet new console -n BinaryHeader -f net10.0
cd BinaryHeader
dotnet add package CStructSharp

Replace Program.cs with this complete program, then run dotnet run:

using CStructSharp;
using CStructSharp.Values;

var layout = new CStruct("struct header { uint16 kind; uint32 length; };");
byte[] bytes = { 0x02, 0x00, 0x06, 0x00, 0x00, 0x00 };
StructValue header = layout.Parse(bytes, "header");

Console.WriteLine($"kind = {header.Get<ushort>("kind")}");
Console.WriteLine($"length = {header.Get<uint>("length")}");

Output:

kind = 2
length = 6

The layout names the fields. The byte array supplies the data. The result is a StructValue: read a member typed with header.Get<ushort>("kind"); dynamic field syntax (header.kind) also works on the JIT, at the cost of compile-time checking. The values:

Field Byte offsets Input bytes Value
kind 0–1 02 00 2
length 2–5 06 00 00 00 6

By default, fields are packed together, numbers use little-endian byte order, and pointers occupy eight bytes. The binary layout basics explain these choices.

The same package ships a source generator. Put the layout on a static partial class and the compiler produces typed classes, Parse, Serialize, in-place setters, and zero-allocation views for it, with the same values and the same failures as the runtime reader:

[CStructLayout("struct header { uint16 kind; uint32 length; };")]
public static partial class Wire { }

Wire.Header header = Wire.Parse(bytes);   // header.Kind == 2, header.Length == 6
byte[] again = Wire.Serialize(header);

Every stream form has an awaitable twin - await layout.ParseAsync(file, "header", cancellationToken: token) reads the bytes while the thread is free and decodes them with the same reader - and a file of records is foreach over Wire.Records(bytes) or layout.ParseMany(bytes, "header"), one record per step.

A [CStructMapped] partial class maps a parsed StructValue to your own properties by name, and the analyzer warns about a path string that does not match the layout it is used with. The generated code series teaches this path from the first class to the decision between runtime and generated. For the background, read how C structs occupy memory and memory addresses and stored data. See reading values for managed result types and the JavaScript API for browser results. Try changing 0x02 to 0x03: kind becomes 3.

A portable C struct definition language

Turn a binary format into an executable specification. CStructSharp combines familiar C struct syntax with portable layout rules, giving you one definition for decoding records, generating bytes, inspecting offsets, and updating individual fields. Load definitions at runtime and use the same format description from C#, Node.js, or a browser to build protocol tools, file inspectors, and binary editors.

  • Model rich binary data. Compose nested structs, overlapping union views, enums with explicit integer storage, and reusable typedef aliases. Represent values with fixed-width integers, IEEE-754 floats, booleans, bitfields, fixed character buffers, and terminated ASCII, UTF-8, or UTF-16 strings.
  • Let the data determine the shape. Use arithmetic and bitwise expressions, #define constants, earlier fields, and caller-supplied variables to size one-dimensional arrays. Select conditional fields with if/else or switch. Describe count-prefixed payloads, fixed multidimensional tables, and arrays of structured records directly in the definition.
  • Control the bytes precisely. Mix little- and big-endian primitives in one record with < and > suffixes. Choose packed or aligned layout, refine alignment with @align(N), reserve bits with unnamed bitfields, and assert expected field offsets with @N. Type widths follow portable rules, and pointer width is configured explicitly, so the format's interpretation stays independent of the host process.
  • Navigate beyond sequential records. Describe stored pointers, pointer arrays, and multiple levels of indirection. Read targets using absolute or relative addressing, or inspect stored addresses without following them. Select nested values with paths such as packet.samples[2].value or root.ptr.value.
  • Generate the code. Put a layout on a [CStructLayout] class and the source generator in the same package writes typed classes, Parse/Serialize/Write, readonly ref struct views that allocate nothing, typed in-place setters, and size and offset constants at build time - the same parser, the same placement, and the same failure texts as the runtime, checked by a parity suite over every fixture. [CStructMapped] generates the mapping into your own classes, with no reflection, so trimmed and Native AOT publishes need no conventions.
  • Streams and pipelines. ParseAsync, WriteAsync, and UpdateAsync read and write with ReadAsync/WriteAsync and a CancellationToken that is checked at every boundary; ReadOnlySequence<byte> input reads a PipeReader's buffer in place; ParseMany and the generated Records walk one record after another lazily, and TryParse, TryGet, and GetOrDefault turn expected failures into values instead of exceptions - see async reads, cancellation, and pipelines.
  • Analyze memory images. CStructSharp.Memory adds unsigned address spaces, mapped regions, BTF/ISF type import, bounded traversal, and offline patches, with the same zero-dependency runtime; see the memory-analysis guide and the runnable synthetic consumer.

Prepare a layout once and reuse it to read StructValue results or C# classes, write new records, and update selected fields in existing data. The definition keeps the format's structure and byte-level rules together as your tools grow from a single header parser into a complete format explorer. The library is trim-safe and Native AOT compatible; see trimming and Native AOT for what a published program contains (and why dynamic stays on the JIT).

Start with the language tutorial, explore the language reference, or consult differences from C when adapting an existing header.

Why CStructSharp instead of …

If you would otherwise use CStructSharp instead
Manual offsets with BinaryReader / BinaryPrimitives The layout text names every field, offset, width, and byte order once; reads, writes, updates, address lookups, and the debug byte map all come from that one description, and a change to the format is a change to the text.
[StructLayout] structs with MemoryMarshal Portable widths never depend on the host process; layouts load at run time, so a tool can accept formats it did not compile against, and variable-length arrays, conditional fields, pointers, and strings are part of the description rather than hand code.
A source generator or a serializer The same layout text drives C#, Node.js, and the browser; on .NET you choose per layout between the run-time CStruct (no build step, layouts loaded at run time) and the [CStructLayout] generator (typed classes, views, and setters emitted at build time), and the two agree on every byte and every error.
Kaitai Struct or another schema language The schema is C: an existing header or a dissect.cstruct definition is the input, with #define, #ifdef, and #pragma pack honored, so format knowledge that already exists as C stays C.
dissect.cstruct (Python) The same definition language and habits on .NET and in JavaScript, with a compiled layout cache, bounded read budgets, trim-safe Native AOT support, and a migration guide for the few places the two libraries read bytes differently.

Use JavaScript in Node.js or a browser

Read large files, buffers, and streamed binary input with automatic paging and worker execution. The large-data guide shows how to pass File, Blob, byte views, fetch responses, and Node streams directly to parse or parseWithDebug.

The npm package includes the prebuilt WebAssembly runtime and TypeScript declarations:

npm install cstructsharp

Save this as example.mjs and run node example.mjs with Node.js 22.14 or later:

import { parse } from "cstructsharp";

const result = await parse(
  "struct header { uint16 kind; uint32 length; };",
  new Uint8Array([2, 0, 6, 0, 0, 0]),
  { root: "header" },
);
if (!result.success) throw new Error(result.error.message);
console.log(result.data.kind); // 2

parse returns the values; parseWithDebug additionally lists each field's byte range for a hex viewer. Node loads the installed runtime from disk; no .NET SDK or server is needed. Browser applications use the same API with the cstructsharp/vite plugin or an explicit static-asset directory. See the npm package README for complete setup, write/update examples, and supported hosts.

Use the standalone browser bundle

Download cstructsharp-wasm-v<VERSION>.zip from GitHub Releases. Extract the complete archive. With Node.js installed, run node serve.mjs in that directory and open http://127.0.0.1:8080/starter/. The included page reads, writes, and updates the same header.

Browser users do not need .NET installed. Keep the runtime files together and serve them over HTTP(S). The browser guide explains the files, JavaScript API, result conversion, and common loading errors.

Continue learning

Versioning and support

CStructSharp follows semantic versioning and is at major version 0: a minor release (0.5 → 0.6) may change the public API, the layout language, or the JavaScript contract, and the changelog marks every such change Breaking with the migration; a patch release never does. Pin 0.5.* in a project that must not absorb breaking changes. The managed API baseline (contracts/api/managed-rc1) and the browser contract (contracts/api/browser-rc1, contractVersion 8) are reviewed together with each change; a breaking JavaScript change increments the contract version.

The NuGet package targets .NET 8 (LTS) and .NET 10 (LTS); a target is dropped in the first minor release after Microsoft ends its support. The npm package supports the Node.js releases that are active or in maintenance (currently 22.14 and later) and evergreen Chromium, Firefox, and WebKit browsers. Release assets describe published versions; the repository's src/CStructSharp/CStructSharp.csproj records the development version.

Work on the project

Package consumers do not need to clone or build this repository. Contributors should start with the repository setup guide, then follow build instructions, testing, and contribution guidance. The repository map explains the projects.

CStructSharp uses the MIT License. Report questions and bugs in the issue tracker.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.1 0 9/25/2026
0.9.0 26 9/24/2026
0.8.3 36 9/24/2026
0.8.2 44 9/23/2026
0.8.1 40 9/23/2026
0.8.0 81 9/21/2026
0.7.0 83 9/21/2026
0.6.0 88 9/19/2026
0.5.0 83 9/17/2026
0.4.3 80 9/15/2026
0.4.2 81 9/15/2026
0.4.1 82 9/14/2026
0.4.0 89 9/14/2026
0.3.4 81 9/12/2026
0.3.3 89 9/10/2026
0.3.2 98 9/10/2026
0.3.1 102 9/9/2026
0.3.0 97 9/9/2026
0.2.12 94 9/7/2026
0.2.11 95 9/6/2026
Loading failed