|
// 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);
}
}
|