using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using MemoryPools.Collections.Specialized.Helpers;
namespace MemoryPools.Collections.Specialized
{
///
/// Pooling dictionary contains two PoolingLists and should be disposed at end of life.
/// Disallowed: removing elements and getting non-generic IEnumerable.
/// When get IEnumerable[TKey, TValue], you need to dispose it (foreach Expr do it automatically).
/// You can safe add any count of elements to dispose them: all collections stores data in 128-sized chunks.
/// These chunks are reusable btw all Pooling* collections. All operations have O(1) complexity.
/// Primary rets IPoolingEnumerable. But you can cast to IEnumerable to work in common manner.
///
public partial class PoolingDictionary :
IDictionary,
IReadOnlyDictionary,
IPoolingEnumerable>,
IDisposable
{
[DebuggerDisplay("Key: {key}, Value: {value}")]
private struct Entry
{
public int hashCode; // Lower 31 bits of hash code, -1 if unused
public int next; // Index of next entry, -1 if last
public TKey key; // Key of entry
public TValue value; // Value of entry
}
private IEqualityComparer _comparer;
private PoolingList _buckets;
private PoolingList _entries;
private int _freeList;
private int _version;
private int _freeCount;
private int _count;
private int _complexity;
private bool _refType;
private const int EndOfChain = -1;
public PoolingDictionary() => Init();
public PoolingDictionary Init(int capacity = 0, IEqualityComparer comparer = default!)
{
if (_buckets != default)
{
return this;
}
_refType = typeof(TKey).IsClass;
var size = HashHelpers.GetPrime(capacity);
_buckets = Pool>.Get().Init();
for (var i = 0; i < size; i++)
{
_buckets.Add(EndOfChain);
}
_entries = Pool>.Get().Init();
_freeList = EndOfChain;
_comparer = comparer ?? EqualityComparer.Default;
return this;
}
public bool TryGetValue(TKey key, out TValue value)
{
var i = FindEntry(key);
if (i >= 0)
{
value = _entries[i].value;
return true;
}
value = default!;
return false;
}
public TValue this[TKey key]
{
get
{
var i = FindEntry(key);
if (i >= 0) return _entries[i].value;
throw new KeyNotFoundException();
}
set => Insert(key, value, false);
}
IEnumerable IReadOnlyDictionary.Keys => Keys;
IEnumerable IReadOnlyDictionary.Values => Values;
public ICollection Keys => throw new NotImplementedException(); // _keys ??= Pool.Get().Init(this);
public ICollection Values => throw new NotImplementedException(); // _values ??= Pool.Get().Init(this);
private int FindEntry(TKey key)
{
if (_refType && key == null!)
{
throw new ArgumentNullException(nameof(key));
}
if (_buckets == null!) return -1;
var hashCode = (key?.GetHashCode() ?? 0) & 0x7FFFFFFF;
for (var i = _buckets[hashCode % _buckets.Count]; i >= 0; i = _entries[i].next)
{
if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key)) return i;
}
return -1;
}
public int Complexity => _complexity;
public void Add(TKey key, TValue value) => Insert(key, value, true);
public bool ContainsKey(TKey key) => FindEntry(key) >= 0;
public bool Remove(TKey key)
{
throw new NotImplementedException();
}
private void Insert(TKey key, TValue value, bool add)
{
if (_refType && key == null!)
{
throw new ArgumentNullException(nameof(key));
}
if (_buckets == null!) Init(PoolsDefaults.DefaultPoolBucketSize);
var hashCode = key!.GetHashCode() & 0x7FFFFFFF;
var targetBucket = hashCode % (_buckets?.Count ?? 0);
var complexity = 0;
for (var i = _buckets![targetBucket]; i >= 0; i = _entries[i].next)
{
if (_entries[i].hashCode == hashCode && _comparer.Equals(_entries[i].key, key))
{
if (add)
{
throw new ArgumentException("Duplicating key found in dictionary");
}
var entrym = _entries[i];
entrym.value = value;
_entries[i] = entrym;
unchecked
{
_version++;
}
return;
}
complexity++;
}
int index;
if (_freeCount > 0)
{
index = _freeList;
_freeList = _entries[index].next;
_freeCount--;
}
else
{
if (_count == _entries.Count)
{
Resize();
targetBucket = hashCode % _buckets.Count;
}
index = _count;
_count++;
}
var entry = _entries[index];
entry.hashCode = hashCode;
entry.next = _buckets[targetBucket];
entry.key = key;
entry.value = value;
_entries[index] = entry;
_buckets[targetBucket] = index;
unchecked
{
_version++;
}
_complexity = Math.Max(_complexity, complexity);
}
private void Resize()
{
Resize(HashHelpers.ExpandPrime(_count), false);
}
private void Resize(int newSize, bool forceNewHashCodes)
{
var newBuckets = Pool>.Get().Init();
while (newBuckets.Count < newSize) newBuckets.Add(EndOfChain);
while (_entries.Count < newSize) _entries.Add(new Entry { hashCode = EndOfChain, next = EndOfChain });
if (forceNewHashCodes)
{
for (var i = 0; i < _count; i++)
{
if (_entries[i].hashCode != -1)
{
var entry = _entries[i];
entry.hashCode = (_entries[i].key?.GetHashCode() ?? 0) & 0x7FFFFFFF;
_entries[i] = entry;
}
}
}
for (int i = 0; i < newSize; i++)
{
if (_entries[i].hashCode >= 0)
{
int bucket = _entries[i].hashCode % newSize;
var entry = _entries[i];
entry.next = newBuckets[bucket];
_entries[i] = entry;
newBuckets[bucket] = i;
}
}
_buckets.Dispose();
Pool>.Return(_buckets);
_buckets = newBuckets;
}
public void Dispose()
{
unchecked
{
_version++;
}
_buckets?.Dispose();
Pool>.Return(_buckets!);
_entries?.Dispose();
Pool>.Return(_entries!);
_buckets = default!;
_entries = default!;
_comparer = default!;
_complexity = _count = _version = _freeCount = _freeList = default!;
}
public void Add(KeyValuePair item) =>
Insert(item.Key, item.Value, true);
public void Clear()
{
_buckets.Clear();
_entries.Clear();
_complexity = 0;
_count = _freeList = _freeCount = 0;
unchecked
{
_version++;
}
}
public bool Contains(KeyValuePair item)
{
var keyHash = (item.Key?.GetHashCode() ?? 0) & 0x7FFFFFFF;
for (var i = 0; i < _entries.Count; i++)
{
if (_entries[i].hashCode == keyHash && _comparer.Equals(_entries[i].key, item.Key) &&
(_entries[i].value?.Equals(item.Value) ?? false))
{
return true;
}
}
return false;
}
public void CopyTo(KeyValuePair[] array, int arrayIndex)
{
if (array.Length - arrayIndex < _entries.Count)
{
throw new IndexOutOfRangeException("Dictionary size bigger than array");
}
for (var i = 0; i < _entries.Count; i++)
{
array[arrayIndex + i] = new KeyValuePair(_entries[i].key, _entries[i].value);
}
}
public bool Remove(KeyValuePair item)
{
throw new NotImplementedException();
}
public int Count => _count;
public bool IsReadOnly => false;
internal class Enumerator : IEnumerator>, IPoolingEnumerator>
{
private PoolingDictionary _src;
private int _pos;
private int _ver;
public Enumerator Init(PoolingDictionary src)
{
_pos = -1;
_src = src;
_ver = _src._version;
return this;
}
public bool MoveNext()
{
if (_pos >= _src.Count) return false;
if (_ver != _src._version)
{
throw new InvalidOperationException("Version of collection was changed while enumeration");
}
_pos++;
return _pos < _src._count;
}
public void Reset()
{
_ver = _src._version;
_pos = -1;
}
object IPoolingEnumerator.Current => Current!;
public KeyValuePair Current
{
get
{
if (_ver != _src._version)
{
throw new InvalidOperationException("Version of collection was changed while enumeration");
}
return new KeyValuePair(_src._entries[_pos].key, _src._entries[_pos].value);
}
}
object IEnumerator.Current => throw new InvalidOperationException("Boxing disallowed");
public void Dispose()
{
Pool.Return(this);
}
}
public IPoolingEnumerator> GetEnumerator() =>
Pool.Get().Init(this);
IEnumerator> IEnumerable>.GetEnumerator() =>
(IEnumerator>)GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator();
IPoolingEnumerator IPoolingEnumerable.GetEnumerator() => GetEnumerator();
}
}