File: MetaDataImportImpl.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Legacy\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.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

[GeneratedComClass]
internal sealed unsafe partial class MetaDataImportImpl : ICustomQueryInterface, IMetaDataImport2, IMetaDataAssemblyImport
{
    private readonly MetadataReader _reader;
    private readonly IMetaDataImport? _legacyImport;
    private readonly IMetaDataImport2? _legacyImport2;
    private readonly IMetaDataAssemblyImport? _legacyAssemblyImport;
    private Dictionary<int, uint>? _interfaceImplToTypeDef;
    private Dictionary<int, uint>? _paramToMethod;

    private static int GetRID(uint token) => (int)EcmaMetadataUtils.GetRowId(token);

    // Tracks GCHandle values allocated by AllocEnum so that CountEnum, ResetEnum,
    // and CloseEnum can distinguish cDAC-created enum handles from legacy HENUMInternal*.
    // ConcurrentDictionary is used because COM objects may be called from multiple threads.
    private readonly ConcurrentDictionary<nint, byte> _cdacEnumHandles = new();

    public MetaDataImportImpl(MetadataReader reader, IMetaDataImport? legacyImport = null)
    {
        _reader = reader;
        _legacyImport = legacyImport;
        _legacyImport2 = legacyImport as IMetaDataImport2;
        _legacyAssemblyImport = legacyImport as IMetaDataAssemblyImport;
    }

    CustomQueryInterfaceResult ICustomQueryInterface.GetInterface(ref Guid iid, out nint ppv)
    {
        ppv = default;

        if (iid == typeof(IMetaDataImport).GUID)
        {
            // ConvertToUnmanaged returns an already-AddRef'd IMetaDataImport2 COM pointer.
            // Return it directly so consumers get the full IMetaDataImport2 vtable.
            ppv = (nint)ComInterfaceMarshaller<IMetaDataImport2>.ConvertToUnmanaged(this);
            return CustomQueryInterfaceResult.Handled;
        }

        return CustomQueryInterfaceResult.NotHandled;
    }


    private sealed class MetadataEnum
    {
        public List<uint> Tokens { get; }
        public int Position { get; set; }

        public MetadataEnum(List<uint> tokens)
        {
            Tokens = tokens;
        }
    }

    private nint AllocEnum(List<uint> tokens)
    {
        MetadataEnum e = new(tokens);
        GCHandle handle = GCHandle.Alloc(e);
        nint ptr = GCHandle.ToIntPtr(handle);
        _cdacEnumHandles.TryAdd(ptr, 0);
        return ptr;
    }

    private MetadataEnum GetEnum(nint hEnum)
    {
        if (hEnum == 0 || !_cdacEnumHandles.ContainsKey(hEnum))
            throw new ArgumentException("Invalid enum handle.", nameof(hEnum));
        GCHandle handle = GCHandle.FromIntPtr(hEnum);
        return (MetadataEnum)(handle.Target ?? throw new ArgumentException("Enum handle target is null.", nameof(hEnum)));
    }

    private int FillEnum(nint* phEnum, List<uint> tokens, uint* rTokens, uint cMax, uint* pcTokens)
    {
        if (phEnum is null)
            return HResults.E_INVALIDARG;

        if (*phEnum == 0)
            *phEnum = AllocEnum(tokens);

        MetadataEnum e = GetEnum(*phEnum);
        uint count = 0;
        while (count < cMax && e.Position < e.Tokens.Count)
        {
            if (rTokens is not null)
                rTokens[count] = e.Tokens[e.Position];
            e.Position++;
            count++;
        }

        if (pcTokens is not null)
            *pcTokens = count;

        return count > 0 ? HResults.S_OK : HResults.S_FALSE;
    }

    void IMetaDataImport.CloseEnum(nint hEnum)
    {
        if (hEnum == 0)
            return;

        if (_cdacEnumHandles.TryRemove(hEnum, out _))
        {
            GCHandle handle = GCHandle.FromIntPtr(hEnum);
            handle.Free();
        }
        else
        {
            _legacyImport?.CloseEnum(hEnum);
        }
    }

    int IMetaDataImport.CountEnum(nint hEnum, uint* pulCount)
    {
        if (hEnum == 0)
        {
            if (pulCount is not null)
                *pulCount = 0;
            return HResults.S_OK;
        }

        if (_cdacEnumHandles.ContainsKey(hEnum))
        {
            MetadataEnum e = GetEnum(hEnum);
            if (pulCount is not null)
                *pulCount = (uint)e.Tokens.Count;
            return HResults.S_OK;
        }

        return _legacyImport is not null ? _legacyImport.CountEnum(hEnum, pulCount) : HResults.E_NOTIMPL;
    }

    int IMetaDataImport.ResetEnum(nint hEnum, uint ulPos)
    {
        if (hEnum == 0)
            return HResults.S_OK;

        if (_cdacEnumHandles.ContainsKey(hEnum))
        {
            MetadataEnum e = GetEnum(hEnum);
            e.Position = (int)Math.Min(ulPos, (uint)e.Tokens.Count);
            return HResults.S_OK;
        }

        return _legacyImport is not null ? _legacyImport.ResetEnum(hEnum, ulPos) : HResults.E_NOTIMPL;
    }

    int IMetaDataImport.EnumTypeDefs(nint* phEnum, uint* rTypeDefs, uint cMax, uint* pcTypeDefs)
    {
        int hr = HResults.S_OK;
        List<uint>? tokens = null;
        try
        {
            if (phEnum is not null && *phEnum != 0)
            {
                hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rTypeDefs, cMax, pcTypeDefs);
            }
            else
            {
                tokens = new();
                foreach (TypeDefinitionHandle h in _reader.TypeDefinitions)
                    tokens.Add((uint)MetadataTokens.GetToken(h));
                hr = FillEnum(phEnum, tokens, rTypeDefs, cMax, pcTypeDefs);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (tokens is not null && _legacyImport is not null)
        {
            nint hEnumLocal = 0;
            List<uint> legacyTokens = new();
            uint* buf = stackalloc uint[64];
            while (true)
            {
                uint count;
                int hrLegacy = _legacyImport.EnumTypeDefs(&hEnumLocal, buf, 64, &count);
                if (hrLegacy < 0 || count == 0) break;
                for (uint i = 0; i < count; i++)
                    legacyTokens.Add(buf[i]);
            }
            _legacyImport.CloseEnum(hEnumLocal);
            Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumTypeDefs count mismatch: cDAC={tokens.Count}, DAC={legacyTokens.Count}");
            for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++)
                Debug.Assert(tokens[i] == legacyTokens[i], $"EnumTypeDefs token mismatch at [{i}]: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumInterfaceImpls(nint* phEnum, uint td, uint* rImpls, uint cMax, uint* pcImpls)
    {
        int hr = HResults.S_OK;
        List<uint>? tokens = null;
        try
        {
            if (phEnum is not null && *phEnum != 0)
            {
                hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rImpls, cMax, pcImpls);
            }
            else
            {
                TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td));
                TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);
                tokens = new();
                foreach (InterfaceImplementationHandle h in typeDef.GetInterfaceImplementations())
                    tokens.Add((uint)MetadataTokens.GetToken(h));
                hr = FillEnum(phEnum, tokens, rImpls, cMax, pcImpls);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (tokens is not null && _legacyImport is not null)
        {
            nint hEnumLocal = 0;
            List<uint> legacyTokens = new();
            uint* buf = stackalloc uint[64];
            while (true)
            {
                uint count;
                int hrLegacy = _legacyImport.EnumInterfaceImpls(&hEnumLocal, td, buf, 64, &count);
                if (hrLegacy < 0 || count == 0) break;
                for (uint i = 0; i < count; i++)
                    legacyTokens.Add(buf[i]);
            }
            _legacyImport.CloseEnum(hEnumLocal);
            Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumInterfaceImpls count mismatch for 0x{td:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}");
            for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++)
                Debug.Assert(tokens[i] == legacyTokens[i], $"EnumInterfaceImpls token mismatch at [{i}] for 0x{td:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumTypeRefs(nint* phEnum, uint* rTypeRefs, uint cMax, uint* pcTypeRefs)
        => _legacyImport is not null ? _legacyImport.EnumTypeRefs(phEnum, rTypeRefs, cMax, pcTypeRefs) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMembers(nint* phEnum, uint cl, uint* rMembers, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumMembers(phEnum, cl, rMembers, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMethods(nint* phEnum, uint cl, uint* rMethods, uint cMax, uint* pcTokens)
    {
        int hr = HResults.S_OK;
        List<uint>? tokens = null;
        try
        {
            if (phEnum is not null && *phEnum != 0)
            {
                hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rMethods, cMax, pcTokens);
            }
            else
            {
                TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(cl));
                TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);
                tokens = new();
                foreach (MethodDefinitionHandle h in typeDef.GetMethods())
                    tokens.Add((uint)MetadataTokens.GetToken(h));
                hr = FillEnum(phEnum, tokens, rMethods, cMax, pcTokens);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (tokens is not null && _legacyImport is not null)
        {
            nint hEnumLocal = 0;
            List<uint> legacyTokens = new();
            uint* buf = stackalloc uint[64];
            while (true)
            {
                uint count;
                int hrLegacy = _legacyImport.EnumMethods(&hEnumLocal, cl, buf, 64, &count);
                if (hrLegacy < 0 || count == 0) break;
                for (uint i = 0; i < count; i++)
                    legacyTokens.Add(buf[i]);
            }
            _legacyImport.CloseEnum(hEnumLocal);
            Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumMethods count mismatch for 0x{cl:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}");
            for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++)
                Debug.Assert(tokens[i] == legacyTokens[i], $"EnumMethods token mismatch at [{i}] for 0x{cl:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumFields(nint* phEnum, uint cl, uint* rFields, uint cMax, uint* pcTokens)
    {
        int hr = HResults.S_OK;
        List<uint>? tokens = null;
        try
        {
            if (phEnum is not null && *phEnum != 0)
            {
                hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rFields, cMax, pcTokens);
            }
            else
            {
                TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(cl));
                TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);
                tokens = new();
                foreach (FieldDefinitionHandle h in typeDef.GetFields())
                    tokens.Add((uint)MetadataTokens.GetToken(h));
                hr = FillEnum(phEnum, tokens, rFields, cMax, pcTokens);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (tokens is not null && _legacyImport is not null)
        {
            nint hEnumLocal = 0;
            List<uint> legacyTokens = new();
            uint* buf = stackalloc uint[64];
            while (true)
            {
                uint count;
                int hrLegacy = _legacyImport.EnumFields(&hEnumLocal, cl, buf, 64, &count);
                if (hrLegacy < 0 || count == 0) break;
                for (uint i = 0; i < count; i++)
                    legacyTokens.Add(buf[i]);
            }
            _legacyImport.CloseEnum(hEnumLocal);
            Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumFields count mismatch for 0x{cl:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}");
            for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++)
                Debug.Assert(tokens[i] == legacyTokens[i], $"EnumFields token mismatch at [{i}] for 0x{cl:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumCustomAttributes(nint* phEnum, uint tk, uint tkType, uint* rCustomAttributes, uint cMax, uint* pcCustomAttributes)
        => _legacyImport is not null ? _legacyImport.EnumCustomAttributes(phEnum, tk, tkType, rCustomAttributes, cMax, pcCustomAttributes) : HResults.E_NOTIMPL;

    int IMetaDataImport2.EnumGenericParams(nint* phEnum, uint tk, uint* rGenericParams, uint cMax, uint* pcGenericParams)
    {
        int hr = HResults.S_OK;
        List<uint>? tokens = null;
        try
        {
            if (phEnum is not null && *phEnum != 0)
            {
                hr = FillEnum(phEnum, GetEnum(*phEnum).Tokens, rGenericParams, cMax, pcGenericParams);
            }
            else
            {
                EntityHandle owner = MetadataTokens.EntityHandle((int)tk);
                GenericParameterHandleCollection genericParams;

                if (owner.Kind == HandleKind.TypeDefinition)
                    genericParams = _reader.GetTypeDefinition((TypeDefinitionHandle)owner).GetGenericParameters();
                else if (owner.Kind == HandleKind.MethodDefinition)
                    genericParams = _reader.GetMethodDefinition((MethodDefinitionHandle)owner).GetGenericParameters();
                else
                {
                    throw new ArgumentException(null, nameof(tk));
                }

                tokens = new();
                foreach (GenericParameterHandle h in genericParams)
                    tokens.Add((uint)MetadataTokens.GetToken(h));
                hr = FillEnum(phEnum, tokens, rGenericParams, cMax, pcGenericParams);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (tokens is not null && _legacyImport2 is not null)
        {
            nint hEnumLocal = 0;
            List<uint> legacyTokens = new();
            uint* buf = stackalloc uint[64];
            while (true)
            {
                uint count;
                int hrLegacy = _legacyImport2.EnumGenericParams(&hEnumLocal, tk, buf, 64, &count);
                if (hrLegacy < 0 || count == 0) break;
                for (uint i = 0; i < count; i++)
                    legacyTokens.Add(buf[i]);
            }
            _legacyImport2.CloseEnum(hEnumLocal);
            Debug.Assert(tokens.Count == legacyTokens.Count, $"EnumGenericParams count mismatch for 0x{tk:X}: cDAC={tokens.Count}, DAC={legacyTokens.Count}");
            for (int i = 0; i < Math.Min(tokens.Count, legacyTokens.Count); i++)
                Debug.Assert(tokens[i] == legacyTokens[i], $"EnumGenericParams token mismatch at [{i}] for 0x{tk:X}: cDAC=0x{tokens[i]:X}, DAC=0x{legacyTokens[i]:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetTypeDefProps(uint td, char* szTypeDef, uint cchTypeDef, uint* pchTypeDef, uint* pdwTypeDefFlags, uint* ptkExtends)
    {
        int hr = HResults.S_OK;
        try
        {
            TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td));
            TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);

            string fullName = GetTypeDefFullName(typeDef);
            OutputBufferHelpers.CopyStringToBuffer(szTypeDef, cchTypeDef, pchTypeDef, fullName, out bool truncated);

            if (pdwTypeDefFlags is not null)
                *pdwTypeDefFlags = (uint)typeDef.Attributes;

            if (ptkExtends is not null)
            {
                EntityHandle baseType = typeDef.BaseType;
                *ptkExtends = baseType.IsNil ? 0 : (uint)MetadataTokens.GetToken(baseType);
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint flagsLocal = 0, extendsLocal = 0, pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchTypeDef];
            int hrLegacy = _legacyImport.GetTypeDefProps(td, szLocal, cchTypeDef, &pchLocal, &flagsLocal, &extendsLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pdwTypeDefFlags is not null)
                    Debug.Assert(*pdwTypeDefFlags == flagsLocal, $"TypeDefFlags mismatch: cDAC=0x{*pdwTypeDefFlags:X}, DAC=0x{flagsLocal:X}");
                if (ptkExtends is not null)
                    Debug.Assert(*ptkExtends == extendsLocal, $"Extends mismatch: cDAC=0x{*ptkExtends:X}, DAC=0x{extendsLocal:X}");
                if (pchTypeDef is not null)
                    Debug.Assert(*pchTypeDef == pchLocal, $"Name length mismatch: cDAC={*pchTypeDef}, DAC={pchLocal}");
                if (szTypeDef is not null && cchTypeDef > 0)
                {
                    string cdacName = new string(szTypeDef);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"TypeDef name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetTypeRefProps(uint tr, uint* ptkResolutionScope, char* szName, uint cchName, uint* pchName)
    {
        int hr = HResults.S_OK;
        try
        {
            TypeReferenceHandle refHandle = MetadataTokens.TypeReferenceHandle(GetRID(tr));
            TypeReference typeRef = _reader.GetTypeReference(refHandle);

            string fullName = GetTypeRefFullName(typeRef);
            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, fullName, out bool truncated);

            if (ptkResolutionScope is not null)
            {
                EntityHandle scope = typeRef.ResolutionScope;
                *ptkResolutionScope = scope.IsNil ? 0 : (uint)MetadataTokens.GetToken(scope);
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint scopeLocal = 0, pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyImport.GetTypeRefProps(tr, &scopeLocal, szLocal, cchName, &pchLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (ptkResolutionScope is not null)
                    Debug.Assert(*ptkResolutionScope == scopeLocal, $"ResolutionScope mismatch: cDAC=0x{*ptkResolutionScope:X}, DAC=0x{scopeLocal:X}");
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (szName is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"TypeRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetMethodProps(uint mb, uint* pClass, char* szMethod, uint cchMethod, uint* pchMethod,
        uint* pdwAttr, byte** ppvSigBlob, uint* pcbSigBlob, uint* pulCodeRVA, uint* pdwImplFlags)
    {
        int hr = HResults.S_OK;
        try
        {
            MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(mb));
            MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle);

            string name = _reader.GetString(methodDef.Name);
            OutputBufferHelpers.CopyStringToBuffer(szMethod, cchMethod, pchMethod, name, out bool truncated);

            if (pClass is not null)
                *pClass = MapGlobalParentToken((uint)MetadataTokens.GetToken(methodDef.GetDeclaringType()));

            if (pdwAttr is not null)
                *pdwAttr = (uint)methodDef.Attributes;

            if (ppvSigBlob is not null || pcbSigBlob is not null)
            {
                BlobHandle sigHandle = methodDef.Signature;
                BlobReader blobReader = _reader.GetBlobReader(sigHandle);
                if (ppvSigBlob is not null)
                    *ppvSigBlob = blobReader.StartPointer;
                if (pcbSigBlob is not null)
                    *pcbSigBlob = (uint)blobReader.Length;
            }

            if (pulCodeRVA is not null)
                *pulCodeRVA = (uint)methodDef.RelativeVirtualAddress;

            if (pdwImplFlags is not null)
                *pdwImplFlags = (uint)methodDef.ImplAttributes;

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint classLocal = 0, attrLocal = 0, rvaLocal = 0, implLocal = 0, pchLocal = 0, cbSigLocal = 0;
            byte* sigLocal = null;
            char* szLocal = stackalloc char[(int)cchMethod];
            int hrLegacy = _legacyImport.GetMethodProps(mb, &classLocal, szLocal, cchMethod, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &rvaLocal, &implLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pClass is not null)
                    Debug.Assert(*pClass == classLocal, $"Class mismatch: cDAC=0x{*pClass:X}, DAC=0x{classLocal:X}");
                if (pdwAttr is not null)
                    Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}");
                if (pchMethod is not null)
                    Debug.Assert(*pchMethod == pchLocal, $"Name length mismatch: cDAC={*pchMethod}, DAC={pchLocal}");
                if (szMethod is not null && cchMethod > 0)
                {
                    string cdacName = new string(szMethod);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"Method name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (pulCodeRVA is not null)
                    Debug.Assert(*pulCodeRVA == rvaLocal, $"RVA mismatch: cDAC=0x{*pulCodeRVA:X}, DAC=0x{rvaLocal:X}");
                if (pdwImplFlags is not null)
                    Debug.Assert(*pdwImplFlags == implLocal, $"ImplFlags mismatch: cDAC=0x{*pdwImplFlags:X}, DAC=0x{implLocal:X}");
                if (ppvSigBlob is not null)
                    ValidateBlobsEqual(*ppvSigBlob, pcbSigBlob is not null ? *pcbSigBlob : cbSigLocal, sigLocal, cbSigLocal, "MethodSig");
                else if (pcbSigBlob is not null)
                    Debug.Assert(*pcbSigBlob == cbSigLocal, $"SigBlob length mismatch: cDAC={*pcbSigBlob}, DAC={cbSigLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetFieldProps(uint mb, uint* pClass, char* szField, uint cchField, uint* pchField,
        uint* pdwAttr, byte** ppvSigBlob, uint* pcbSigBlob, uint* pdwCPlusTypeFlag,
        void** ppValue, uint* pcchValue)
    {
        int hr = HResults.S_OK;
        try
        {
            FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle(GetRID(mb));
            FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle);

            string name = _reader.GetString(fieldDef.Name);
            OutputBufferHelpers.CopyStringToBuffer(szField, cchField, pchField, name, out bool truncated);

            if (pClass is not null)
                *pClass = MapGlobalParentToken((uint)MetadataTokens.GetToken(fieldDef.GetDeclaringType()));

            if (pdwAttr is not null)
                *pdwAttr = (uint)fieldDef.Attributes;

            if (ppvSigBlob is not null || pcbSigBlob is not null)
            {
                BlobHandle sigHandle = fieldDef.Signature;
                BlobReader blobReader = _reader.GetBlobReader(sigHandle);
                if (ppvSigBlob is not null)
                    *ppvSigBlob = blobReader.StartPointer;
                if (pcbSigBlob is not null)
                    *pcbSigBlob = (uint)blobReader.Length;
            }

            if (pdwCPlusTypeFlag is not null)
                *pdwCPlusTypeFlag = (uint)CorElementType.Void;
            if (ppValue is not null)
                *ppValue = null;
            if (pcchValue is not null)
                *pcchValue = 0;

            ConstantHandle constHandle = fieldDef.GetDefaultValue();
            if (!constHandle.IsNil && (pdwCPlusTypeFlag is not null || ppValue is not null))
            {
                Constant constant = _reader.GetConstant(constHandle);
                if (pdwCPlusTypeFlag is not null)
                    *pdwCPlusTypeFlag = (uint)constant.TypeCode;
                if (ppValue is not null || pcchValue is not null)
                {
                    BlobReader valueReader = _reader.GetBlobReader(constant.Value);
                    if (ppValue is not null)
                        *ppValue = valueReader.StartPointer;
                    if (pcchValue is not null)
                        *pcchValue = (uint)constant.TypeCode == (uint)CorElementType.String ? (uint)valueReader.Length / sizeof(char) : (uint)valueReader.Length;
                }
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint classLocal = 0, attrLocal = 0, pchLocal = 0, cbSigLocal = 0, cpTypeLocal = 0, cchValueLocal = 0;
            byte* sigLocal = null;
            void* valueLocal = null;
            char* szLocal = stackalloc char[(int)cchField];
            int hrLegacy = _legacyImport.GetFieldProps(mb, &classLocal, szLocal, cchField, &pchLocal, &attrLocal, &sigLocal, &cbSigLocal, &cpTypeLocal, &valueLocal, &cchValueLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pClass is not null)
                    Debug.Assert(*pClass == classLocal, $"Class mismatch: cDAC=0x{*pClass:X}, DAC=0x{classLocal:X}");
                if (pdwAttr is not null)
                    Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}");
                if (pchField is not null)
                    Debug.Assert(*pchField == pchLocal, $"Name length mismatch: cDAC={*pchField}, DAC={pchLocal}");
                if (szField is not null && cchField > 0)
                {
                    string cdacName = new string(szField);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"Field name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (pdwCPlusTypeFlag is not null)
                    Debug.Assert(*pdwCPlusTypeFlag == cpTypeLocal, $"CPlusTypeFlag mismatch: cDAC=0x{*pdwCPlusTypeFlag:X}, DAC=0x{cpTypeLocal:X}");
                if (ppvSigBlob is not null)
                    ValidateBlobsEqual(*ppvSigBlob, pcbSigBlob is not null ? *pcbSigBlob : cbSigLocal, sigLocal, cbSigLocal, "FieldSig");
                else if (pcbSigBlob is not null)
                    Debug.Assert(*pcbSigBlob == cbSigLocal, $"SigBlob length mismatch: cDAC={*pcbSigBlob}, DAC={cbSigLocal}");
                if (ppValue is not null)
                    ValidateBlobsEqual((byte*)*ppValue, pcchValue is not null ? *pcchValue : cchValueLocal, (byte*)valueLocal, cchValueLocal, "FieldConstant");
                else if (pcchValue is not null)
                    Debug.Assert(*pcchValue == cchValueLocal, $"Constant length mismatch: cDAC={*pcchValue}, DAC={cchValueLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetMemberProps(uint mb, uint* pClass, char* szMember, uint cchMember, uint* pchMember,
        uint* pdwAttr, byte** ppvSigBlob, uint* pcbSigBlob, uint* pulCodeRVA, uint* pdwImplFlags,
        uint* pdwCPlusTypeFlag, void** ppValue, uint* pcchValue)
    {
        uint tableIndex = mb >> 24;
        if (tableIndex == 0x06) // MethodDef
        {
            int hr = ((IMetaDataImport)this).GetMethodProps(mb, pClass, szMember, cchMember, pchMember, pdwAttr, ppvSigBlob, pcbSigBlob, pulCodeRVA, pdwImplFlags);
            if (pdwCPlusTypeFlag is not null)
                *pdwCPlusTypeFlag = 0;
            if (ppValue is not null)
                *ppValue = null;
            if (pcchValue is not null)
                *pcchValue = 0;
            return hr;
        }

        if (tableIndex == 0x04) // FieldDef
        {
            int hr = ((IMetaDataImport)this).GetFieldProps(mb, pClass, szMember, cchMember, pchMember, pdwAttr, ppvSigBlob, pcbSigBlob, pdwCPlusTypeFlag, ppValue, pcchValue);
            if (pulCodeRVA is not null)
                *pulCodeRVA = 0;
            if (pdwImplFlags is not null)
                *pdwImplFlags = 0;
            return hr;
        }

        return HResults.E_INVALIDARG;
    }

    int IMetaDataImport.GetInterfaceImplProps(uint iiImpl, uint* pClass, uint* ptkIface)
    {
        int hr = HResults.S_OK;
        try
        {
            InterfaceImplementationHandle implHandle = MetadataTokens.InterfaceImplementationHandle(GetRID(iiImpl));
            InterfaceImplementation impl = _reader.GetInterfaceImplementation(implHandle);

            if (pClass is not null)
            {
                _interfaceImplToTypeDef ??= BuildInterfaceImplLookup();
                *pClass = _interfaceImplToTypeDef.TryGetValue(GetRID(iiImpl), out uint ownerToken)
                    ? ownerToken : 0;
            }

            if (ptkIface is not null)
                *ptkIface = (uint)MetadataTokens.GetToken(impl.Interface);

            hr = HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint classLocal = 0, ifaceLocal = 0;
            int hrLegacy = _legacyImport.GetInterfaceImplProps(iiImpl, &classLocal, &ifaceLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pClass is not null)
                    Debug.Assert(*pClass == classLocal, $"Class mismatch: cDAC=0x{*pClass:X}, DAC=0x{classLocal:X}");
                if (ptkIface is not null)
                    Debug.Assert(*ptkIface == ifaceLocal, $"Interface mismatch: cDAC=0x{*ptkIface:X}, DAC=0x{ifaceLocal:X}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetNestedClassProps(uint tdNestedClass, uint* ptdEnclosingClass)
    {
        int hr = HResults.S_OK;
        try
        {
            TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(tdNestedClass));
            TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);
            TypeDefinitionHandle declaringType = typeDef.GetDeclaringType();

            if (ptdEnclosingClass is not null)
                *ptdEnclosingClass = declaringType.IsNil ? 0 : (uint)MetadataTokens.GetToken(declaringType);

            hr = declaringType.IsNil ? CldbHResults.CLDB_E_RECORD_NOTFOUND : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint enclosingLocal = 0;
            int hrLegacy = _legacyImport.GetNestedClassProps(tdNestedClass, &enclosingLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0 && ptdEnclosingClass is not null)
                Debug.Assert(*ptdEnclosingClass == enclosingLocal, $"Enclosing class mismatch: cDAC=0x{*ptdEnclosingClass:X}, DAC=0x{enclosingLocal:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport2.GetGenericParamProps(uint gp, uint* pulParamSeq, uint* pdwParamFlags, uint* ptOwner,
        uint* reserved, char* wzname, uint cchName, uint* pchName)
    {
        int hr = HResults.S_OK;
        try
        {
            GenericParameterHandle gpHandle = MetadataTokens.GenericParameterHandle(GetRID(gp));
            GenericParameter genericParam = _reader.GetGenericParameter(gpHandle);

            if (pulParamSeq is not null)
                *pulParamSeq = (uint)genericParam.Index;

            if (pdwParamFlags is not null)
                *pdwParamFlags = (uint)genericParam.Attributes;

            if (ptOwner is not null)
                *ptOwner = (uint)MetadataTokens.GetToken(genericParam.Parent);

            if (reserved is not null)
                *reserved = 0;

            string name = _reader.GetString(genericParam.Name);
            OutputBufferHelpers.CopyStringToBuffer(wzname, cchName, pchName, name, out bool truncated);

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport2 is not null)
        {
            uint seqLocal = 0, flagsLocal = 0, ownerLocal = 0, pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyImport2.GetGenericParamProps(gp, &seqLocal, &flagsLocal, &ownerLocal, null, szLocal, cchName, &pchLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pulParamSeq is not null)
                    Debug.Assert(*pulParamSeq == seqLocal, $"ParamSeq mismatch: cDAC={*pulParamSeq}, DAC={seqLocal}");
                if (pdwParamFlags is not null)
                    Debug.Assert(*pdwParamFlags == flagsLocal, $"ParamFlags mismatch: cDAC=0x{*pdwParamFlags:X}, DAC=0x{flagsLocal:X}");
                if (ptOwner is not null)
                    Debug.Assert(*ptOwner == ownerLocal, $"Owner mismatch: cDAC=0x{*ptOwner:X}, DAC=0x{ownerLocal:X}");
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (wzname is not null && cchName > 0)
                {
                    string cdacName = new string(wzname);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"GenericParam name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetRVA(uint tk, uint* pulCodeRVA, uint* pdwImplFlags)
    {
        int hr = HResults.S_OK;
        try
        {
            uint tableIndex = tk >> 24;
            if (tableIndex == 0x06) // MethodDef
            {
                MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(tk));
                MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle);
                if (pulCodeRVA is not null)
                    *pulCodeRVA = (uint)methodDef.RelativeVirtualAddress;
                if (pdwImplFlags is not null)
                    *pdwImplFlags = (uint)methodDef.ImplAttributes;
            }
            else if (tableIndex == 0x04) // FieldDef
            {
                FieldDefinitionHandle fieldHandle = MetadataTokens.FieldDefinitionHandle(GetRID(tk));
                FieldDefinition fieldDef = _reader.GetFieldDefinition(fieldHandle);
                if (pulCodeRVA is not null)
                    *pulCodeRVA = (uint)fieldDef.GetRelativeVirtualAddress();
                if (pdwImplFlags is not null)
                    *pdwImplFlags = 0;
            }
            else
            {
                hr = HResults.E_INVALIDARG;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint rvaLocal = 0, implLocal = 0;
            int hrLegacy = _legacyImport.GetRVA(tk, &rvaLocal, &implLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pulCodeRVA is not null)
                    Debug.Assert(*pulCodeRVA == rvaLocal, $"RVA mismatch: cDAC=0x{*pulCodeRVA:X}, DAC=0x{rvaLocal:X}");
                if (pdwImplFlags is not null)
                    Debug.Assert(*pdwImplFlags == implLocal, $"ImplFlags mismatch: cDAC=0x{*pdwImplFlags:X}, DAC=0x{implLocal:X}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetSigFromToken(uint mdSig, byte** ppvSig, uint* pcbSig)
    {
        int hr = HResults.S_OK;
        try
        {
            StandaloneSignatureHandle sigHandle = MetadataTokens.StandaloneSignatureHandle(GetRID(mdSig));
            StandaloneSignature sig = _reader.GetStandaloneSignature(sigHandle);
            BlobReader blobReader = _reader.GetBlobReader(sig.Signature);

            if (ppvSig is not null)
                *ppvSig = blobReader.StartPointer;
            if (pcbSig is not null)
                *pcbSig = (uint)blobReader.Length;

            hr = HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint cbLocal = 0;
            byte* sigLocal = null;
            int hrLegacy = _legacyImport.GetSigFromToken(mdSig, &sigLocal, &cbLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (ppvSig is not null)
                    ValidateBlobsEqual(*ppvSig, pcbSig is not null ? *pcbSig : cbLocal, sigLocal, cbLocal, "StandaloneSig");
                else if (pcbSig is not null)
                    Debug.Assert(*pcbSig == cbLocal, $"Sig length mismatch: cDAC={*pcbSig}, DAC={cbLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetCustomAttributeByName(uint tkObj, char* szName, void** ppData, uint* pcbData)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ppData is not null)
                *ppData = null;
            if (pcbData is not null)
                *pcbData = 0;

            string targetName = new string(szName);
            EntityHandle parent = MetadataTokens.EntityHandle((int)tkObj);
            bool found = false;

            foreach (CustomAttributeHandle caHandle in _reader.GetCustomAttributes(parent))
            {
                CustomAttribute ca = _reader.GetCustomAttribute(caHandle);
                string attrTypeName = GetCustomAttributeTypeName(ca.Constructor);
                if (string.Equals(attrTypeName, targetName, StringComparison.Ordinal))
                {
                    BlobReader blobReader = _reader.GetBlobReader(ca.Value);
                    if (ppData is not null)
                        *ppData = blobReader.StartPointer;
                    if (pcbData is not null)
                        *pcbData = (uint)blobReader.Length;
                    found = true;
                    break;
                }
            }

            if (!found)
                hr = HResults.S_FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint cbLocal = 0;
            void* dataLocal = null;
            int hrLegacy = _legacyImport.GetCustomAttributeByName(tkObj, szName, &dataLocal, &cbLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (ppData is not null)
                    ValidateBlobsEqual((byte*)*ppData, pcbData is not null ? *pcbData : cbLocal, (byte*)dataLocal, cbLocal, "CustomAttribute");
                else if (pcbData is not null)
                    Debug.Assert(*pcbData == cbLocal, $"CustomAttribute length mismatch: cDAC={*pcbData}, DAC={cbLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.IsValidToken(uint tk)
    {
        int rid = GetRID(tk);
        int tokenType = (int)(tk >> 24);

        if (rid == 0)
            return 0; // FALSE

        const int UserStringTokenType = 0x70;
        if (tokenType == UserStringTokenType)
        {
            int heapSize = _reader.GetHeapSize(HeapIndex.UserString);
            return rid < heapSize ? 1 : 0;
        }

        if (tokenType < 0 || tokenType > (int)TableIndex.CustomDebugInformation)
            return 0; // FALSE

        int rowCount = _reader.GetTableRowCount((TableIndex)tokenType);
        return rid <= rowCount ? 1 : 0; // TRUE or FALSE
    }

    private string GetCustomAttributeTypeName(EntityHandle constructor)
    {
        if (constructor.Kind == HandleKind.MethodDefinition)
        {
            MethodDefinition method = _reader.GetMethodDefinition((MethodDefinitionHandle)constructor);
            TypeDefinition typeDef = _reader.GetTypeDefinition(method.GetDeclaringType());
            return GetTypeDefFullName(typeDef);
        }
        if (constructor.Kind == HandleKind.MemberReference)
        {
            MemberReference memberRef = _reader.GetMemberReference((MemberReferenceHandle)constructor);
            EntityHandle parent = memberRef.Parent;
            if (parent.Kind == HandleKind.TypeReference)
            {
                TypeReference typeRef = _reader.GetTypeReference((TypeReferenceHandle)parent);
                return GetTypeRefFullName(typeRef);
            }
            if (parent.Kind == HandleKind.TypeDefinition)
            {
                TypeDefinition typeDef = _reader.GetTypeDefinition((TypeDefinitionHandle)parent);
                return GetTypeDefFullName(typeDef);
            }
        }
        return string.Empty;
    }

    int IMetaDataImport.FindTypeDefByName(char* szTypeDef, uint tkEnclosingClass, uint* ptd)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ptd is not null)
                *ptd = 0;

            string targetName = new string(szTypeDef);

            bool found = false;
            foreach (TypeDefinitionHandle tdh in _reader.TypeDefinitions)
            {
                TypeDefinition typeDef = _reader.GetTypeDefinition(tdh);
                string fullName = GetTypeDefFullName(typeDef);

                if (!string.Equals(fullName, targetName, StringComparison.Ordinal))
                    continue;

                if (tkEnclosingClass != 0)
                {
                    TypeDefinitionHandle declaringType = typeDef.GetDeclaringType();
                    if (declaringType.IsNil || (uint)MetadataTokens.GetToken(declaringType) != tkEnclosingClass)
                        continue;
                }

                if (ptd is not null)
                    *ptd = (uint)MetadataTokens.GetToken(tdh);

                found = true;
                break;
            }

            if (!found)
                hr = CldbHResults.CLDB_E_RECORD_NOTFOUND;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint tdLocal = 0;
            int hrLegacy = _legacyImport.FindTypeDefByName(szTypeDef, tkEnclosingClass, &tdLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0 && ptd is not null)
                Debug.Assert(*ptd == tdLocal, $"TypeDef mismatch: cDAC=0x{*ptd:X}, DAC=0x{tdLocal:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetScopeProps(char* szName, uint cchName, uint* pchName, Guid* pmvid)
        => _legacyImport is not null ? _legacyImport.GetScopeProps(szName, cchName, pchName, pmvid) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetModuleFromScope(uint* pmd)
        => _legacyImport is not null ? _legacyImport.GetModuleFromScope(pmd) : HResults.E_NOTIMPL;

    int IMetaDataImport.ResolveTypeRef(uint tr, Guid* riid, void** ppIScope, uint* ptd)
        => _legacyImport is not null ? _legacyImport.ResolveTypeRef(tr, riid, ppIScope, ptd) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMembersWithName(nint* phEnum, uint cl, char* szName, uint* rMembers, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumMembersWithName(phEnum, cl, szName, rMembers, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMethodsWithName(nint* phEnum, uint cl, char* szName, uint* rMethods, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumMethodsWithName(phEnum, cl, szName, rMethods, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumFieldsWithName(nint* phEnum, uint cl, char* szName, uint* rFields, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumFieldsWithName(phEnum, cl, szName, rFields, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumParams(nint* phEnum, uint mb, uint* rParams, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumParams(phEnum, mb, rParams, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMemberRefs(nint* phEnum, uint tkParent, uint* rMemberRefs, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumMemberRefs(phEnum, tkParent, rMemberRefs, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMethodImpls(nint* phEnum, uint td, uint* rMethodBody, uint* rMethodDecl, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumMethodImpls(phEnum, td, rMethodBody, rMethodDecl, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumPermissionSets(nint* phEnum, uint tk, uint dwActions, uint* rPermission, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumPermissionSets(phEnum, tk, dwActions, rPermission, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.FindMember(uint td, char* szName, byte* pvSigBlob, uint cbSigBlob, uint* pmb)
        => _legacyImport is not null ? _legacyImport.FindMember(td, szName, pvSigBlob, cbSigBlob, pmb) : HResults.E_NOTIMPL;

    int IMetaDataImport.FindMethod(uint td, char* szName, byte* pvSigBlob, uint cbSigBlob, uint* pmb)
        => _legacyImport is not null ? _legacyImport.FindMethod(td, szName, pvSigBlob, cbSigBlob, pmb) : HResults.E_NOTIMPL;

    int IMetaDataImport.FindField(uint td, char* szName, byte* pvSigBlob, uint cbSigBlob, uint* pmb)
        => _legacyImport is not null ? _legacyImport.FindField(td, szName, pvSigBlob, cbSigBlob, pmb) : HResults.E_NOTIMPL;

    int IMetaDataImport.FindMemberRef(uint td, char* szName, byte* pvSigBlob, uint cbSigBlob, uint* pmr)
        => _legacyImport is not null ? _legacyImport.FindMemberRef(td, szName, pvSigBlob, cbSigBlob, pmr) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetMemberRefProps(uint mr, uint* ptk, char* szMember, uint cchMember, uint* pchMember,
        byte** ppvSigBlob, uint* pbSig)
    {
        int hr = HResults.S_OK;
        try
        {
            MemberReferenceHandle refHandle = MetadataTokens.MemberReferenceHandle(GetRID(mr));
            MemberReference memberRef = _reader.GetMemberReference(refHandle);

            string name = _reader.GetString(memberRef.Name);
            OutputBufferHelpers.CopyStringToBuffer(szMember, cchMember, pchMember, name, out bool truncated);

            if (ptk is not null)
                *ptk = MapGlobalParentToken((uint)MetadataTokens.GetToken(memberRef.Parent));

            if (ppvSigBlob is not null || pbSig is not null)
            {
                BlobReader blobReader = _reader.GetBlobReader(memberRef.Signature);
                if (ppvSigBlob is not null)
                    *ppvSigBlob = blobReader.StartPointer;
                if (pbSig is not null)
                    *pbSig = (uint)blobReader.Length;
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint tkLocal = 0, pchLocal = 0, cbSigLocal = 0;
            byte* sigLocal = null;
            char* szLocal = stackalloc char[(int)cchMember];
            int hrLegacy = _legacyImport.GetMemberRefProps(mr, &tkLocal, szLocal, cchMember, &pchLocal, &sigLocal, &cbSigLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (ptk is not null)
                    Debug.Assert(*ptk == tkLocal, $"Parent mismatch: cDAC=0x{*ptk:X}, DAC=0x{tkLocal:X}");
                if (pchMember is not null)
                    Debug.Assert(*pchMember == pchLocal, $"Name length mismatch: cDAC={*pchMember}, DAC={pchLocal}");
                if (szMember is not null && cchMember > 0)
                {
                    string cdacName = new string(szMember);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"MemberRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (ppvSigBlob is not null)
                    ValidateBlobsEqual(*ppvSigBlob, pbSig is not null ? *pbSig : cbSigLocal, sigLocal, cbSigLocal, "MemberRefSig");
                else if (pbSig is not null)
                    Debug.Assert(*pbSig == cbSigLocal, $"SigBlob length mismatch: cDAC={*pbSig}, DAC={cbSigLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumProperties(nint* phEnum, uint td, uint* rProperties, uint cMax, uint* pcProperties)
        => _legacyImport is not null ? _legacyImport.EnumProperties(phEnum, td, rProperties, cMax, pcProperties) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumEvents(nint* phEnum, uint td, uint* rEvents, uint cMax, uint* pcEvents)
        => _legacyImport is not null ? _legacyImport.EnumEvents(phEnum, td, rEvents, cMax, pcEvents) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetEventProps(uint ev, uint* pClass, char* szEvent, uint cchEvent, uint* pchEvent,
        uint* pdwEventFlags, uint* ptkEventType, uint* pmdAddOn, uint* pmdRemoveOn, uint* pmdFire,
        uint* rmdOtherMethod, uint cMax, uint* pcOtherMethod)
        => _legacyImport is not null ? _legacyImport.GetEventProps(ev, pClass, szEvent, cchEvent, pchEvent, pdwEventFlags, ptkEventType, pmdAddOn, pmdRemoveOn, pmdFire, rmdOtherMethod, cMax, pcOtherMethod) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumMethodSemantics(nint* phEnum, uint mb, uint* rEventProp, uint cMax, uint* pcEventProp)
        => _legacyImport is not null ? _legacyImport.EnumMethodSemantics(phEnum, mb, rEventProp, cMax, pcEventProp) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetMethodSemantics(uint mb, uint tkEventProp, uint* pdwSemanticsFlags)
        => _legacyImport is not null ? _legacyImport.GetMethodSemantics(mb, tkEventProp, pdwSemanticsFlags) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetClassLayout(uint td, uint* pdwPackSize, void* rFieldOffset, uint cMax, uint* pcFieldOffset, uint* pulClassSize)
    {
        int hr = HResults.S_OK;
        try
        {
            TypeDefinitionHandle typeHandle = MetadataTokens.TypeDefinitionHandle(GetRID(td));
            TypeDefinition typeDef = _reader.GetTypeDefinition(typeHandle);
            TypeLayout layout = typeDef.GetLayout();

            if (layout.IsDefault)
            {
                hr = CldbHResults.CLDB_E_RECORD_NOTFOUND;
            }
            else
            {
                if (pdwPackSize is not null)
                    *pdwPackSize = (uint)layout.PackingSize;

                if (pulClassSize is not null)
                    *pulClassSize = (uint)layout.Size;

                if (rFieldOffset is not null || pcFieldOffset is not null)
                {
                    uint* fieldOffsets = (uint*)rFieldOffset;
                    uint count = 0;
                    foreach (FieldDefinitionHandle fh in typeDef.GetFields())
                    {
                        if (fieldOffsets is not null && count < cMax)
                        {
                            // Each entry is {FieldDef token (uint), ulOffset (uint)}
                            fieldOffsets[count * 2] = (uint)MetadataTokens.GetToken(fh);
                            int offset = _reader.GetFieldDefinition(fh).GetOffset();
                            fieldOffsets[count * 2 + 1] = offset >= 0 ? (uint)offset : 0xFFFFFFFF;
                        }
                        count++;
                    }
                    if (pcFieldOffset is not null)
                        *pcFieldOffset = count;
                }

                hr = HResults.S_OK;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint packLocal = 0, sizeLocal = 0, fieldCountLocal = 0;
            int hrLegacy = _legacyImport.GetClassLayout(td, &packLocal, null, 0, &fieldCountLocal, &sizeLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pdwPackSize is not null)
                    Debug.Assert(*pdwPackSize == packLocal, $"PackSize mismatch: cDAC={*pdwPackSize}, DAC={packLocal}");
                if (pulClassSize is not null)
                    Debug.Assert(*pulClassSize == sizeLocal, $"ClassSize mismatch: cDAC={*pulClassSize}, DAC={sizeLocal}");
                if (pcFieldOffset is not null)
                    Debug.Assert(*pcFieldOffset == fieldCountLocal, $"FieldOffset count mismatch: cDAC={*pcFieldOffset}, DAC={fieldCountLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetFieldMarshal(uint tk, byte** ppvNativeType, uint* pcbNativeType)
        => _legacyImport is not null ? _legacyImport.GetFieldMarshal(tk, ppvNativeType, pcbNativeType) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetPermissionSetProps(uint pm, uint* pdwAction, void** ppvPermission, uint* pcbPermission)
        => _legacyImport is not null ? _legacyImport.GetPermissionSetProps(pm, pdwAction, ppvPermission, pcbPermission) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetModuleRefProps(uint mur, char* szName, uint cchName, uint* pchName)
    {
        int hr = HResults.S_OK;
        try
        {
            ModuleReferenceHandle modRefHandle = MetadataTokens.ModuleReferenceHandle(GetRID(mur));
            ModuleReference modRef = _reader.GetModuleReference(modRefHandle);

            string name = _reader.GetString(modRef.Name);
            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, name, out bool truncated);

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyImport.GetModuleRefProps(mur, szLocal, cchName, &pchLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (szName is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"ModuleRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.EnumModuleRefs(nint* phEnum, uint* rModuleRefs, uint cmax, uint* pcModuleRefs)
        => _legacyImport is not null ? _legacyImport.EnumModuleRefs(phEnum, rModuleRefs, cmax, pcModuleRefs) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetTypeSpecFromToken(uint typespec, byte** ppvSig, uint* pcbSig)
    {
        int hr = HResults.S_OK;
        try
        {
            TypeSpecificationHandle tsHandle = MetadataTokens.TypeSpecificationHandle(GetRID(typespec));
            TypeSpecification typeSpec = _reader.GetTypeSpecification(tsHandle);
            BlobReader blobReader = _reader.GetBlobReader(typeSpec.Signature);

            if (ppvSig is not null)
                *ppvSig = blobReader.StartPointer;
            if (pcbSig is not null)
                *pcbSig = (uint)blobReader.Length;

            hr = HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint cbLocal = 0;
            byte* sigLocal = null;
            int hrLegacy = _legacyImport.GetTypeSpecFromToken(typespec, &sigLocal, &cbLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (ppvSig is not null)
                    ValidateBlobsEqual(*ppvSig, pcbSig is not null ? *pcbSig : cbLocal, sigLocal, cbLocal, "TypeSpec");
                else if (pcbSig is not null)
                    Debug.Assert(*pcbSig == cbLocal, $"Sig length mismatch: cDAC={*pcbSig}, DAC={cbLocal}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetNameFromToken(uint tk, byte** pszUtf8NamePtr)
        => _legacyImport is not null ? _legacyImport.GetNameFromToken(tk, pszUtf8NamePtr) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumUnresolvedMethods(nint* phEnum, uint* rMethods, uint cMax, uint* pcTokens)
        => _legacyImport is not null ? _legacyImport.EnumUnresolvedMethods(phEnum, rMethods, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetUserString(uint stk, char* szString, uint cchString, uint* pchString)
    {
        int hr = HResults.S_OK;
        try
        {
            // Read the user string from raw #US heap bytes to match native behavior exactly.
            // Native does: GetUserString → check size is odd → TruncateBySize(1) → GetSize()/sizeof(WCHAR).
            // Using raw bytes avoids potential discrepancies with MetadataReader.GetUserString().
            int heapMetadataOffset = _reader.GetHeapMetadataOffset(HeapIndex.UserString);
            int heapSize = _reader.GetHeapSize(HeapIndex.UserString);
            int handleOffset = GetRID(stk);

            byte* heapBase = _reader.MetadataPointer + heapMetadataOffset;
            int remaining = heapSize - handleOffset;
            if (remaining <= 0)
                throw Marshal.GetExceptionForHR(CldbHResults.CLDB_E_FILE_CORRUPT)!;

            BlobReader blobReader = new BlobReader(heapBase + handleOffset, remaining);
            int blobSize = blobReader.ReadCompressedInteger();

            // Validate blob fits within the remaining heap to prevent out-of-bounds reads.
            if (blobSize > blobReader.RemainingBytes)
                throw Marshal.GetExceptionForHR(CldbHResults.CLDB_E_FILE_CORRUPT)!;

            // Native rejects even-sized blobs (missing terminal byte) as corrupt.
            if ((blobSize % sizeof(char)) == 0)
                throw Marshal.GetExceptionForHR(CldbHResults.CLDB_E_FILE_CORRUPT)!;

            int charCount = (blobSize - 1) / sizeof(char);

            if (pchString is not null)
                *pchString = (uint)charCount;

            if (szString is not null && cchString > 0)
            {
                char* dataPtr = (char*)blobReader.CurrentPointer;
                int copyChars = Math.Min(charCount, (int)cchString);
                new ReadOnlySpan<char>(dataPtr, copyChars).CopyTo(new Span<char>(szString, copyChars));

                if ((uint)charCount > cchString)
                {
                    szString[cchString - 1] = '\0';
                    hr = CldbHResults.CLDB_S_TRUNCATION;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchString];
            int hrLegacy = _legacyImport.GetUserString(stk, szLocal, cchString, &pchLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pchString is not null)
                    Debug.Assert(*pchString == pchLocal, $"String length mismatch: cDAC={*pchString}, DAC={pchLocal}");
                if (szString is not null && cchString > 0)
                {
                    // GetUserString does not null-terminate its output buffer (matching native behavior),
                    // so we must use length-bounded string construction instead of new string(char*).
                    int compareLen = Math.Min((int)pchLocal, (int)cchString);
                    string cdacStr = new string(szString, 0, compareLen);
                    string dacStr = new string(szLocal, 0, compareLen);
                    Debug.Assert(cdacStr == dacStr, $"UserString content mismatch: cDAC='{cdacStr}', DAC='{dacStr}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetPinvokeMap(uint tk, uint* pdwMappingFlags, char* szImportName, uint cchImportName,
        uint* pchImportName, uint* pmrImportDLL)
        => _legacyImport is not null ? _legacyImport.GetPinvokeMap(tk, pdwMappingFlags, szImportName, cchImportName, pchImportName, pmrImportDLL) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumSignatures(nint* phEnum, uint* rSignatures, uint cmax, uint* pcSignatures)
        => _legacyImport is not null ? _legacyImport.EnumSignatures(phEnum, rSignatures, cmax, pcSignatures) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumTypeSpecs(nint* phEnum, uint* rTypeSpecs, uint cmax, uint* pcTypeSpecs)
        => _legacyImport is not null ? _legacyImport.EnumTypeSpecs(phEnum, rTypeSpecs, cmax, pcTypeSpecs) : HResults.E_NOTIMPL;

    int IMetaDataImport.EnumUserStrings(nint* phEnum, uint* rStrings, uint cmax, uint* pcStrings)
        => _legacyImport is not null ? _legacyImport.EnumUserStrings(phEnum, rStrings, cmax, pcStrings) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetParamForMethodIndex(uint md, uint ulParamSeq, uint* ppd)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ppd is not null)
                *ppd = 0;

            MethodDefinitionHandle methodHandle = MetadataTokens.MethodDefinitionHandle(GetRID(md));
            MethodDefinition methodDef = _reader.GetMethodDefinition(methodHandle);

            bool found = false;
            foreach (ParameterHandle ph in methodDef.GetParameters())
            {
                Parameter param = _reader.GetParameter(ph);
                if (param.SequenceNumber == (int)ulParamSeq)
                {
                    if (ppd is not null)
                        *ppd = (uint)MetadataTokens.GetToken(ph);
                    found = true;
                    break;
                }
            }

            if (!found)
                hr = CldbHResults.CLDB_E_RECORD_NOTFOUND;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint pdLocal = 0;
            int hrLegacy = _legacyImport.GetParamForMethodIndex(md, ulParamSeq, &pdLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0 && ppd is not null)
                Debug.Assert(*ppd == pdLocal, $"Param token mismatch: cDAC=0x{*ppd:X}, DAC=0x{pdLocal:X}");
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetCustomAttributeProps(uint cv, uint* ptkObj, uint* ptkType, void** ppBlob, uint* pcbSize)
        => _legacyImport is not null ? _legacyImport.GetCustomAttributeProps(cv, ptkObj, ptkType, ppBlob, pcbSize) : HResults.E_NOTIMPL;

    int IMetaDataImport.FindTypeRef(uint tkResolutionScope, char* szName, uint* ptr)
        => _legacyImport is not null ? _legacyImport.FindTypeRef(tkResolutionScope, szName, ptr) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetPropertyProps(uint prop, uint* pClass, char* szProperty, uint cchProperty, uint* pchProperty,
        uint* pdwPropFlags, byte** ppvSig, uint* pbSig, uint* pdwCPlusTypeFlag,
        void** ppDefaultValue, uint* pcchDefaultValue, uint* pmdSetter, uint* pmdGetter,
        uint* rmdOtherMethod, uint cMax, uint* pcOtherMethod)
        => _legacyImport is not null ? _legacyImport.GetPropertyProps(prop, pClass, szProperty, cchProperty, pchProperty, pdwPropFlags, ppvSig, pbSig, pdwCPlusTypeFlag, ppDefaultValue, pcchDefaultValue, pmdSetter, pmdGetter, rmdOtherMethod, cMax, pcOtherMethod) : HResults.E_NOTIMPL;

    int IMetaDataImport.GetParamProps(uint tk, uint* pmd, uint* pulSequence, char* szName, uint cchName, uint* pchName,
        uint* pdwAttr, uint* pdwCPlusTypeFlag, void** ppValue, uint* pcchValue)
    {
        int hr = HResults.S_OK;
        try
        {
            ParameterHandle paramHandle = MetadataTokens.ParameterHandle(GetRID(tk));
            Parameter param = _reader.GetParameter(paramHandle);

            string name = _reader.GetString(param.Name);
            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, name, out bool truncated);

            if (pmd is not null)
            {
                _paramToMethod ??= BuildParamToMethodLookup();
                *pmd = _paramToMethod.TryGetValue(MetadataTokens.GetRowNumber(paramHandle), out uint methodToken) ? methodToken : 0;
            }

            if (pulSequence is not null)
                *pulSequence = (uint)param.SequenceNumber;

            if (pdwAttr is not null)
                *pdwAttr = (uint)param.Attributes;

            if (pdwCPlusTypeFlag is not null)
                *pdwCPlusTypeFlag = (uint)CorElementType.Void;
            if (ppValue is not null)
                *ppValue = null;
            if (pcchValue is not null)
                *pcchValue = 0;

            ConstantHandle constHandle = param.GetDefaultValue();
            if (!constHandle.IsNil && (pdwCPlusTypeFlag is not null || ppValue is not null))
            {
                Constant constant = _reader.GetConstant(constHandle);
                if (pdwCPlusTypeFlag is not null)
                    *pdwCPlusTypeFlag = (uint)constant.TypeCode;
                if (ppValue is not null || pcchValue is not null)
                {
                    BlobReader valueReader = _reader.GetBlobReader(constant.Value);
                    if (ppValue is not null)
                        *ppValue = valueReader.StartPointer;
                    if (pcchValue is not null)
                        *pcchValue = (uint)constant.TypeCode == (uint)CorElementType.String ? (uint)valueReader.Length / sizeof(char) : (uint)valueReader.Length;
                }
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImport is not null)
        {
            uint mdLocal = 0, seqLocal = 0, attrLocal = 0, pchLocal = 0;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyImport.GetParamProps(tk, &mdLocal, &seqLocal, szLocal, cchName, &pchLocal, &attrLocal, null, null, null);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pmd is not null)
                    Debug.Assert(*pmd == mdLocal, $"Method mismatch: cDAC=0x{*pmd:X}, DAC=0x{mdLocal:X}");
                if (pulSequence is not null)
                    Debug.Assert(*pulSequence == seqLocal, $"Sequence mismatch: cDAC={*pulSequence}, DAC={seqLocal}");
                if (pdwAttr is not null)
                    Debug.Assert(*pdwAttr == attrLocal, $"Attr mismatch: cDAC=0x{*pdwAttr:X}, DAC=0x{attrLocal:X}");
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (szName is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"Param name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataImport.GetNativeCallConvFromSig(void* pvSig, uint cbSig, uint* pCallConv)
        => _legacyImport is not null ? _legacyImport.GetNativeCallConvFromSig(pvSig, cbSig, pCallConv) : HResults.E_NOTIMPL;

    int IMetaDataImport.IsGlobal(uint pd, int* pbGlobal)
        => _legacyImport is not null ? _legacyImport.IsGlobal(pd, pbGlobal) : HResults.E_NOTIMPL;

    // IMetaDataImport2 methods — delegate to legacy via _legacyImport2
    int IMetaDataImport2.GetMethodSpecProps(uint mi, uint* tkParent, byte** ppvSigBlob, uint* pcbSigBlob)
        => _legacyImport2 is not null ? _legacyImport2.GetMethodSpecProps(mi, tkParent, ppvSigBlob, pcbSigBlob) : HResults.E_NOTIMPL;

    int IMetaDataImport2.EnumGenericParamConstraints(nint* phEnum, uint tk, uint* rGenericParamConstraints, uint cMax, uint* pcGenericParamConstraints)
        => _legacyImport2 is not null ? _legacyImport2.EnumGenericParamConstraints(phEnum, tk, rGenericParamConstraints, cMax, pcGenericParamConstraints) : HResults.E_NOTIMPL;

    int IMetaDataImport2.GetGenericParamConstraintProps(uint gpc, uint* ptGenericParam, uint* ptkConstraintType)
        => _legacyImport2 is not null ? _legacyImport2.GetGenericParamConstraintProps(gpc, ptGenericParam, ptkConstraintType) : HResults.E_NOTIMPL;

    int IMetaDataImport2.GetPEKind(uint* pdwPEKind, uint* pdwMachine)
        => _legacyImport2 is not null ? _legacyImport2.GetPEKind(pdwPEKind, pdwMachine) : HResults.E_NOTIMPL;

    int IMetaDataImport2.GetVersionString(char* pwzBuf, uint ccBufSize, uint* pccBufSize)
        => _legacyImport2 is not null ? _legacyImport2.GetVersionString(pwzBuf, ccBufSize, pccBufSize) : HResults.E_NOTIMPL;

    int IMetaDataImport2.EnumMethodSpecs(nint* phEnum, uint tk, uint* rMethodSpecs, uint cMax, uint* pcMethodSpecs)
        => _legacyImport2 is not null ? _legacyImport2.EnumMethodSpecs(phEnum, tk, rMethodSpecs, cMax, pcMethodSpecs) : HResults.E_NOTIMPL;

    // =============================================
    // IMetaDataAssemblyImport
    // =============================================

    int IMetaDataAssemblyImport.GetAssemblyProps(uint mda, byte** ppbPublicKey, uint* pcbPublicKey,
        uint* pulHashAlgId, char* szName, uint cchName, uint* pchName,
        ASSEMBLYMETADATA* pMetaData, uint* pdwAssemblyFlags)
    {
        int hr = HResults.S_OK;
        try
        {
            // Validate that the token is the assembly definition token
            if (mda != 0x20000001)
                throw Marshal.GetExceptionForHR(CldbHResults.CLDB_E_RECORD_NOTFOUND)!;

            AssemblyDefinition assemblyDef = _reader.GetAssemblyDefinition();
            string name = _reader.GetString(assemblyDef.Name);

            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, name, out bool truncated);

            if (!assemblyDef.PublicKey.IsNil)
            {
                BlobReader publicKeyReader = _reader.GetBlobReader(assemblyDef.PublicKey);
                if (ppbPublicKey is not null)
                    *ppbPublicKey = publicKeyReader.CurrentPointer;
                if (pcbPublicKey is not null)
                    *pcbPublicKey = (uint)publicKeyReader.Length;
            }
            else
            {
                if (ppbPublicKey is not null)
                    *ppbPublicKey = null;
                if (pcbPublicKey is not null)
                    *pcbPublicKey = 0;
            }
            if (pulHashAlgId is not null)
                *pulHashAlgId = (uint)assemblyDef.HashAlgorithm;

            if (pMetaData is not null)
            {
                System.Version version = assemblyDef.Version;
                pMetaData->usMajorVersion = (ushort)version.Major;
                pMetaData->usMinorVersion = (ushort)version.Minor;
                pMetaData->usBuildNumber = (ushort)version.Build;
                pMetaData->usRevisionNumber = (ushort)version.Revision;

                string culture = _reader.GetString(assemblyDef.Culture);
                OutputBufferHelpers.CopyStringToBuffer(pMetaData->szLocale, pMetaData->cbLocale, null, culture, out bool localTruncated);
                truncated |= localTruncated;
                pMetaData->cbLocale = (uint)(culture.Length + 1);
                pMetaData->ulProcessor = 0;
                pMetaData->ulOS = 0;
            }

            if (pdwAssemblyFlags is not null)
            {
                uint flags = (uint)assemblyDef.Flags;
                // Native RegMeta ORs afPublicKey (0x0001) into flags when public key blob is non-empty
                if (!assemblyDef.PublicKey.IsNil && _reader.GetBlobReader(assemblyDef.PublicKey).Length > 0)
                    flags |= 0x0001;
                *pdwAssemblyFlags = flags;
            }

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyAssemblyImport is not null)
        {
            uint pchLocal = 0, hashAlgLocal = 0, flagsLocal = 0, cbPublicKeyLocal = 0;
            byte* publicKeyLocal = null;
            ASSEMBLYMETADATA metaLocal = default;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyAssemblyImport.GetAssemblyProps(mda, &publicKeyLocal, &cbPublicKeyLocal, &hashAlgLocal, szLocal, cchName, &pchLocal, &metaLocal, &flagsLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (szName is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"Assembly name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (pulHashAlgId is not null)
                    Debug.Assert(*pulHashAlgId == hashAlgLocal, $"HashAlgId mismatch: cDAC=0x{*pulHashAlgId:X}, DAC=0x{hashAlgLocal:X}");
                if (pdwAssemblyFlags is not null)
                    Debug.Assert(*pdwAssemblyFlags == flagsLocal, $"Flags mismatch: cDAC=0x{*pdwAssemblyFlags:X}, DAC=0x{flagsLocal:X}");
                if (ppbPublicKey is not null)
                    ValidateBlobsEqual(*ppbPublicKey, pcbPublicKey is not null ? *pcbPublicKey : cbPublicKeyLocal, publicKeyLocal, cbPublicKeyLocal, "AssemblyPublicKey");
                else if (pcbPublicKey is not null)
                    Debug.Assert(*pcbPublicKey == cbPublicKeyLocal, $"PublicKey length mismatch: cDAC={*pcbPublicKey}, DAC={cbPublicKeyLocal}");
                if (pMetaData is not null)
                {
                    Debug.Assert(pMetaData->usMajorVersion == metaLocal.usMajorVersion, $"MajorVersion mismatch: cDAC={pMetaData->usMajorVersion}, DAC={metaLocal.usMajorVersion}");
                    Debug.Assert(pMetaData->usMinorVersion == metaLocal.usMinorVersion, $"MinorVersion mismatch: cDAC={pMetaData->usMinorVersion}, DAC={metaLocal.usMinorVersion}");
                    Debug.Assert(pMetaData->usBuildNumber == metaLocal.usBuildNumber, $"BuildNumber mismatch: cDAC={pMetaData->usBuildNumber}, DAC={metaLocal.usBuildNumber}");
                    Debug.Assert(pMetaData->usRevisionNumber == metaLocal.usRevisionNumber, $"RevisionNumber mismatch: cDAC={pMetaData->usRevisionNumber}, DAC={metaLocal.usRevisionNumber}");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataAssemblyImport.GetAssemblyRefProps(uint mdar, byte** ppbPublicKeyOrToken, uint* pcbPublicKeyOrToken,
        char* szName, uint cchName, uint* pchName, ASSEMBLYMETADATA* pMetaData,
        byte** ppbHashValue, uint* pcbHashValue, uint* pdwAssemblyRefFlags)
    {
        int hr = HResults.S_OK;
        try
        {
            AssemblyReferenceHandle refHandle = MetadataTokens.AssemblyReferenceHandle(GetRID(mdar));
            AssemblyReference assemblyRef = _reader.GetAssemblyReference(refHandle);
            string name = _reader.GetString(assemblyRef.Name);

            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, name, out bool truncated);

            if (!assemblyRef.PublicKeyOrToken.IsNil)
            {
                BlobReader publicKeyReader = _reader.GetBlobReader(assemblyRef.PublicKeyOrToken);
                if (ppbPublicKeyOrToken is not null)
                    *ppbPublicKeyOrToken = publicKeyReader.CurrentPointer;
                if (pcbPublicKeyOrToken is not null)
                    *pcbPublicKeyOrToken = (uint)publicKeyReader.Length;
            }
            else
            {
                if (ppbPublicKeyOrToken is not null)
                    *ppbPublicKeyOrToken = null;
                if (pcbPublicKeyOrToken is not null)
                    *pcbPublicKeyOrToken = 0;
            }

            if (pMetaData is not null)
            {
                System.Version version = assemblyRef.Version;
                pMetaData->usMajorVersion = (ushort)version.Major;
                pMetaData->usMinorVersion = (ushort)version.Minor;
                pMetaData->usBuildNumber = (ushort)version.Build;
                pMetaData->usRevisionNumber = (ushort)version.Revision;

                string culture = _reader.GetString(assemblyRef.Culture);
                OutputBufferHelpers.CopyStringToBuffer(pMetaData->szLocale, pMetaData->cbLocale, null, culture, out bool localTruncated);
                truncated |= localTruncated;
                pMetaData->cbLocale = (uint)(culture.Length + 1);
                pMetaData->ulProcessor = 0;
                pMetaData->ulOS = 0;
            }

            if (!assemblyRef.HashValue.IsNil)
            {
                BlobReader hashReader = _reader.GetBlobReader(assemblyRef.HashValue);
                if (ppbHashValue is not null)
                    *ppbHashValue = hashReader.CurrentPointer;
                if (pcbHashValue is not null)
                    *pcbHashValue = (uint)hashReader.Length;
            }
            else
            {
                if (ppbHashValue is not null)
                    *ppbHashValue = null;
                if (pcbHashValue is not null)
                    *pcbHashValue = 0;
            }

            if (pdwAssemblyRefFlags is not null)
                *pdwAssemblyRefFlags = (uint)assemblyRef.Flags;

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyAssemblyImport is not null)
        {
            uint pchLocal = 0, flagsLocal = 0, cbPublicKeyLocal = 0, cbHashLocal = 0;
            byte* publicKeyLocal = null, hashLocal = null;
            ASSEMBLYMETADATA metaLocal = default;
            char* szLocal = stackalloc char[(int)cchName];
            int hrLegacy = _legacyAssemblyImport.GetAssemblyRefProps(mdar, &publicKeyLocal, &cbPublicKeyLocal, szLocal, cchName, &pchLocal, &metaLocal, &hashLocal, &cbHashLocal, &flagsLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (pchName is not null)
                    Debug.Assert(*pchName == pchLocal, $"Name length mismatch: cDAC={*pchName}, DAC={pchLocal}");
                if (szName is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szLocal);
                    Debug.Assert(cdacName == dacName, $"AssemblyRef name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (pdwAssemblyRefFlags is not null)
                    Debug.Assert(*pdwAssemblyRefFlags == flagsLocal, $"Flags mismatch: cDAC=0x{*pdwAssemblyRefFlags:X}, DAC=0x{flagsLocal:X}");
                if (ppbPublicKeyOrToken is not null)
                    ValidateBlobsEqual(*ppbPublicKeyOrToken, pcbPublicKeyOrToken is not null ? *pcbPublicKeyOrToken : cbPublicKeyLocal, publicKeyLocal, cbPublicKeyLocal, "AssemblyRefPublicKey");
                else if (pcbPublicKeyOrToken is not null)
                    Debug.Assert(*pcbPublicKeyOrToken == cbPublicKeyLocal, $"PublicKey length mismatch: cDAC={*pcbPublicKeyOrToken}, DAC={cbPublicKeyLocal}");
                if (ppbHashValue is not null)
                    ValidateBlobsEqual(*ppbHashValue, pcbHashValue is not null ? *pcbHashValue : cbHashLocal, hashLocal, cbHashLocal, "AssemblyRefHash");
                else if (pcbHashValue is not null)
                    Debug.Assert(*pcbHashValue == cbHashLocal, $"Hash length mismatch: cDAC={*pcbHashValue}, DAC={cbHashLocal}");
                if (pMetaData is not null)
                {
                    Debug.Assert(pMetaData->usMajorVersion == metaLocal.usMajorVersion, $"MajorVersion mismatch: cDAC={pMetaData->usMajorVersion}, DAC={metaLocal.usMajorVersion}");
                    Debug.Assert(pMetaData->usMinorVersion == metaLocal.usMinorVersion, $"MinorVersion mismatch: cDAC={pMetaData->usMinorVersion}, DAC={metaLocal.usMinorVersion}");
                    Debug.Assert(pMetaData->usBuildNumber == metaLocal.usBuildNumber, $"BuildNumber mismatch: cDAC={pMetaData->usBuildNumber}, DAC={metaLocal.usBuildNumber}");
                    Debug.Assert(pMetaData->usRevisionNumber == metaLocal.usRevisionNumber, $"RevisionNumber mismatch: cDAC={pMetaData->usRevisionNumber}, DAC={metaLocal.usRevisionNumber}");
                }
            }
        }
#endif
        return hr;
    }

    int IMetaDataAssemblyImport.GetFileProps(uint mdf, char* szName, uint cchName, uint* pchName,
        byte** ppbHashValue, uint* pcbHashValue, uint* pdwFileFlags)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.GetFileProps(mdf, szName, cchName, pchName, ppbHashValue, pcbHashValue, pdwFileFlags) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.GetExportedTypeProps(uint mdct, char* szName, uint cchName, uint* pchName,
        uint* ptkImplementation, uint* ptkTypeDef, uint* pdwExportedTypeFlags)
    {
        int hr = HResults.S_OK;
        try
        {
            ExportedTypeHandle handle = MetadataTokens.ExportedTypeHandle(GetRID(mdct));
            ExportedType exportedType = _reader.GetExportedType(handle);

            string name = _reader.GetString(exportedType.Name);
            string ns = _reader.GetString(exportedType.Namespace);
            string fullName = string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}";
            OutputBufferHelpers.CopyStringToBuffer(szName, cchName, pchName, fullName, out bool truncated);

            if (ptkImplementation is not null)
            {
                EntityHandle impl = exportedType.Implementation;
                *ptkImplementation = impl.IsNil ? 0 : (uint)MetadataTokens.GetToken(impl);
            }

            if (ptkTypeDef is not null)
                *ptkTypeDef = (uint)exportedType.GetTypeDefinitionId();

            if (pdwExportedTypeFlags is not null)
                *pdwExportedTypeFlags = (uint)exportedType.Attributes;

            hr = truncated ? CldbHResults.CLDB_S_TRUNCATION : HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyAssemblyImport is not null)
        {
            char* szNameLocal = stackalloc char[(int)cchName];
            uint pchNameLocal = 0;
            uint tkImplementationLocal = 0;
            uint tkTypeDefLocal = 0;
            uint dwExportedTypeFlagsLocal = 0;
            int hrLegacy = _legacyAssemblyImport.GetExportedTypeProps(mdct, szNameLocal, cchName, &pchNameLocal,
                &tkImplementationLocal, &tkTypeDefLocal, &dwExportedTypeFlagsLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0)
            {
                if (szName is not null && szNameLocal is not null && cchName > 0)
                {
                    string cdacName = new string(szName);
                    string dacName = new string(szNameLocal);
                    Debug.Assert(cdacName == dacName, $"ExportedType name mismatch: cDAC='{cdacName}', DAC='{dacName}'");
                }
                if (pchName is not null)
                    Debug.Assert(*pchName == pchNameLocal, $"ExportedType name length mismatch: cDAC={*pchName}, DAC={pchNameLocal}");
                if (ptkImplementation is not null)
                    Debug.Assert(*ptkImplementation == tkImplementationLocal, $"ExportedType implementation mismatch: cDAC=0x{*ptkImplementation:X}, DAC=0x{tkImplementationLocal:X}");
                if (ptkTypeDef is not null)
                    Debug.Assert(*ptkTypeDef == tkTypeDefLocal, $"ExportedType typeDef mismatch: cDAC=0x{*ptkTypeDef:X}, DAC=0x{tkTypeDefLocal:X}");
                if (pdwExportedTypeFlags is not null)
                    Debug.Assert(*pdwExportedTypeFlags == dwExportedTypeFlagsLocal, $"ExportedType flags mismatch: cDAC=0x{*pdwExportedTypeFlags:X}, DAC=0x{dwExportedTypeFlagsLocal:X}");
            }
        }
#endif
        return hr;
    }

    int IMetaDataAssemblyImport.GetManifestResourceProps(uint mdmr, char* szName, uint cchName, uint* pchName,
        uint* ptkImplementation, uint* pdwOffset, uint* pdwResourceFlags)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.GetManifestResourceProps(mdmr, szName, cchName, pchName, ptkImplementation, pdwOffset, pdwResourceFlags) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.EnumAssemblyRefs(nint* phEnum, uint* rAssemblyRefs, uint cMax, uint* pcTokens)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.EnumAssemblyRefs(phEnum, rAssemblyRefs, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.EnumFiles(nint* phEnum, uint* rFiles, uint cMax, uint* pcTokens)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.EnumFiles(phEnum, rFiles, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.EnumExportedTypes(nint* phEnum, uint* rExportedTypes, uint cMax, uint* pcTokens)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.EnumExportedTypes(phEnum, rExportedTypes, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.EnumManifestResources(nint* phEnum, uint* rManifestResources, uint cMax, uint* pcTokens)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.EnumManifestResources(phEnum, rManifestResources, cMax, pcTokens) : HResults.E_NOTIMPL;

    int IMetaDataAssemblyImport.GetAssemblyFromScope(uint* ptkAssembly)
    {
        if (ptkAssembly is not null)
            *ptkAssembly = 0x20000001; // TokenFromRid(1, mdtAssembly)

        int hr = HResults.S_OK;
#if DEBUG
        if (_legacyAssemblyImport is not null)
        {
            uint tkLocal = 0;
            int hrLegacy = _legacyAssemblyImport.GetAssemblyFromScope(&tkLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0 && ptkAssembly is not null)
                Debug.Assert(*ptkAssembly == tkLocal, $"Assembly token mismatch: cDAC=0x{*ptkAssembly:X}, DAC=0x{tkLocal:X}");
        }
#endif
        return hr;
    }

    int IMetaDataAssemblyImport.FindExportedTypeByName(char* szName, uint mdtExportedType, uint* ptkExportedType)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ptkExportedType is not null)
                *ptkExportedType = 0;

            string targetName = new string(szName);

            bool found = false;
            foreach (ExportedTypeHandle eth in _reader.ExportedTypes)
            {
                ExportedType exportedType = _reader.GetExportedType(eth);
                string name = _reader.GetString(exportedType.Name);
                string ns = _reader.GetString(exportedType.Namespace);
                string fullName = string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}";

                if (!string.Equals(fullName, targetName, StringComparison.Ordinal))
                    continue;

                if (mdtExportedType != 0)
                {
                    EntityHandle impl = exportedType.Implementation;
                    if (impl.IsNil || (uint)MetadataTokens.GetToken(impl) != mdtExportedType)
                        continue;
                }

                if (ptkExportedType is not null)
                    *ptkExportedType = (uint)MetadataTokens.GetToken(eth);

                found = true;
                break;
            }

            if (!found)
                hr = CldbHResults.CLDB_E_RECORD_NOTFOUND;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyAssemblyImport is not null)
        {
            uint tkExportedTypeLocal = 0;
            int hrLegacy = _legacyAssemblyImport.FindExportedTypeByName(szName, mdtExportedType, &tkExportedTypeLocal);
            Debug.ValidateHResult(hr, hrLegacy);
            if (hr >= 0 && hrLegacy >= 0 && ptkExportedType is not null)
                Debug.Assert(*ptkExportedType == tkExportedTypeLocal, $"ExportedType mismatch: cDAC=0x{*ptkExportedType:X}, DAC=0x{tkExportedTypeLocal:X}");
        }
#endif
        return hr;
    }

    int IMetaDataAssemblyImport.FindManifestResourceByName(char* szName, uint* ptkManifestResource)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.FindManifestResourceByName(szName, ptkManifestResource) : HResults.E_NOTIMPL;

    void IMetaDataAssemblyImport.CloseEnum(nint hEnum)
        => ((IMetaDataImport)this).CloseEnum(hEnum);

    int IMetaDataAssemblyImport.FindAssembliesByName(char* szAppBase, char* szPrivateBin, char* szAssemblyName,
        nint* ppIUnk, uint cMax, uint* pcAssemblies)
        => _legacyAssemblyImport is not null ? _legacyAssemblyImport.FindAssembliesByName(szAppBase, szPrivateBin, szAssemblyName, ppIUnk, cMax, pcAssemblies) : HResults.E_NOTIMPL;

    // Helpers and lookup builders

    private string GetTypeDefFullName(TypeDefinition typeDef)
    {
        string name = _reader.GetString(typeDef.Name);
        string ns = _reader.GetString(typeDef.Namespace);
        return string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}";
    }

    private string GetTypeRefFullName(TypeReference typeRef)
    {
        string name = _reader.GetString(typeRef.Name);
        string ns = _reader.GetString(typeRef.Namespace);
        return string.IsNullOrEmpty(ns) ? name : $"{ns}.{name}";
    }

    // Native RegMeta maps the global <Module> type (TypeDef RID 1) to mdTypeDefNil (0x00000000)
    // when returning parent tokens from GetMethodProps, GetFieldProps, and GetMemberRefProps.
    private static uint MapGlobalParentToken(uint token)
    {
        // TypeDef RID 1 has token 0x02000001
        return token == 0x02000001 ? 0 : token;
    }

#if DEBUG
    private static void ValidateBlobsEqual(byte* cdacBlob, uint cdacLen, byte* dacBlob, uint dacLen, string name)
    {
        Debug.Assert(cdacLen == dacLen, $"{name} length mismatch: cDAC={cdacLen}, DAC={dacLen}");
        if (cdacLen == dacLen && cdacLen > 0 && cdacBlob is not null && dacBlob is not null)
        {
            ReadOnlySpan<byte> cdacSpan = new(cdacBlob, (int)cdacLen);
            ReadOnlySpan<byte> dacSpan = new(dacBlob, (int)dacLen);
            Debug.Assert(cdacSpan.SequenceEqual(dacSpan), $"{name} content mismatch (length={cdacLen})");
        }
    }
#endif

    private Dictionary<int, uint> BuildInterfaceImplLookup()
    {
        Dictionary<int, uint> lookup = new();
        foreach (TypeDefinitionHandle tdh in _reader.TypeDefinitions)
        {
            uint typeToken = (uint)MetadataTokens.GetToken(tdh);
            foreach (InterfaceImplementationHandle ih in _reader.GetTypeDefinition(tdh).GetInterfaceImplementations())
            {
                lookup[MetadataTokens.GetRowNumber(ih)] = typeToken;
            }
        }
        return lookup;
    }

    private Dictionary<int, uint> BuildParamToMethodLookup()
    {
        Dictionary<int, uint> lookup = new();
        foreach (TypeDefinitionHandle tdh in _reader.TypeDefinitions)
        {
            foreach (MethodDefinitionHandle mdh in _reader.GetTypeDefinition(tdh).GetMethods())
            {
                uint methodToken = (uint)MetadataTokens.GetToken(mdh);
                foreach (ParameterHandle ph in _reader.GetMethodDefinition(mdh).GetParameters())
                {
                    lookup[MetadataTokens.GetRowNumber(ph)] = methodToken;
                }
            }
        }
        return lookup;
    }

}