#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Robust.Shared.Utility
{
///
/// An immutable dictionary of mutable and immutable sets for use as an index of unique values related to another collection.
/// Imitates the behavior of a read-focused index in a RDBMS.
///
///
/// Use when the index's keys don't change that rapidly or when fast lookup is preferred over creation time.
/// It is not intended to explicitly construct this index before use.
/// Do not refer to a by it's interface.
/// See for details.
///
/// The type of key.
/// The type of value.
///
///
[PublicAPI]
public struct UniqueIndex : IUniqueIndex where TKey : notnull
{
private ImmutableDictionary>? _index;
///
public int KeyCount => _index?.Count ?? 0;
///
public bool Add(TKey key, TValue value)
{
ISet? set;
if (_index is null)
{
set = new HashSet {value};
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair>(key, set)});
return true;
}
if (_index.TryGetValue(key, out set))
{
return set.Add(value);
}
_index = _index.Add(key, new HashSet {value});
return true;
}
///
public int AddRange(TKey key, IEnumerable values)
{
ISet? set;
if (_index is null)
{
set = new HashSet(values);
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair>(key, set)});
return set.Count;
}
if (_index.TryGetValue(key, out set))
{
var c = set.Count;
set.UnionWith(values);
return set.Count - c;
}
_index = _index.Add(key, set = new HashSet(values));
return set.Count;
}
///
public bool Remove(TKey key)
{
if (_index == null)
{
return false;
}
var newIndex = _index.SetItem(key, new HashSet());
if (_index != newIndex)
{
return false;
}
_index = newIndex;
return true;
}
///
public bool Remove(TKey key, TValue value)
{
// ReSharper disable once InvertIf
if (_index == null)
{
return false;
}
return _index.TryGetValue(key, out var set)
&& set.Remove(value);
}
///
public int RemoveRange(TKey key, IEnumerable values)
{
if (_index == null)
{
return 0;
}
if (!_index.TryGetValue(key, out var set))
{
return 0;
}
var c = set.Count;
set.ExceptWith(set);
return c - set.Count;
}
///
public bool Replace(TKey key, TValue oldValue, TValue newValue)
{
if (_index == null)
{
return false;
}
if (!_index.TryGetValue(key, out var set))
{
return false;
}
return set.Remove(oldValue)
&& set.Add(newValue);
}
///
public void Touch(TKey key)
{
_index ??= ImmutableDictionary>.Empty;
if (_index.ContainsKey(key)) return;
_index = _index.Add(key, new HashSet());
}
///
public bool Freeze(TKey key)
{
if (_index is null)
{
return false;
}
if (!_index.TryGetValue(key, out var set)
|| set is ImmutableHashSet)
{
return false;
}
_index = _index.SetItem(key, ImmutableHashSet.CreateRange(set));
return true;
}
///
public void Initialize(IEnumerable keys)
=> Initialize(keys.Select(k => new KeyValuePair>(k, new HashSet())));
///
public void Initialize(IEnumerable>> index)
{
if (_index != null) throw new InvalidOperationException("Already initialized.");
_index = ImmutableDictionary.CreateRange(index);
}
public ISet this[TKey key]
{
get
{
ISet? set;
if (_index is null)
{
_index = ImmutableDictionary>.Empty;
}
else
{
if (_index.TryGetValue(key, out set))
{
return set;
}
}
_index = _index.Add(key, set = new HashSet());
return set;
}
}
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator>> GetEnumerator()
{
if (_index != null)
{
return _index.GetEnumerator();
}
return Enumerable.Empty>>().GetEnumerator();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}