HashSet.vala 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. namespace Invercargill.DataStructures {
  2. public class HashSet<T> : Enumerable<T>, Lot<T>, ReadOnlyCollection<T>, ReadOnlySet<T>, Set<T> {
  3. [Compact]
  4. private class HashSetItem<T> {
  5. public uint hash;
  6. public T item;
  7. }
  8. private HashSetItem<T>* tombstone;
  9. private HashSetItem<T>*[] buckets;
  10. private uint n_items = 0;
  11. private uint n_buckets = 16;
  12. private uint n_collissions = 0;
  13. private SafeReadFunc<T>? safe_read;
  14. private SafeWriteFunc<T>? safe_write;
  15. private HashDelegate<T> hash_func;
  16. private EqualityDelegate<T> equal_func;
  17. construct {
  18. setup();
  19. }
  20. public HashSet(HashDelegate<T>? value_hash_func = null, EqualityDelegate<T>? value_equal_func = null) {
  21. setup(value_hash_func, value_equal_func);
  22. }
  23. private void setup(HashDelegate<T>? value_hash_func = null, EqualityDelegate<T>? value_equal_func = null) {
  24. tombstone = new HashSetItem<T>();
  25. hash_func = value_hash_func ?? Operators.hash<T>();
  26. equal_func = value_equal_func ?? Operators.equality<T>();
  27. safe_read = get_safe_read_function_for<T>();
  28. safe_write = get_safe_write_function_for<T>();
  29. buckets = new HashSetItem<T>[n_buckets];
  30. }
  31. private void ensure_room_for_bucket(uint index) {
  32. var target = buckets.length;
  33. while(target <= index) {
  34. target = buckets.length * 2;
  35. }
  36. if(target != buckets.length) {
  37. buckets.resize(target);
  38. }
  39. }
  40. private void double_buckets() {
  41. n_buckets *= 2;
  42. var old_buckets = buckets;
  43. buckets = new HashSetItem<T>*[n_buckets];
  44. n_collissions = 0;
  45. for(uint i = 0; i < old_buckets.length; i++) {
  46. var bucket = old_buckets[i];
  47. if(bucket == null || bucket == tombstone) {
  48. continue;
  49. }
  50. var bucket_index = bucket->hash % n_buckets;
  51. while(buckets[bucket_index] != null) {
  52. bucket_index++;
  53. n_collissions++;
  54. ensure_room_for_bucket(bucket_index);
  55. }
  56. buckets[bucket_index] = bucket;
  57. }
  58. }
  59. private uint bucket_for(T item) {
  60. var hash = hash_func(item);
  61. return hash % n_buckets;
  62. }
  63. public override uint? peek_count() {
  64. return n_items;
  65. }
  66. public override uint length { get { return n_items; }}
  67. public override EnumerableInfo get_info() {
  68. return new EnumerableInfo.infer_ultimate (this, EnumerableCategory.IN_MEMORY);
  69. }
  70. public override Tracker<T> get_tracker () {
  71. return Iterate.range(0, (int)n_buckets)
  72. .where(i => buckets[i] != null && buckets[i] != tombstone)
  73. .select<T>(i => buckets[i]->item)
  74. .get_tracker();
  75. }
  76. public bool add (T item) {
  77. return add_internal(item, false);
  78. }
  79. public new void @set(T item) {
  80. add_internal(item, true);
  81. }
  82. private bool add_internal (owned T item, bool overwrite) {
  83. if(n_collissions > n_buckets / 4) {
  84. double_buckets();
  85. }
  86. var bucket_hash = hash_func(item);
  87. var bucket_index = bucket_hash % n_buckets;
  88. var bucket = buckets[bucket_index];
  89. while(bucket != null && bucket != tombstone) {
  90. if(bucket->hash == bucket_hash && equal_func(bucket->item, item)) {
  91. if(overwrite)
  92. bucket->item = (owned)item;
  93. return overwrite;
  94. }
  95. bucket_index++;
  96. n_collissions++;
  97. ensure_room_for_bucket(bucket_index);
  98. bucket = buckets[bucket_index];
  99. }
  100. HashSetItem<T>* new_bucket = new HashSetItem<T>();
  101. new_bucket->hash = bucket_hash;
  102. new_bucket->item = (owned)item;
  103. buckets[bucket_index] = new_bucket;
  104. n_items++;
  105. return true;
  106. }
  107. public bool try_find(T search, out T item) {
  108. var bucket_index = bucket_for(search);
  109. while(bucket_index < buckets.length && buckets[bucket_index] != null) {
  110. if(buckets[bucket_index] != tombstone) {
  111. var ours = buckets[bucket_index]->item;
  112. if(equal_func(ours, search)) {
  113. item = ours;
  114. return true;
  115. }
  116. }
  117. bucket_index++;
  118. }
  119. item = null;
  120. return false;
  121. }
  122. public void remove_first_where (Invercargill.PredicateDelegate<T> predicate) {
  123. remove(first_or_default(predicate));
  124. }
  125. public void remove_all_where (Invercargill.PredicateDelegate<T> predicate) {
  126. except_with(where(predicate));
  127. }
  128. public bool has(T item) {
  129. T? _;
  130. return try_find(item, out _);
  131. }
  132. public bool equals(Enumerable<T> other) {
  133. if(other == this) {
  134. return true;
  135. }
  136. return other.non_common(this, hash_func, equal_func).count() == 0;
  137. }
  138. public bool is_subset_of(Enumerable<T> other) {
  139. return this.exclude(other, hash_func, equal_func).count() == 0;
  140. }
  141. public T? remove(T item) {
  142. var bucket_index = bucket_for(item);
  143. while(bucket_index < buckets.length && buckets[bucket_index] != null) {
  144. if(buckets[bucket_index] != tombstone) {
  145. var ours = buckets[bucket_index]->item;
  146. if(equal_func(ours, item)) {
  147. delete buckets[bucket_index];
  148. buckets[bucket_index] = tombstone;
  149. n_items--;
  150. return ours;
  151. }
  152. }
  153. bucket_index++;
  154. }
  155. return null;
  156. }
  157. public void clear() {
  158. n_items = 0;
  159. n_buckets = 16;
  160. n_collissions = 0;
  161. delete_items();
  162. buckets = new HashSetItem<T>*[n_buckets];
  163. }
  164. private void delete_items() {
  165. for(uint i = 0; i < buckets.length; i++) {
  166. if(buckets[i] != null && buckets[i] != tombstone)
  167. delete buckets[i];
  168. }
  169. }
  170. ~HashSet() {
  171. delete_items();
  172. delete tombstone;
  173. }
  174. }
  175. }