Inspect byte ranges and addresses
Normal parsing tells you what the data means. Diagnostic parsing also tells you which bytes produced each value. This is useful for hex viewers, format inspectors, and error reports.
Use ParseWithDebug when you need a struct's values and ranges together; it returns a ParseResult whose Value
is the same StructValue that Parse returns and whose Debug list holds one DebugData record per value read.
ReadValueWithDebug does the same for any selection (a union, an array, a scalar) and returns a ReadResult. Use
ResolveAddress when you need only the absolute stream position of one path. DebugData lives in the
CStructSharp.Diagnostics namespace.
Capture value ranges
The executable example uses a three-byte packed structure:
struct sample {
uint8 tag;
uint16 value;
};
private static void InspectRanges()
{
var layout = new CStruct("struct sample { uint8 tag; uint16 value; };");
using var stream = new MemoryStream([0xA1, 0x34, 0x12]);
(StructValue result, IReadOnlyList<DebugData> ranges) = layout.ParseWithDebug(stream, "sample");
Equal((byte)0xA1, result.Get<byte>("tag"));
True(ranges.Any(item => item.Start == 1 && item.End == 3), "Value range was not reported.");
stream.Position = 0;
Equal(1L, layout.ResolveAddress(stream, "sample.value"));
Equal(0L, stream.Position);
}
With input A1 34 12, sample.value occupies the half-open range [1, 3): it starts at position 1 and ends just
before position 3. Half-open ranges make the byte count easy to calculate: End - Start, or 3 - 1 = 2; the
record's Length property does this for you.
A typedef root keeps the name you selected. For typedef sample packet;, reading packet reports
packet.value, not sample.value or packet.packet.value. Inline and chained aliases follow the same rule.
Each DebugData record is an immutable value with the item's Path (sample.value), Start and End
positions, TypeName, and decoded Value. Select the bytes from your own input with Start..End; only a union
captured as raw storage carries its bytes in Bytes. Treat records as diagnostic output. They expose exact input
values, so filter them before sending them outside your application's trusted logs or diagnostic tools.
Resolve a position without returning the value
long address = layout.ResolveAddress(stream, "sample.value");
The example receives address 1. ResolveAddress restores the caller's original stream position after the lookup,
so it can be used to annotate a stream without consuming the selected field.
Pointer paths need careful wording: root.pointer.address resolves the byte position where the pointer itself is
stored, while root.pointer.value follows one level and resolves the target position.
Cost and common mistakes
Debug capture does more work and allocates diagnostic records, so use ordinary reads in hot paths that do not need byte ranges. Both debug parsing and address resolution require a readable, seekable stream because traversal may revisit positions.
If a range looks wrong, verify the stream's position at operation entry, packed versus aligned placement, the root path, array indices, and whether a pointer accessor followed a target. All returned stream positions are absolute; they include any nonzero root starting position.
See Paths and selection for coordinate rules and
DebugData for the generated member reference.