root/branches/phobos-1.x/phobos/internal/aaA.d

Revision 2283, 21.5 kB (checked in by walter, 1 year ago)

remove reliance on varargs

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