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