Initial push
This commit is contained in:
453
src/SqrtSpace.SpaceTime.Collections/AdaptiveDictionary.cs
Normal file
453
src/SqrtSpace.SpaceTime.Collections/AdaptiveDictionary.cs
Normal file
@@ -0,0 +1,453 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using SqrtSpace.SpaceTime.Core;
|
||||
|
||||
namespace SqrtSpace.SpaceTime.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary that automatically adapts its implementation based on size
|
||||
/// </summary>
|
||||
public class AdaptiveDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> where TKey : notnull
|
||||
{
|
||||
private IDictionary<TKey, TValue> _implementation;
|
||||
private readonly AdaptiveStrategy _strategy;
|
||||
private readonly IEqualityComparer<TKey> _comparer;
|
||||
|
||||
// Thresholds for switching implementations
|
||||
private const int ArrayThreshold = 16;
|
||||
private const int DictionaryThreshold = 10_000;
|
||||
private const int ExternalThreshold = 1_000_000;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new adaptive dictionary
|
||||
/// </summary>
|
||||
public AdaptiveDictionary() : this(0, null, AdaptiveStrategy.Automatic)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new adaptive dictionary with specified capacity
|
||||
/// </summary>
|
||||
public AdaptiveDictionary(int capacity, IEqualityComparer<TKey>? comparer = null, AdaptiveStrategy strategy = AdaptiveStrategy.Automatic)
|
||||
{
|
||||
_comparer = comparer ?? EqualityComparer<TKey>.Default;
|
||||
_strategy = strategy;
|
||||
_implementation = CreateImplementation(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current implementation type
|
||||
/// </summary>
|
||||
public ImplementationType CurrentImplementation
|
||||
{
|
||||
get
|
||||
{
|
||||
return _implementation switch
|
||||
{
|
||||
ArrayDictionary<TKey, TValue> => ImplementationType.Array,
|
||||
Dictionary<TKey, TValue> => ImplementationType.Dictionary,
|
||||
SortedDictionary<TKey, TValue> => ImplementationType.SortedDictionary,
|
||||
ExternalDictionary<TKey, TValue> => ImplementationType.External,
|
||||
_ => ImplementationType.Unknown
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets memory usage statistics
|
||||
/// </summary>
|
||||
public MemoryStatistics GetMemoryStatistics()
|
||||
{
|
||||
var itemSize = IntPtr.Size * 2; // Rough estimate for key-value pair
|
||||
var totalSize = Count * itemSize;
|
||||
var memoryLevel = MemoryHierarchy.DetectSystem().GetOptimalLevel(totalSize);
|
||||
|
||||
return new MemoryStatistics
|
||||
{
|
||||
ItemCount = Count,
|
||||
EstimatedMemoryBytes = totalSize,
|
||||
MemoryLevel = memoryLevel,
|
||||
Implementation = CurrentImplementation
|
||||
};
|
||||
}
|
||||
|
||||
#region IDictionary Implementation
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get => _implementation[key];
|
||||
set
|
||||
{
|
||||
_implementation[key] = value;
|
||||
AdaptIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => _implementation.Keys;
|
||||
public ICollection<TValue> Values => _implementation.Values;
|
||||
public int Count => _implementation.Count;
|
||||
public bool IsReadOnly => _implementation.IsReadOnly;
|
||||
|
||||
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
|
||||
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => Values;
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
_implementation.Add(key, value);
|
||||
AdaptIfNeeded();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
_implementation.Add(item);
|
||||
AdaptIfNeeded();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_implementation.Clear();
|
||||
AdaptIfNeeded();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item) => _implementation.Contains(item);
|
||||
public bool ContainsKey(TKey key) => _implementation.ContainsKey(key);
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => _implementation.CopyTo(array, arrayIndex);
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
var result = _implementation.Remove(key);
|
||||
AdaptIfNeeded();
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
var result = _implementation.Remove(item);
|
||||
AdaptIfNeeded();
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => _implementation.TryGetValue(key, out value);
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _implementation.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
#endregion
|
||||
|
||||
private void AdaptIfNeeded()
|
||||
{
|
||||
if (_strategy != AdaptiveStrategy.Automatic)
|
||||
return;
|
||||
|
||||
IDictionary<TKey, TValue>? newImplementation = Count switch
|
||||
{
|
||||
<= ArrayThreshold when CurrentImplementation != ImplementationType.Array =>
|
||||
new ArrayDictionary<TKey, TValue>(_comparer),
|
||||
|
||||
> ArrayThreshold and <= DictionaryThreshold when CurrentImplementation == ImplementationType.Array =>
|
||||
new Dictionary<TKey, TValue>(_comparer),
|
||||
|
||||
> DictionaryThreshold and <= ExternalThreshold when CurrentImplementation != ImplementationType.SortedDictionary =>
|
||||
new SortedDictionary<TKey, TValue>(Comparer<TKey>.Create((x, y) => _comparer.GetHashCode(x).CompareTo(_comparer.GetHashCode(y)))),
|
||||
|
||||
> ExternalThreshold when CurrentImplementation != ImplementationType.External =>
|
||||
new ExternalDictionary<TKey, TValue>(_comparer),
|
||||
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (newImplementation != null)
|
||||
{
|
||||
// Copy data to new implementation
|
||||
foreach (var kvp in _implementation)
|
||||
{
|
||||
newImplementation.Add(kvp);
|
||||
}
|
||||
|
||||
// Dispose old implementation if needed
|
||||
if (_implementation is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
_implementation = newImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<TKey, TValue> CreateImplementation(int capacity)
|
||||
{
|
||||
return capacity switch
|
||||
{
|
||||
<= ArrayThreshold => new ArrayDictionary<TKey, TValue>(_comparer),
|
||||
<= DictionaryThreshold => new Dictionary<TKey, TValue>(capacity, _comparer),
|
||||
<= ExternalThreshold => new SortedDictionary<TKey, TValue>(Comparer<TKey>.Create((x, y) => _comparer.GetHashCode(x).CompareTo(_comparer.GetHashCode(y)))),
|
||||
_ => new ExternalDictionary<TKey, TValue>(_comparer)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Array-based dictionary for small collections
|
||||
/// </summary>
|
||||
internal class ArrayDictionary<TKey, TValue> : IDictionary<TKey, TValue> where TKey : notnull
|
||||
{
|
||||
private readonly List<KeyValuePair<TKey, TValue>> _items;
|
||||
private readonly IEqualityComparer<TKey> _comparer;
|
||||
|
||||
public ArrayDictionary(IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
_items = new List<KeyValuePair<TKey, TValue>>();
|
||||
_comparer = comparer;
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
var index = FindIndex(key);
|
||||
if (index < 0) throw new KeyNotFoundException();
|
||||
return _items[index].Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
var index = FindIndex(key);
|
||||
if (index < 0)
|
||||
{
|
||||
_items.Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
_items[index] = new KeyValuePair<TKey, TValue>(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => _items.Select(kvp => kvp.Key).ToList();
|
||||
public ICollection<TValue> Values => _items.Select(kvp => kvp.Value).ToList();
|
||||
public int Count => _items.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (ContainsKey(key)) throw new ArgumentException("Key already exists");
|
||||
_items.Add(new KeyValuePair<TKey, TValue>(key, value));
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear() => _items.Clear();
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
var index = FindIndex(item.Key);
|
||||
return index >= 0 && EqualityComparer<TValue>.Default.Equals(_items[index].Value, item.Value);
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key) => FindIndex(key) >= 0;
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
_items.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
var index = FindIndex(key);
|
||||
if (index < 0) return false;
|
||||
_items.RemoveAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return _items.Remove(item);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
var index = FindIndex(key);
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _items[index].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _items.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
private int FindIndex(TKey key)
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
if (_comparer.Equals(_items[i].Key, key))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// External dictionary for very large collections
|
||||
/// </summary>
|
||||
internal class ExternalDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDisposable where TKey : notnull
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> _cache;
|
||||
private readonly ExternalStorage<KeyValuePair<TKey, TValue>> _storage;
|
||||
private readonly IEqualityComparer<TKey> _comparer;
|
||||
private readonly int _cacheSize;
|
||||
private int _totalCount;
|
||||
|
||||
public ExternalDictionary(IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
_comparer = comparer;
|
||||
_cache = new Dictionary<TKey, TValue>(_comparer);
|
||||
_storage = new ExternalStorage<KeyValuePair<TKey, TValue>>();
|
||||
_cacheSize = SpaceTimeCalculator.CalculateSqrtInterval(1_000_000);
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cache.TryGetValue(key, out var value))
|
||||
return value;
|
||||
|
||||
// Search in external storage
|
||||
foreach (var kvp in ReadAllFromStorage())
|
||||
{
|
||||
if (_comparer.Equals(kvp.Key, key))
|
||||
return kvp.Value;
|
||||
}
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_cache.Count >= _cacheSize)
|
||||
{
|
||||
SpillCacheToDisk();
|
||||
}
|
||||
_cache[key] = value;
|
||||
_totalCount = Math.Max(_totalCount, _cache.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<TKey> Keys => throw new NotSupportedException("Keys collection not supported for external dictionary");
|
||||
public ICollection<TValue> Values => throw new NotSupportedException("Values collection not supported for external dictionary");
|
||||
public int Count => _totalCount;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
if (ContainsKey(key)) throw new ArgumentException("Key already exists");
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item) => Add(item.Key, item.Value);
|
||||
public void Clear()
|
||||
{
|
||||
_cache.Clear();
|
||||
_storage.Dispose();
|
||||
_totalCount = 0;
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item) => ContainsKey(item.Key);
|
||||
public bool ContainsKey(TKey key) => _cache.ContainsKey(key) || ExistsInStorage(key);
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => throw new NotSupportedException();
|
||||
|
||||
public bool Remove(TKey key) => _cache.Remove(key);
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
|
||||
|
||||
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = this[key];
|
||||
return true;
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
foreach (var kvp in _cache)
|
||||
yield return kvp;
|
||||
|
||||
foreach (var kvp in ReadAllFromStorage())
|
||||
yield return kvp;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose() => _storage.Dispose();
|
||||
|
||||
private void SpillCacheToDisk()
|
||||
{
|
||||
_storage.SpillToDiskAsync(_cache).GetAwaiter().GetResult();
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
private bool ExistsInStorage(TKey key)
|
||||
{
|
||||
foreach (var kvp in ReadAllFromStorage())
|
||||
{
|
||||
if (_comparer.Equals(kvp.Key, key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<KeyValuePair<TKey, TValue>> ReadAllFromStorage()
|
||||
{
|
||||
// This is simplified - production would be more efficient
|
||||
return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation type of adaptive collection
|
||||
/// </summary>
|
||||
public enum ImplementationType
|
||||
{
|
||||
Unknown,
|
||||
Array,
|
||||
Dictionary,
|
||||
SortedDictionary,
|
||||
External
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Memory usage statistics
|
||||
/// </summary>
|
||||
public class MemoryStatistics
|
||||
{
|
||||
public int ItemCount { get; init; }
|
||||
public long EstimatedMemoryBytes { get; init; }
|
||||
public MemoryLevel MemoryLevel { get; init; }
|
||||
public ImplementationType Implementation { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strategy for adaptive collections
|
||||
/// </summary>
|
||||
public enum AdaptiveStrategy
|
||||
{
|
||||
/// <summary>Automatically adapt based on size</summary>
|
||||
Automatic,
|
||||
/// <summary>Always use array implementation</summary>
|
||||
ForceArray,
|
||||
/// <summary>Always use dictionary implementation</summary>
|
||||
ForceDictionary,
|
||||
/// <summary>Always use external implementation</summary>
|
||||
ForceExternal
|
||||
}
|
||||
Reference in New Issue
Block a user