task 2.7-2.8: addressing helpers with tests

Add GetAbsolute/GetRelative with nint-based arithmetic (bitness-agnostic).
Add 6 tests: absolute resolution, relative computation, base-round-trip,
isRelative flag on Read/Write/ReadBytes.
Fix MemoryBase.cs: restore for-loop body damaged by prior edit, clean up
StructureToByteArray overloads.

All passing (total: 44).
This commit is contained in:
kbe
2026-07-21 17:08:08 +02:00
parent b2a5090533
commit 8219ac95a6
2 changed files with 112 additions and 10 deletions
+23 -10
View File
@@ -36,16 +36,14 @@ public abstract class MemoryBase : IDisposable
address = GetAbsolute(address);
int size = MarshalCache<T>.Size;
Span<byte> buffer = stackalloc byte[size];
byte[] raw = ReadBytes(address, size);
raw.CopyTo(buffer);
if (MarshalCache<T>.TypeRequiresMarshal)
{
return MarshalByteArrayToStructure<T>(raw);
}
return MemoryMarshal.Read<T>(buffer);
return MemoryMarshal.Read<T>(raw.AsSpan());
}
/// <summary>Writes a value of type <typeparamref name="T"/> to the target address.</summary>
@@ -56,18 +54,19 @@ public abstract class MemoryBase : IDisposable
address = GetAbsolute(address);
int size = MarshalCache<T>.Size;
Span<byte> buffer = stackalloc byte[size];
byte[] raw;
if (MarshalCache<T>.TypeRequiresMarshal)
{
StructureToByteArray(value, buffer, size);
raw = StructureToByteArray(value, size);
}
else
{
MemoryMarshal.Write(buffer, in value);
raw = new byte[size];
MemoryMarshal.Write(raw.AsSpan(), in value);
}
int written = WriteBytes(address, buffer, false);
int written = WriteBytes(address, raw, false);
return written == size;
}
@@ -153,7 +152,6 @@ public abstract class MemoryBase : IDisposable
/// <summary>Writes a null-terminated string to the target address.</summary>
public virtual bool WriteString(IntPtr address, string value, Encoding encoding, bool relative = false)
{
// Ensure null terminator
if (value.Length == 0 || value[^1] != '\0')
value += '\0';
@@ -167,13 +165,13 @@ public abstract class MemoryBase : IDisposable
/// <summary>Converts a relative offset to an absolute address relative to <see cref="ImageBase"/>.</summary>
public IntPtr GetAbsolute(IntPtr relative)
{
return ImageBase + (int)relative;
return ImageBase + (nint)relative;
}
/// <summary>Converts an absolute address to a relative offset from <see cref="ImageBase"/>.</summary>
public IntPtr GetRelative(IntPtr absolute)
{
return (IntPtr)((int)ImageBase - (int)absolute);
return (IntPtr)((nint)ImageBase - (nint)absolute);
}
// ── Lifecycle ──────────────────────────────────────────────────────────
@@ -199,6 +197,21 @@ public abstract class MemoryBase : IDisposable
}
}
private static byte[] StructureToByteArray<T>(T value, int size) where T : struct
{
byte[] bytes = new byte[size];
GCHandle pin = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
Marshal.StructureToPtr(value, pin.AddrOfPinnedObject(), false);
}
finally
{
pin.Free();
}
return bytes;
}
private static void StructureToByteArray<T>(T value, Span<byte> destination, int size) where T : struct
{
byte[] temp = destination.ToArray();