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