1 /**
2  * This module contains all functions related to an object's lifetime:
3  * allocation, resizing, deallocation, and finalization.
4  *
5  * Copyright: Copyright (C) 2004-2007 Digital Mars, www.digitalmars.com.
6  *            All rights reserved.
7  * License:
8  *  This software is provided 'as-is', without any express or implied
9  *  warranty. In no event will the authors be held liable for any damages
10  *  arising from the use of this software.
11  *
12  *  Permission is granted to anyone to use this software for any purpose,
13  *  including commercial applications, and to alter it and redistribute it
14  *  freely, in both source and binary form, subject to the following
15  *  restrictions:
16  *
17  *  o  The origin of this software must not be misrepresented; you must not
18  *     claim that you wrote the original software. If you use this software
19  *     in a product, an acknowledgment in the product documentation would be
20  *     appreciated but is not required.
21  *  o  Altered source versions must be plainly marked as such, and must not
22  *     be misrepresented as being the original software.
23  *  o  This notice may not be removed or altered from any source
24  *     distribution.
25  * Authors:   Walter Bright, Sean Kelly
26  */
27 module rt.compiler.dmd.rt.lifetime;
28 
29 
30 private
31 {
32     import tango.stdc.stdlib : free, malloc;
33     import tango.stdc.string : memcpy, memset, memcmp;
34     import tango.stdc.stdarg;
35     debug(PRINTF) import tango.stdc.stdio : printf;
36 
37     alias void[] array_t;
38 }
39 
40 
41 private
42 {
43     enum BlkAttr : uint
44     {
45         FINALIZE = 0b0000_0001,
46         NO_SCAN  = 0b0000_0010,
47         NO_MOVE  = 0b0000_0100,
48         ALL_BITS = 0b1111_1111
49     }
50 
51     struct BlkInfo
52     {
53         void*  base;
54         size_t size;
55         uint   attr;
56     }
57 
58     extern (C) uint gc_getAttr( void* p );
59     extern (C) uint gc_setAttr( void* p, uint a );
60     extern (C) uint gc_clrAttr( void* p, uint a );
61 
62     extern (C) void*  gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
63     extern (C) void*  gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init);
64     extern (C) size_t gc_extend( void* p, size_t mx, size_t sz );
65     extern (C) void   gc_free( void* p );
66 
67     extern (C) void*   gc_addrOf( void* p );
68     extern (C) size_t  gc_sizeOf( void* p );
69     extern (C) BlkInfo gc_query( void* p );
70 
71     extern (C) void onFinalizeError( ClassInfo c, Exception e );
72     extern (C) void onOutOfMemoryError();
73 
74     extern (C) void _d_monitordelete(Object h, bool det = true);
75 
76     enum
77     {
78         PAGESIZE = 4096
79     }
80 
81     alias bool function(Object) CollectHandler;
82     CollectHandler collectHandler = null;
83 }
84 
85 
86 /**
87  *
88  */
89 extern (C) Object _d_newclass(ClassInfo ci)
90 {
91     void* p;
92 
93     debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
94     if (ci.flags & 1) // if COM object
95     {   /* COM objects are not garbage collected, they are reference counted
96          * using AddRef() and Release().  They get free'd by C's free()
97          * function called by Release() when Release()'s reference count goes
98          * to zero.
99      */
100         p = malloc(ci.init.length);
101         if (!p)
102             onOutOfMemoryError();
103     }
104     else
105     {
106         PointerMap pm;
107         version (D_HavePointerMap) {
108             pm = ci.pointermap;
109         }
110         p = gc_malloc(ci.init.length,
111                       BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0),
112                       pm);
113         debug(PRINTF) printf(" p = %p\n", p);
114     }
115 
116     debug(PRINTF)
117     {
118         printf("p = %p\n", p);
119         printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length);
120         printf("vptr = %p\n", *cast(void**) ci.init);
121         printf("vtbl[0] = %p\n", (*cast(void***) ci.init)[0]);
122         printf("vtbl[1] = %p\n", (*cast(void***) ci.init)[1]);
123         printf("init[0] = %x\n", (cast(uint*) ci.init)[0]);
124         printf("init[1] = %x\n", (cast(uint*) ci.init)[1]);
125         printf("init[2] = %x\n", (cast(uint*) ci.init)[2]);
126         printf("init[3] = %x\n", (cast(uint*) ci.init)[3]);
127         printf("init[4] = %x\n", (cast(uint*) ci.init)[4]);
128     }
129 
130     // initialize it
131     (cast(byte*) p)[0 .. ci.init.length] = ci.init[];
132 
133     debug(PRINTF) printf("initialization done\n");
134     return cast(Object) p;
135 }
136 
137 
138 /**
139  *
140  */
141 extern (C) void _d_delinterface(void** p)
142 {
143     if (*p)
144     {
145         Interface* pi = **cast(Interface ***)*p;
146         Object     o  = cast(Object)(*p - pi.offset);
147 
148         _d_delclass(&o);
149         *p = null;
150     }
151 }
152 
153 
154 // used for deletion
155 private extern (D) alias void (*fp_t)(Object);
156 
157 
158 /**
159  *
160  */
161 extern (C) void _d_delclass(Object* p)
162 {
163     if (*p)
164     {
165         debug(PRINTF) printf("_d_delclass(%p)\n", *p);
166 
167         ClassInfo **pc = cast(ClassInfo **)*p;
168         if (*pc)
169         {
170             ClassInfo c = **pc;
171 
172             rt_finalize(cast(void*) *p);
173 
174             if (c.deallocator)
175             {
176                 fp_t fp = cast(fp_t)c.deallocator;
177                 (*fp)(*p); // call deallocator
178                 *p = null;
179                 return;
180             }
181         }
182         else
183         {
184             rt_finalize(cast(void*) *p);
185         }
186         gc_free(cast(void*) *p);
187         *p = null;
188     }
189 }
190 
191 
192 /**
193  * Allocate a new array of length elements.
194  * ti is the type of the resulting array, or pointer to element.
195  * (For when the array is initialized to 0)
196  */
197 extern(C) array_t _d_newarrayT(TypeInfo ti, size_t length)
198 {
199     void* p;
200     array_t result;
201     auto size = ti.next.tsize();                // array element size
202 
203     debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size);
204     if (length == 0 || size == 0)
205         result = array_t.init;
206     else
207     {
208         version (D_InlineAsm_X86)
209         {
210             asm
211             {
212                 mov     EAX,size        ;
213                 mul     EAX,length      ;
214                 mov     size,EAX        ;
215                 jc      Loverflow       ;
216             }
217         }
218         else version (D_InlineAsm_X86_64)
219             asm
220             {
221                 mov     RAX,size        ;
222                 mul     RAX,length      ;
223                 mov     size,RAX        ;
224                 jc      Loverflow       ;
225             }
226         else
227             size *= length;
228 
229         PointerMap pm;
230         version (D_HavePointerMap)  pm = ti.next.pointermap();
231 
232         p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
233         debug(PRINTF) printf(" p = %p\n", p);
234         memset(p, 0, size);
235         result = cast(array_t)p[0..length];
236     }
237     return result;
238 
239 Loverflow:
240     onOutOfMemoryError();
241 }
242 
243 /**
244  * For when the array has a non-zero initializer.
245  */
246 extern(C) array_t _d_newarrayiT(TypeInfo ti, size_t length)
247 {
248     array_t result;
249     auto size = ti.next.tsize();                // array element size
250 
251     debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size);
252 
253     if (length == 0 || size == 0)
254         result = array_t.init;
255     else
256     {
257         auto initializer = ti.next.init();
258         auto isize = initializer.length;
259         auto q = initializer.ptr;
260         version (D_InlineAsm_X86)
261         {
262             asm
263             {
264                 mov     EAX,size        ;
265                 mul     EAX,length      ;
266                 mov     size,EAX        ;
267                 jc      Loverflow       ;
268             }
269         }
270         else version (D_InlineAsm_X86_64)
271             asm
272             {
273                 mov     RAX,size        ;
274                 mul     RAX,length      ;
275                 mov     size,RAX        ;
276                 jc      Loverflow       ;
277             }
278         else
279             size *= length;
280 
281         PointerMap pm;
282         version (D_HavePointerMap) {
283             pm = ti.next.pointermap();
284         }
285         auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
286         debug(PRINTF) printf(" p = %p\n", p);
287         if (isize == 1)
288             memset(p, *cast(ubyte*)q, size);
289         else if (isize == int.sizeof)
290         {
291             int init = *cast(int*)q;
292             size /= int.sizeof;
293             for (size_t u = 0; u < size; u++)
294             {
295                 (cast(int*)p)[u] = init;
296             }
297         }
298         else
299         {
300             for (size_t u = 0; u < size; u += isize)
301             {
302                 memcpy(p + u, q, isize);
303             }
304         }
305         va_end(q);
306         result = cast(array_t)p[0..length];
307     }
308     return result;
309 
310 Loverflow:
311     onOutOfMemoryError();
312 }
313 
314 /**
315  *
316  */
317 extern(C) array_t _d_newarraymT(TypeInfo ti, int ndims, ...)
318 {
319     array_t result;
320 
321     debug(PRINTF) printf("_d_newarraymT(ndims = %d)\n", ndims);
322     if (ndims == 0)
323         result = array_t.init;
324     else
325     {
326         va_list q;
327 
328         version(X86)
329             va_start(q, ndims);
330         else version(X86_64)
331             va_start(q, __va_argsave);
332         else
333             static assert(false, "platform not supported");
334 
335         void[] foo(TypeInfo ti, va_list ap, int ndims)
336         {
337             size_t dim;
338             va_arg(ap, dim);
339             void[] p;
340 
341             debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims);
342             if (ndims == 1)
343             {
344                 auto r = _d_newarrayT(ti, dim);
345                 p = *cast(void[]*)(&r);
346             }
347             else
348             {
349                 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
350 
351                 version (X86)
352                 {
353                     va_list ap2;
354                     va_copy(ap2, ap);
355                 }
356 
357                 for (size_t i = 0; i < dim; i++)
358                 {
359                     version (X86_64)
360                     {
361                         __va_list argsave = *cast(__va_list*)ap;
362                         va_list ap2 = &argsave;
363                     }
364                     (cast(void[]*)p.ptr)[i] = foo(ti.next, ap2, ndims - 1);
365                 }
366             }
367             return p;
368         }
369 
370         result = cast(typeof(result))foo(ti, q, ndims);
371         debug(PRINTF) printf("result = %llx\n", result);
372 
373         version (none)
374         {
375             for (int i = 0; i < ndims; i++)
376             {
377                 printf("index %d: %d\n", i, va_arg!(int)(q));
378             }
379         }
380         va_end(q);
381     }
382     return result;
383 }
384 
385 
386 /**
387  *
388  */
389 extern(C) array_t _d_newarraymiT(TypeInfo ti, size_t ndims, ...)
390 {
391     array_t result;
392 
393     debug(PRINTF) printf("_d_newarraymiT(ndims = %d)\n", ndims);
394     if (ndims == 0)
395         result = array_t.init;
396     else
397     {
398         va_list q;
399 
400         version(X86)
401             va_start(q, ndims);
402         else version(X86_64)
403             va_start(q, __va_argsave);
404         else
405             static assert(false, "platform not supported");
406 
407         void[] foo(TypeInfo ti, va_list ap, size_t ndims)
408         {
409             size_t dim;
410             va_arg(ap, dim);
411             void[] p;
412 
413             if (ndims == 1)
414             {
415                 auto r = _d_newarrayiT(ti, dim);
416                 p = *cast(void[]*)(&r);
417             }
418             else
419             {
420                 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim];
421 
422                 version (X86)
423                 {
424                     va_list ap2;
425                     va_copy(ap2, ap);
426                 }
427 
428                 for (size_t i = 0; i < dim; i++)
429                 {
430                     version (X86_64)
431                     {
432                         __va_list argsave = *cast(__va_list*)ap;
433                         va_list ap2 = &argsave;
434                     }
435                     (cast(void[]*)p.ptr)[i] = foo(ti.next, ap2, ndims - 1);
436                 }
437             }
438 
439             return p;
440         }
441 
442         result = cast(typeof(result))foo(ti, q, ndims);
443         debug(PRINTF) printf("result = %llx\n", result);
444 
445         version (none)
446         {
447             for (int i = 0; i < ndims; i++)
448             {
449                 printf("index %d: %d\n", i, va_arg!(int)(q));
450                 printf("init = %d\n", va_arg!(int)(q));
451             }
452         }
453         va_end(q);
454     }
455     return result;
456 }
457 
458 
459 /**
460  *
461  */
462 struct Array
463 {
464     size_t length;
465     byte*  data;
466 }
467 
468 
469 /**
470  *
471  */
472 void* _d_allocmemory(size_t nbytes)
473 {
474     return gc_malloc(nbytes);
475 }
476 
477 
478 /**
479  *
480  */
481 extern (C) void _d_delarray(Array *p)
482 {
483     if (p)
484     {
485         assert(!p.length || p.data);
486 
487         if (p.data)
488             gc_free(p.data);
489         p.data = null;
490         p.length = 0;
491     }
492 }
493 
494 
495 /**
496  *
497  */
498 extern (C) void _d_delmemory(void* *p)
499 {
500     if (*p)
501     {
502         gc_free(*p);
503         *p = null;
504     }
505 }
506 
507 
508 /**
509  *
510  */
511 extern (C) void _d_callinterfacefinalizer(void *p)
512 {
513     if (p)
514     {
515         Interface *pi = **cast(Interface ***)p;
516         Object o = cast(Object)(p - pi.offset);
517         debug(PRINTF)  printf("finalizing...");
518         rt_finalize(cast(void*)o);
519     }
520 }
521 
522 
523 /**
524  *
525  */
526 extern (C) void _d_callfinalizer(void* p)
527 {
528     debug(PRINTF)  printf("dcall finalizing...");
529     rt_finalize( p );
530 }
531 
532 
533 /**
534  *
535  */
536 extern (C) void  rt_setCollectHandler(CollectHandler h)
537 {
538     collectHandler = h;
539 }
540 
541 
542 /**
543  *
544  */
545 extern (C) void rt_finalize(void* p, bool det = true)
546 {
547     debug(PRINTF) printf("rt_finalize(p = %p)\n", p);
548 
549     if (p) // not necessary if called from gc
550     {
551         if (det)
552         {
553             debug(PRINTF) printf("\t det=true, calling dispose\n");
554            (cast(Object)p).dispose();
555         }
556         debug(PRINTF) printf("\tcasting to class info\n");
557 
558         ClassInfo** pc = cast(ClassInfo**)p;
559 
560         debug(PRINTF) printf("\tdereferencing pc: %lu\n", *pc);
561 
562         if (*pc)
563         {
564             ClassInfo c = **pc;
565 
566             try
567             {
568                 debug(PRINTF) printf("\tdoing tests.. collecthandler is %lu\n", cast(void*)collectHandler);
569                 if (det || collectHandler is null || collectHandler(cast(Object)p))
570                 {
571                     do
572                     {
573                         if (c.destructor !is null)
574                         {
575                             void delegate() dg;
576                             dg.ptr = p;
577                             dg.funcptr = cast(void function()) c.destructor;
578                             debug(PRINTF) printf("\tcalling dtor of %s\n", c.name.ptr);
579                             dg(); // call destructor
580                             debug(PRINTF) printf("\tdtor done.\n");
581                         }
582                         c = c.base;
583                     } while (c);
584                 }
585                 if ((cast(void**)p)[1]) // if monitor is not null
586                     _d_monitordelete(cast(Object)p, det);
587             }
588             catch (Exception e)
589             {
590                 debug(PRINTF) printf("\tException while finalizing\n");
591                 onFinalizeError(**pc, e);
592             }
593             finally
594             {
595                 *pc = null; // zero vptr
596             }
597         }
598     }
599     debug(PRINTF) printf("\tLeaving.\n");
600 }
601 
602 
603 /**
604  * Resize dynamic arrays with 0 initializers.
605  */
606 extern (C) byte[] _d_arraysetlengthT(TypeInfo ti, size_t newlength, Array *p)
607 in
608 {
609     assert(ti);
610     assert(!p.length || p.data);
611 }
612 body
613 {
614     byte* newdata;
615     size_t sizeelem = ti.next.tsize();
616 
617     debug(PRINTF)
618     {
619         printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
620         if (p)
621             printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
622     }
623 
624     if (newlength)
625     {
626         version (D_InlineAsm_X86)
627         {
628             size_t newsize = void;
629 
630             asm
631             {
632                 mov EAX, newlength;
633                 mul EAX, sizeelem;
634                 mov newsize, EAX;
635                 jc  Loverflow;
636             }
637         }
638         else
639         {
640             size_t newsize = sizeelem * newlength;
641 
642             if (newsize / newlength != sizeelem)
643                 goto Loverflow;
644         }
645 
646         debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
647 
648         if (p.data)
649         {
650             newdata = p.data;
651             if (newlength > p.length)
652             {
653                 size_t size = p.length * sizeelem;
654                 auto   info = gc_query(p.data);
655 
656                 if (info.size <= newsize || info.base != p.data)
657                 {
658                     if (info.size >= PAGESIZE && info.base == p.data)
659                     {   // Try to extend in-place
660                         auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
661                         if (u)
662                         {
663                             goto L1;
664                         }
665                     }
666                     PointerMap pm;
667                     version (D_HavePointerMap) {
668                         pm = ti.next.pointermap();
669                     }
670                     newdata = cast(byte *)gc_malloc(newsize + 1,
671                             !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
672                     newdata[0 .. size] = p.data[0 .. size];
673                 }
674              L1:
675                 newdata[size .. newsize] = 0;
676             }
677         }
678         else
679         {
680             PointerMap pm;
681             version (D_HavePointerMap) {
682                 pm = ti.next.pointermap();
683             }
684             newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
685         }
686     }
687     else
688     {
689         newdata = p.data;
690     }
691 
692     p.data = newdata;
693     p.length = newlength;
694     return newdata[0 .. newlength];
695 
696 Loverflow:
697     onOutOfMemoryError();
698 }
699 
700 
701 /**
702  * Resize arrays for non-zero initializers.
703  *      p               pointer to array lvalue to be updated
704  *      newlength       new .length property of array
705  *      sizeelem        size of each element of array
706  *      initsize        size of initializer
707  *      ...             initializer
708  */
709 extern (C) byte[] _d_arraysetlengthiT(TypeInfo ti, size_t newlength, Array *p)
710 in
711 {
712     assert(!p.length || p.data);
713 }
714 body
715 {
716     byte* newdata;
717     size_t sizeelem = ti.next.tsize();
718     void[] initializer = ti.next.init();
719     size_t initsize = initializer.length;
720 
721     assert(sizeelem);
722     assert(initsize);
723     assert(initsize <= sizeelem);
724     assert((sizeelem / initsize) * initsize == sizeelem);
725 
726     debug(PRINTF)
727     {
728         printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize);
729         if (p)
730             printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
731     }
732 
733     if (newlength)
734     {
735         version (D_InlineAsm_X86)
736         {
737             size_t newsize = void;
738 
739             asm
740             {
741                 mov     EAX,newlength   ;
742                 mul     EAX,sizeelem    ;
743                 mov     newsize,EAX     ;
744                 jc      Loverflow       ;
745             }
746         }
747         else
748         {
749             size_t newsize = sizeelem * newlength;
750 
751             if (newsize / newlength != sizeelem)
752                 goto Loverflow;
753         }
754         debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
755 
756         size_t size = p.length * sizeelem;
757 
758         if (p.data)
759         {
760             newdata = p.data;
761             if (newlength > p.length)
762             {
763                 auto info = gc_query(p.data);
764 
765                 if (info.size <= newsize || info.base != p.data)
766                 {
767                     if (info.size >= PAGESIZE && info.base == p.data)
768                     {   // Try to extend in-place
769                         auto u = gc_extend(p.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
770                         if (u)
771                         {
772                             goto L1;
773                         }
774                     }
775                     PointerMap pm;
776                     version (D_HavePointerMap) {
777                         pm = ti.next.pointermap();
778                     }
779                     newdata = cast(byte *)gc_malloc(newsize + 1,
780                             !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
781                     newdata[0 .. size] = p.data[0 .. size];
782                 L1: ;
783                 }
784             }
785         }
786         else
787         {
788             PointerMap pm;
789             version (D_HavePointerMap) {
790                 pm = ti.next.pointermap();
791             }
792             newdata = cast(byte *)gc_malloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
793         }
794 
795         auto q = initializer.ptr; // pointer to initializer
796 
797         if (newsize > size)
798         {
799             if (initsize == 1)
800             {
801                 debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q);
802                 newdata[size .. newsize] = *(cast(byte*)q);
803             }
804             else
805             {
806                 for (size_t u = size; u < newsize; u += initsize)
807                 {
808                     memcpy(newdata + u, q, initsize);
809                 }
810             }
811         }
812     }
813     else
814     {
815         newdata = p.data;
816     }
817 
818     p.data = newdata;
819     p.length = newlength;
820     return newdata[0 .. newlength];
821 
822 Loverflow:
823     onOutOfMemoryError();
824 }
825 
826 /**************************************
827  * Extend an array by n elements.
828  * Caller must initialize that element.
829  */
830 extern (C) byte[] _d_arrayappendcTX(TypeInfo ti, inout byte[] x, size_t n)
831 {
832     auto sizeelem = ti.next.tsize();            // array element size
833     auto info = gc_query(x.ptr);
834     auto length = x.length;
835     auto newlength = length + n;
836     auto newsize = newlength * sizeelem;
837 
838     assert(info.size == 0 || length * sizeelem <= info.size);
839 
840     //printf("_d_arrayappendcTX(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, cap);
841 
842     if (newsize >= info.size)
843     {   byte* newdata;
844 
845         if (info.size >= PAGESIZE)
846         {   // Try to extend in-place
847             auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
848             if (u)
849             {
850                 goto L1;
851             }
852         }
853 
854         PointerMap pm;
855         version (D_HavePointerMap) {
856             pm = ti.next.pointermap();
857         }
858 
859         uint attr = !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0;
860         newdata = cast(byte *) gc_malloc(newCapacity(newlength, sizeelem) + 1, attr, pm);
861 
862         memcpy(newdata, x.ptr, length * sizeelem);
863 
864         (cast(void **)(&x))[1] = newdata;
865     }
866 
867   L1:
868     *cast(size_t *)&x = newlength;
869     assert((cast(size_t)x.ptr & 15) == 0);
870     assert(gc_query(x.ptr).size > x.length * sizeelem);
871     return x;
872 }
873 
874 
875 /**
876  * Append y[] to array x[].
877  * size is size of each array element.
878  */
879 extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)
880 {
881     auto sizeelem = ti.next.tsize();            // array element size
882     auto info = gc_query(px.data);
883     auto length = px.length;
884     auto newlength = length + y.length;
885     auto newsize = newlength * sizeelem;
886 
887     if (info.size < newsize || info.base != px.data)
888     {   byte* newdata;
889 
890         if (info.size >= PAGESIZE && info.base == px.data)
891         {   // Try to extend in-place
892             auto u = gc_extend(px.data, (newsize + 1) - info.size, (newsize + 1) - info.size);
893             if (u)
894             {
895                 goto L1;
896             }
897         }
898         PointerMap pm;
899         version (D_HavePointerMap) {
900             pm = ti.next.pointermap();
901         }
902         uint attr = !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0;
903         newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, attr, pm);
904         memcpy(newdata, px.data, length * sizeelem);
905         px.data = newdata;
906     }
907   L1:
908     px.length = newlength;
909     memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem);
910     return *cast(long*)px;
911 }
912 
913 
914 /**
915  *
916  */
917 size_t newCapacity(size_t newlength, size_t size)
918 {
919     version(none)
920     {
921         size_t newcap = newlength * size;
922     }
923     else
924     {
925         /*
926          * Better version by Fawzi, inspired by the one of Dave Fladebo:
927          * This uses an inverse logorithmic algorithm to pre-allocate a bit more
928          * space for larger arrays.
929          * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
930          * common cases, memory allocation is 1 to 1. The small overhead added
931          * doesn't affect small array perf. (it's virtually the same as
932          * current).
933          * - Larger arrays have some space pre-allocated.
934          * - As the arrays grow, the relative pre-allocated space shrinks.
935          * - The logorithmic algorithm allocates relatively more space for
936          * mid-size arrays, making it very fast for medium arrays (for
937          * mid-to-large arrays, this turns out to be quite a bit faster than the
938          * equivalent realloc() code in C, on Linux at least. Small arrays are
939          * just as fast as GCC).
940          * - Perhaps most importantly, overall memory usage and stress on the GC
941          * is decreased significantly for demanding environments.
942          */
943         size_t newcap = newlength * size;
944         size_t newext = 0;
945 
946         if (newcap > PAGESIZE)
947         {
948             const size_t b=0; // flatness factor, how fast the extra space decreases with array size
949             const size_t a=100; // allocate at most a% of the requested size as extra space (rounding will change this)
950             const size_t minBits=1; // minimum bit size
951 
952 
953             static size_t log2plusB(size_t c)
954             {
955                 // could use the bsr bit op
956                 size_t i=b+1;
957                 while(c >>= 1){
958                     ++i;
959                 }
960                 return i;
961             }
962             long mult = 100 + a*(minBits+b) / log2plusB(newlength);
963 
964             newext = cast(size_t)((newcap * mult) / 100);
965             newext += size-(newext % size); // round up
966             debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size);
967         }
968         newcap = newext > newcap ? newext : newcap; // just to handle overflows
969         debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
970     }
971     return newcap;
972 }
973 
974 
975 /**
976  *
977  */
978 extern (C) byte[] _d_arrayappendcT(TypeInfo ti, ref byte[] x, ...)
979 {
980     auto sizeelem = ti.next.tsize();            // array element size
981     auto info = gc_query(x.ptr);
982     auto length = x.length;
983     auto newlength = length + 1;
984     auto newsize = newlength * sizeelem;
985 
986     assert(info.size == 0 || length * sizeelem <= info.size);
987 
988     debug(PRINTF) printf("_d_arrayappendcT(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size);
989 
990     if (info.size <= newsize || info.base != x.ptr)
991     {   byte* newdata;
992 
993         if (info.size >= PAGESIZE && info.base == x.ptr)
994         {   // Try to extend in-place
995             auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
996             if (u)
997             {
998                 goto L1;
999             }
1000         }
1001         debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d, ti=%.*s)\n", length, newlength, info.size, ti.toString());
1002         auto newcap = newCapacity(newlength, sizeelem);
1003         assert(newcap >= newlength * sizeelem);
1004         PointerMap pm;
1005         version (D_HavePointerMap) {
1006             pm = ti.next.pointermap();
1007         }
1008         uint attr = !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0;
1009         newdata = cast(byte *)gc_malloc(newcap + 1, attr, pm);
1010         memcpy(newdata, x.ptr, length * sizeelem);
1011         (cast(void**)(&x))[1] = newdata;
1012     }
1013   L1:
1014     byte *argp = cast(byte *)(&ti + 2);
1015 
1016     *cast(size_t *)&x = newlength;
1017     x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem];
1018     assert((cast(size_t)x.ptr & 15) == 0);
1019     assert(gc_sizeOf(x.ptr) > x.length * sizeelem);
1020     return x;
1021 }
1022 
1023 import tango.io.Stdout;
1024 
1025 /**
1026  * Append dchar to char[]
1027  */
1028 extern (C) char[] _d_arrayappendcd(ref char[] x, dchar c)
1029 {
1030     const sizeelem = c.sizeof;            // array element size
1031     auto info = gc_query(x.ptr);
1032     auto length = x.length;
1033 
1034     // c could encode into from 1 to 4 characters
1035     int nchars;
1036     if (c <= 0x7F)
1037         nchars = 1;
1038     else if (c <= 0x7FF)
1039         nchars = 2;
1040     else if (c <= 0xFFFF)
1041         nchars = 3;
1042     else if (c <= 0x10FFFF)
1043         nchars = 4;
1044     else
1045 	assert(0);	// invalid utf character - should we throw an exception instead?
1046 
1047     auto newlength = length + nchars;
1048     auto newsize = newlength * sizeelem;
1049 
1050     assert(info.size == 0 || length * sizeelem <= info.size);
1051 
1052     debug(PRINTF) printf("_d_arrayappendcd(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size);
1053 
1054     if (info.size <= newsize || info.base != x.ptr)
1055     {   byte* newdata;
1056 
1057         if (info.size >= PAGESIZE && info.base == x.ptr)
1058         {   // Try to extend in-place
1059             auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
1060             if (u)
1061             {
1062                 goto L1;
1063             }
1064         }
1065         debug(PRINTF) printf("_d_arrayappendcd(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
1066         auto newcap = newCapacity(newlength, sizeelem);
1067         assert(newcap >= newlength * sizeelem);
1068         newdata = cast(byte *)gc_malloc(newcap + 1, BlkAttr.NO_SCAN);
1069         memcpy(newdata, x.ptr, length * sizeelem);
1070         (cast(void**)(&x))[1] = newdata;
1071     }
1072   L1:
1073     *cast(size_t *)&x = newlength;
1074     char* ptr = &x.ptr[length];
1075 
1076     if (c <= 0x7F)
1077     {
1078         ptr[0] = cast(char) c;
1079     }
1080     else if (c <= 0x7FF)
1081     {
1082         ptr[0] = cast(char)(0xC0 | (c >> 6));
1083         ptr[1] = cast(char)(0x80 | (c & 0x3F));
1084     }
1085     else if (c <= 0xFFFF)
1086     {
1087         ptr[0] = cast(char)(0xE0 | (c >> 12));
1088         ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
1089         ptr[2] = cast(char)(0x80 | (c & 0x3F));
1090     }
1091     else if (c <= 0x10FFFF)
1092     {
1093         ptr[0] = cast(char)(0xF0 | (c >> 18));
1094         ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
1095         ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
1096         ptr[3] = cast(char)(0x80 | (c & 0x3F));
1097     }
1098     else
1099 	assert(0);
1100 
1101     assert((cast(size_t)x.ptr & 15) == 0);
1102     assert(gc_sizeOf(x.ptr) > x.length * sizeelem);
1103     return x;
1104 }
1105 
1106 
1107 /**
1108  * Append dchar to wchar[]
1109  */
1110 extern (C) wchar[] _d_arrayappendwd(ref wchar[] x, dchar c)
1111 {
1112     const sizeelem = c.sizeof;            // array element size
1113     auto info = gc_query(x.ptr);
1114     auto length = x.length;
1115 
1116     // c could encode into from 1 to 2 w characters
1117     int nchars;
1118     if (c <= 0xFFFF)
1119         nchars = 1;
1120     else
1121         nchars = 2;
1122 
1123     auto newlength = length + nchars;
1124     auto newsize = newlength * sizeelem;
1125 
1126     assert(info.size == 0 || length * sizeelem <= info.size);
1127 
1128     debug(PRINTF) printf("_d_arrayappendwd(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size);
1129 
1130     if (info.size <= newsize || info.base != x.ptr)
1131     {   byte* newdata;
1132 
1133         if (info.size >= PAGESIZE && info.base == x.ptr)
1134         {   // Try to extend in-place
1135             auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size);
1136             if (u)
1137             {
1138                 goto L1;
1139             }
1140         }
1141         debug(PRINTF) printf("_d_arrayappendwd(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size);
1142         auto newcap = newCapacity(newlength, sizeelem);
1143         assert(newcap >= newlength * sizeelem);
1144         newdata = cast(byte *)gc_malloc(newcap + 1, BlkAttr.NO_SCAN);
1145         memcpy(newdata, x.ptr, length * sizeelem);
1146         (cast(void**)(&x))[1] = newdata;
1147     }
1148   L1:
1149     *cast(size_t *)&x = newlength;
1150     wchar* ptr = &x.ptr[length];
1151 
1152     if (c <= 0xFFFF)
1153     {
1154         ptr[0] = cast(wchar) c;
1155     }
1156     else
1157     {
1158     	ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
1159     	ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
1160     }
1161 
1162     assert((cast(size_t)x.ptr & 15) == 0);
1163     assert(gc_sizeOf(x.ptr) > x.length * sizeelem);
1164     return x;
1165 }
1166 
1167 
1168 /**
1169  *
1170  */
1171 extern (C) byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y)
1172 out (result)
1173 {
1174     auto sizeelem = ti.next.tsize();            // array element size
1175     debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr);
1176     assert(result.length == x.length + y.length);
1177     for (size_t i = 0; i < x.length * sizeelem; i++)
1178         assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
1179     for (size_t i = 0; i < y.length * sizeelem; i++)
1180         assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
1181 
1182     size_t cap = gc_sizeOf(result.ptr);
1183     assert(!cap || cap > result.length * sizeelem);
1184 }
1185 body
1186 {
1187     version (none)
1188     {
1189         /* Cannot use this optimization because:
1190          *  char[] a, b;
1191          *  char c = 'a';
1192          *  b = a ~ c;
1193          *  c = 'b';
1194          * will change the contents of b.
1195          */
1196         if (!y.length)
1197             return x;
1198         if (!x.length)
1199             return y;
1200     }
1201 
1202     debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr);
1203     auto sizeelem = ti.next.tsize();            // array element size
1204     debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
1205     size_t xlen = x.length * sizeelem;
1206     size_t ylen = y.length * sizeelem;
1207     size_t len  = xlen + ylen;
1208 
1209     if (!len)
1210         return null;
1211 
1212     PointerMap pm;
1213     version (D_HavePointerMap) {
1214         pm = ti.next.pointermap();
1215     }
1216     byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
1217     memcpy(p, x.ptr, xlen);
1218     memcpy(p + xlen, y.ptr, ylen);
1219     p[len] = 0;
1220     return p[0 .. x.length + y.length];
1221 }
1222 
1223 
1224 /**
1225  *
1226  */
1227 extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)
1228 {   void* a;
1229     size_t length;
1230     byte[]* p;
1231     uint i;
1232     byte[] b;
1233     auto size = ti.next.tsize(); // array element size
1234 
1235     version (X86)
1236     {
1237         p = cast(byte[]*)(&n + 1);
1238         for (i = 0; i < n; i++)
1239         {
1240             b = *p++;
1241             length += b.length;
1242         }
1243     }
1244     else version (X86_64)
1245     {
1246         __va_list argsave = __va_argsave.va;
1247         va_list ap;
1248         va_start(ap, __va_argsave);
1249         for (i = 0; i < n; i++)
1250         {
1251             b.length = 0;
1252             va_arg(ap, b);
1253             length += b.length;
1254         }
1255         va_end(ap);
1256     }
1257 
1258     if (!length)
1259         return null;
1260 
1261     PointerMap pm;
1262     version (D_HavePointerMap) {
1263         pm = ti.next.pointermap();
1264     }
1265     a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
1266 
1267     version (X86)
1268     {
1269         p = cast(byte[]*)(&n + 1);
1270         size_t j = 0;
1271         for (i = 0; i < n; i++)
1272         {
1273             b = *p++;
1274             if (b.length)
1275             {
1276                 memcpy(a + j, b.ptr, b.length * size);
1277                 j += b.length * size;
1278             }
1279         }
1280     }
1281     else version (X86_64)
1282     {
1283         va_list ap2 = &argsave;
1284         size_t j = 0;
1285         for (i = 0; i < n; i++)
1286         {
1287             b.length = 0;
1288             va_arg(ap2, b);
1289             if (b.length)
1290             {
1291                 memcpy(a + j, b.ptr, b.length * size);
1292                 j += b.length * size;
1293             }
1294         }
1295         va_end(ap2);
1296     }
1297 
1298     byte[] result;
1299     *cast(size_t *)&result = length;       // jam length
1300     (cast(void **)&result)[1] = a;      // jam ptr
1301     return result;
1302 }
1303 
1304 extern (C) void* _d_arrayliteralTX(TypeInfo ti, size_t length)
1305 {
1306     auto sizeelem = ti.next.tsize();            // array element size
1307     void* result;
1308 
1309     //printf("_d_arrayliteralTX(sizeelem = %d, length = %d)\n", sizeelem, length);
1310     if (length == 0 || sizeelem == 0)
1311         result = null;
1312     else
1313     {
1314         PointerMap pm;
1315         version (D_HavePointerMap) {
1316             pm = ti.next.pointermap();
1317         }
1318         result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
1319     }
1320     return result;
1321 }
1322 
1323 /**
1324  *
1325  */
1326 version (X86) extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...)
1327 {
1328     auto sizeelem = ti.next.tsize();            // array element size
1329     void* result;
1330 
1331     debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length);
1332     if (length == 0 || sizeelem == 0)
1333         result = null;
1334     else
1335     {
1336         PointerMap pm;
1337         version (D_HavePointerMap) {
1338             pm = ti.next.pointermap();
1339         }
1340         result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
1341 
1342         version(X86)
1343         {
1344             va_list q;
1345             va_start!(size_t)(q, length);
1346 
1347             size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1);
1348 
1349             if (stacksize == sizeelem)
1350             {
1351                 memcpy(result, q, length * sizeelem);
1352             }
1353             else
1354             {
1355                 for (size_t i = 0; i < length; i++)
1356                 {
1357                     memcpy(result + i * sizeelem, q, sizeelem);
1358                     q += stacksize;
1359                 }
1360             }
1361 
1362             va_end(q);
1363         }
1364         else version(X86_64)
1365         {
1366             va_list q;
1367             va_start(q, __va_argsave);
1368             for (size_t i = 0; i < length; i++)
1369             {
1370                 va_arg(q, ti.next, result + i * sizeelem);
1371             }
1372             va_end(q);
1373         }
1374     }
1375     return result;
1376 }
1377 
1378 
1379 /**
1380  * Support for array.dup property.
1381  */
1382 struct Array2
1383 {
1384     size_t length;
1385     void*  ptr;
1386 }
1387 
1388 
1389 /**
1390  *
1391  */
1392 extern (C) array_t _adDupT(TypeInfo ti, Array2 a)
1393 out (result)
1394 {
1395     auto sizeelem = ti.next.tsize();            // array element size
1396     assert(memcmp((*cast(Array2*)&result).ptr, a.ptr, a.length * sizeelem) == 0);
1397 }
1398 body
1399 {
1400     Array2 r;
1401 
1402     if (a.length)
1403     {
1404         auto sizeelem = ti.next.tsize();                // array element size
1405         auto size = a.length * sizeelem;
1406         PointerMap pm;
1407         version (D_HavePointerMap) {
1408             pm = ti.next.pointermap();
1409         }
1410         r.ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm);
1411         r.length = a.length;
1412         memcpy(r.ptr, a.ptr, size);
1413     }
1414     return *cast(array_t*)(&r);
1415 }
1416 
1417 
1418 unittest
1419 {
1420     int[] a;
1421     int[] b;
1422     int i;
1423 
1424     a = new int[3];
1425     a[0] = 1; a[1] = 2; a[2] = 3;
1426     b = a.dup;
1427     assert(b.length == 3);
1428     for (i = 0; i < 3; i++)
1429         assert(b[i] == i + 1);
1430 }