Table of Contents

Class MemoryWalker

Namespace
CStructSharp.Memory
Assembly
CStructSharp.dll

Bounded traversals over linked structures in memory: circular sentinel lists and trees with shared children.

public static class MemoryWalker
Inheritance
MemoryWalker
Inherited Members

Remarks

MemorySession follows one explicit path. Real analysis repeats that step across many nodes: walk the task list, visit every child of a tree. Repetition over untrusted bytes needs safeguards that a single read does not: a corrupt link can form a cycle, a shared child can appear twice, and a list can be far longer than expected. This class supplies those safeguards (node limits, one shared work budget, and identity tracking by source object and address) while a caller-supplied callback supplies the format-specific part: how to read a link, or which entries of a node are children.

The helpers impose no operating-system conventions on the schema language. A sentinel list excludes its head from the result because the head is not a data item; a tree includes its root and visits each distinct node once. Only unavailable memory (Unmapped or MissingBytes) becomes a partial MemoryWalkResult; cancellation, budget exhaustion, and other failures propagate as exceptions so an incomplete traversal can never be mistaken for a complete one.

Methods

ContainingRecord(ulong, ulong)

Recovers the address of a record from the address of one of its members, as C's container_of does.

public static ulong ContainingRecord(ulong memberAddress, ulong memberOffset)

Parameters

memberAddress ulong

Address of the embedded member.

memberOffset ulong

Byte offset of that member within its containing record, from the schema.

Returns

ulong

The unsigned address of the containing record.

Remarks

Intrusive lists store their link field inside the data record, so a walk yields link addresses and the record starts memberOffset bytes earlier. The subtraction is checked; choosing the right containing type and confirming its bytes exist remain the caller's job.

SentinelList(MemoryRegion, Func<MemoryRegion, MemoryAccessContext, MemoryRegion>, int, MemoryAccessContext?, Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>?)

Follows links from a sentinel head until the walk returns to it, repeats a data node, hits the node limit, or finds memory missing.

public static MemoryWalkResult SentinelList(MemoryRegion sentinel, Func<MemoryRegion, MemoryAccessContext, MemoryRegion> next, int maxNodes = 100000, MemoryAccessContext? context = null, Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>? identity = null)

Parameters

sentinel MemoryRegion

List head whose source and address mark successful completion.

next Func<MemoryRegion, MemoryAccessContext, MemoryRegion>

Reads one node's link and returns the next node's region, using the shared context.

maxNodes int

Maximum number of data nodes to return.

context MemoryAccessContext

Shared operation budget and cancellation, or null to create a default budget.

identity Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>

Optional canonical source/address key for aliased source views. The callback must preserve distinct address spaces and forward the context to any translation. Null uses source object identity and address. Returned nodes retain their original source views.

Returns

MemoryWalkResult

The data nodes visited, in order, and the reason the walk stopped.

Remarks

A circular sentinel list has a head node that holds no data; the last data node links back to it. The callback is called first on the head and then on each data node, returning the region the node's link points at. Coming back to the head's source object and address is success. Reaching a data node that was already returned is a cycle and is reported as RepeatedNode. The head is not included in Nodes.

The limit is checked after resolving the next link, so maxNodes bounds the returned collection, not the exact number of reads. The callback must pass its MemoryAccessContext argument to any session read so the whole walk shares one budget.

Tree(MemoryRegion, Func<MemoryRegion, MemoryAccessContext, IEnumerable<MemoryRegion>>, int, MemoryAccessContext?, Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>?)

Visits every distinct node reachable from a root, asking the callback for each node's children.

public static MemoryWalkResult Tree(MemoryRegion root, Func<MemoryRegion, MemoryAccessContext, IEnumerable<MemoryRegion>> children, int maxNodes = 100000, MemoryAccessContext? context = null, Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>? identity = null)

Parameters

root MemoryRegion

Region of the root node.

children Func<MemoryRegion, MemoryAccessContext, IEnumerable<MemoryRegion>>

Returns the child regions of a node, using the shared context for any reads.

maxNodes int

Maximum number of nodes to return.

context MemoryAccessContext

Shared operation budget and cancellation, or null to create a default budget.

identity Func<MemoryRegion, MemoryAccessContext, (IMemorySource Source, ulong Address)>

Optional canonical source/address key for aliased views; null keeps source object identity and address. Translation must use the shared context. Returned nodes retain their original source views.

Returns

MemoryWalkResult

The nodes visited, in order, and the reason the walk stopped.

Remarks

This is an iterative depth-first walk over an explicit stack, so a deep tree cannot overflow the call stack. Because children are pushed in enumeration order and popped last-in first-out, they are visited in reverse of that order. A node is identified by its source object and address; length and label are not part of the key. A child shared by several parents is visited once, and a cycle terminates through the same check.

A node is added to the result before its children are requested, so a partial result includes the node whose child lookup failed. The callback owns the interpretation of tagged entries and leaf values; it must enumerate finitely and pass the shared context to any reads.