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