1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 2000-2021 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright), Dave Fladebo 7 * License: Distributed under the Boost Software License, Version 1.0. 8 * http://www.boost.org/LICENSE_1_0.txt 9 * Source: https://github.com/dlang/dmd/blob/master/src/dmd/backend/aarray.d 10 */ 11 12 module dmd.backend.aarray; 13 14 import core.stdc.stdio; 15 import core.stdc.stdlib; 16 import core.stdc.string; 17 18 alias hash_t = size_t; 19 20 version (MARS) 21 import dmd.root.hash; 22 23 nothrow: 24 25 /********************* 26 * This is the "bucket" used by the AArray. 27 */ 28 private struct aaA 29 { 30 aaA *next; 31 hash_t hash; // hash of the key 32 /* key */ // key value goes here 33 /* value */ // value value goes here 34 } 35 36 /************************** 37 * Associative Array type. 38 * Params: 39 * TKey = type that has members Key, getHash(), and equals() 40 * Value = value type 41 */ 42 43 struct AArray(TKey, Value) 44 { 45 nothrow: 46 alias Key = TKey.Key; // key type 47 48 ~this() 49 { 50 destroy(); 51 } 52 53 /**** 54 * Frees all the data used by AArray 55 */ 56 void destroy() 57 { 58 if (buckets) 59 { 60 foreach (e; buckets) 61 { 62 while (e) 63 { 64 auto en = e; 65 e = e.next; 66 free(en); 67 } 68 } 69 free(buckets.ptr); 70 buckets = null; 71 nodes = 0; 72 } 73 } 74 75 /******** 76 * Returns: 77 * Number of entries in the AArray 78 */ 79 size_t length() 80 { 81 return nodes; 82 } 83 84 /************************************************* 85 * Get pointer to value in associative array indexed by key. 86 * Add entry for key if it is not already there. 87 * Params: 88 * pKey = pointer to key 89 * Returns: 90 * pointer to Value 91 */ 92 93 Value* get(Key* pkey) 94 { 95 //printf("AArray::get()\n"); 96 const aligned_keysize = aligntsize(Key.sizeof); 97 98 if (!buckets.length) 99 { 100 alias aaAp = aaA*; 101 const len = prime_list[0]; 102 auto p = cast(aaAp*)calloc(len, aaAp.sizeof); 103 assert(p); 104 buckets = p[0 .. len]; 105 } 106 107 hash_t key_hash = tkey.getHash(pkey); 108 const i = key_hash % buckets.length; 109 //printf("key_hash = %x, buckets.length = %d, i = %d\n", key_hash, buckets.length, i); 110 aaA* e; 111 auto pe = &buckets[i]; 112 while ((e = *pe) != null) 113 { 114 if (key_hash == e.hash && 115 tkey.equals(pkey, cast(Key*)(e + 1))) 116 { 117 goto Lret; 118 } 119 pe = &e.next; 120 } 121 122 // Not found, create new elem 123 //printf("create new one\n"); 124 e = cast(aaA *) malloc(aaA.sizeof + aligned_keysize + Value.sizeof); 125 assert(e); 126 memcpy(e + 1, pkey, Key.sizeof); 127 memset(cast(void *)(e + 1) + aligned_keysize, 0, Value.sizeof); 128 e.hash = key_hash; 129 e.next = null; 130 *pe = e; 131 132 ++nodes; 133 //printf("length = %d, nodes = %d\n", buckets_length, nodes); 134 if (nodes > buckets.length * 4) 135 { 136 //printf("rehash()\n"); 137 rehash(); 138 } 139 140 Lret: 141 return cast(Value*)(cast(void*)(e + 1) + aligned_keysize); 142 } 143 144 /************************************************* 145 * Determine if key is in aa. 146 * Params: 147 * pKey = pointer to key 148 * Returns: 149 * null not in aa 150 * !=null in aa, return pointer to value 151 */ 152 153 Value* isIn(Key* pkey) 154 { 155 //printf("AArray.isIn(), .length = %d, .ptr = %p\n", nodes, buckets.ptr); 156 if (!nodes) 157 return null; 158 159 const key_hash = tkey.getHash(pkey); 160 //printf("hash = %d\n", key_hash); 161 const i = key_hash % buckets.length; 162 auto e = buckets[i]; 163 while (e != null) 164 { 165 if (key_hash == e.hash && 166 tkey.equals(pkey, cast(Key*)(e + 1))) 167 { 168 return cast(Value*)(cast(void*)(e + 1) + aligntsize(Key.sizeof)); 169 } 170 171 e = e.next; 172 } 173 174 // Not found 175 return null; 176 } 177 178 179 /************************************************* 180 * Delete key entry in aa[]. 181 * If key is not in aa[], do nothing. 182 * Params: 183 * pKey = pointer to key 184 */ 185 186 void del(Key *pkey) 187 { 188 if (!nodes) 189 return; 190 191 const key_hash = tkey.getHash(pkey); 192 //printf("hash = %d\n", key_hash); 193 const i = key_hash % buckets.length; 194 auto pe = &buckets[i]; 195 aaA* e; 196 while ((e = *pe) != null) // null means not found 197 { 198 if (key_hash == e.hash && 199 tkey.equals(pkey, cast(Key*)(e + 1))) 200 { 201 *pe = e.next; 202 --nodes; 203 free(e); 204 break; 205 } 206 pe = &e.next; 207 } 208 } 209 210 211 /******************************************** 212 * Produce array of keys from aa. 213 * Returns: 214 * malloc'd array of keys 215 */ 216 217 Key[] keys() 218 { 219 if (!nodes) 220 return null; 221 222 auto p = cast(Key *)malloc(nodes * Key.sizeof); 223 assert(p); 224 auto q = p; 225 foreach (e; buckets) 226 { 227 while (e) 228 { 229 memcpy(q, e + 1, Key.sizeof); 230 ++q; 231 e = e.next; 232 } 233 } 234 return p[0 .. nodes]; 235 } 236 237 /******************************************** 238 * Produce array of values from aa. 239 * Returns: 240 * malloc'd array of values 241 */ 242 243 Value[] values() 244 { 245 if (!nodes) 246 return null; 247 248 const aligned_keysize = aligntsize(Key.sizeof); 249 auto p = cast(Value *)malloc(nodes * Value.sizeof); 250 assert(p); 251 auto q = p; 252 foreach (e; buckets) 253 { 254 while (e) 255 { 256 memcpy(q, cast(void*)(e + 1) + aligned_keysize, Value.sizeof); 257 ++q; 258 e = e.next; 259 } 260 } 261 return p[0 .. nodes]; 262 } 263 264 /******************************************** 265 * Rehash an array. 266 */ 267 268 void rehash() 269 { 270 //printf("Rehash\n"); 271 if (!nodes) 272 return; 273 274 size_t newbuckets_length = prime_list[$ - 1]; 275 276 foreach (prime; prime_list[0 .. $ - 1]) 277 { 278 if (nodes <= prime) 279 { 280 newbuckets_length = prime; 281 break; 282 } 283 } 284 auto newbuckets = cast(aaA**)calloc(newbuckets_length, (aaA*).sizeof); 285 assert(newbuckets); 286 287 foreach (e; buckets) 288 { 289 while (e) 290 { 291 auto en = e.next; 292 auto b = &newbuckets[e.hash % newbuckets_length]; 293 e.next = *b; 294 *b = e; 295 e = en; 296 } 297 } 298 299 free(buckets.ptr); 300 buckets = null; 301 buckets = newbuckets[0 .. newbuckets_length]; 302 } 303 304 alias applyDg = nothrow int delegate(Key*, Value*); 305 /********************************************* 306 * For each element in the AArray, 307 * call dg(Key* pkey, Value* pvalue) 308 * If dg returns !=0, stop and return that value. 309 * Params: 310 * dg = delegate to call for each key/value pair 311 * Returns: 312 * !=0 : value returned by first dg() call that returned non-zero 313 * 0 : no entries in aa, or all dg() calls returned 0 314 */ 315 316 int apply(applyDg dg) 317 { 318 if (!nodes) 319 return 0; 320 321 //printf("AArray.apply(aa = %p, keysize = %d, dg = %p)\n", &this, Key.sizeof, dg); 322 323 const aligned_keysize = aligntsize(Key.sizeof); 324 325 foreach (e; buckets) 326 { 327 while (e) 328 { 329 auto result = dg(cast(Key*)(e + 1), cast(Value*)(e + 1) + aligned_keysize); 330 if (result) 331 return result; 332 e = e.next; 333 } 334 } 335 336 return 0; 337 } 338 339 private: 340 341 aaA*[] buckets; 342 size_t nodes; // number of nodes 343 TKey tkey; 344 } 345 346 private: 347 348 /********************************** 349 * Align to next pointer boundary, so value 350 * will be aligned. 351 * Params: 352 * tsize = offset to be aligned 353 * Returns: 354 * aligned offset 355 */ 356 357 size_t aligntsize(size_t tsize) 358 { 359 // Is pointer alignment on the x64 4 bytes or 8? 360 return (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1); 361 } 362 363 immutable uint[14] prime_list = 364 [ 365 97, 389, 366 1543, 6151, 367 24_593, 98_317, 368 393_241, 1_572_869, 369 6_291_469, 25_165_843, 370 100_663_319, 402_653_189, 371 1_610_612_741, 4_294_967_291U, 372 ]; 373 374 /***************************************************************/ 375 376 /*** 377 * A TKey for basic types 378 * Params: 379 * K = a basic type 380 */ 381 public struct Tinfo(K) 382 { 383 nothrow: 384 alias Key = K; 385 386 static hash_t getHash(Key* pk) 387 { 388 return cast(hash_t)*pk; 389 } 390 391 static bool equals(Key* pk1, Key* pk2) 392 { 393 return *pk1 == *pk2; 394 } 395 } 396 397 /***************************************************************/ 398 399 /**** 400 * A TKey that is a string 401 */ 402 public struct TinfoChars 403 { 404 nothrow: 405 alias Key = const(char)[]; 406 407 static hash_t getHash(Key* pk) 408 { 409 version (MARS) 410 { 411 auto buf = *pk; 412 return calcHash(cast(const(ubyte[]))buf); 413 } 414 else 415 { 416 auto buf = *pk; 417 hash_t hash = 0; 418 foreach (v; buf) 419 hash = hash * 11 + v; 420 return hash; 421 } 422 } 423 424 static bool equals(Key* pk1, Key* pk2) 425 { 426 auto buf1 = *pk1; 427 auto buf2 = *pk2; 428 return buf1.length == buf2.length && 429 memcmp(buf1.ptr, buf2.ptr, buf1.length) == 0; 430 } 431 } 432 433 // Interface for C++ code 434 public extern (C++) struct AAchars 435 { 436 nothrow: 437 alias AA = AArray!(TinfoChars, uint); 438 AA aa; 439 440 static AAchars* create() 441 { 442 auto a = cast(AAchars*)calloc(1, AAchars.sizeof); 443 assert(a); 444 return a; 445 } 446 447 static void destroy(AAchars* aac) 448 { 449 aac.aa.destroy(); 450 free(aac); 451 } 452 453 extern(D) uint* get(const(char)[] buf) 454 { 455 return aa.get(&buf); 456 } 457 458 uint length() 459 { 460 return cast(uint)aa.length(); 461 } 462 } 463 464 /***************************************************************/ 465 466 // Key is the slice specified by (*TinfoPair.pbase)[Pair.start .. Pair.end] 467 468 public struct Pair { uint start, end; } 469 470 public struct TinfoPair 471 { 472 nothrow: 473 alias Key = Pair; 474 475 ubyte** pbase; 476 477 hash_t getHash(Key* pk) 478 { 479 version (MARS) 480 { 481 auto buf = (*pbase)[pk.start .. pk.end]; 482 return calcHash(buf); 483 } 484 else 485 { 486 auto buf = (*pbase)[pk.start .. pk.end]; 487 hash_t hash = 0; 488 foreach (v; buf) 489 hash = hash * 11 + v; 490 return hash; 491 } 492 } 493 494 bool equals(Key* pk1, Key* pk2) 495 { 496 const len1 = pk1.end - pk1.start; 497 const len2 = pk2.end - pk2.start; 498 499 auto buf1 = *pk1; 500 auto buf2 = *pk2; 501 return len1 == len2 && 502 memcmp(*pbase + pk1.start, *pbase + pk2.start, len1) == 0; 503 } 504 } 505 506 // Interface for C++ code 507 public extern (C++) struct AApair 508 { 509 nothrow: 510 alias AA = AArray!(TinfoPair, uint); 511 AA aa; 512 513 static AApair* create(ubyte** pbase) 514 { 515 auto a = cast(AApair*)calloc(1, AApair.sizeof); 516 assert(a); 517 a.aa.tkey.pbase = pbase; 518 return a; 519 } 520 521 static void destroy(AApair* aap) 522 { 523 aap.aa.destroy(); 524 free(aap); 525 } 526 527 uint* get(uint start, uint end) 528 { 529 auto p = Pair(start, end); 530 return aa.get(&p); 531 } 532 533 uint length() 534 { 535 return cast(uint)aa.length(); 536 } 537 } 538 539 // Interface for C++ code 540 public extern (C++) struct AApair2 541 { 542 nothrow: 543 alias AA = AArray!(TinfoPair, Pair); 544 AA aa; 545 546 static AApair2* create(ubyte** pbase) 547 { 548 auto a = cast(AApair2*)calloc(1, AApair2.sizeof); 549 assert(a); 550 a.aa.tkey.pbase = pbase; 551 return a; 552 } 553 554 static void destroy(AApair2* aap) 555 { 556 aap.aa.destroy(); 557 free(aap); 558 } 559 560 Pair* get(uint start, uint end) 561 { 562 auto p = Pair(start, end); 563 return aa.get(&p); 564 } 565 566 uint length() 567 { 568 return cast(uint)aa.length(); 569 } 570 } 571 572 /*************************************************************/ 573 574 version (none) 575 { 576 577 /* Since -betterC doesn't support unittests, do it this way 578 * for the time being. 579 * This is a stand-alone file anyway. 580 */ 581 582 int main() 583 { 584 testAArray(); 585 testAApair(); 586 587 return 0; 588 } 589 590 void testAArray() 591 { 592 int dg(int* pk, bool* pv) { return 3; } 593 int dgz(int* pk, bool* pv) { return 0; } 594 595 AArray!(Tinfo!int, bool) aa; 596 aa.rehash(); 597 assert(aa.keys() == null); 598 assert(aa.values() == null); 599 assert(aa.apply(&dg) == 0); 600 601 assert(aa.length == 0); 602 int k = 8; 603 aa.del(&k); 604 bool v = true; 605 assert(!aa.isIn(&k)); 606 bool *pv = aa.get(&k); 607 *pv = true; 608 int j = 9; 609 pv = aa.get(&j); 610 *pv = false; 611 aa.rehash(); 612 613 assert(aa.length() == 2); 614 assert(*aa.get(&k) == true); 615 assert(*aa.get(&j) == false); 616 617 assert(aa.apply(&dg) == 3); 618 assert(aa.apply(&dgz) == 0); 619 620 aa.del(&k); 621 assert(aa.length() == 1); 622 assert(!aa.isIn(&k)); 623 assert(*aa.isIn(&j) == false); 624 625 auto keys = aa.keys(); 626 assert(keys.length == 1); 627 assert(keys[0] == 9); 628 629 auto values = aa.values(); 630 assert(values.length == 1); 631 assert(values[0] == false); 632 } 633 634 void testAApair() 635 { 636 const(char)* buf = "abcb"; 637 auto aap = AApair.create(cast(ubyte**)&buf); 638 auto pu = aap.get(1,2); 639 *pu = 10; 640 assert(aap.length == 1); 641 pu = aap.get(3,4); 642 assert(*pu == 10); 643 AApair.destroy(aap); 644 } 645 646 }