Table of Contents

Bound the work of a read

Advanced · C#. Limits count work done by an operation. A complete input can fail because its allowed read budget is too small.

Run this example

Prerequisites: the repository's .NET 10 SDK and a checkout of this source. Run from the repository root:

dotnet run --project docs/examples/CStructSharp.Docs.Examples.csproj -c Release -- bounded-read

The runner checks three-byte budget fails; six-byte budget reads length 6. Success includes PASS bounded-read.

Try the related browser lesson.

Complete program

The layout, options, input bytes, helper methods, and required types are all included. To adapt it outside the repository, create a .NET 10 console project, add CStructSharp, and replace Program.cs with this complete file. These examples follow the source version; use a matching package when testing a release.

Download the complete C# source.

// Generated from executable documentation examples. Edit the source region, then regenerate.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Buffers;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CStructSharp;
using CStructSharp.Codecs;
using CStructSharp.Diagnostics;
using CStructSharp.Introspection;
using CStructSharp.Values;

internal static partial class Program
{
    public static void Main()
    {
        BoundedRead();
        Console.WriteLine("PASS bounded-read");
    }

    private static void BoundedRead()
    {
        var layout = new CStruct("struct header { uint16 kind; uint32 length; };");
        byte[] bytes = [2, 0, 6, 0, 0, 0];
        Throws<CStructReadLimitException>(() => layout.Parse(bytes.AsSpan(), "header", options: new ReadOptions { MaxTotalBytesRead = 3 }));
        StructValue header = layout.Parse(bytes.AsSpan(), "header", options: new ReadOptions { MaxTotalBytesRead = 6 });
        Equal(6U, header.Get<uint>("length"));
    }

    private static void Equal<T>(T expected, T actual)
    {
        if (!EqualityComparer<T>.Default.Equals(expected, actual))
        {
            throw new InvalidOperationException($"Expected '{expected}', received '{actual}'.");
        }
    }

    private static void Throws<TException>(Action action)
        where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException)
        {
            return;
        }

        throw new InvalidOperationException($"Expected {typeof(TException).Name}.");
    }
}

Try it and diagnose mistakes

Use a limit of 5.

Answer: The six-byte read still fails. Set limits from the format, not by repeatedly raising them until arbitrary data succeeds. The program contains assertions for its original inputs. When changing an input intentionally, update the expected assertion too; an unchanged assertion is not evidence that the new value is wrong.

Continue with the related guide or choose another recipe.