using System.Collections;
using System.Runtime;
using System.Runtime.CompilerServices;
namespace XFESystem.Browser;
/// <summary>Page-owned ephemerons. Buckets contain no strong key references;
/// a value that refers back to its key cannot keep a detached node alive.
/// Pruning and release explicitly free native handles without finalizers.</summary>
internal sealed class DependentIdentityMap<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
where TKey : class where TValue : class
{
private sealed class Entry(TKey key,TValue value)
{
internal DependentHandle Handle = new(key,value);
}
private readonly Dictionary<int,List<Entry>> buckets=[];
private readonly List<int> emptyBuckets=[];
private int count;
public long HandlesCreated {get;private set;}
public long HandlesDisposed {get;private set;}
public int Count {get{Prune();return count;}}
public IEnumerable<TKey> Keys => this.Select(pair=>pair.Key);
public TValue this[TKey key] => TryGetValue(key,out var value)?value:throw new KeyNotFoundException();
public bool ContainsKey(TKey key)=>TryGetValue(key,out _);
public bool TryGetValue(TKey key,out TValue value)
{
if(buckets.TryGetValue(RuntimeHelpers.GetHashCode(key),out var bucket))
foreach(var entry in bucket)
{
var pair=entry.Handle.TargetAndDependent;
if(ReferenceEquals(pair.Target,key)&&pair.Dependent is TValue found){value=found;return true;}
}
value=null!;return false;
}
public void Add(TKey key,TValue value)
{
ArgumentNullException.ThrowIfNull(key);ArgumentNullException.ThrowIfNull(value);
int hash=RuntimeHelpers.GetHashCode(key);
if(!buckets.TryGetValue(hash,out var bucket))buckets.Add(hash,bucket=[]);
for(int index=bucket.Count-1;index>=0;index--)
{
var entry=bucket[index];var pair=entry.Handle.TargetAndDependent;
if(pair.Target is null){DisposeEntry(entry);bucket.RemoveAt(index);continue;}
if(ReferenceEquals(pair.Target,key))throw new ArgumentException("The identity key already exists.",nameof(key));
}
bucket.Add(new(key,value));count++;HandlesCreated++;
}
public bool Remove(TKey key)=>Remove(key,out _);
public bool Remove(TKey key,out TValue value)
{
int hash=RuntimeHelpers.GetHashCode(key);
if(buckets.TryGetValue(hash,out var bucket))
for(int index=0;index<bucket.Count;index++)
{
var entry=bucket[index];var pair=entry.Handle.TargetAndDependent;
if(!ReferenceEquals(pair.Target,key))continue;
value=(TValue)pair.Dependent!;DisposeEntry(entry);bucket.RemoveAt(index);
if(bucket.Count==0)buckets.Remove(hash);return true;
}
value=null!;return false;
}
public void Clear()
{
foreach(var bucket in buckets.Values)foreach(var entry in bucket)DisposeEntry(entry);
buckets.Clear();emptyBuckets.Clear();
}
private void DisposeEntry(Entry entry){entry.Handle.Dispose();count--;HandlesDisposed++;}
private void Prune()
{
emptyBuckets.Clear();
foreach(var item in buckets)
{
var bucket=item.Value;
for(int index=bucket.Count-1;index>=0;index--)
if(bucket[index].Handle.Target is null){DisposeEntry(bucket[index]);bucket.RemoveAt(index);}
if(bucket.Count==0)emptyBuckets.Add(item.Key);
}
foreach(int hash in emptyBuckets)buckets.Remove(hash);
emptyBuckets.Clear();
}
public IEnumerator<KeyValuePair<TKey,TValue>> GetEnumerator()
{
foreach(var bucket in buckets.Values)foreach(var entry in bucket)
{
var pair=entry.Handle.TargetAndDependent;
if(pair.Target is TKey key&&pair.Dependent is TValue value)yield return new(key,value);
}
}
IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();
}
using System.Collections;
using System.Runtime;
using System.Runtime.CompilerServices;
namespace XFESystem.Browser;
/// <summary>Page-owned ephemerons. Buckets contain no strong key references;
/// a value that refers back to its key cannot keep a detached node alive.
/// Pruning and release explicitly free native handles without finalizers.</summary>
internal sealed class DependentIdentityMap<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
where TKey : class where TValue : class
{
private sealed class Entry(TKey key,TValue value)
{
internal DependentHandle Handle = new(key,value);
}
private readonly Dictionary<int,List<Entry>> buckets=[];
private readonly List<int> emptyBuckets=[];
private int count;
public long HandlesCreated {get;private set;}
public long HandlesDisposed {get;private set;}
public int Count {get{Prune();return count;}}
public IEnumerable<TKey> Keys => this.Select(pair=>pair.Key);
public TValue this[TKey key] => TryGetValue(key,out var value)?value:throw new KeyNotFoundException();
public bool ContainsKey(TKey key)=>TryGetValue(key,out _);
public bool TryGetValue(TKey key,out TValue value)
{
if(buckets.TryGetValue(RuntimeHelpers.GetHashCode(key),out var bucket))
foreach(var entry in bucket)
{
var pair=entry.Handle.TargetAndDependent;
if(ReferenceEquals(pair.Target,key)&&pair.Dependent is TValue found){value=found;return true;}
}
value=null!;return false;
}
public void Add(TKey key,TValue value)
{
ArgumentNullException.ThrowIfNull(key);ArgumentNullException.ThrowIfNull(value);
int hash=RuntimeHelpers.GetHashCode(key);
if(!buckets.TryGetValue(hash,out var bucket))buckets.Add(hash,bucket=[]);
for(int index=bucket.Count-1;index>=0;index--)
{
var entry=bucket[index];var pair=entry.Handle.TargetAndDependent;
if(pair.Target is null){DisposeEntry(entry);bucket.RemoveAt(index);continue;}
if(ReferenceEquals(pair.Target,key))throw new ArgumentException("The identity key already exists.",nameof(key));
}
bucket.Add(new(key,value));count++;HandlesCreated++;
}
public bool Remove(TKey key)=>Remove(key,out _);
public bool Remove(TKey key,out TValue value)
{
int hash=RuntimeHelpers.GetHashCode(key);
if(buckets.TryGetValue(hash,out var bucket))
for(int index=0;index<bucket.Count;index++)
{
var entry=bucket[index];var pair=entry.Handle.TargetAndDependent;
if(!ReferenceEquals(pair.Target,key))continue;
value=(TValue)pair.Dependent!;DisposeEntry(entry);bucket.RemoveAt(index);
if(bucket.Count==0)buckets.Remove(hash);return true;
}
value=null!;return false;
}
public void Clear()
{
foreach(var bucket in buckets.Values)foreach(var entry in bucket)DisposeEntry(entry);
buckets.Clear();emptyBuckets.Clear();
}
private void DisposeEntry(Entry entry){entry.Handle.Dispose();count--;HandlesDisposed++;}
private void Prune()
{
emptyBuckets.Clear();
foreach(var item in buckets)
{
var bucket=item.Value;
for(int index=bucket.Count-1;index>=0;index--)
if(bucket[index].Handle.Target is null){DisposeEntry(bucket[index]);bucket.RemoveAt(index);}
if(bucket.Count==0)emptyBuckets.Add(item.Key);
}
foreach(int hash in emptyBuckets)buckets.Remove(hash);
emptyBuckets.Clear();
}
public IEnumerator<KeyValuePair<TKey,TValue>> GetEnumerator()
{
foreach(var bucket in buckets.Values)foreach(var entry in bucket)
{
var pair=entry.Handle.TargetAndDependent;
if(pair.Target is TKey key&&pair.Dependent is TValue value)yield return new(key,value);
}
}
IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();
}