| File: EnumMemory\MiniMetadataWriter.cs | Web Access |
| Project: Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Text; using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader.Legacy.EnumMemory; internal sealed class MiniMetadataWriter(Target target, MemoryRegionEmitter emitter) { private const uint MiniMetadataSignature = 0x6d727473; private const uint EENameStreamSignature = 0x614e4545; private const int StreamsHeaderSize = 12; private const int EENameStreamHeaderSize = 8; private readonly OrderedDictionary<TargetPointer, string> _names = []; public bool Contains(TargetPointer address) => _names.ContainsKey(address); public void Add(TargetPointer address, string name) => _names.Add(address, name); public void Write() { TargetPointer bufferAddress = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffAddress)); uint capacity = target.Read<uint>(target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffMaxSize)); if (bufferAddress == TargetPointer.Null || capacity < StreamsHeaderSize + EENameStreamHeaderSize) { return; } if (target.Read<uint>(bufferAddress) == MiniMetadataSignature) { uint size = target.Read<uint>(bufferAddress + sizeof(uint)); if (size < StreamsHeaderSize + EENameStreamHeaderSize || size > capacity) throw new InvalidOperationException($"Invalid mini-metadata stream size: {size}."); byte[] existing = new byte[size]; target.ReadBuffer(bufferAddress, existing); emitter.Add(bufferAddress.Value, size); emitter.Update(bufferAddress.Value, existing); return; } byte[] buffer = new byte[capacity]; int offset = StreamsHeaderSize + EENameStreamHeaderSize; uint count = 0; foreach ((TargetPointer address, string name) in _names) { int nameSize = Encoding.UTF8.GetByteCount(name); int entrySize = checked(target.PointerSize + nameSize + 1); if (offset > buffer.Length - entrySize) continue; WritePointer(buffer.AsSpan(offset, target.PointerSize), address.Value, target); offset += target.PointerSize; offset += Encoding.UTF8.GetBytes(name, buffer.AsSpan(offset, nameSize)); buffer[offset++] = 0; count++; } int padding = (target.PointerSize - offset % target.PointerSize) % target.PointerSize; int sizeWritten = offset + Math.Min(padding, buffer.Length - offset); WriteUInt32(buffer.AsSpan(0, sizeof(uint)), MiniMetadataSignature, target); WriteUInt32(buffer.AsSpan(4, sizeof(uint)), checked((uint)sizeWritten), target); WriteUInt32(buffer.AsSpan(8, sizeof(uint)), 1, target); WriteUInt32(buffer.AsSpan(StreamsHeaderSize, sizeof(uint)), EENameStreamSignature, target); WriteUInt32(buffer.AsSpan(StreamsHeaderSize + 4, sizeof(uint)), count, target); emitter.Add(bufferAddress.Value, checked((uint)sizeWritten)); emitter.Update(bufferAddress.Value, buffer.AsSpan(0, sizeWritten)); } private static void WritePointer(Span<byte> destination, ulong value, Target target) { if (target.PointerSize == sizeof(uint)) { WriteUInt32(destination, checked((uint)value), target); } else if (target.IsLittleEndian) { BinaryPrimitives.WriteUInt64LittleEndian(destination, value); } else { BinaryPrimitives.WriteUInt64BigEndian(destination, value); } } private static void WriteUInt32(Span<byte> destination, uint value, Target target) { if (target.IsLittleEndian) BinaryPrimitives.WriteUInt32LittleEndian(destination, value); else BinaryPrimitives.WriteUInt32BigEndian(destination, value); } }