1 /// Author: Aziz Köksal2 /// License: GPL33 /// $(Maturity low)4 moduledil.AArray;
5 6 importcommon;
7 8 importcore.bitop : bsr;
9 10 aliasKey = size_t;
11 aliasValue = void*;
12 13 /// An item in an associative array. Can be chained together with other items.14 structAANode15 {
16 Keykey; /// A hash value.17 Valuevalue; /// 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 implemented25 /// which uses dil.Array for custom allocation.26 structAArray27 {
28 AANode*[] buckets = [null]; /// The number of buckets is a power of 2.29 size_tcount; /// Number of nodes.30 31 /// Returns the number of nodes.32 size_tlen()
33 {
34 returncount;
35 }
36 37 /// Returns the number of buckets.38 size_tblen()
39 {
40 returnbuckets.length;
41 }
42 43 /// Returns the index of a key in the buckets array.44 size_ttoindex(Keykey)
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 returnkey & (blen() - 1);
49 }
50 51 /// Returns the value of a key, or null if it doesn't exist.52 Valueget(Keykey)
53 {
54 if (auton = find(key))
55 returnn.value;
56 returnnull;
57 }
58 59 /// Sets the value of a key.60 voidset(Keykey, Valuevalue)
61 {
62 getadd(key).value = value;
63 }
64 65 /// Adds a pair to the array, assuming it doesn't exist already.66 voidadd(Keykey, Valuevalue)
67 {
68 assert(find(key) isnull);
69 autoi = toindex(key);
70 autopbucket = &buckets[i];
71 *pbucket = newAANode(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(Keykey)
80 {
81 autoi = toindex(key);
82 autopbucket = &buckets[i];
83 for (auton = *pbucket; n; n = n.next)
84 if (n.key == key)
85 returnn; // Found the node.86 *pbucket = newAANode(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 boolremove(Keykey)
96 {
97 autoi = toindex(key);
98 AANode** pn = &buckets[i]; /// Reference to the previous node.99 for (auton = *pn; n; n = n.next)
100 {
101 if (n.key == key)
102 {
103 *pn = n.next;
104 count--;
105 returntrue;
106 }
107 pn = &n.next;
108 }
109 returnfalse;
110 }
111 112 /// Finds the node matching the key.113 AANode* find(Keykey)
114 {
115 autoi = toindex(key);
116 for (auton = buckets[i]; n; n = n.next)
117 if (n.key == key)
118 returnn;
119 returnnull;
120 }
121 122 /// Allocates a new bucket list and relocates the nodes from the old one.123 voidrehash()
124 {
125 autonewlen = 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 = newAANode*[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 autonext_node = n.next;
144 size_ti = 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 staticchar[] toHex(size_tx)
154 {
155 immutableH = "0123456789abcdef"; // Hex numerals.156 autos = newchar[size_t.sizeof * 2 + 2];
157 s[0] = '0';
158 s[1] = 'x';
159 autop = 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 returns;
169 }
170 171 /// Prints as hex by default.172 staticcstringkeyPrinter(Keyk)
173 {
174 returntoHex(k);
175 }
176 177 /// Prints as hex by default.178 staticcstringvaluePrinter(Valuev)
179 {
180 returntoHex(cast(size_t)v);
181 }
182 183 /// Prints the contents of this array.184 /// Supply own functions for customization.185 char[] print(cstringfunction(Key) printKey = &keyPrinter,
186 cstringfunction(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 returns;
208 }
209 }