| File: Data\DacEnumerableHash.cs | Web Access |
| Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; namespace Microsoft.Diagnostics.DataContractReader.Data; /// <summary> /// Parses hash tables that are implemented by DacEnumerableHash defined in dacenumerablehash.h /// Requires the following datadescriptor fields on the passed type: /// Buckets - Pointer to array of VolatileEntry pointers /// Count - Count of elements /// VolatileEntryValue - Offset of the value in the VolatileEntry struct /// VolatileEntryNextEntry - Offset of the next entry pointer in the VolatileEntry struct /// </summary> internal sealed class DacEnumerableHash { private const int SLOT_LENGTH = 0; private const int SKIP_SPECIAL_SLOTS = 3; private const int END_SENTINEL = 0x1; private readonly Target _target; private readonly Target.TypeInfo _type; public DacEnumerableHash(Target target, TargetPointer address, Target.TypeInfo type) { // init fields _target = target; _type = type; Buckets = _target.ReadPointerField(address, _type, nameof(Buckets)); Count = _target.ReadField<uint>(address, _type, nameof(Count)); // read items in the hash table uint length = GetLength(); List<TargetPointer> entries = []; for (int i = 0; i < length; i++) { // indexes 0, 1, 2 have special purposes. buckets start at SKIP_SPECIAL_SLOTS int bucketOffset = i + SKIP_SPECIAL_SLOTS; TargetPointer chainElement = _target.ReadPointer(Buckets + (ulong)(bucketOffset * _target.PointerSize)); List<TargetPointer> elements = ReadChain(chainElement); entries.AddRange(elements); } // In STRESS testing, we may stop while this table is resizing, so we // can't assert that Count equals the number of walked entries. Entries = entries; } public TargetPointer Buckets { get; init; } public uint Count { get; init; } public IReadOnlyList<TargetPointer> Entries { get; init; } internal sealed class VolatileEntry { public VolatileEntry(Target target, TargetPointer address, Target.TypeInfo type) { // offsets are stored on the parent type VolatileEntryValue = address + (ulong)type.Fields[nameof(VolatileEntryValue)].Offset; VolatileEntryNextEntry = target.ReadPointerField(address, type, nameof(VolatileEntryNextEntry)); } public TargetPointer VolatileEntryValue { get; init; } public TargetPointer VolatileEntryNextEntry { get; init; } } private uint GetLength() { // First pointer is a size_t length TargetPointer length = _target.ReadPointer(Buckets + (ulong)(SLOT_LENGTH * _target.PointerSize)); return (uint)length; } private static bool IsEndSentinel(TargetPointer value) { return ((ulong)value & END_SENTINEL) == END_SENTINEL; } private List<TargetPointer> ReadChain(TargetPointer chainElement) { List<TargetPointer> elements = []; while (!IsEndSentinel(chainElement)) { VolatileEntry volatileEntry = new(_target, chainElement, _type); elements.Add(volatileEntry.VolatileEntryValue); chainElement = volatileEntry.VolatileEntryNextEntry; } return elements; } }