File: src\tools\illink\src\ILLink.Shared\DataFlow\DefaultValueDictionary.cs
Web Access
Project: src\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
// This is needed due to NativeAOT which doesn't enable nullable globally yet
#nullable enable
 
namespace ILLink.Shared.DataFlow
{
    // This is a dictionary along with a default value, where every possible key either maps to
    // the default value, or another value. The default value is never explicitly stored in the dictionary,
    // and the empty dictionary (where all possible keys have the default value) is represented without
    // actually allocating a dictionary.
    public struct DefaultValueDictionary<TKey, TValue> : IEquatable<DefaultValueDictionary<TKey, TValue>>,
        IEnumerable<KeyValuePair<TKey, TValue>>
        where TKey : IEquatable<TKey>
        where TValue : IEquatable<TValue>
    {
        private Dictionary<TKey, TValue>? Dictionary;
        private readonly TValue DefaultValue;
 
        public DefaultValueDictionary(TValue defaultValue) => (Dictionary, DefaultValue) = (null, defaultValue);
 
        private DefaultValueDictionary(TValue defaultValue, Dictionary<TKey, TValue> dictionary) => (Dictionary, DefaultValue) = (dictionary, defaultValue);
 
        public DefaultValueDictionary(DefaultValueDictionary<TKey, TValue> other)
        {
            Dictionary = other.Dictionary == null ? null : new Dictionary<TKey, TValue>(other.Dictionary);
            DefaultValue = other.DefaultValue;
        }
 
        public TValue Get(TKey key) => Dictionary?.TryGetValue(key, out var value) == true ? value : DefaultValue;
 
        public void Set(TKey key, TValue value)
        {
            if (value.Equals(DefaultValue))
                Dictionary?.Remove(key);
            else
                (Dictionary ??= new Dictionary<TKey, TValue>())[key] = value;
        }
 
        public bool Equals(DefaultValueDictionary<TKey, TValue> other)
        {
            if (!DefaultValue.Equals(other.DefaultValue))
                return false;
 
            if (Dictionary == null)
                return other.Dictionary == null;
 
            if (other.Dictionary == null)
                return false;
 
            if (Dictionary.Count != other.Dictionary.Count)
                return false;
 
            foreach (var kvp in other.Dictionary)
            {
                if (!Get(kvp.Key).Equals(kvp.Value))
                    return false;
            }
 
            return true;
        }
 
        public override bool Equals(object? obj) => obj is DefaultValueDictionary<TKey, TValue> other && Equals(other);
 
        public int Count => Dictionary?.Count ?? 0;
 
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return Dictionary?.GetEnumerator() ?? Enumerable.Empty<KeyValuePair<TKey, TValue>>().GetEnumerator();
        }
 
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
        public override string ToString()
        {
            StringBuilder sb = new();
            sb.Append('{');
            if (Dictionary != null)
            {
                foreach (var kvp in Dictionary)
                    sb.AppendLine().Append('\t').Append(kvp.Key.ToString()).Append(" -> ").Append(kvp.Value.ToString());
            }
            sb.AppendLine().Append("\t_ -> ").Append(DefaultValue.ToString());
            sb.AppendLine().Append('}');
            return sb.ToString();
        }
 
        public DefaultValueDictionary<TKey, TValue> Clone()
        {
            var defaultValue = DefaultValue is IDeepCopyValue<TValue> copyDefaultValue ? copyDefaultValue.DeepCopy() : DefaultValue;
            if (Dictionary == null)
                return new DefaultValueDictionary<TKey, TValue>(defaultValue);
 
            var dict = new Dictionary<TKey, TValue>();
            foreach (var kvp in Dictionary)
            {
                var key = kvp.Key;
                var value = kvp.Value;
                dict.Add(key, value is IDeepCopyValue<TValue> copyValue ? copyValue.DeepCopy() : value);
            }
            return new DefaultValueDictionary<TKey, TValue>(defaultValue, dict);
        }
 
        // Prevent warning CS0659 https://learn.microsoft.com/dotnet/csharp/misc/cs0659.
        // This type should never be used as a dictionary key.
        public override int GetHashCode() => throw new NotImplementedException();
 
        public static bool operator ==(DefaultValueDictionary<TKey, TValue> left, DefaultValueDictionary<TKey, TValue> right) => left.Equals(right);
        public static bool operator !=(DefaultValueDictionary<TKey, TValue> left, DefaultValueDictionary<TKey, TValue> right) => !(left == right);
    }
}