1 /** 2 * Associative array implementation. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: Walter Bright, http://www.digitalmars.com 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d, root/_aav.d) 8 * Documentation: https://dlang.org/phobos/dmd_root_aav.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/aav.d 10 */ 11 12 module dmd.root.aav; 13 14 import core.stdc..string; 15 import dmd.root.rmem; 16 17 private size_t hash(size_t a) pure nothrow @nogc @safe 18 { 19 a ^= (a >> 20) ^ (a >> 12); 20 return a ^ (a >> 7) ^ (a >> 4); 21 } 22 23 struct KeyValueTemplate(K,V) 24 { 25 K key; 26 V value; 27 } 28 29 alias Key = void*; 30 alias Value = void*; 31 32 alias KeyValue = KeyValueTemplate!(Key, Value); 33 34 struct aaA 35 { 36 aaA* next; 37 KeyValue keyValue; 38 alias keyValue this; 39 } 40 41 struct AA 42 { 43 aaA** b; 44 size_t b_length; 45 size_t nodes; // total number of aaA nodes 46 aaA*[4] binit; // initial value of b[] 47 aaA aafirst; // a lot of these AA's have only one entry 48 } 49 50 /**************************************************** 51 * Determine number of entries in associative array. 52 */ 53 private size_t dmd_aaLen(const AA* aa) pure nothrow @nogc @safe 54 { 55 return aa ? aa.nodes : 0; 56 } 57 58 /************************************************* 59 * Get pointer to value in associative array indexed by key. 60 * Add entry for key if it is not already there, returning a pointer to a null Value. 61 * Create the associative array if it does not already exist. 62 */ 63 private Value* dmd_aaGet(AA** paa, Key key) pure nothrow 64 { 65 //printf("paa = %p\n", paa); 66 if (!*paa) 67 { 68 AA* a = cast(AA*)mem.xmalloc(AA.sizeof); 69 a.b = cast(aaA**)a.binit; 70 a.b_length = 4; 71 a.nodes = 0; 72 a.binit[0] = null; 73 a.binit[1] = null; 74 a.binit[2] = null; 75 a.binit[3] = null; 76 *paa = a; 77 assert((*paa).b_length == 4); 78 } 79 //printf("paa = %p, *paa = %p\n", paa, *paa); 80 assert((*paa).b_length); 81 size_t i = hash(cast(size_t)key) & ((*paa).b_length - 1); 82 aaA** pe = &(*paa).b[i]; 83 aaA* e; 84 while ((e = *pe) !is null) 85 { 86 if (key == e.key) 87 return &e.value; 88 pe = &e.next; 89 } 90 // Not found, create new elem 91 //printf("create new one\n"); 92 size_t nodes = ++(*paa).nodes; 93 e = (nodes != 1) ? cast(aaA*)mem.xmalloc(aaA.sizeof) : &(*paa).aafirst; 94 //e = new aaA(); 95 e.next = null; 96 e.key = key; 97 e.value = null; 98 *pe = e; 99 //printf("length = %d, nodes = %d\n", (*paa)->b_length, nodes); 100 if (nodes > (*paa).b_length * 2) 101 { 102 //printf("rehash\n"); 103 dmd_aaRehash(paa); 104 } 105 return &e.value; 106 } 107 108 /************************************************* 109 * Get value in associative array indexed by key. 110 * Returns NULL if it is not already there. 111 */ 112 private Value dmd_aaGetRvalue(AA* aa, Key key) pure nothrow @nogc 113 { 114 //printf("_aaGetRvalue(key = %p)\n", key); 115 if (aa) 116 { 117 size_t i; 118 size_t len = aa.b_length; 119 i = hash(cast(size_t)key) & (len - 1); 120 aaA* e = aa.b[i]; 121 while (e) 122 { 123 if (key == e.key) 124 return e.value; 125 e = e.next; 126 } 127 } 128 return null; // not found 129 } 130 131 /** 132 Gets a range of key/values for `aa`. 133 134 Returns: a range of key/values for `aa`. 135 */ 136 @property auto asRange(AA* aa) pure nothrow @nogc 137 { 138 return AARange!(Key, Value)(aa); 139 } 140 141 private struct AARange(K,V) 142 { 143 AA* aa; 144 // current index into bucket array `aa.b` 145 size_t bIndex; 146 aaA* current; 147 148 this(AA* aa) pure nothrow @nogc 149 { 150 if (aa) 151 { 152 this.aa = aa; 153 toNext(); 154 } 155 } 156 157 @property bool empty() const pure nothrow @nogc @safe 158 { 159 return current is null; 160 } 161 162 @property auto front() const pure nothrow @nogc 163 { 164 return cast(KeyValueTemplate!(K,V))current.keyValue; 165 } 166 167 void popFront() pure nothrow @nogc 168 { 169 if (current.next) 170 current = current.next; 171 else 172 { 173 bIndex++; 174 toNext(); 175 } 176 } 177 178 private void toNext() pure nothrow @nogc 179 { 180 for (; bIndex < aa.b_length; bIndex++) 181 { 182 if (auto next = aa.b[bIndex]) 183 { 184 current = next; 185 return; 186 } 187 } 188 current = null; 189 } 190 } 191 192 unittest 193 { 194 AA* aa = null; 195 foreach(keyValue; aa.asRange) 196 assert(0); 197 198 enum totalKeyLength = 50; 199 foreach (i; 1 .. totalKeyLength + 1) 200 { 201 auto key = cast(void*)i; 202 { 203 auto valuePtr = dmd_aaGet(&aa, key); 204 assert(valuePtr); 205 *valuePtr = key; 206 } 207 bool[totalKeyLength] found; 208 size_t rangeCount = 0; 209 foreach (keyValue; aa.asRange) 210 { 211 assert(keyValue.key <= key); 212 assert(keyValue.key == keyValue.value); 213 rangeCount++; 214 assert(!found[cast(size_t)keyValue.key - 1]); 215 found[cast(size_t)keyValue.key - 1] = true; 216 } 217 assert(rangeCount == i); 218 } 219 } 220 221 /******************************************** 222 * Rehash an array. 223 */ 224 private void dmd_aaRehash(AA** paa) pure nothrow 225 { 226 //printf("Rehash\n"); 227 if (*paa) 228 { 229 AA* aa = *paa; 230 if (aa) 231 { 232 size_t len = aa.b_length; 233 if (len == 4) 234 len = 32; 235 else 236 len *= 4; 237 aaA** newb = cast(aaA**)mem.xmalloc(aaA.sizeof * len); 238 memset(newb, 0, len * (aaA*).sizeof); 239 for (size_t k = 0; k < aa.b_length; k++) 240 { 241 aaA* e = aa.b[k]; 242 while (e) 243 { 244 aaA* enext = e.next; 245 size_t j = hash(cast(size_t)e.key) & (len - 1); 246 e.next = newb[j]; 247 newb[j] = e; 248 e = enext; 249 } 250 } 251 if (aa.b != cast(aaA**)aa.binit) 252 mem.xfree(aa.b); 253 aa.b = newb; 254 aa.b_length = len; 255 } 256 } 257 } 258 259 unittest 260 { 261 AA* aa = null; 262 Value v = dmd_aaGetRvalue(aa, null); 263 assert(!v); 264 Value* pv = dmd_aaGet(&aa, null); 265 assert(pv); 266 *pv = cast(void*)3; 267 v = dmd_aaGetRvalue(aa, null); 268 assert(v == cast(void*)3); 269 } 270 271 struct AssocArray(K,V) 272 { 273 private AA* aa; 274 275 /** 276 Returns: The number of key/value pairs. 277 */ 278 @property size_t length() const pure nothrow @nogc @safe 279 { 280 return dmd_aaLen(aa); 281 } 282 283 /** 284 Lookup value associated with `key` and return the address to it. If the `key` 285 has not been added, it adds it and returns the address to the new value. 286 287 Params: 288 key = key to lookup the value for 289 290 Returns: the address to the value associated with `key`. If `key` does not exist, it 291 is added and the address to the new value is returned. 292 */ 293 V* getLvalue(const(K) key) pure nothrow 294 { 295 return cast(V*)dmd_aaGet(&aa, cast(void*)key); 296 } 297 298 /** 299 Lookup and return the value associated with `key`, if the `key` has not been 300 added, it returns null. 301 302 Params: 303 key = key to lookup the value for 304 305 Returns: the value associated with `key` if present, otherwise, null. 306 */ 307 V opIndex(const(K) key) pure nothrow @nogc 308 { 309 return cast(V)dmd_aaGetRvalue(aa, cast(void*)key); 310 } 311 312 /** 313 Gets a range of key/values for `aa`. 314 315 Returns: a range of key/values for `aa`. 316 */ 317 @property auto asRange() pure nothrow @nogc 318 { 319 return AARange!(K,V)(aa); 320 } 321 } 322 323 /// 324 unittest 325 { 326 auto foo = new Object(); 327 auto bar = new Object(); 328 329 AssocArray!(Object, Object) aa; 330 331 assert(aa[foo] is null); 332 assert(aa.length == 0); 333 334 auto fooValuePtr = aa.getLvalue(foo); 335 *fooValuePtr = bar; 336 337 assert(aa[foo] is bar); 338 assert(aa.length == 1); 339 }