namespace WinDbg;
using System;
/// <summary>
/// represents a class for working with type dumps of <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/">WinDbg</see>
/// <see href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/dt--display-type-">td</see> command.
/// </summary>
internal class DisplayTypeDump
{
private readonly string[] dump;
/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">an array of strings representing the type dump.</param>
internal DisplayTypeDump(string[] dump) => this.dump = dump;
/// <summary>
/// initializes a new instance of the <see cref="DisplayTypeDump"/> class.
/// </summary>
/// <param name="dump">a string representing the type dump.</param>
/// <param name="seperator">the separator used to split the dump string.</param>
internal DisplayTypeDump(string dump, string seperator) =>
this.dump = dump.Split(seperator);
/// <summary>
/// represents the 32nd character of the ASCII table.
/// </summary>
private const char BlankSpace = (char)ConsoleKey.Spacebar;
/// <summary>
/// gets the hexadecimal value from a given string.
/// </summary>
/// <param name="vessel">the input string containing the hexadecimal value.</param>
/// <returns>the hexadecimal value as an integer.</returns>
internal static int GetHexValue(string vessel)
{
const int HexBase = 16;
var start = new Index(3);
var end = new Index(vessel.IndexOf(BlankSpace, 6));
var hexNumberString = vessel[start..end];
var hexValue = Convert.ToInt32(hexNumberString, HexBase);
return hexValue;
}
/// <summary>
/// gets the field name from the input string.
/// </summary>
/// <param name="vessel">the input string containing the field name.</param>
/// <returns>the extracted field name.</returns>
internal static string GetFieldName(string vessel)
{
var start = new Index(7);
var end = new Index(vessel.IndexOf(BlankSpace, start.Value));
var fieldName = vessel[start..end];
return fieldName;
}
/// <summary>
/// gets the offset in bytes for a target field in the type dump.
/// </summary>
/// <param name="target">the target field whose offset is to be calculated.</param>
/// <param name="startField">the optional starting field for offset calculation.</param>
/// <returns>the offset in bytes.</returns>
internal int GetOffsetInBytes(string target, string startField = null!)
{
var previousHex = 0;
var startFieldProvided = !string.IsNullOrEmpty(startField);
var offsetSum = 0;
for (var i = 0; i < dump.Length; ++i)
{
var fieldName = GetFieldName(dump[i]);
// target reached!
if (fieldName.Equals(target)) break;
var currentHex = GetHexValue(dump[i]);
// skip duplicates of sequentially laid values,
// only considering the first occurrence.
// --------------------------------------------
// potentially skipping the very first value
// of the array since it's often 0.
if (currentHex == previousHex) continue;
if (startFieldProvided && !fieldName.Equals(startField))
{
offsetSum -= currentHex;
continue;
}
offsetSum += currentHex;
previousHex = currentHex;
}
return Math.Abs(offsetSum);
}
}