1 /** 2 * ae.utils.container.hashtable 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.container.hashtable; 15 16 struct HashTableItem(K, V) 17 { 18 K k; 19 HashTableItem* next; 20 V v; 21 } 22 23 /// A hash table with a static size. 24 struct HashTable(K, V, uint SIZE, alias ALLOCATOR, alias HASHFUNC="k") 25 { 26 alias K KEY; 27 alias V VALUE; 28 29 import std.functional; 30 import std.exception; 31 32 alias unaryFun!(HASHFUNC, "k") hashFunc; 33 34 // hashFunc returns a hash, get its type 35 alias typeof(hashFunc(K.init)) H; 36 static assert(is(H : ulong), "Numeric hash type expected"); 37 38 alias HashTableItem!(K, V) Item; 39 40 struct Data 41 { 42 Item*[SIZE] items; 43 } 44 45 static template Impl(alias data) 46 { 47 V* get(in K k) 48 { 49 auto h = hashFunc(k) % SIZE; 50 auto item = data.items[h]; 51 while (item) 52 { 53 if (item.k == k) 54 return &item.v; 55 item = item.next; 56 } 57 return null; 58 } 59 60 V* opIn_r(in K k) { return get(k); } // Issue 11842 61 62 V get(in K k, V def) 63 { 64 auto pv = get(k); 65 return pv ? *pv : def; 66 } 67 68 /// Returns a pointer to the value storage space for a new value. 69 /// Assumes the key does not yet exist in the table. 70 V* add(in K k) 71 { 72 auto h = hashFunc(k) % SIZE; 73 auto newItem = ALLOCATOR.allocate!Item(); 74 newItem.k = k; 75 newItem.next = data.items[h]; 76 data.items[h] = newItem; 77 return &newItem.v; 78 } 79 80 /// Returns a pointer to the value storage space for a new 81 /// or existing value. 82 V* getOrAdd(in K k) 83 { 84 auto h = hashFunc(k) % SIZE; 85 auto item = data.items[h]; 86 while (item) 87 { 88 if (item.k == k) 89 return &item.v; 90 item = item.next; 91 } 92 93 auto newItem = ALLOCATOR.allocate!Item(); 94 newItem.k = k; 95 newItem.next = data.items[h]; 96 data.items[h] = newItem; 97 return &newItem.v; 98 } 99 100 void set(in K k, ref V v) { *getOrAdd(k) = v; } 101 102 int opApply(int delegate(ref K, ref V) dg) 103 { 104 int result = 0; 105 106 outerLoop: 107 for (uint h=0; h<SIZE; h++) 108 { 109 auto item = data.items[h]; 110 while (item) 111 { 112 result = dg(item.k, item.v); 113 if (result) 114 break outerLoop; 115 item = item.next; 116 } 117 } 118 return result; 119 } 120 121 ref V opIndex(in K k) 122 { 123 auto pv = get(k); 124 enforce(pv, "Key not in HashTable"); 125 return *pv; 126 } 127 128 void opIndexAssign(ref V v, in K k) { set(k, v); } 129 130 size_t getLength() 131 { 132 size_t count = 0; 133 for (uint h=0; h<SIZE; h++) 134 { 135 auto item = data.items[h]; 136 while (item) 137 { 138 count++; 139 item = item.next; 140 } 141 } 142 return count; 143 } 144 145 void clear() 146 { 147 data.items[] = null; 148 } 149 150 void freeAll() 151 { 152 static if (is(typeof(ALLOCATOR.freeAll()))) 153 ALLOCATOR.freeAll(); 154 } 155 } 156 } 157 158 unittest 159 { 160 import ae.utils.alloc; 161 162 static struct Test 163 { 164 WrapParts!(RegionAllocator!()) allocator; 165 alias HashTable!(int, string, 16, allocator) HT; 166 HT.Data htData; 167 alias ht = HT.Impl!htData; 168 169 mixin(mixAliasForward!(ht, q{ht})); 170 } 171 Test ht; 172 173 assert(5 !in ht); 174 auto s = "five"; 175 ht[5] = s; 176 assert(5 in ht); 177 assert(ht[5] == "five"); 178 }