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 }