Project · I/O & HTTP

Build and verify a framed UTF-8 message

Define a versioned byte format with an explicit big-endian length and run twelve independent boundary checks.

All C# source files below are loaded together in the playground. Files written by a run live in its virtual directory.

Program.cs

FrameChecks.Run();

Protocol/FrameCodec.cs

using System.Buffers.Binary;
using System.Text;
public static class FrameCodec {
    public const int MaxPayloadBytes = 32;
    private static readonly Encoding TextEncoding = new UTF8Encoding(false, true);
    public static byte[] Encode(string text) {
        byte[] payload = TextEncoding.GetBytes(text);
        if (payload.Length > MaxPayloadBytes) throw new InvalidDataException("Payload limit");
        byte[] result = new byte[3 + payload.Length]; result[0] = 1;
        BinaryPrimitives.WriteUInt16BigEndian(result.AsSpan(1, 2), checked((ushort)payload.Length));
        payload.CopyTo(result, 3); return result;
    }
    // The caller owns input. This format accepts exactly one complete frame.
    public static string Read(Stream input) {
        Span<byte> header = stackalloc byte[3]; input.ReadExactly(header);
        if (header[0] != 1) throw new InvalidDataException("Version");
        int length = BinaryPrimitives.ReadUInt16BigEndian(header.Slice(1));
        if (length > MaxPayloadBytes) throw new InvalidDataException("Payload limit");
        byte[] payload = new byte[length]; input.ReadExactly(payload);
        if (input.ReadByte() != -1) throw new InvalidDataException("Trailing bytes");
        return TextEncoding.GetString(payload);
    }
}

Testing/CappedReadStream.cs

public sealed class CappedReadStream : Stream {
    private readonly MemoryStream inner;
    public CappedReadStream(byte[] bytes) { inner = new MemoryStream(bytes, writable: false); }
    public override bool CanRead => inner.CanRead;
    public override bool CanSeek => false;
    public override bool CanWrite => false;
    public override long Length => throw new NotSupportedException();
    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
    public override int Read(byte[] buffer, int offset, int count) {
        ArgumentNullException.ThrowIfNull(buffer); ArgumentOutOfRangeException.ThrowIfNegative(offset); ArgumentOutOfRangeException.ThrowIfNegative(count);
        if (offset > buffer.Length - count) throw new ArgumentException("Invalid buffer range");
        return inner.Read(buffer, offset, Math.Min(count, 1));
    }
    public override int Read(Span<byte> buffer) => inner.Read(buffer.Slice(0, Math.Min(buffer.Length, 1)));
    public override void Flush() { }
    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
    public override void SetLength(long value) => throw new NotSupportedException();
    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
    protected override void Dispose(bool disposing) { if (disposing) inner.Dispose(); base.Dispose(disposing); }
}

Testing/FrameChecks.cs

using System.Text;
public static class FrameChecks {
    public static void Run() {
        int checks = 0;
        void Check(bool condition) { if (!condition) throw new Exception("Check failed"); checks++; }
        void Reject<T>(byte[] data) where T : Exception {
            using var input = new MemoryStream(data, writable: false);
            try { FrameCodec.Read(input); } catch (T) { checks++; return; }
            throw new Exception("Expected " + typeof(T).Name);
        }
        byte[] frame = FrameCodec.Encode("rune");
        Check(Convert.ToHexString(frame) == "01000472756E65");
        using (var input = new CappedReadStream(frame)) Check(FrameCodec.Read(input) == "rune" && input.CanRead);
        using (var input = new MemoryStream(FrameCodec.Encode("λ"))) Check(FrameCodec.Read(input) == "λ");
        Reject<InvalidDataException>(new byte[] { 2, 0, 0 });
        Reject<InvalidDataException>(new byte[] { 1, 0, 33 });
        Reject<EndOfStreamException>(new byte[] { 1, 0, 2, 65 });
        Reject<DecoderFallbackException>(new byte[] { 1, 0, 2, 0xC3, 0x28 });
        Reject<InvalidDataException>(new byte[] { 1, 0, 0, 99 });
        using (var input = new CappedReadStream(frame)) {
            void RejectRead(Action action) {
                try { action(); } catch (ArgumentException) { checks++; return; }
                throw new Exception("Expected invalid read rejection");
            }
            RejectRead(() => input.Read(null!, 0, 0));
            RejectRead(() => input.Read(new byte[4], -1, 1));
            RejectRead(() => input.Read(new byte[4], 0, -1));
            RejectRead(() => input.Read(new byte[4], 0, 5));
        }
        Console.WriteLine("frame checks:" + checks);
        Console.WriteLine("transport:memory only");
    }
}

FramedMessage.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>