HashTableSlim.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.CompilerServices;
  4. namespace Ryujinx.Graphics.Vulkan
  5. {
  6. interface IRefEquatable<T>
  7. {
  8. bool Equals(ref T other);
  9. }
  10. class HashTableSlim<TKey, TValue> where TKey : IRefEquatable<TKey>
  11. {
  12. private const int TotalBuckets = 16; // Must be power of 2
  13. private const int TotalBucketsMask = TotalBuckets - 1;
  14. private struct Entry
  15. {
  16. public int Hash;
  17. public TKey Key;
  18. public TValue Value;
  19. }
  20. private struct Bucket
  21. {
  22. public int Length;
  23. public Entry[] Entries;
  24. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  25. public readonly Span<Entry> AsSpan()
  26. {
  27. return Entries == null ? Span<Entry>.Empty : Entries.AsSpan(0, Length);
  28. }
  29. }
  30. private readonly Bucket[] _hashTable = new Bucket[TotalBuckets];
  31. public IEnumerable<TKey> Keys
  32. {
  33. get
  34. {
  35. foreach (Bucket bucket in _hashTable)
  36. {
  37. for (int i = 0; i < bucket.Length; i++)
  38. {
  39. yield return bucket.Entries[i].Key;
  40. }
  41. }
  42. }
  43. }
  44. public IEnumerable<TValue> Values
  45. {
  46. get
  47. {
  48. foreach (Bucket bucket in _hashTable)
  49. {
  50. for (int i = 0; i < bucket.Length; i++)
  51. {
  52. yield return bucket.Entries[i].Value;
  53. }
  54. }
  55. }
  56. }
  57. public void Add(ref TKey key, TValue value)
  58. {
  59. Entry entry = new()
  60. {
  61. Hash = key.GetHashCode(),
  62. Key = key,
  63. Value = value,
  64. };
  65. int hashCode = key.GetHashCode();
  66. int bucketIndex = hashCode & TotalBucketsMask;
  67. ref Bucket bucket = ref _hashTable[bucketIndex];
  68. if (bucket.Entries != null)
  69. {
  70. int index = bucket.Length;
  71. if (index >= bucket.Entries.Length)
  72. {
  73. Array.Resize(ref bucket.Entries, index + 1);
  74. }
  75. bucket.Entries[index] = entry;
  76. }
  77. else
  78. {
  79. bucket.Entries =
  80. [
  81. entry
  82. ];
  83. }
  84. bucket.Length++;
  85. }
  86. public bool Remove(ref TKey key)
  87. {
  88. int hashCode = key.GetHashCode();
  89. ref Bucket bucket = ref _hashTable[hashCode & TotalBucketsMask];
  90. Span<Entry> entries = bucket.AsSpan();
  91. for (int i = 0; i < entries.Length; i++)
  92. {
  93. ref Entry entry = ref entries[i];
  94. if (entry.Hash == hashCode && entry.Key.Equals(ref key))
  95. {
  96. entries[(i + 1)..].CopyTo(entries[i..]);
  97. bucket.Length--;
  98. return true;
  99. }
  100. }
  101. return false;
  102. }
  103. public bool TryGetValue(ref TKey key, out TValue value)
  104. {
  105. int hashCode = key.GetHashCode();
  106. Span<Entry> entries = _hashTable[hashCode & TotalBucketsMask].AsSpan();
  107. for (int i = 0; i < entries.Length; i++)
  108. {
  109. ref Entry entry = ref entries[i];
  110. if (entry.Hash == hashCode && entry.Key.Equals(ref key))
  111. {
  112. value = entry.Value;
  113. return true;
  114. }
  115. }
  116. value = default;
  117. return false;
  118. }
  119. }
  120. }