File: System\Reflection\Metadata\Ecma335\BlobDictionary.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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Internal;
#if NET
using System.Runtime.InteropServices;
#endif
 
namespace System.Reflection.Metadata.Ecma335
{
    [DebuggerDisplay("Count = {Count}")]
    internal readonly struct BlobDictionary
    {
        private readonly Dictionary<int, KeyValuePair<ImmutableArray<byte>, BlobHandle>> _dictionary;
 
        // A simple LCG. Constants taken from
        // https://github.com/imneme/pcg-c/blob/83252d9c23df9c82ecb42210afed61a7b42402d7/include/pcg_variants.h#L276-L284
        private static int GetNextDictionaryKey(int dictionaryKey) =>
            (int)((uint)dictionaryKey * 747796405 + 2891336453);
 
#if NET
        private unsafe ref KeyValuePair<ImmutableArray<byte>, BlobHandle> GetValueRefOrAddDefault(ReadOnlySpan<byte> key, out bool exists)
        {
            int dictionaryKey = Hash.GetFNVHashCode(key);
            while (true)
            {
                ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_dictionary, dictionaryKey, out exists);
                if (!exists || entry.Key.AsSpan().SequenceEqual(key))
                {
#pragma warning disable CS9082 // Local is returned by reference but was initialized to a value that cannot be returned by reference
                    // In .NET 6 the assembly of GetValueRefOrAddDefault was compiled with earlier ref safety rules
                    // and caused an error, which was turned into a warning because of unsafe and was suppressed.
                    return ref entry;
#pragma warning restore CS9082
                }
                dictionaryKey = GetNextDictionaryKey(dictionaryKey);
            }
        }
 
        public BlobHandle GetOrAdd(ReadOnlySpan<byte> key, ImmutableArray<byte> immutableKey, BlobHandle value, out bool exists)
        {
            ref var entry = ref GetValueRefOrAddDefault(key, out exists);
            if (exists)
            {
                return entry.Value;
            }
 
            // If we are given an immutable array, do not allocate a new one.
            if (immutableKey.IsDefault)
            {
                immutableKey = key.ToImmutableArray();
            }
            else
            {
                Debug.Assert(immutableKey.AsSpan().SequenceEqual(key));
            }
 
            entry = new(immutableKey, value);
            return value;
        }
#else
        public BlobHandle GetOrAdd(ReadOnlySpan<byte> key, ImmutableArray<byte> immutableKey, BlobHandle value, out bool exists)
        {
            int dictionarykey = Hash.GetFNVHashCode(key);
            KeyValuePair<ImmutableArray<byte>, BlobHandle> entry;
            while (true)
            {
                if (!(exists = _dictionary.TryGetValue(dictionarykey, out entry))
                    || entry.Key.AsSpan().SequenceEqual(key))
                {
                    break;
                }
                dictionarykey = GetNextDictionaryKey(dictionarykey);
            }
 
            if (exists)
            {
                return entry.Value;
            }
 
            // If we are given an immutable array, do not allocate a new one.
            if (immutableKey.IsDefault)
            {
                immutableKey = key.ToImmutableArray();
            }
            else
            {
                Debug.Assert(immutableKey.AsSpan().SequenceEqual(key));
            }
 
            _dictionary.Add(dictionarykey, new(immutableKey, value));
            return value;
        }
#endif
 
        public BlobDictionary(int capacity = 0)
        {
            _dictionary = new(capacity);
        }
 
        public int Count => _dictionary.Count;
 
        public Dictionary<int, KeyValuePair<ImmutableArray<byte>, BlobHandle>>.Enumerator GetEnumerator() =>
            _dictionary.GetEnumerator();
    }
}