root/trunk/src/rt/aaA.d

Revision 478, 20.5 kB (checked in by braddr, 1 year ago)

Add more unit testing

  • Property svn:eol-style set to native
Line 
1 /**
2  * Implementation of associative arrays.
3  *
4  * Copyright: Copyright Digital Mars 2000 - 2010.
5  * License:   <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
6  * Authors:   Walter Bright, Sean Kelly
7  */
8  
9 /*          Copyright Digital Mars 2000 - 2010.
10  * Distributed under the Boost Software License, Version 1.0.
11  *    (See accompanying file LICENSE_1_0.txt or copy at
12  *          http://www.boost.org/LICENSE_1_0.txt)
13  */
14 module rt.aaA;
15
16 private
17 {
18     import core.stdc.stdarg;
19     import core.stdc.string;
20     import core.stdc.stdio;
21
22     enum BlkAttr : uint
23     {
24         FINALIZE    = 0b0000_0001,
25         NO_SCAN     = 0b0000_0010,
26         NO_MOVE     = 0b0000_0100,
27         APPENDABLE  = 0b0000_1000,
28         ALL_BITS    = 0b1111_1111
29     }
30
31     extern (C) void* gc_malloc( size_t sz, uint ba = 0 );
32     extern (C) void* gc_calloc( size_t sz, uint ba = 0 );
33     extern (C) void  gc_free( void* p );
34 }
35
36 // Auto-rehash and pre-allocate - Dave Fladebo
37
38 static immutable size_t[] prime_list = [
39               31UL,
40               97UL,            389UL,
41            1_543UL,          6_151UL,
42           24_593UL,         98_317UL,
43           393_241UL,      1_572_869UL,
44         6_291_469UL,     25_165_843UL,
45       100_663_319UL,    402_653_189UL,
46     1_610_612_741UL,  4_294_967_291UL,
47 //  8_589_934_513UL, 17_179_869_143UL
48 ];
49
50 /* This is the type of the return value for dynamic arrays.
51  * It should be a type that is returned in registers.
52  * Although DMD will return types of Array in registers,
53  * gcc will not, so we instead use a 'long'.
54  */
55 alias void[] ArrayRet_t;
56
57 struct Array
58 {
59     size_t length;
60     void* ptr;
61 }
62
63 struct aaA
64 {
65     aaA *next;
66     hash_t hash;
67     /* key   */
68     /* value */
69 }
70
71 struct BB
72 {
73     aaA*[] b;
74     size_t nodes;       // total number of aaA nodes
75     TypeInfo keyti;     // TODO: replace this with TypeInfo_AssociativeArray when available in _aaGet()
76     aaA*[4] binit;      // initial value of b[]
77 }
78
79 /* This is the type actually seen by the programmer, although
80  * it is completely opaque.
81  */
82
83 struct AA
84 {
85     BB* a;
86 }
87
88 /**********************************
89  * Align to next pointer boundary, so that
90  * GC won't be faced with misaligned pointers
91  * in value.
92  */
93
94 size_t aligntsize(size_t tsize)
95 {
96     version (X86_64)
97         // Size of key needed to align value on 16 bytes
98         return (tsize + 15) & ~(15);
99     else
100         return (tsize + size_t.sizeof - 1) & ~(size_t.sizeof - 1);
101 }
102
103 extern (C):
104
105 /*************************************************
106  * Invariant for aa.
107  */
108
109 /+
110 void _aaInvAh(aaA*[] aa)
111 {
112     for (size_t i = 0; i < aa.length; i++)
113     {
114         if (aa[i])
115             _aaInvAh_x(aa[i]);
116     }
117 }
118
119 private int _aaCmpAh_x(aaA *e1, aaA *e2)
120 {   int c;
121
122     c = e1.hash - e2.hash;
123     if (c == 0)
124     {
125         c = e1.key.length - e2.key.length;
126         if (c == 0)
127             c = memcmp((char *)e1.key, (char *)e2.key, e1.key.length);
128     }
129     return c;
130 }
131
132 private void _aaInvAh_x(aaA *e)
133 {
134     hash_t key_hash;
135     aaA *e1;
136     aaA *e2;
137
138     key_hash = getHash(e.key);
139     assert(key_hash == e.hash);
140
141     while (1)
142     {   int c;
143
144         e1 = e.left;
145         if (e1)
146         {
147             _aaInvAh_x(e1);             // ordinary recursion
148             do
149             {
150                 c = _aaCmpAh_x(e1, e);
151                 assert(c < 0);
152                 e1 = e1.right;
153             } while (e1 != null);
154         }
155
156         e2 = e.right;
157         if (e2)
158         {
159             do
160             {
161                 c = _aaCmpAh_x(e, e2);
162                 assert(c < 0);
163                 e2 = e2.left;
164             } while (e2 != null);
165             e = e.right;                // tail recursion
166         }
167         else
168             break;
169     }
170 }
171 +/
172
173 /****************************************************
174  * Determine number of entries in associative array.
175  */
176
177 size_t _aaLen(AA aa)
178 in
179 {
180     //printf("_aaLen()+\n");
181     //_aaInv(aa);
182 }
183 out (result)
184 {
185     size_t len = 0;
186
187     if (aa.a)
188     {
189         foreach (e; aa.a.b)
190         {
191             while (e)
192             {   len++;
193                 e = e.next;
194             }
195         }
196     }
197     assert(len == result);
198
199     //printf("_aaLen()-\n");
200 }
201 body
202 {
203     return aa.a ? aa.a.nodes : 0;
204 }
205
206
207 /*************************************************
208  * Get pointer to value in associative array indexed by key.
209  * Add entry for key if it is not already there.
210  */
211
212 // retained for backwards compatibility
213 void* _aaGet(AA* aa, TypeInfo keyti, size_t valuesize, ...)
214 {
215     return _aaGetX(aa, keyti, valuesize, cast(void*)(&valuesize + 1));
216 }
217
218 void* _aaGetX(AA* aa, TypeInfo keyti, size_t valuesize, void* pkey)
219 in
220 {
221     assert(aa);
222 }
223 out (result)
224 {
225     assert(result);
226     assert(aa.a);
227     assert(aa.a.b.length);
228     //assert(_aaInAh(*aa.a, key));
229 }
230 body
231 {
232     size_t i;
233     aaA *e;
234     //printf("keyti = %p\n", keyti);
235     //printf("aa = %p\n", aa);
236     auto keysize = aligntsize(keyti.tsize());
237
238     if (!aa.a)
239     {   aa.a = new BB();
240         aa.a.b = aa.a.binit;
241     }
242     //printf("aa = %p\n", aa);
243     //printf("aa.a = %p\n", aa.a);
244     aa.a.keyti = keyti;
245
246     auto key_hash = keyti.getHash(pkey);
247     //printf("hash = %d\n", key_hash);
248     i = key_hash % aa.a.b.length;
249     auto pe = &aa.a.b[i];
250     while ((e = *pe) !is null)
251     {
252         if (key_hash == e.hash)
253         {
254             auto c = keyti.compare(pkey, e + 1);
255             if (c == 0)
256                 goto Lret;
257         }
258         pe = &e.next;
259     }
260
261     // Not found, create new elem
262     //printf("create new one\n");
263     size_t size = aaA.sizeof + keysize + valuesize;
264     e = cast(aaA *) gc_calloc(size);
265     memcpy(e + 1, pkey, keyti.tsize);
266     memset(cast(byte*)(e + 1) + keyti.tsize, 0, keysize - keyti.tsize);
267     e.hash = key_hash;
268     *pe = e;
269
270     auto nodes = ++aa.a.nodes;
271     //printf("length = %d, nodes = %d\n", aa.a.b.length, nodes);
272     if (nodes > aa.a.b.length * 4)
273     {
274         //printf("rehash\n");
275         _aaRehash(aa,keyti);
276     }
277
278 Lret:
279     return cast(void *)(e + 1) + keysize;
280 }
281
282
283 /*************************************************
284  * Get pointer to value in associative array indexed by key.
285  * Returns null if it is not already there.
286  */
287
288 void* _aaGetRvalue(AA aa, TypeInfo keyti, size_t valuesize, ...)
289 {
290     return _aaGetRvalueX(aa, keyti, valuesize, cast(void*)(&valuesize + 1));
291 }
292
293 void* _aaGetRvalueX(AA aa, TypeInfo keyti, size_t valuesize, void* pkey)
294 {
295     //printf("_aaGetRvalue(valuesize = %u)\n", valuesize);
296     if (!aa.a)
297         return null;
298
299     auto keysize = aligntsize(keyti.tsize());
300     auto len = aa.a.b.length;
301
302     if (len)
303     {
304         auto key_hash = keyti.getHash(pkey);
305         //printf("hash = %d\n", key_hash);
306         size_t i = key_hash % len;
307         auto e = aa.a.b[i];
308         while (e !is null)
309         {
310             if (key_hash == e.hash)
311             {
312                 auto c = keyti.compare(pkey, e + 1);
313                 if (c == 0)
314                     return cast(void *)(e + 1) + keysize;
315             }
316             e = e.next;
317         }
318     }
319     return null;    // not found, caller will throw exception
320 }
321
322
323 /*************************************************
324  * Determine if key is in aa.
325  * Returns:
326  *      null    not in aa
327  *      !=null  in aa, return pointer to value
328  */
329
330 void* _aaIn(AA aa, TypeInfo keyti, ...)
331 {
332     return _aaInX(aa, keyti, cast(void*)(&keyti + 1));
333 }
334
335 void* _aaInX(AA aa, TypeInfo keyti, void* pkey)
336 in
337 {
338 }
339 out (result)
340 {
341     //assert(result == 0 || result == 1);
342 }
343 body
344 {
345     if (aa.a)
346     {
347         //printf("_aaIn(), .length = %d, .ptr = %x\n", aa.a.length, cast(uint)aa.a.ptr);
348         auto len = aa.a.b.length;
349
350         if (len)
351         {
352             auto key_hash = keyti.getHash(pkey);
353             //printf("hash = %d\n", key_hash);
354             const i = key_hash % len;
355             auto e = aa.a.b[i];
356             while (e !is null)
357             {
358                 if (key_hash == e.hash)
359                 {
360                     auto c = keyti.compare(pkey, e + 1);
361                     if (c == 0)
362                         return cast(void *)(e + 1) + aligntsize(keyti.tsize());
363                 }
364                 e = e.next;
365             }
366         }
367     }
368
369     // Not found
370     return null;
371 }
372
373 /*************************************************
374  * Delete key entry in aa[].
375  * If key is not in aa[], do nothing.
376  */
377
378 void _aaDel(AA aa, TypeInfo keyti, ...)
379 {
380     return _aaDelX(aa, keyti, cast(void*)(&keyti + 1));
381 }
382
383 void _aaDelX(AA aa, TypeInfo keyti, void* pkey)
384 {
385     aaA *e;
386
387     if (aa.a && aa.a.b.length)
388     {
389         auto key_hash = keyti.getHash(pkey);
390         //printf("hash = %d\n", key_hash);
391         size_t i = key_hash % aa.a.b.length;
392         auto pe = &aa.a.b[i];
393         while ((e = *pe) !is null) // null means not found
394         {
395             if (key_hash == e.hash)
396             {
397                 auto c = keyti.compare(pkey, e + 1);
398                 if (c == 0)
399                 {
400                     *pe = e.next;
401                     aa.a.nodes--;
402                     gc_free(e);
403                     break;
404                 }
405             }
406             pe = &e.next;
407         }
408     }
409 }
410
411
412 /********************************************
413  * Produce array of values from aa.
414  */
415
416 ArrayRet_t _aaValues(AA aa, size_t keysize, size_t valuesize)
417 {
418     size_t resi;
419     Array a;
420    
421     auto alignsize = aligntsize(keysize);
422
423     if (aa.a)
424     {
425         a.length = _aaLen(aa);
426         a.ptr = cast(byte*) gc_malloc(a.length * valuesize,
427                                       valuesize < (void*).sizeof ? BlkAttr.NO_SCAN : 0);
428         resi = 0;
429         foreach (e; aa.a.b)
430         {
431             while (e)
432             {
433                 memcpy(a.ptr + resi * valuesize,
434                        cast(byte*)e + aaA.sizeof + alignsize,
435                        valuesize);
436                 resi++;
437                 e = e.next;
438             }
439         }
440         assert(resi == a.length);
441     }
442     return *cast(ArrayRet_t*)(&a);
443 }
444
445
446 /********************************************
447  * Rehash an array.
448  */
449
450 void* _aaRehash(AA* paa, TypeInfo keyti)
451 in
452 {
453     //_aaInvAh(paa);
454 }
455 out (result)
456 {
457     //_aaInvAh(result);
458 }
459 body
460 {
461     //printf("Rehash\n");
462     if (paa.a)
463     {
464         BB newb;
465         auto aa = paa.a;
466         auto len = _aaLen(*paa);
467         if (len)
468         {   size_t i;
469
470             for (i = 0; i < prime_list.length - 1; i++)
471             {
472                 if (len <= prime_list[i])
473                     break;
474             }
475             len = prime_list[i];
476             newb.b = new aaA*[len];
477
478             foreach (e; aa.b)
479             {
480                 while (e)
481                 {   auto enext = e.next;
482                     const j = e.hash % len;
483                     e.next = newb.b[j];
484                     newb.b[j] = e;
485                     e = enext;
486                 }
487             }
488             if (aa.b.ptr == aa.binit.ptr)
489                 aa.binit[] = null;
490             else
491                 delete aa.b;
492
493             newb.nodes = aa.nodes;
494             newb.keyti = aa.keyti;
495         }
496
497         *paa.a = newb;
498     }
499     return (*paa).a;
500 }
501
502 /********************************************
503  * Produce array of N byte keys from aa.
504  */
505
506 ArrayRet_t _aaKeys(AA aa, size_t keysize)
507 {
508     auto len = _aaLen(aa);
509     if (!len)
510         return null;
511     auto res = (cast(byte*) gc_malloc(len * keysize,
512                                  !(aa.a.keyti.flags() & 1) ? BlkAttr.NO_SCAN : 0))[0 .. len * keysize];
513     size_t resi = 0;
514     foreach (e; aa.a.b)
515     {
516         while (e)
517         {
518             memcpy(&res[resi * keysize], cast(byte*)(e + 1), keysize);
519             resi++;
520             e = e.next;
521         }
522     }
523     assert(resi == len);
524
525     Array a;
526     a.length = len;
527     a.ptr = res.ptr;
528     return *cast(ArrayRet_t*)(&a);
529 }
530
531 unittest
532 {
533     int[string] aa;
534
535     aa["hello"] = 3;
536     assert(aa["hello"] == 3);
537     aa["hello"]++;
538     assert(aa["hello"] == 4);
539
540     assert(aa.length == 1);
541
542     string[] keys = aa.keys;
543     assert(keys.length == 1);
544     assert(memcmp(keys[0].ptr, cast(char*)"hello", 5) == 0);
545
546     int[] values = aa.values;
547     assert(values.length == 1);
548     assert(values[0] == 4);
549
550     aa.rehash;
551     assert(aa.length == 1);
552     assert(aa["hello"] == 4);
553
554     aa["foo"] = 1;
555     aa["bar"] = 2;
556     aa["batz"] = 3;
557
558     assert(aa.keys.length == 4);
559     assert(aa.values.length == 4);
560
561     foreach(a; aa.keys)
562     {
563         assert(a.length != 0);
564         assert(a.ptr != null);
565         //printf("key: %.*s -> value: %d\n", a.length, a.ptr, aa[a]);
566     }
567
568     foreach(v; aa.values)
569     {
570         assert(v != 0);
571         //printf("value: %d\n", v);
572     }
573 }
574
575
576 /**********************************************
577  * 'apply' for associative arrays - to support foreach
578  */
579
580 // dg is D, but _aaApply() is C
581 extern (D) typedef int delegate(void *) dg_t;
582
583 int _aaApply(AA aa, size_t keysize, dg_t dg)
584 {   int result;
585
586     //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg);
587
588     auto alignsize = aligntsize(keysize);
589
590     if (aa.a)
591     {
592     Loop:
593         foreach (e; aa.a.b)
594         {
595             while (e)
596             {
597                 result = dg(cast(void *)(e + 1) + alignsize);
598                 if (result)
599                     break Loop;
600                 e = e.next;
601             }
602         }
603     }
604     return result;
605 }
606
607 // dg is D, but _aaApply2() is C
608 extern (D) typedef int delegate(void *, void *) dg2_t;
609
610 int _aaApply2(AA aa, size_t keysize, dg2_t dg)
611 {   int result;
612
613     //printf("_aaApply(aa = x%llx, keysize = %d, dg = x%llx)\n", aa.a, keysize, dg);
614
615     auto alignsize = aligntsize(keysize);
616
617     if (aa.a)
618     {
619     Loop:
620         foreach (e; aa.a.b)
621         {
622             while (e)
623             {
624                 result = dg(cast(void *)(e + 1), cast(void *)(e + 1) + alignsize);
625                 if (result)
626                     break Loop;
627                 e = e.next;
628             }
629         }
630     }
631     return result;
632 }
633
634
635 /***********************************
636  * Construct an associative array of type ti from
637  * length pairs of key/value pairs.
638  */
639
640 extern (C)
641 BB* _d_assocarrayliteralT(TypeInfo_AssociativeArray ti, size_t length, ...)
642 {
643     auto valuesize = ti.next.tsize();           // value size
644     auto keyti = ti.key;
645     auto keysize = keyti.tsize();               // key size
646     BB* result;
647
648     //printf("_d_assocarrayliteralT(keysize = %d, valuesize = %d, length = %d)\n", keysize, valuesize, length);
649     //printf("tivalue = %.*s\n", ti.next.classinfo.name);
650     if (length == 0 || valuesize == 0 || keysize == 0)
651     {
652         ;
653     }
654     else
655     {
656         va_list q;
657         version(X86_64) va_start(q, __va_argsave); else va_start(q, length);
658
659         result = new BB();
660         result.keyti = keyti;
661         size_t i;
662
663         for (i = 0; i < prime_list.length - 1; i++)
664         {
665             if (length <= prime_list[i])
666                 break;
667         }
668         auto len = prime_list[i];
669         result.b = new aaA*[len];
670
671         size_t keystacksize   = (keysize   + int.sizeof - 1) & ~(int.sizeof - 1);
672         size_t valuestacksize = (valuesize + int.sizeof - 1) & ~(int.sizeof - 1);
673
674         size_t keytsize = aligntsize(keysize);
675
676         for (size_t j = 0; j < length; j++)
677         {   void* pkey = q;
678             q += keystacksize;
679             void* pvalue = q;
680             q += valuestacksize;
681             aaA* e;
682
683             auto key_hash = keyti.getHash(pkey);
684             //printf("hash = %d\n", key_hash);
685             i = key_hash % len;
686             auto pe = &result.b[i];
687             while (1)
688             {
689                 e = *pe;
690                 if (!e)
691                 {
692                     // Not found, create new elem
693                     //printf("create new one\n");
694                     e = cast(aaA *) cast(void*) new void[aaA.sizeof + keytsize + valuesize];
695                     memcpy(e + 1, pkey, keysize);
696                     e.hash = key_hash;
697                     *pe = e;
698                     result.nodes++;
699                     break;
700                 }
701                 if (key_hash == e.hash)
702                 {
703                     auto c = keyti.compare(pkey, e + 1);
704                     if (c == 0)
705                         break;
706                 }
707                 pe = &e.next;
708             }
709             memcpy(cast(void *)(e + 1) + keytsize, pvalue, valuesize);
710         }
711
712         va_end(q);
713     }
714     return result;
715 }
716
717 extern (C)
718 BB* _d_assocarrayliteralTX(TypeInfo_AssociativeArray ti, void[] keys, void[] values)
719 {
720     auto valuesize = ti.next.tsize();           // value size
721     auto keyti = ti.key;
722     auto keysize = keyti.tsize();               // key size
723     auto length = keys.length;
724     BB* result;
725
726     //printf("_d_assocarrayliteralT(keysize = %d, valuesize = %d, length = %d)\n", keysize, valuesize, length);
727     //printf("tivalue = %.*s\n", ti.next.classinfo.name);
728     assert(length == values.length);
729     if (length == 0 || valuesize == 0 || keysize == 0)
730     {
731         ;
732     }
733     else
734     {
735         result = new BB();
736         result.keyti = keyti;
737
738         size_t i;
739         for (i = 0; i < prime_list.length - 1; i++)
740         {
741             if (length <= prime_list[i])
742                 break;
743         }
744         auto len = prime_list[i];
745         result.b = new aaA*[len];
746
747         size_t keytsize = aligntsize(keysize);
748
749         for (size_t j = 0; j < length; j++)
750         {   auto pkey = keys.ptr + j * keysize;
751             auto pvalue = values.ptr + j * valuesize;
752             aaA* e;
753
754             auto key_hash = keyti.getHash(pkey);
755             //printf("hash = %d\n", key_hash);
756             i = key_hash % len;
757             auto pe = &result.b[i];
758             while (1)
759             {
760                 e = *pe;
761                 if (!e)
762                 {
763                     // Not found, create new elem
764                     //printf("create new one\n");
765                     e = cast(aaA *) cast(void*) new void[aaA.sizeof + keytsize + valuesize];
766                     memcpy(e + 1, pkey, keysize);
767                     e.hash = key_hash;
768                     *pe = e;
769                     result.nodes++;
770                     break;
771                 }
772                 if (key_hash == e.hash)
773                 {
774                     auto c = keyti.compare(pkey, e + 1);
775                     if (c == 0)
776                         break;
777                 }
778                 pe = &e.next;
779             }
780             memcpy(cast(void *)(e + 1) + keytsize, pvalue, valuesize);
781         }
782     }
783     return result;
784 }
785
786
787 /***********************************
788  * Compare AA contents for equality.
789  * Returns:
790  *      1       equal
791  *      0       not equal
792  */
793 int _aaEqual(TypeInfo_AssociativeArray ti, AA e1, AA e2)
794 {
795     //printf("_aaEqual()\n");
796     //printf("keyti = %.*s\n", ti.key.classinfo.name);
797     //printf("valueti = %.*s\n", ti.next.classinfo.name);
798
799     if (e1.a is e2.a)
800         return 1;
801
802     size_t len = _aaLen(e1);
803     if (len != _aaLen(e2))
804         return 0;
805
806     /* Algorithm: Visit each key/value pair in e1. If that key doesn't exist
807      * in e2, or if the value in e1 doesn't match the one in e2, the arrays
808      * are not equal, and exit early.
809      * After all pairs are checked, the arrays must be equal.
810      */
811
812     auto keyti = ti.key;
813     auto valueti = ti.next;
814     const keysize = aligntsize(keyti.tsize());
815     const len2 = e2.a.b.length;
816
817     int _aaKeys_x(aaA* e)
818     {
819         do
820         {
821             auto pkey = cast(void*)(e + 1);
822             auto pvalue = pkey + keysize;
823             //printf("key = %d, value = %g\n", *cast(int*)pkey, *cast(double*)pvalue);
824
825             // We have key/value for e1. See if they exist in e2
826
827             auto key_hash = keyti.getHash(pkey);
828             //printf("hash = %d\n", key_hash);
829             const i = key_hash % len2;
830             auto f = e2.a.b[i];
831             while (1)
832             {
833                 //printf("f is %p\n", f);
834                 if (f is null)
835                     return 0;                   // key not found, so AA's are not equal
836                 if (key_hash == f.hash)
837                 {
838                     //printf("hash equals\n");
839                     auto c = keyti.compare(pkey, f + 1);
840                     if (c == 0)
841                     {   // Found key in e2. Compare values
842                         //printf("key equals\n");
843                         auto pvalue2 = cast(void *)(f + 1) + keysize;
844                         if (valueti.equals(pvalue, pvalue2))
845                         {
846                             //printf("value equals\n");
847                             break;
848                         }
849                         else
850                             return 0;           // values don't match, so AA's are not equal
851                     }
852                 }
853                 f = f.next;
854             }
855
856             // Look at next entry in e1
857             e = e.next;
858         } while (e !is null);
859         return 1;                       // this subtree matches
860     }
861
862     foreach (e; e1.a.b)
863     {
864         if (e)
865         {   if (_aaKeys_x(e) == 0)
866                 return 0;
867         }
868     }
869
870     return 1;           // equal
871 }
Note: See TracBrowser for help on using the browser.