DataReader
On this page
Nalix.Environment.Memory.DataReader is a high-performance, allocation-free ref struct used for reading serialized data from various buffer types.
It provides a unified reading abstraction over managed arrays, unmanaged memory pointers, spans, and memories, avoiding the need for multiple overloads in serialization code.
Supported Data Sources
DataReader can be constructed around any of the following underlying types:
byte[]: Managed arrays.ReadOnlySpan<byte>: Read-only spans.ReadOnlyMemory<byte>: Read-only memory chunks.byte*: Unmanaged raw pointers.
Key Properties & Lifecycle
BytesRead(int): The number of bytes that have been consumed/advanced from the buffer.BytesRemaining(int): The number of bytes still available to be read.IsFailed(bool): Gets or sets whether a non-throwing read operation encountered a bounds or format error.Dispose()(void): Resets the reader state and releases any pinned memory handle.
Reading Data
DataReader is designed to be fast and inlineable. It avoids maintaining complex state, relying simply on a reference to the buffer and a position index.
1. GetSpanReference
public readonly ref byte GetSpanReference(int sizeHint)
Retrieves a reference to the first byte in the requested span, ensuring that at least sizeHint bytes are available. If sizeHint exceeds BytesRemaining, a SerializationFailureException is thrown.
This method allows for highly optimized, zero-copy reading using Unsafe.ReadUnaligned or similar memory operations.
2. Advance
public void Advance(int count)
Advances the internal read position by count bytes. This is typically called immediately after reading data via the reference obtained from GetSpanReference.
Example Usage
public bool ReadBoolean(ref DataReader reader)
{
// 1. Get reference to the next byte
ref byte b = ref reader.GetSpanReference(sizeof(byte));
// 2. Advance the reader position
reader.Advance(sizeof(byte));
// 3. Return the parsed value
return b != 0;
}
LEB128 VarInt Encoding (Nalix.Environment.Memory.Leb128)
Leb128 (src/Nalix.Environment/Memory/Leb128.cs) provides branch-free, pointer-accelerated Little-Endian Base 128 (LEB128) variable-length integer encoding and decoding (MaxByteCount = 5) used by TransportFraming.VarInt and protocol headers:
namespace Nalix.Environment.Memory;
public static class Leb128
{
public const int MaxByteCount = 5;
public static int GetByteCount(uint value);
public static int GetByteCount(int value);
public static int Write(Span<byte> buffer, uint value);
public static int Write(Span<byte> buffer, int value);
public static int WriteUnrolled(Span<byte> buffer, uint value);
public static bool TryRead(ReadOnlySpan<byte> buffer, out uint value, out int bytesRead);
public static bool TryRead(ReadOnlySpan<byte> buffer, out int value, out int bytesRead);
}
GetByteCountcomputes the exact encoded byte length (1..5) in $O(1)$ usingBitOperations.LeadingZeroCount.WriteandWriteUnrolledencodevalueintobufferand throwArgumentExceptionifbuffer.Length < GetByteCount(value).TryReadreturnsfalsewhenbufferends mid-sequence (allowing stream framing readers to wait for more socket bytes) and throwsFormatExceptionif a sequence exceeds 5 bytes (OverlongLeb128).