1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity low) 4 module dil.AArray; 5 6 import common; 7 8 import core.bitop : bsr; 9 10 alias Key = size_t; 11 alias Value = void*; 12 13 /// An item in an associative array. Can be chained together with other items. 14 struct AANode 15 { 16 Key key; /// A hash value. 17 Value value; /// The value associated with the key. 18 AANode* next; /// Links to the next node. 19 } 20 21 /// An associative array implementation which grows by powers of two. 22 /// Relies on the GC for memory. Does not handle hash collisions. 23 /// Note: This code is a reference for understanding how hash maps work. 24 /// It's not meant to be used in DIL. A fast version needs to be implemented 25 /// which uses dil.Array for custom allocation. 26 struct AArray 27 { 28 AANode*[] buckets = [null]; /// The number of buckets is a power of 2. 29 size_t count; /// Number of nodes. 30 31 /// Returns the number of nodes. 32 size_t len() 33 { 34 return count; 35 } 36 37 /// Returns the number of buckets. 38 size_t blen() 39 { 40 return buckets.length; 41 } 42 43 /// Returns the index of a key in the buckets array. 44 size_t toindex(Key key) 45 { // This is basically a modulo operation: key % blen() 46 // But since the length will always be a power of 2, 47 // the and-operator can be used as a faster method. 48 return key & (blen() - 1); 49 } 50 51 /// Returns the value of a key, or null if it doesn't exist. 52 Value get(Key key) 53 { 54 if (auto n = find(key)) 55 return n.value; 56 return null; 57 } 58 59 /// Sets the value of a key. 60 void set(Key key, Value value) 61 { 62 getadd(key).value = value; 63 } 64 65 /// Adds a pair to the array, assuming it doesn't exist already. 66 void add(Key key, Value value) 67 { 68 assert(find(key) is null); 69 auto i = toindex(key); 70 auto pbucket = &buckets[i]; 71 *pbucket = new AANode(key, value, *pbucket); 72 count++; 73 if (count > blen() * 2) 74 rehash(); 75 } 76 77 /// Finds a node by key or adds a new one if inexistent. 78 /// Useful when the AA is on the lhs, e.g.: aa["b"] = 1; 79 AANode* getadd(Key key) 80 { 81 auto i = toindex(key); 82 auto pbucket = &buckets[i]; 83 for (auto n = *pbucket; n; n = n.next) 84 if (n.key == key) 85 return n; // Found the node. 86 *pbucket = new AANode(key, null, *pbucket); // Create a new node. 87 count++; 88 if (count > blen() * 2) 89 rehash(); 90 return *pbucket; 91 } 92 93 /// Removes a key value pair from the array. 94 /// Returns: true if the item was removed, false if it didn't exist. 95 bool remove(Key key) 96 { 97 auto i = toindex(key); 98 AANode** pn = &buckets[i]; /// Reference to the previous node. 99 for (auto n = *pn; n; n = n.next) 100 { 101 if (n.key == key) 102 { 103 *pn = n.next; 104 count--; 105 return true; 106 } 107 pn = &n.next; 108 } 109 return false; 110 } 111 112 /// Finds the node matching the key. 113 AANode* find(Key key) 114 { 115 auto i = toindex(key); 116 for (auto n = buckets[i]; n; n = n.next) 117 if (n.key == key) 118 return n; 119 return null; 120 } 121 122 /// Allocates a new bucket list and relocates the nodes from the old one. 123 void rehash() 124 { 125 auto newlen = this.count; 126 if (!newlen) 127 return; 128 // Check if not a power of 2. 129 if (newlen & (newlen - 1)) 130 { // Round up to the next power of 2. 131 newlen = 2 << bsr(newlen); 132 if (newlen == 0) // Did it overflow? 133 newlen = size_t.max / 2 + 1; // Set the highest bit. 134 } 135 assert(newlen && !(newlen & (newlen - 1)), "zero or not power of 2"); 136 // Allocate a new list of buckets. 137 AANode*[] newb = new AANode*[newlen]; 138 newlen--; // Subtract now to avoid doing it in the loop. 139 // Move the nodes to the new array. 140 foreach (n; buckets) 141 while (n) 142 { 143 auto next_node = n.next; 144 size_t i = n.key & newlen; 145 n.next = newb[i]; 146 newb[i] = n; // n becomes the new head at index i. 147 n = next_node; // Continue with the next node in the chain. 148 } 149 buckets = newb; 150 } 151 152 /// Formats a number as a string with hexadecimal characters. 153 static char[] toHex(size_t x) 154 { 155 immutable H = "0123456789abcdef"; // Hex numerals. 156 auto s = new char[size_t.sizeof * 2 + 2]; 157 s[0] = '0'; 158 s[1] = 'x'; 159 auto p = s.ptr + s.length; 160 while (*--p != 'x') 161 { 162 *p = H[x & 0xF]; 163 p--; 164 x >>= 4; 165 *p = H[x & 0xF]; 166 x >>= 4; 167 } 168 return s; 169 } 170 171 /// Prints as hex by default. 172 static cstring keyPrinter(Key k) 173 { 174 return toHex(k); 175 } 176 177 /// Prints as hex by default. 178 static cstring valuePrinter(Value v) 179 { 180 return toHex(cast(size_t)v); 181 } 182 183 /// Prints the contents of this array. 184 /// Supply own functions for customization. 185 char[] print(cstring function(Key) printKey = &keyPrinter, 186 cstring function(Value) printValue = &valuePrinter) 187 { 188 char[] s = "[".dup; 189 foreach (i, n; buckets) 190 { 191 if (i) 192 s ~= "], "; 193 s ~= "["; 194 while (n) 195 { 196 s ~= "("; 197 s ~= printKey(n.key); 198 s ~= ", "; 199 s ~= printValue(n.value); 200 s ~= ")"; 201 n = n.next; 202 if (n) 203 s ~= ", "; 204 } 205 } 206 s ~= "]"; 207 return s; 208 } 209 }