File: System\Reflection\Metadata\Handle.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata.Ecma335;
 
namespace System.Reflection.Metadata
{
    /// <summary>
    /// Represents any metadata entity (type reference/definition/specification, method definition, custom attribute, etc.) or value (string, blob, guid, user string).
    /// </summary>
    /// <remarks>
    /// Use <see cref="Handle"/> to store multiple kinds of handles.
    /// </remarks>
    public readonly struct Handle : IEquatable<Handle>
    {
        private readonly int _value;
 
        // bits:
        //    7: IsVirtual
        // 0..6: token type
        private readonly byte _vType;
 
        /// <summary>
        /// Creates <see cref="Handle"/> from a token or a token combined with a virtual flag.
        /// </summary>
        internal static Handle FromVToken(uint vToken)
        {
            return new Handle((byte)(vToken >> TokenTypeIds.RowIdBitCount), (int)(vToken & TokenTypeIds.RIDMask));
        }
 
        internal Handle(byte vType, int value)
        {
            _vType = vType;
            _value = value;
 
            Debug.Assert(value >= 0);
 
            // No table can have more than 2^24 rows.
            // User String heap is also limited by 2^24 since user strings have tokens in IL.
            // We limit the size of #Blob, #String and #GUID heaps to 2^29 (max compressed integer) in order
            // to keep the sizes of corresponding handles to 32 bit. As a result we don't support reading metadata
            // files with heaps larger than 0.5GB.
            Debug.Assert(IsHeapHandle && value <= HeapHandleType.OffsetMask ||
                         !IsHeapHandle && value <= TokenTypeIds.RIDMask);
        }
 
        // for entity handles:
        internal int RowId
        {
            get
            {
                Debug.Assert(!IsHeapHandle);
                return _value;
            }
        }
 
        // for heap handles:
        internal int Offset
        {
            get
            {
                Debug.Assert(IsHeapHandle);
                return _value;
            }
        }
 
        /// <summary>
        /// Token type (0x##000000), does not include virtual flag.
        /// </summary>
        internal uint EntityHandleType
        {
            get { return Type << TokenTypeIds.RowIdBitCount; }
        }
 
        /// <summary>
        /// Small token type (0x##), does not include virtual flag.
        /// </summary>
        internal uint Type
        {
            get { return _vType & HandleType.TypeMask; }
        }
 
        /// <summary>
        /// Value stored in an <see cref="EntityHandle"/>.
        /// </summary>
        internal uint EntityHandleValue
        {
            get
            {
                Debug.Assert((_value & TokenTypeIds.RIDMask) == _value);
                return (uint)_vType << TokenTypeIds.RowIdBitCount | (uint)_value;
            }
        }
 
        /// <summary>
        /// Value stored in a concrete entity handle (see <see cref="TypeDefinitionHandle"/>, <see cref="MethodDefinitionHandle"/>, etc.).
        /// </summary>
        internal uint SpecificEntityHandleValue
        {
            get
            {
                Debug.Assert((_value & TokenTypeIds.RIDMask) == _value);
                return (_vType & HandleType.VirtualBit) << TokenTypeIds.RowIdBitCount | (uint)_value;
            }
        }
 
        internal byte VType
        {
            get { return _vType; }
        }
 
        internal bool IsVirtual
        {
            get { return (_vType & HandleType.VirtualBit) != 0; }
        }
 
        internal bool IsHeapHandle
        {
            get { return (_vType & HandleType.HeapMask) == HandleType.HeapMask; }
        }
 
        public HandleKind Kind
        {
            get
            {
                uint type = Type;
 
                // Do not surface extra non-virtual string type bits in public handle kind
                if ((type & ~HandleType.NonVirtualStringTypeMask) == HandleType.String)
                {
                    return HandleKind.String;
                }
 
                return (HandleKind)type;
            }
        }
 
        public bool IsNil
        {
            // virtual handles are never nil
            get { return ((uint)_value | (_vType & HandleType.VirtualBit)) == 0; }
        }
 
        internal bool IsEntityOrUserStringHandle
        {
            get { return Type <= HandleType.UserString; }
        }
 
        internal int Token
        {
            get
            {
                Debug.Assert(IsEntityOrUserStringHandle);
                Debug.Assert(!IsVirtual);
                Debug.Assert((_value & TokenTypeIds.RIDMask) == _value);
 
                return _vType << TokenTypeIds.RowIdBitCount | _value;
            }
        }
 
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            return obj is Handle handle && Equals(handle);
        }
 
        public bool Equals(Handle other)
        {
            return _value == other._value && _vType == other._vType;
        }
 
        public override int GetHashCode()
        {
            return _value ^ (_vType << 24);
        }
 
        public static bool operator ==(Handle left, Handle right)
        {
            return left.Equals(right);
        }
 
        public static bool operator !=(Handle left, Handle right)
        {
            return !left.Equals(right);
        }
 
        internal static int Compare(Handle left, Handle right)
        {
            // All virtual tokens will be sorted after non-virtual tokens.
            // The order of handles that differ in kind is undefined,
            // but we include it so that we ensure consistency with == and != operators.
            return ((long)(uint)left._value | (long)left._vType << 32).CompareTo((long)(uint)right._value | (long)right._vType << 32);
        }
 
        public static readonly ModuleDefinitionHandle ModuleDefinition = new ModuleDefinitionHandle(1);
        public static readonly AssemblyDefinitionHandle AssemblyDefinition = new AssemblyDefinitionHandle(1);
    }
}