File: System\Reflection\TypeLoading\General\Ecma\MetadataTable.cs
Web Access
Project: src\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Threading;
 
namespace System.Reflection.TypeLoading.Ecma
{
    /// <summary>
    /// Thread-safe interning table for objects that map 1-1 with ECMA tokens.
    ///
    /// The key type is hard-coded to EntityHandle.
    /// The "T" type is the value type (e.g. RoTypeDefinition objects)
    /// The "C" type is an optional context value passed through the factory methods (so we don't to allocate a closure each time.)
    /// </summary>
    internal sealed class MetadataTable<T, C>
        where T : class
    {
        private readonly T?[] _table;
 
        public MetadataTable(int count)
        {
            Count = count;
            _table = new T?[count];
        }
 
        public T GetOrAdd(EntityHandle handle, C context, Func<EntityHandle, C, T> factory)
        {
            Debug.Assert(!handle.IsNil);
            Debug.Assert(factory != null);
 
            int index = handle.GetToken().GetTokenRowNumber() - 1;
            T?[] table = _table;
            T? result = Volatile.Read(ref table[index]);
            if (result != null)
                return result;
            T newValue = factory(handle, context);
            return Interlocked.CompareExchange(ref table[index], newValue, null) ?? newValue;
        }
 
        public int Count { get; }
 
        /// <summary>
        /// Return a read-only enumeration of the table (safe to hand back to app code.)
        /// </summary>
        public IEnumerable<T?> EnumerateValues(int skip = 0)
        {
            for (int i = skip; i < _table.Length; i++)
            {
                yield return _table[i];
            }
        }
 
        /// <summary>
        /// Return a newly allocated array containing the contents (safe to hand back to app code.)
        /// </summary>
        public TOut[] ToArray<TOut>(int skip = 0)
        {
            TOut[] newArray = new TOut[Count - skip];
            Array.Copy(_table, skip, newArray, 0, newArray.Length);
            return newArray;
        }
    }
}