1 /**
2  * This module contains all functions related to an object's lifetime:
3  * allocation, resizing, deallocation, and finalization.
4  *
5  * Copyright: Copyright Digital Mars 2000 - 2012.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors:   Walter Bright, Sean Kelly, Steven Schveighoffer
10  * Source: $(DRUNTIMESRC rt/_lifetime.d)
11  */
12 
13 module rt.lifetime;
14 
15 import core.attribute : weak;
16 import core.memory;
17 debug(PRINTF) import core.stdc.stdio;
18 static import rt.tlsgc;
19 
20 alias BlkInfo = GC.BlkInfo;
21 alias BlkAttr = GC.BlkAttr;
22 
23 private
24 {
25     alias bool function(Object) CollectHandler;
26     __gshared CollectHandler collectHandler = null;
27 
28     extern (C) void _d_monitordelete(Object h, bool det);
29 
30     enum : size_t
31     {
32         PAGESIZE = 4096,
33         BIGLENGTHMASK = ~(PAGESIZE - 1),
34         SMALLPAD = 1,
35         MEDPAD = ushort.sizeof,
36         LARGEPREFIX = 16, // 16 bytes padding at the front of the array
37         LARGEPAD = LARGEPREFIX + 1,
38         MAXSMALLSIZE = 256-SMALLPAD,
39         MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD
40     }
41 }
42 
43 extern (C) void lifetime_init()
44 {
45     // this is run before static ctors, so it is safe to modify immutables
46 }
47 
48 /**
49  *
50  */
51 extern (C) void* _d_allocmemory(size_t sz) @weak
52 {
53     return GC.malloc(sz);
54 }
55 
56 /**
57  *
58  */
59 extern (C) Object _d_newclass(const ClassInfo ci) @weak
60 {
61     import core.stdc.stdlib;
62     import core.exception : onOutOfMemoryError;
63     void* p;
64     auto init = ci.initializer;
65 
66     debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name);
67     if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass)
68     {   /* COM objects are not garbage collected, they are reference counted
69          * using AddRef() and Release().  They get free'd by C's free()
70          * function called by Release() when Release()'s reference count goes
71          * to zero.
72      */
73         p = malloc(init.length);
74         if (!p)
75             onOutOfMemoryError();
76     }
77     else
78     {
79         // TODO: should this be + 1 to avoid having pointers to the next block?
80         BlkAttr attr = BlkAttr.NONE;
81         // extern(C++) classes don't have a classinfo pointer in their vtable so the GC can't finalize them
82         if (ci.m_flags & TypeInfo_Class.ClassFlags.hasDtor
83             && !(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass))
84             attr |= BlkAttr.FINALIZE;
85         if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)
86             attr |= BlkAttr.NO_SCAN;
87         p = GC.malloc(init.length, attr, ci);
88         debug(PRINTF) printf(" p = %p\n", p);
89     }
90 
91     debug(PRINTF)
92     {
93         printf("p = %p\n", p);
94         printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, init.ptr, cast(ulong)init.length);
95         printf("vptr = %p\n", *cast(void**) init);
96         printf("vtbl[0] = %p\n", (*cast(void***) init)[0]);
97         printf("vtbl[1] = %p\n", (*cast(void***) init)[1]);
98         printf("init[0] = %x\n", (cast(uint*) init)[0]);
99         printf("init[1] = %x\n", (cast(uint*) init)[1]);
100         printf("init[2] = %x\n", (cast(uint*) init)[2]);
101         printf("init[3] = %x\n", (cast(uint*) init)[3]);
102         printf("init[4] = %x\n", (cast(uint*) init)[4]);
103     }
104 
105     // initialize it
106     p[0 .. init.length] = init[];
107 
108     debug(PRINTF) printf("initialization done\n");
109     return cast(Object) p;
110 }
111 
112 
113 /**
114  *
115  */
116 extern (C) void _d_delinterface(void** p)
117 {
118     if (*p)
119     {
120         Interface* pi = **cast(Interface ***)*p;
121         Object     o  = cast(Object)(*p - pi.offset);
122 
123         _d_delclass(&o);
124         *p = null;
125     }
126 }
127 
128 
129 // used for deletion
130 private extern (D) alias void function (Object) fp_t;
131 
132 
133 /**
134  *
135  */
136 extern (C) void _d_delclass(Object* p) @weak
137 {
138     if (*p)
139     {
140         debug(PRINTF) printf("_d_delclass(%p)\n", *p);
141 
142         ClassInfo **pc = cast(ClassInfo **)*p;
143         if (*pc)
144         {
145             ClassInfo c = **pc;
146 
147             rt_finalize(cast(void*) *p);
148 
149             if (c.deallocator)
150             {
151                 fp_t fp = cast(fp_t)c.deallocator;
152                 (*fp)(*p); // call deallocator
153                 *p = null;
154                 return;
155             }
156         }
157         else
158         {
159             rt_finalize(cast(void*) *p);
160         }
161         GC.free(cast(void*) *p);
162         *p = null;
163     }
164 }
165 
166 /**
167  * This is called for a delete statement where the value
168  * being deleted is a pointer to a struct with a destructor
169  * but doesn't have an overloaded delete operator.
170  */
171 extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) @weak
172 {
173     if (*p)
174     {
175         debug(PRINTF) printf("_d_delstruct(%p, %p)\n", *p, cast(void*)inf);
176 
177         inf.destroy(*p);
178         GC.free(*p);
179         *p = null;
180     }
181 }
182 
183 // strip const/immutable/shared/inout from type info
184 inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
185 {
186     TypeInfo ti = cast() cti;
187     while (ti)
188     {
189         // avoid dynamic type casts
190         auto tti = typeid(ti);
191         if (tti is typeid(TypeInfo_Const))
192             ti = (cast(TypeInfo_Const)cast(void*)ti).base;
193         else if (tti is typeid(TypeInfo_Invariant))
194             ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
195         else if (tti is typeid(TypeInfo_Shared))
196             ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
197         else if (tti is typeid(TypeInfo_Inout))
198             ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
199         else
200             break;
201     }
202     return ti;
203 }
204 
205 // size used to store the TypeInfo at the end of an allocation for structs that have a destructor
206 size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
207 {
208     if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
209     {
210         auto sti = cast(TypeInfo_Struct)cast(void*)ti;
211         if (sti.xdtor)
212             return size_t.sizeof;
213     }
214     return 0;
215 }
216 
217 /** dummy class used to lock for shared array appending */
218 private class ArrayAllocLengthLock
219 {}
220 
221 
222 /**
223   Set the allocated length of the array block.  This is called
224   any time an array is appended to or its length is set.
225 
226   The allocated block looks like this for blocks < PAGESIZE:
227 
228   |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize|
229 
230 
231   The size of the allocated length at the end depends on the block size:
232 
233   a block of 16 to 256 bytes has an 8-bit length.
234 
235   a block with 512 to pagesize/2 bytes has a 16-bit length.
236 
237   For blocks >= pagesize, the length is a size_t and is at the beginning of the
238   block.  The reason we have to do this is because the block can extend into
239   more pages, so we cannot trust the block length if it sits at the end of the
240   block, because it might have just been extended.  If we can prove in the
241   future that the block is unshared, we may be able to change this, but I'm not
242   sure it's important.
243 
244   In order to do put the length at the front, we have to provide 16 bytes
245   buffer space in case the block has to be aligned properly.  In x86, certain
246   SSE instructions will only work if the data is 16-byte aligned.  In addition,
247   we need the sentinel byte to prevent accidental pointers to the next block.
248   Because of the extra overhead, we only do this for page size and above, where
249   the overhead is minimal compared to the block size.
250 
251   So for those blocks, it looks like:
252 
253   |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte|
254 
255   where elem0 starts 16 bytes after the first byte.
256   */
257 bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow
258 {
259     import core.atomic;
260 
261     size_t typeInfoSize = structTypeInfoSize(tinext);
262 
263     if (info.size <= 256)
264     {
265         import core.checkedint;
266 
267         bool overflow;
268         auto newlength_padded = addu(newlength,
269                                      addu(SMALLPAD, typeInfoSize, overflow),
270                                      overflow);
271 
272         if (newlength_padded > info.size || overflow)
273             // new size does not fit inside block
274             return false;
275 
276         auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD);
277         if (oldlength != ~0)
278         {
279             if (isshared)
280             {
281                 return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength);
282             }
283             else
284             {
285                 if (*length == cast(ubyte)oldlength)
286                     *length = cast(ubyte)newlength;
287                 else
288                     return false;
289             }
290         }
291         else
292         {
293             // setting the initial length, no cas needed
294             *length = cast(ubyte)newlength;
295         }
296         if (typeInfoSize)
297         {
298             auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
299             *typeInfo = cast() tinext;
300         }
301     }
302     else if (info.size < PAGESIZE)
303     {
304         if (newlength + MEDPAD + typeInfoSize > info.size)
305             // new size does not fit inside block
306             return false;
307         auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD);
308         if (oldlength != ~0)
309         {
310             if (isshared)
311             {
312                 return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength);
313             }
314             else
315             {
316                 if (*length == oldlength)
317                     *length = cast(ushort)newlength;
318                 else
319                     return false;
320             }
321         }
322         else
323         {
324             // setting the initial length, no cas needed
325             *length = cast(ushort)newlength;
326         }
327         if (typeInfoSize)
328         {
329             auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof);
330             *typeInfo = cast() tinext;
331         }
332     }
333     else
334     {
335         if (newlength + LARGEPAD > info.size)
336             // new size does not fit inside block
337             return false;
338         auto length = cast(size_t *)(info.base);
339         if (oldlength != ~0)
340         {
341             if (isshared)
342             {
343                 return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength);
344             }
345             else
346             {
347                 if (*length == oldlength)
348                     *length = newlength;
349                 else
350                     return false;
351             }
352         }
353         else
354         {
355             // setting the initial length, no cas needed
356             *length = newlength;
357         }
358         if (typeInfoSize)
359         {
360             auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof);
361             *typeInfo = cast()tinext;
362         }
363     }
364     return true; // resize succeeded
365 }
366 
367 /**
368   get the allocation size of the array for the given block (without padding or type info)
369   */
370 size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow
371 {
372     if (info.size <= 256)
373         return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD);
374 
375     if (info.size < PAGESIZE)
376         return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD);
377 
378     return *cast(size_t *)(info.base);
379 }
380 
381 /**
382   get the start of the array for the given block
383   */
384 void *__arrayStart(return scope BlkInfo info) nothrow pure
385 {
386     return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0);
387 }
388 
389 /**
390   get the padding required to allocate size bytes.  Note that the padding is
391   NOT included in the passed in size.  Therefore, do NOT call this function
392   with the size of an allocated block.
393   */
394 size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted
395 {
396     return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext));
397 }
398 
399 /**
400   clear padding that might not be zeroed by the GC (it assumes it is within the
401   requested size from the start, but it is actually at the end of the allocated block)
402   */
403 private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) nothrow pure
404 {
405     import core.stdc.string;
406     if (padsize > MEDPAD && !(info.attr & BlkAttr.NO_SCAN) && info.base)
407     {
408         if (info.size < PAGESIZE)
409             memset(info.base + arrsize, 0, padsize);
410         else
411             memset(info.base, 0, LARGEPREFIX);
412     }
413 }
414 
415 /**
416   allocate an array memory block by applying the proper padding and
417   assigning block attributes if not inherited from the existing block
418   */
419 BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
420 {
421     import core.checkedint;
422 
423     size_t typeInfoSize = structTypeInfoSize(tinext);
424     size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize);
425 
426     bool overflow;
427     auto padded_size = addu(arrsize, padsize, overflow);
428 
429     if (overflow)
430         return BlkInfo();
431 
432     uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE;
433     if (typeInfoSize)
434         attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
435 
436     auto bi = GC.qalloc(padded_size, attr, tinext);
437     __arrayClearPad(bi, arrsize, padsize);
438     return bi;
439 }
440 
441 BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext)
442 {
443     import core.checkedint;
444 
445     if (!info.base)
446         return __arrayAlloc(arrsize, ti, tinext);
447 
448     immutable padsize = __arrayPad(arrsize, tinext);
449     bool overflow;
450     auto padded_size = addu(arrsize, padsize, overflow);
451     if (overflow)
452     {
453         return BlkInfo();
454     }
455 
456     auto bi = GC.qalloc(padded_size, info.attr, tinext);
457     __arrayClearPad(bi, arrsize, padsize);
458     return bi;
459 }
460 
461 /**
462   cache for the lookup of the block info
463   */
464 enum N_CACHE_BLOCKS=8;
465 
466 // note this is TLS, so no need to sync.
467 BlkInfo *__blkcache_storage;
468 
469 static if (N_CACHE_BLOCKS==1)
470 {
471     version=single_cache;
472 }
473 else
474 {
475     //version=simple_cache; // uncomment to test simple cache strategy
476     //version=random_cache; // uncomment to test random cache strategy
477 
478     // ensure N_CACHE_BLOCKS is power of 2.
479     static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS));
480 
481     version (random_cache)
482     {
483         int __nextRndNum = 0;
484     }
485     int __nextBlkIdx;
486 }
487 
488 @property BlkInfo *__blkcache() nothrow
489 {
490     if (!__blkcache_storage)
491     {
492         import core.stdc.stdlib;
493         import core.stdc.string;
494         // allocate the block cache for the first time
495         immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS;
496         __blkcache_storage = cast(BlkInfo *)malloc(size);
497         memset(__blkcache_storage, 0, size);
498     }
499     return __blkcache_storage;
500 }
501 
502 // called when thread is exiting.
503 static ~this()
504 {
505     // free the blkcache
506     if (__blkcache_storage)
507     {
508         import core.stdc.stdlib;
509         free(__blkcache_storage);
510         __blkcache_storage = null;
511     }
512 }
513 
514 
515 // we expect this to be called with the lock in place
516 void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow
517 {
518     // called after the mark routine to eliminate block cache data when it
519     // might be ready to sweep
520 
521     debug(PRINTF) printf("processing GC Marks, %x\n", cache);
522     if (cache)
523     {
524         debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS)
525         {
526             printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr);
527         }
528         auto cache_end = cache + N_CACHE_BLOCKS;
529         for (;cache < cache_end; ++cache)
530         {
531             if (cache.base != null && !isMarked(cache.base))
532             {
533                 debug(PRINTF) printf("clearing cache entry at %x\n", cache.base);
534                 cache.base = null; // clear that data.
535             }
536         }
537     }
538 }
539 
540 unittest
541 {
542     // Bugzilla 10701 - segfault in GC
543     ubyte[] result; result.length = 4096;
544     GC.free(result.ptr);
545     GC.collect();
546 }
547 
548 /**
549   Get the cached block info of an interior pointer.  Returns null if the
550   interior pointer's block is not cached.
551 
552   NOTE: The base ptr in this struct can be cleared asynchronously by the GC,
553         so any use of the returned BlkInfo should copy it and then check the
554         base ptr of the copy before actually using it.
555 
556   TODO: Change this function so the caller doesn't have to be aware of this
557         issue.  Either return by value and expect the caller to always check
558         the base ptr as an indication of whether the struct is valid, or set
559         the BlkInfo as a side-effect and return a bool to indicate success.
560   */
561 BlkInfo *__getBlkInfo(void *interior) nothrow
562 {
563     BlkInfo *ptr = __blkcache;
564     version (single_cache)
565     {
566         if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
567             return ptr;
568         return null; // not in cache.
569     }
570     else version (simple_cache)
571     {
572         foreach (i; 0..N_CACHE_BLOCKS)
573         {
574             if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size)
575                 return ptr;
576             ptr++;
577         }
578     }
579     else
580     {
581         // try to do a smart lookup, using __nextBlkIdx as the "head"
582         auto curi = ptr + __nextBlkIdx;
583         for (auto i = curi; i >= ptr; --i)
584         {
585             if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
586                 return i;
587         }
588 
589         for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i)
590         {
591             if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size)
592                 return i;
593         }
594     }
595     return null; // not in cache.
596 }
597 
598 void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow
599 {
600     version (single_cache)
601     {
602         *__blkcache = bi;
603     }
604     else
605     {
606         version (simple_cache)
607         {
608             if (curpos)
609                 *curpos = bi;
610             else
611             {
612                 // note, this is a super-simple algorithm that does not care about
613                 // most recently used.  It simply uses a round-robin technique to
614                 // cache block info.  This means that the ordering of the cache
615                 // doesn't mean anything.  Certain patterns of allocation may
616                 // render the cache near-useless.
617                 __blkcache[__nextBlkIdx] = bi;
618                 __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
619             }
620         }
621         else version (random_cache)
622         {
623             // strategy: if the block currently is in the cache, move the
624             // current block index to the a random element and evict that
625             // element.
626             auto cache = __blkcache;
627             if (!curpos)
628             {
629                 __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1);
630                 curpos = cache + __nextBlkIdx;
631             }
632             else
633             {
634                 __nextBlkIdx = curpos - cache;
635             }
636             *curpos = bi;
637         }
638         else
639         {
640             //
641             // strategy: If the block currently is in the cache, swap it with
642             // the head element.  Otherwise, move the head element up by one,
643             // and insert it there.
644             //
645             auto cache = __blkcache;
646             if (!curpos)
647             {
648                 __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1);
649                 curpos = cache + __nextBlkIdx;
650             }
651             else if (curpos !is cache + __nextBlkIdx)
652             {
653                 *curpos = cache[__nextBlkIdx];
654                 curpos = cache + __nextBlkIdx;
655             }
656             *curpos = bi;
657         }
658     }
659 }
660 
661 /**
662  * Shrink the "allocated" length of an array to be the exact size of the array.
663  * It doesn't matter what the current allocated length of the array is, the
664  * user is telling the runtime that he knows what he is doing.
665  */
666 extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/
667 {
668     // note, we do not care about shared.  We are setting the length no matter
669     // what, so no lock is required.
670     debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length);
671     auto tinext = unqualify(ti.next);
672     auto size = tinext.tsize;                  // array element size
673     auto cursize = arr.length * size;
674     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
675     auto bic = isshared ? null : __getBlkInfo(arr.ptr);
676     auto info = bic ? *bic : GC.query(arr.ptr);
677     if (info.base && (info.attr & BlkAttr.APPENDABLE))
678     {
679         auto newsize = (arr.ptr - __arrayStart(info)) + cursize;
680 
681         debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize);
682 
683         // destroy structs that become unused memory when array size is shrinked
684         if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
685         {
686             auto sti = cast(TypeInfo_Struct)cast(void*)tinext;
687             if (sti.xdtor)
688             {
689                 auto oldsize = __arrayAllocLength(info, tinext);
690                 if (oldsize > cursize)
691                     finalize_array(arr.ptr + cursize, oldsize - cursize, sti);
692             }
693         }
694         // Note: Since we "assume" the append is safe, it means it is not shared.
695         // Since it is not shared, we also know it won't throw (no lock).
696         if (!__setArrayAllocLength(info, newsize, false, tinext))
697         {
698             import core.exception : onInvalidMemoryOperationError;
699             onInvalidMemoryOperationError();
700         }
701 
702         // cache the block if not already done.
703         if (!isshared && !bic)
704             __insertBlkInfoCache(info, null);
705     }
706 }
707 
708 package bool hasPostblit(in TypeInfo ti)
709 {
710     return (&ti.postblit).funcptr !is &TypeInfo.postblit;
711 }
712 
713 void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
714 {
715     if (!hasPostblit(ti))
716         return;
717 
718     if (auto tis = cast(TypeInfo_Struct)ti)
719     {
720         // this is a struct, check the xpostblit member
721         auto pblit = tis.xpostblit;
722         if (!pblit)
723             // postblit not specified, no point in looping.
724             return;
725 
726         // optimized for struct, call xpostblit directly for each element
727         immutable size = ti.tsize;
728         const eptr = ptr + len;
729         for (;ptr < eptr;ptr += size)
730             pblit(ptr);
731     }
732     else
733     {
734         // generic case, call the typeinfo's postblit function
735         immutable size = ti.tsize;
736         const eptr = ptr + len;
737         for (;ptr < eptr;ptr += size)
738             ti.postblit(ptr);
739     }
740 }
741 
742 
743 /**
744  * set the array capacity.  If the array capacity isn't currently large enough
745  * to hold the requested capacity (in number of elements), then the array is
746  * resized/reallocated to the appropriate size.  Pass in a requested capacity
747  * of 0 to get the current capacity.  Returns the number of elements that can
748  * actually be stored once the resizing is done.
749  */
750 extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak
751 in
752 {
753     assert(ti);
754     assert(!(*p).length || (*p).ptr);
755 }
756 do
757 {
758     import core.stdc.string;
759     import core.exception : onOutOfMemoryError;
760 
761     // step 1, get the block
762     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
763     auto bic = isshared ? null : __getBlkInfo((*p).ptr);
764     auto info = bic ? *bic : GC.query((*p).ptr);
765     auto tinext = unqualify(ti.next);
766     auto size = tinext.tsize;
767     version (D_InlineAsm_X86)
768     {
769         size_t reqsize = void;
770 
771         asm
772         {
773             mov EAX, newcapacity;
774             mul EAX, size;
775             mov reqsize, EAX;
776             jnc  Lcontinue;
777         }
778     }
779     else version (D_InlineAsm_X86_64)
780     {
781         size_t reqsize = void;
782 
783         asm
784         {
785             mov RAX, newcapacity;
786             mul RAX, size;
787             mov reqsize, RAX;
788             jnc  Lcontinue;
789         }
790     }
791     else
792     {
793         import core.checkedint : mulu;
794 
795         bool overflow = false;
796         size_t reqsize = mulu(size, newcapacity, overflow);
797         if (!overflow)
798             goto Lcontinue;
799     }
800 Loverflow:
801     onOutOfMemoryError();
802     assert(0);
803 Lcontinue:
804 
805     // step 2, get the actual "allocated" size.  If the allocated size does not
806     // match what we expect, then we will need to reallocate anyways.
807 
808     // TODO: this probably isn't correct for shared arrays
809     size_t curallocsize = void;
810     size_t curcapacity = void;
811     size_t offset = void;
812     size_t arraypad = void;
813     if (info.base && (info.attr & BlkAttr.APPENDABLE))
814     {
815         if (info.size <= 256)
816         {
817             arraypad = SMALLPAD + structTypeInfoSize(tinext);
818             curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad));
819         }
820         else if (info.size < PAGESIZE)
821         {
822             arraypad = MEDPAD + structTypeInfoSize(tinext);
823             curallocsize = *(cast(ushort *)(info.base + info.size - arraypad));
824         }
825         else
826         {
827             curallocsize = *(cast(size_t *)(info.base));
828             arraypad = LARGEPAD;
829         }
830 
831 
832         offset = (*p).ptr - __arrayStart(info);
833         if (offset + (*p).length * size != curallocsize)
834         {
835             curcapacity = 0;
836         }
837         else
838         {
839             // figure out the current capacity of the block from the point
840             // of view of the array.
841             curcapacity = info.size - offset - arraypad;
842         }
843     }
844     else
845     {
846         curallocsize = curcapacity = offset = 0;
847     }
848     debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset);
849 
850     if (curcapacity >= reqsize)
851     {
852         // no problems, the current allocated size is large enough.
853         return curcapacity / size;
854     }
855 
856     // step 3, try to extend the array in place.
857     if (info.size >= PAGESIZE && curcapacity != 0)
858     {
859         auto extendsize = reqsize + offset + LARGEPAD - info.size;
860         auto u = GC.extend(info.base, extendsize, extendsize);
861         if (u)
862         {
863             // extend worked, save the new current allocated size
864             if (bic)
865                 bic.size = u; // update cache
866             curcapacity = u - offset - LARGEPAD;
867             return curcapacity / size;
868         }
869     }
870 
871     // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size.
872     auto datasize = (*p).length * size;
873     // copy attributes from original block, or from the typeinfo if the
874     // original block doesn't exist.
875     info = __arrayAlloc(reqsize, info, ti, tinext);
876     if (info.base is null)
877         goto Loverflow;
878     // copy the data over.
879     // note that malloc will have initialized the data we did not request to 0.
880     auto tgt = __arrayStart(info);
881     memcpy(tgt, (*p).ptr, datasize);
882 
883     // handle postblit
884     __doPostblit(tgt, datasize, tinext);
885 
886     if (!(info.attr & BlkAttr.NO_SCAN))
887     {
888         // need to memset the newly requested data, except for the data that
889         // malloc returned that we didn't request.
890         void *endptr = tgt + reqsize;
891         void *begptr = tgt + datasize;
892 
893         // sanity check
894         assert(endptr >= begptr);
895         memset(begptr, 0, endptr - begptr);
896     }
897 
898     // set up the correct length
899     __setArrayAllocLength(info, datasize, isshared, tinext);
900     if (!isshared)
901         __insertBlkInfoCache(info, bic);
902 
903     *p = (cast(void*)tgt)[0 .. (*p).length];
904 
905     // determine the padding.  This has to be done manually because __arrayPad
906     // assumes you are not counting the pad size, and info.size does include
907     // the pad.
908     if (info.size <= 256)
909         arraypad = SMALLPAD + structTypeInfoSize(tinext);
910     else if (info.size < PAGESIZE)
911         arraypad = MEDPAD + structTypeInfoSize(tinext);
912     else
913         arraypad = LARGEPAD;
914 
915     curcapacity = info.size - arraypad;
916     return curcapacity / size;
917 }
918 
919 /**
920  * Allocate a new uninitialized array of length elements.
921  * ti is the type of the resulting array, or pointer to element.
922  */
923 extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak
924 {
925     import core.exception : onOutOfMemoryError;
926 
927     auto tinext = unqualify(ti.next);
928     auto size = tinext.tsize;
929 
930     debug(PRINTF) printf("_d_newarrayU(length = x%x, size = %d)\n", length, size);
931     if (length == 0 || size == 0)
932         return null;
933 
934     version (D_InlineAsm_X86)
935     {
936         asm pure nothrow @nogc
937         {
938             mov     EAX,size        ;
939             mul     EAX,length      ;
940             mov     size,EAX        ;
941             jnc     Lcontinue       ;
942         }
943     }
944     else version (D_InlineAsm_X86_64)
945     {
946         asm pure nothrow @nogc
947         {
948             mov     RAX,size        ;
949             mul     RAX,length      ;
950             mov     size,RAX        ;
951             jnc     Lcontinue       ;
952         }
953     }
954     else
955     {
956         import core.checkedint : mulu;
957 
958         bool overflow = false;
959         size = mulu(size, length, overflow);
960         if (!overflow)
961             goto Lcontinue;
962     }
963 Loverflow:
964     onOutOfMemoryError();
965     assert(0);
966 Lcontinue:
967 
968     auto info = __arrayAlloc(size, ti, tinext);
969     if (!info.base)
970         goto Loverflow;
971     debug(PRINTF) printf(" p = %p\n", info.base);
972     // update the length of the array
973     auto arrstart = __arrayStart(info);
974     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
975     __setArrayAllocLength(info, size, isshared, tinext);
976     return arrstart[0..length];
977 }
978 
979 /**
980  * Allocate a new array of length elements.
981  * ti is the type of the resulting array, or pointer to element.
982  * (For when the array is initialized to 0)
983  */
984 extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak
985 {
986     import core.stdc.string;
987 
988     void[] result = _d_newarrayU(ti, length);
989     auto tinext = unqualify(ti.next);
990     auto size = tinext.tsize;
991 
992     memset(result.ptr, 0, size * length);
993     return result;
994 }
995 
996 /**
997  * For when the array has a non-zero initializer.
998  */
999 extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak
1000 {
1001     import core.internal.traits : AliasSeq;
1002 
1003     void[] result = _d_newarrayU(ti, length);
1004     auto tinext = unqualify(ti.next);
1005     auto size = tinext.tsize;
1006 
1007     auto init = tinext.initializer();
1008 
1009     switch (init.length)
1010     {
1011     foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
1012     {
1013     case T.sizeof:
1014         if (tinext.talign % T.alignof == 0)
1015         {
1016             (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
1017             return result;
1018         }
1019         goto default;
1020     }
1021 
1022     default:
1023     {
1024         import core.stdc.string;
1025         immutable sz = init.length;
1026         for (size_t u = 0; u < size * length; u += sz)
1027             memcpy(result.ptr + u, init.ptr, sz);
1028         return result;
1029     }
1030     }
1031 }
1032 
1033 
1034 /**
1035  *
1036  */
1037 void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims)
1038 {
1039     debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length);
1040     if (dims.length == 0)
1041         return null;
1042 
1043     void[] foo(const TypeInfo ti, size_t[] dims)
1044     {
1045         auto tinext = unqualify(ti.next);
1046         auto dim = dims[0];
1047 
1048         debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, dims.length);
1049         if (dims.length == 1)
1050         {
1051             auto r = op(ti, dim);
1052             return *cast(void[]*)(&r);
1053         }
1054 
1055         auto allocsize = (void[]).sizeof * dim;
1056         auto info = __arrayAlloc(allocsize, ti, tinext);
1057         auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
1058         __setArrayAllocLength(info, allocsize, isshared, tinext);
1059         auto p = __arrayStart(info)[0 .. dim];
1060 
1061         foreach (i; 0..dim)
1062         {
1063             (cast(void[]*)p.ptr)[i] = foo(tinext, dims[1..$]);
1064         }
1065         return p;
1066     }
1067 
1068     auto result = foo(ti, dims);
1069     debug(PRINTF) printf("result = %llx\n", result.ptr);
1070 
1071     return result;
1072 }
1073 
1074 
1075 /**
1076  *
1077  */
1078 extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak
1079 {
1080     debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length);
1081 
1082     if (dims.length == 0)
1083         return null;
1084     else
1085     {
1086         return _d_newarrayOpT!(_d_newarrayT)(ti, dims);
1087     }
1088 }
1089 
1090 
1091 /**
1092  *
1093  */
1094 extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak
1095 {
1096     debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length);
1097 
1098     if (dims.length == 0)
1099         return null;
1100     else
1101     {
1102         return _d_newarrayOpT!(_d_newarrayiT)(ti, dims);
1103     }
1104 }
1105 
1106 /**
1107  * Allocate an uninitialized non-array item.
1108  * This is an optimization to avoid things needed for arrays like the __arrayPad(size).
1109  */
1110 extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak
1111 {
1112     auto ti = unqualify(_ti);
1113     auto flags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0;
1114     immutable tiSize = structTypeInfoSize(ti);
1115     immutable itemSize = ti.tsize;
1116     immutable size = itemSize + tiSize;
1117     if (tiSize)
1118         flags |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE;
1119 
1120     auto blkInf = GC.qalloc(size, flags, ti);
1121     auto p = blkInf.base;
1122 
1123     if (tiSize)
1124     {
1125         // the GC might not have cleared the padding area in the block
1126         *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null;
1127         *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti;
1128     }
1129 
1130     return p;
1131 }
1132 
1133 /// Same as above, zero initializes the item.
1134 extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak
1135 {
1136     import core.stdc.string;
1137     auto p = _d_newitemU(_ti);
1138     memset(p, 0, _ti.tsize);
1139     return p;
1140 }
1141 
1142 /// Same as above, for item with non-zero initializer.
1143 extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak
1144 {
1145     import core.stdc.string;
1146     auto p = _d_newitemU(_ti);
1147     auto init = _ti.initializer();
1148     assert(init.length <= _ti.tsize);
1149     memcpy(p, init.ptr, init.length);
1150     return p;
1151 }
1152 
1153 /**
1154  *
1155  */
1156 struct Array
1157 {
1158     size_t length;
1159     byte*  data;
1160 }
1161 
1162 debug(PRINTF)
1163 {
1164     extern(C) void printArrayCache()
1165     {
1166         auto ptr = __blkcache;
1167         printf("CACHE: \n");
1168         foreach (i; 0 .. N_CACHE_BLOCKS)
1169         {
1170             printf("  %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr);
1171         }
1172     }
1173 }
1174 
1175 /**
1176  *
1177  */
1178 extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) @weak
1179 {
1180     if (p)
1181     {
1182         auto bic = __getBlkInfo(p.ptr);
1183         auto info = bic ? *bic : GC.query(p.ptr);
1184 
1185         if (info.base && (info.attr & BlkAttr.APPENDABLE))
1186         {
1187             if (ti) // ti non-null only if ti is a struct with dtor
1188             {
1189                 void* start = __arrayStart(info);
1190                 size_t length = __arrayAllocLength(info, ti);
1191                 finalize_array(start, length, ti);
1192             }
1193 
1194             // if p is in the cache, clear it there as well
1195             if (bic)
1196                 bic.base = null;
1197 
1198             GC.free(info.base);
1199             *p = null;
1200         }
1201     }
1202 }
1203 
1204 deprecated unittest
1205 {
1206     __gshared size_t countDtor = 0;
1207     struct S
1208     {
1209         int x;
1210         ~this() { countDtor++; }
1211     }
1212     // destroy large array with x.ptr not base address of allocation
1213     auto x = new S[10000];
1214     void* p = x.ptr;
1215     assert(GC.addrOf(p) != null);
1216     _d_delarray_t(cast(void[]*)&x, typeid(typeof(x[0]))); // delete x;
1217     assert(GC.addrOf(p) == null);
1218     assert(countDtor == 10000);
1219 
1220     // destroy full array even if only slice passed
1221     auto y = new S[400];
1222     auto z = y[200 .. 300];
1223     p = z.ptr;
1224     assert(GC.addrOf(p) != null);
1225     _d_delarray_t(cast(void[]*)&z, typeid(typeof(z[0]))); // delete z;
1226     assert(GC.addrOf(p) == null);
1227     assert(countDtor == 10000 + 400);
1228 }
1229 
1230 /**
1231  *
1232  */
1233 extern (C) void _d_delmemory(void* *p) @weak
1234 {
1235     if (*p)
1236     {
1237         GC.free(*p);
1238         *p = null;
1239     }
1240 }
1241 
1242 
1243 /**
1244  *
1245  */
1246 extern (C) void _d_callinterfacefinalizer(void *p) @weak
1247 {
1248     if (p)
1249     {
1250         Interface *pi = **cast(Interface ***)p;
1251         Object o = cast(Object)(p - pi.offset);
1252         rt_finalize(cast(void*)o);
1253     }
1254 }
1255 
1256 
1257 /**
1258  *
1259  */
1260 extern (C) void _d_callfinalizer(void* p) @weak
1261 {
1262     rt_finalize( p );
1263 }
1264 
1265 
1266 /**
1267  *
1268  */
1269 extern (C) void rt_setCollectHandler(CollectHandler h)
1270 {
1271     collectHandler = h;
1272 }
1273 
1274 
1275 /**
1276  *
1277  */
1278 extern (C) CollectHandler rt_getCollectHandler()
1279 {
1280     return collectHandler;
1281 }
1282 
1283 
1284 /**
1285  *
1286  */
1287 extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow
1288 {
1289     if (attr & BlkAttr.STRUCTFINAL)
1290     {
1291         if (attr & BlkAttr.APPENDABLE)
1292             return hasArrayFinalizerInSegment(p, size, segment);
1293         return hasStructFinalizerInSegment(p, size, segment);
1294     }
1295 
1296     // otherwise class
1297     auto ppv = cast(void**) p;
1298     if (!p || !*ppv)
1299         return false;
1300 
1301     auto c = *cast(ClassInfo*)*ppv;
1302     do
1303     {
1304         auto pf = c.destructor;
1305         if (cast(size_t)(pf - segment.ptr) < segment.length) return true;
1306     }
1307     while ((c = c.base) !is null);
1308 
1309     return false;
1310 }
1311 
1312 int hasStructFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow
1313 {
1314     if (!p)
1315         return false;
1316 
1317     auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
1318     return cast(size_t)(cast(void*)ti.xdtor - segment.ptr) < segment.length;
1319 }
1320 
1321 int hasArrayFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow
1322 {
1323     if (!p)
1324         return false;
1325 
1326     TypeInfo_Struct si = void;
1327     if (size < PAGESIZE)
1328         si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
1329     else
1330         si = *cast(TypeInfo_Struct*)(p + size_t.sizeof);
1331 
1332     return cast(size_t)(cast(void*)si.xdtor - segment.ptr) < segment.length;
1333 }
1334 
1335 // called by the GC
1336 void finalize_array2(void* p, size_t size) nothrow
1337 {
1338     debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p);
1339 
1340     TypeInfo_Struct si = void;
1341     if (size <= 256)
1342     {
1343         si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
1344         size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD);
1345     }
1346     else if (size < PAGESIZE)
1347     {
1348         si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
1349         size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD);
1350     }
1351     else
1352     {
1353         si = *cast(TypeInfo_Struct*)(p + size_t.sizeof);
1354         size = *cast(size_t*)p;
1355         p += LARGEPREFIX;
1356     }
1357 
1358     try
1359     {
1360         finalize_array(p, size, si);
1361     }
1362     catch (Exception e)
1363     {
1364         import core.exception : onFinalizeError;
1365         onFinalizeError(si, e);
1366     }
1367 }
1368 
1369 void finalize_array(void* p, size_t size, const TypeInfo_Struct si)
1370 {
1371     // Due to the fact that the delete operator calls destructors
1372     // for arrays from the last element to the first, we maintain
1373     // compatibility here by doing the same.
1374     auto tsize = si.tsize;
1375     for (auto curP = p + size - tsize; curP >= p; curP -= tsize)
1376     {
1377         // call destructor
1378         si.destroy(curP);
1379     }
1380 }
1381 
1382 // called by the GC
1383 void finalize_struct(void* p, size_t size) nothrow
1384 {
1385     debug(PRINTF) printf("finalize_struct(p = %p)\n", p);
1386 
1387     auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof);
1388     try
1389     {
1390         ti.destroy(p); // call destructor
1391     }
1392     catch (Exception e)
1393     {
1394         import core.exception : onFinalizeError;
1395         onFinalizeError(ti, e);
1396     }
1397 }
1398 
1399 /**
1400  *
1401  */
1402 extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow
1403 {
1404     debug(PRINTF) printf("rt_finalize2(p = %p)\n", p);
1405 
1406     auto ppv = cast(void**) p;
1407     if (!p || !*ppv)
1408         return;
1409 
1410     auto pc = cast(ClassInfo*) *ppv;
1411     try
1412     {
1413         if (det || collectHandler is null || collectHandler(cast(Object) p))
1414         {
1415             auto c = *pc;
1416             do
1417             {
1418                 if (c.destructor)
1419                     (cast(fp_t) c.destructor)(cast(Object) p); // call destructor
1420             }
1421             while ((c = c.base) !is null);
1422         }
1423 
1424         if (ppv[1]) // if monitor is not null
1425             _d_monitordelete(cast(Object) p, det);
1426 
1427         if (resetMemory)
1428         {
1429             auto w = (*pc).initializer;
1430             p[0 .. w.length] = w[];
1431         }
1432     }
1433     catch (Exception e)
1434     {
1435         import core.exception : onFinalizeError;
1436         onFinalizeError(*pc, e);
1437     }
1438     finally
1439     {
1440         *ppv = null; // zero vptr even if `resetMemory` is false
1441     }
1442 }
1443 
1444 extern (C) void rt_finalize(void* p, bool det = true) nothrow
1445 {
1446     rt_finalize2(p, det, true);
1447 }
1448 
1449 extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow
1450 {
1451     // to verify: reset memory necessary?
1452     if (!(attr & BlkAttr.STRUCTFINAL))
1453         rt_finalize2(p, false, false); // class
1454     else if (attr & BlkAttr.APPENDABLE)
1455         finalize_array2(p, size); // array of structs
1456     else
1457         finalize_struct(p, size); // struct
1458 }
1459 
1460 
1461 /**
1462  * Resize dynamic arrays with 0 initializers.
1463  */
1464 extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak
1465 in
1466 {
1467     assert(ti);
1468     assert(!(*p).length || (*p).ptr);
1469 }
1470 do
1471 {
1472     import core.stdc.string;
1473     import core.exception : onOutOfMemoryError;
1474 
1475     debug(PRINTF)
1476     {
1477         //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
1478         if (p)
1479             printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
1480     }
1481 
1482     if (newlength <= (*p).length)
1483     {
1484         *p = (*p)[0 .. newlength];
1485         void* newdata = (*p).ptr;
1486         return newdata[0 .. newlength];
1487     }
1488     auto tinext = unqualify(ti.next);
1489     size_t sizeelem = tinext.tsize;
1490 
1491     /* Calculate: newsize = newlength * sizeelem
1492      */
1493     bool overflow = false;
1494     version (D_InlineAsm_X86)
1495     {
1496         size_t newsize = void;
1497 
1498         asm pure nothrow @nogc
1499         {
1500             mov EAX, newlength;
1501             mul EAX, sizeelem;
1502             mov newsize, EAX;
1503             setc overflow;
1504         }
1505     }
1506     else version (D_InlineAsm_X86_64)
1507     {
1508         size_t newsize = void;
1509 
1510         asm pure nothrow @nogc
1511         {
1512             mov RAX, newlength;
1513             mul RAX, sizeelem;
1514             mov newsize, RAX;
1515             setc overflow;
1516         }
1517     }
1518     else
1519     {
1520         import core.checkedint : mulu;
1521         const size_t newsize = mulu(sizeelem, newlength, overflow);
1522     }
1523     if (overflow)
1524     {
1525         onOutOfMemoryError();
1526         assert(0);
1527     }
1528 
1529     debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
1530 
1531     const isshared = typeid(ti) is typeid(TypeInfo_Shared);
1532 
1533     if (!(*p).ptr)
1534     {
1535         // pointer was null, need to allocate
1536         auto info = __arrayAlloc(newsize, ti, tinext);
1537         if (info.base is null)
1538         {
1539             onOutOfMemoryError();
1540             assert(0);
1541         }
1542         __setArrayAllocLength(info, newsize, isshared, tinext);
1543         if (!isshared)
1544             __insertBlkInfoCache(info, null);
1545         void* newdata = cast(byte *)__arrayStart(info);
1546         memset(newdata, 0, newsize);
1547         *p = newdata[0 .. newlength];
1548         return *p;
1549     }
1550 
1551     const size_t size = (*p).length * sizeelem;
1552     auto   bic = isshared ? null : __getBlkInfo((*p).ptr);
1553     auto   info = bic ? *bic : GC.query((*p).ptr);
1554 
1555     /* Attempt to extend past the end of the existing array.
1556      * If not possible, allocate new space for entire array and copy.
1557      */
1558     bool allocateAndCopy = false;
1559     void* newdata = (*p).ptr;
1560     if (info.base && (info.attr & BlkAttr.APPENDABLE))
1561     {
1562         // calculate the extent of the array given the base.
1563         const size_t offset = (*p).ptr - __arrayStart(info);
1564         if (info.size >= PAGESIZE)
1565         {
1566             // size of array is at the front of the block
1567             if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1568             {
1569                 // check to see if it failed because there is not
1570                 // enough space
1571                 if (*(cast(size_t*)info.base) == size + offset)
1572                 {
1573                     // not enough space, try extending
1574                     auto extendsize = newsize + offset + LARGEPAD - info.size;
1575                     auto u = GC.extend(info.base, extendsize, extendsize);
1576                     if (u)
1577                     {
1578                         // extend worked, now try setting the length
1579                         // again.
1580                         info.size = u;
1581                         if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1582                         {
1583                             if (!isshared)
1584                                 __insertBlkInfoCache(info, bic);
1585                             memset(newdata + size, 0, newsize - size);
1586                             *p = newdata[0 .. newlength];
1587                             return *p;
1588                         }
1589                     }
1590                 }
1591 
1592                 // couldn't do it, reallocate
1593                 allocateAndCopy = true;
1594             }
1595             else if (!isshared && !bic)
1596             {
1597                 // add this to the cache, it wasn't present previously.
1598                 __insertBlkInfoCache(info, null);
1599             }
1600         }
1601         else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1602         {
1603             // could not resize in place
1604             allocateAndCopy = true;
1605         }
1606         else if (!isshared && !bic)
1607         {
1608             // add this to the cache, it wasn't present previously.
1609             __insertBlkInfoCache(info, null);
1610         }
1611     }
1612     else
1613         allocateAndCopy = true;
1614 
1615     if (allocateAndCopy)
1616     {
1617         if (info.base)
1618         {
1619             if (bic)
1620             {
1621                 // a chance that flags have changed since this was cached, we should fetch the most recent flags
1622                 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
1623             }
1624             info = __arrayAlloc(newsize, info, ti, tinext);
1625         }
1626         else
1627         {
1628             info = __arrayAlloc(newsize, ti, tinext);
1629         }
1630 
1631         if (info.base is null)
1632         {
1633             onOutOfMemoryError();
1634             assert(0);
1635         }
1636 
1637         __setArrayAllocLength(info, newsize, isshared, tinext);
1638         if (!isshared)
1639             __insertBlkInfoCache(info, bic);
1640         newdata = cast(byte *)__arrayStart(info);
1641         newdata[0 .. size] = (*p).ptr[0 .. size];
1642 
1643         /* Do postblit processing, as we are making a copy and the
1644          * original array may have references.
1645          * Note that this may throw.
1646          */
1647         __doPostblit(newdata, size, tinext);
1648     }
1649 
1650     // Zero the unused portion of the newly allocated space
1651     memset(newdata + size, 0, newsize - size);
1652 
1653     *p = newdata[0 .. newlength];
1654     return *p;
1655 }
1656 
1657 
1658 /**
1659  * Resize arrays for non-zero initializers.
1660  *      p               pointer to array lvalue to be updated
1661  *      newlength       new .length property of array
1662  *      sizeelem        size of each element of array
1663  *      initsize        size of initializer
1664  *      ...             initializer
1665  */
1666 extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak
1667 in
1668 {
1669     assert(!(*p).length || (*p).ptr);
1670 }
1671 do
1672 {
1673     import core.stdc.string;
1674     import core.exception : onOutOfMemoryError;
1675 
1676     debug(PRINTF)
1677     {
1678         //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
1679         if (p)
1680             printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length);
1681     }
1682 
1683     if (newlength <= (*p).length)
1684     {
1685         *p = (*p)[0 .. newlength];
1686         void* newdata = (*p).ptr;
1687         return newdata[0 .. newlength];
1688     }
1689     auto tinext = unqualify(ti.next);
1690     size_t sizeelem = tinext.tsize;
1691 
1692     /* Calculate: newsize = newlength * sizeelem
1693      */
1694     bool overflow = false;
1695     version (D_InlineAsm_X86)
1696     {
1697         size_t newsize = void;
1698 
1699         asm pure nothrow @nogc
1700         {
1701             mov EAX, newlength;
1702             mul EAX, sizeelem;
1703             mov newsize, EAX;
1704             setc overflow;
1705         }
1706     }
1707     else version (D_InlineAsm_X86_64)
1708     {
1709         size_t newsize = void;
1710 
1711         asm pure nothrow @nogc
1712         {
1713             mov RAX, newlength;
1714             mul RAX, sizeelem;
1715             mov newsize, RAX;
1716             setc overflow;
1717         }
1718     }
1719     else
1720     {
1721         import core.checkedint : mulu;
1722         const size_t newsize = mulu(sizeelem, newlength, overflow);
1723     }
1724     if (overflow)
1725     {
1726         onOutOfMemoryError();
1727         assert(0);
1728     }
1729 
1730     debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength);
1731 
1732     const isshared = typeid(ti) is typeid(TypeInfo_Shared);
1733 
1734     static void doInitialize(void *start, void *end, const void[] initializer)
1735     {
1736         if (initializer.length == 1)
1737         {
1738             memset(start, *(cast(ubyte*)initializer.ptr), end - start);
1739         }
1740         else
1741         {
1742             auto q = initializer.ptr;
1743             immutable initsize = initializer.length;
1744             for (; start < end; start += initsize)
1745             {
1746                 memcpy(start, q, initsize);
1747             }
1748         }
1749     }
1750 
1751     if (!(*p).ptr)
1752     {
1753         // pointer was null, need to allocate
1754         auto info = __arrayAlloc(newsize, ti, tinext);
1755         if (info.base is null)
1756         {
1757             onOutOfMemoryError();
1758             assert(0);
1759         }
1760         __setArrayAllocLength(info, newsize, isshared, tinext);
1761         if (!isshared)
1762             __insertBlkInfoCache(info, null);
1763         void* newdata = cast(byte *)__arrayStart(info);
1764         doInitialize(newdata, newdata + newsize, tinext.initializer);
1765         *p = newdata[0 .. newlength];
1766         return *p;
1767     }
1768 
1769     const size_t size = (*p).length * sizeelem;
1770     auto   bic = isshared ? null : __getBlkInfo((*p).ptr);
1771     auto   info = bic ? *bic : GC.query((*p).ptr);
1772 
1773     /* Attempt to extend past the end of the existing array.
1774      * If not possible, allocate new space for entire array and copy.
1775      */
1776     bool allocateAndCopy = false;
1777     void* newdata = (*p).ptr;
1778 
1779     if (info.base && (info.attr & BlkAttr.APPENDABLE))
1780     {
1781         // calculate the extent of the array given the base.
1782         const size_t offset = (*p).ptr - __arrayStart(info);
1783         if (info.size >= PAGESIZE)
1784         {
1785             // size of array is at the front of the block
1786             if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1787             {
1788                 // check to see if it failed because there is not
1789                 // enough space
1790                 if (*(cast(size_t*)info.base) == size + offset)
1791                 {
1792                     // not enough space, try extending
1793                     auto extendsize = newsize + offset + LARGEPAD - info.size;
1794                     auto u = GC.extend(info.base, extendsize, extendsize);
1795                     if (u)
1796                     {
1797                         // extend worked, now try setting the length
1798                         // again.
1799                         info.size = u;
1800                         if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1801                         {
1802                             if (!isshared)
1803                                 __insertBlkInfoCache(info, bic);
1804                             doInitialize(newdata + size, newdata + newsize, tinext.initializer);
1805                             *p = newdata[0 .. newlength];
1806                             return *p;
1807                         }
1808                     }
1809                 }
1810 
1811                 // couldn't do it, reallocate
1812                 allocateAndCopy = true;
1813             }
1814             else if (!isshared && !bic)
1815             {
1816                 // add this to the cache, it wasn't present previously.
1817                 __insertBlkInfoCache(info, null);
1818             }
1819         }
1820         else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
1821         {
1822             // could not resize in place
1823             allocateAndCopy = true;
1824         }
1825         else if (!isshared && !bic)
1826         {
1827             // add this to the cache, it wasn't present previously.
1828             __insertBlkInfoCache(info, null);
1829         }
1830     }
1831     else
1832         allocateAndCopy = true;
1833 
1834     if (allocateAndCopy)
1835     {
1836         if (info.base)
1837         {
1838             if (bic)
1839             {
1840                 // a chance that flags have changed since this was cached, we should fetch the most recent flags
1841                 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
1842             }
1843             info = __arrayAlloc(newsize, info, ti, tinext);
1844         }
1845         else
1846         {
1847             info = __arrayAlloc(newsize, ti, tinext);
1848         }
1849 
1850         if (info.base is null)
1851         {
1852             onOutOfMemoryError();
1853             assert(0);
1854         }
1855 
1856         __setArrayAllocLength(info, newsize, isshared, tinext);
1857         if (!isshared)
1858             __insertBlkInfoCache(info, bic);
1859         newdata = cast(byte *)__arrayStart(info);
1860         newdata[0 .. size] = (*p).ptr[0 .. size];
1861 
1862         /* Do postblit processing, as we are making a copy and the
1863          * original array may have references.
1864          * Note that this may throw.
1865          */
1866         __doPostblit(newdata, size, tinext);
1867     }
1868 
1869     // Initialize the unused portion of the newly allocated space
1870     doInitialize(newdata + size, newdata + newsize, tinext.initializer);
1871     *p = newdata[0 .. newlength];
1872     return *p;
1873 }
1874 
1875 /**
1876  * Append y[] to array x[]
1877  */
1878 extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) @weak
1879 {
1880     import core.stdc.string;
1881     auto length = x.length;
1882     auto tinext = unqualify(ti.next);
1883     auto sizeelem = tinext.tsize;              // array element size
1884     _d_arrayappendcTX(ti, x, y.length);
1885     memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem);
1886 
1887     // do postblit
1888     __doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext);
1889     return x;
1890 }
1891 
1892 
1893 /**
1894  *
1895  */
1896 size_t newCapacity(size_t newlength, size_t size)
1897 {
1898     version (none)
1899     {
1900         size_t newcap = newlength * size;
1901     }
1902     else
1903     {
1904         /*
1905          * Better version by Dave Fladebo:
1906          * This uses an inverse logorithmic algorithm to pre-allocate a bit more
1907          * space for larger arrays.
1908          * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most
1909          * common cases, memory allocation is 1 to 1. The small overhead added
1910          * doesn't affect small array perf. (it's virtually the same as
1911          * current).
1912          * - Larger arrays have some space pre-allocated.
1913          * - As the arrays grow, the relative pre-allocated space shrinks.
1914          * - The logorithmic algorithm allocates relatively more space for
1915          * mid-size arrays, making it very fast for medium arrays (for
1916          * mid-to-large arrays, this turns out to be quite a bit faster than the
1917          * equivalent realloc() code in C, on Linux at least. Small arrays are
1918          * just as fast as GCC).
1919          * - Perhaps most importantly, overall memory usage and stress on the GC
1920          * is decreased significantly for demanding environments.
1921          */
1922         size_t newcap = newlength * size;
1923         size_t newext = 0;
1924 
1925         if (newcap > PAGESIZE)
1926         {
1927             //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0)));
1928 
1929             // redo above line using only integer math
1930 
1931             /*static int log2plus1(size_t c)
1932             {   int i;
1933 
1934                 if (c == 0)
1935                     i = -1;
1936                 else
1937                     for (i = 1; c >>= 1; i++)
1938                     {
1939                     }
1940                 return i;
1941             }*/
1942 
1943             /* The following setting for mult sets how much bigger
1944              * the new size will be over what is actually needed.
1945              * 100 means the same size, more means proportionally more.
1946              * More means faster but more memory consumption.
1947              */
1948             //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap));
1949             //long mult = 100 + (1000L * size) / log2plus1(newcap);
1950             import core.bitop;
1951             long mult = 100 + (1000L) / (bsr(newcap) + 1);
1952 
1953             // testing shows 1.02 for large arrays is about the point of diminishing return
1954             //
1955             // Commented out because the multipler will never be < 102.  In order for it to be < 2,
1956             // then 1000L / (bsr(x) + 1) must be > 2.  The highest bsr(x) + 1
1957             // could be is 65 (64th bit set), and 1000L / 64 is much larger
1958             // than 2.  We need 500 bit integers for 101 to be achieved :)
1959             /*if (mult < 102)
1960                 mult = 102;*/
1961             /*newext = cast(size_t)((newcap * mult) / 100);
1962             newext -= newext % size;*/
1963             // This version rounds up to the next element, and avoids using
1964             // mod.
1965             newext = cast(size_t)((newlength * mult + 99) / 100) * size;
1966             debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size);
1967         }
1968         newcap = newext > newcap ? newext : newcap;
1969         debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
1970     }
1971     return newcap;
1972 }
1973 
1974 
1975 /**************************************
1976  * Extend an array by n elements.
1977  * Caller must initialize those elements.
1978  */
1979 extern (C)
1980 byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak
1981 {
1982     import core.stdc.string;
1983     // This is a cut&paste job from _d_arrayappendT(). Should be refactored.
1984 
1985     // only optimize array append where ti is not a shared type
1986     auto tinext = unqualify(ti.next);
1987     auto sizeelem = tinext.tsize;              // array element size
1988     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
1989     auto bic = isshared ? null : __getBlkInfo(px.ptr);
1990     auto info = bic ? *bic : GC.query(px.ptr);
1991     auto length = px.length;
1992     auto newlength = length + n;
1993     auto newsize = newlength * sizeelem;
1994     auto size = length * sizeelem;
1995     size_t newcap = void; // for scratch space
1996 
1997     // calculate the extent of the array given the base.
1998     size_t offset = cast(void*)px.ptr - __arrayStart(info);
1999     if (info.base && (info.attr & BlkAttr.APPENDABLE))
2000     {
2001         if (info.size >= PAGESIZE)
2002         {
2003             // size of array is at the front of the block
2004             if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
2005             {
2006                 // check to see if it failed because there is not
2007                 // enough space
2008                 newcap = newCapacity(newlength, sizeelem);
2009                 if (*(cast(size_t*)info.base) == size + offset)
2010                 {
2011                     // not enough space, try extending
2012                     auto extendoffset = offset + LARGEPAD - info.size;
2013                     auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset);
2014                     if (u)
2015                     {
2016                         // extend worked, now try setting the length
2017                         // again.
2018                         info.size = u;
2019                         if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
2020                         {
2021                             if (!isshared)
2022                                 __insertBlkInfoCache(info, bic);
2023                             goto L1;
2024                         }
2025                     }
2026                 }
2027 
2028                 // couldn't do it, reallocate
2029                 goto L2;
2030             }
2031             else if (!isshared && !bic)
2032             {
2033                 __insertBlkInfoCache(info, null);
2034             }
2035         }
2036         else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset))
2037         {
2038             // could not resize in place
2039             newcap = newCapacity(newlength, sizeelem);
2040             goto L2;
2041         }
2042         else if (!isshared && !bic)
2043         {
2044             __insertBlkInfoCache(info, null);
2045         }
2046     }
2047     else
2048     {
2049         // not appendable or is null
2050         newcap = newCapacity(newlength, sizeelem);
2051         if (info.base)
2052         {
2053     L2:
2054             if (bic)
2055             {
2056                 // a chance that flags have changed since this was cached, we should fetch the most recent flags
2057                 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE;
2058             }
2059             info = __arrayAlloc(newcap, info, ti, tinext);
2060         }
2061         else
2062         {
2063             info = __arrayAlloc(newcap, ti, tinext);
2064         }
2065         __setArrayAllocLength(info, newsize, isshared, tinext);
2066         if (!isshared)
2067             __insertBlkInfoCache(info, bic);
2068         auto newdata = cast(byte *)__arrayStart(info);
2069         memcpy(newdata, px.ptr, length * sizeelem);
2070         // do postblit processing
2071         __doPostblit(newdata, length * sizeelem, tinext);
2072         (cast(void **)(&px))[1] = newdata;
2073     }
2074 
2075   L1:
2076     *cast(size_t *)&px = newlength;
2077     return px;
2078 }
2079 
2080 
2081 /**
2082  * Append dchar to char[]
2083  */
2084 extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak
2085 {
2086     // c could encode into from 1 to 4 characters
2087     char[4] buf = void;
2088     char[] appendthis; // passed to appendT
2089     if (c <= 0x7F)
2090     {
2091         buf.ptr[0] = cast(char)c;
2092         appendthis = buf[0..1];
2093     }
2094     else if (c <= 0x7FF)
2095     {
2096         buf.ptr[0] = cast(char)(0xC0 | (c >> 6));
2097         buf.ptr[1] = cast(char)(0x80 | (c & 0x3F));
2098         appendthis = buf[0..2];
2099     }
2100     else if (c <= 0xFFFF)
2101     {
2102         buf.ptr[0] = cast(char)(0xE0 | (c >> 12));
2103         buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
2104         buf.ptr[2] = cast(char)(0x80 | (c & 0x3F));
2105         appendthis = buf[0..3];
2106     }
2107     else if (c <= 0x10FFFF)
2108     {
2109         buf.ptr[0] = cast(char)(0xF0 | (c >> 18));
2110         buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
2111         buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
2112         buf.ptr[3] = cast(char)(0x80 | (c & 0x3F));
2113         appendthis = buf[0..4];
2114     }
2115     else
2116     {
2117         import core.exception : onUnicodeError;
2118         onUnicodeError("Invalid UTF-8 sequence", 0);      // invalid utf character
2119     }
2120 
2121     //
2122     // TODO: This always assumes the array type is shared, because we do not
2123     // get a typeinfo from the compiler.  Assuming shared is the safest option.
2124     // Once the compiler is fixed, the proper typeinfo should be forwarded.
2125     //
2126 
2127     // Hack because _d_arrayappendT takes `x` as a reference
2128     auto xx = cast(shared(char)[])x;
2129     object._d_arrayappendTImpl!(shared(char)[])._d_arrayappendT(xx, cast(shared(char)[])appendthis);
2130     x = cast(byte[])xx;
2131     return x;
2132 }
2133 
2134 unittest
2135 {
2136     import core.exception : UnicodeException;
2137 
2138     /* Using inline try {} catch {} blocks fails to catch the UnicodeException
2139      * thrown.
2140      * https://issues.dlang.org/show_bug.cgi?id=16799
2141      */
2142     static void assertThrown(T : Throwable = Exception, E)(lazy E expr, string msg)
2143     {
2144         try
2145             expr;
2146         catch (T e) {
2147             assert(e.msg == msg);
2148             return;
2149         }
2150     }
2151 
2152     static void f()
2153     {
2154         string ret;
2155         int i = -1;
2156         ret ~= i;
2157     }
2158 
2159     assertThrown!UnicodeException(f(), "Invalid UTF-8 sequence");
2160 }
2161 
2162 
2163 /**
2164  * Append dchar to wchar[]
2165  */
2166 extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak
2167 {
2168     // c could encode into from 1 to 2 w characters
2169     wchar[2] buf = void;
2170     wchar[] appendthis; // passed to appendT
2171     if (c <= 0xFFFF)
2172     {
2173         buf.ptr[0] = cast(wchar) c;
2174         appendthis = buf[0..1];
2175     }
2176     else
2177     {
2178         buf.ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
2179         buf.ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
2180         appendthis = buf[0..2];
2181     }
2182 
2183     //
2184     // TODO: This always assumes the array type is shared, because we do not
2185     // get a typeinfo from the compiler.  Assuming shared is the safest option.
2186     // Once the compiler is fixed, the proper typeinfo should be forwarded.
2187     //
2188 
2189     auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length];
2190     object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis);
2191     x = (cast(byte*)xx.ptr)[0 .. xx.length];
2192     return x;
2193 }
2194 
2195 
2196 /**
2197  *
2198  */
2199 extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak
2200 out (result)
2201 {
2202     auto tinext = unqualify(ti.next);
2203     auto sizeelem = tinext.tsize;              // array element size
2204     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);
2205     assert(result.length == x.length + y.length);
2206 
2207     // If a postblit is involved, the contents of result might rightly differ
2208     // from the bitwise concatenation of x and y.
2209     if (!hasPostblit(tinext))
2210     {
2211         for (size_t i = 0; i < x.length * sizeelem; i++)
2212             assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
2213         for (size_t i = 0; i < y.length * sizeelem; i++)
2214             assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]);
2215     }
2216 
2217     size_t cap = GC.sizeOf(result.ptr);
2218     assert(!cap || cap > result.length * sizeelem);
2219 }
2220 do
2221 {
2222     import core.stdc.string;
2223     version (none)
2224     {
2225         /* Cannot use this optimization because:
2226          *  char[] a, b;
2227          *  char c = 'a';
2228          *  b = a ~ c;
2229          *  c = 'b';
2230          * will change the contents of b.
2231          */
2232         if (!y.length)
2233             return x;
2234         if (!x.length)
2235             return y;
2236     }
2237 
2238     auto tinext = unqualify(ti.next);
2239     auto sizeelem = tinext.tsize;              // array element size
2240     debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem);
2241     size_t xlen = x.length * sizeelem;
2242     size_t ylen = y.length * sizeelem;
2243     size_t len  = xlen + ylen;
2244 
2245     if (!len)
2246         return null;
2247 
2248     auto info = __arrayAlloc(len, ti, tinext);
2249     byte* p = cast(byte*)__arrayStart(info);
2250     p[len] = 0; // guessing this is to optimize for null-terminated arrays?
2251     memcpy(p, x.ptr, xlen);
2252     memcpy(p + xlen, y.ptr, ylen);
2253     // do postblit processing
2254     __doPostblit(p, xlen + ylen, tinext);
2255 
2256     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
2257     __setArrayAllocLength(info, len, isshared, tinext);
2258     return p[0 .. x.length + y.length];
2259 }
2260 
2261 
2262 /**
2263  *
2264  */
2265 extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak
2266 {
2267     import core.stdc.string;
2268 
2269     size_t length;
2270     auto tinext = unqualify(ti.next);
2271     auto size = tinext.tsize;   // array element size
2272 
2273     foreach (b; arrs)
2274         length += b.length;
2275 
2276     if (!length)
2277         return null;
2278 
2279     auto allocsize = length * size;
2280     auto info = __arrayAlloc(allocsize, ti, tinext);
2281     auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
2282     __setArrayAllocLength(info, allocsize, isshared, tinext);
2283     void *a = __arrayStart (info);
2284 
2285     size_t j = 0;
2286     foreach (b; arrs)
2287     {
2288         if (b.length)
2289         {
2290             memcpy(a + j, b.ptr, b.length * size);
2291             j += b.length * size;
2292         }
2293     }
2294 
2295     // do postblit processing
2296     __doPostblit(a, j, tinext);
2297 
2298     return a[0..length];
2299 }
2300 
2301 
2302 /**
2303  * Allocate the array, rely on the caller to do the initialization of the array.
2304  */
2305 extern (C)
2306 void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak
2307 {
2308     auto tinext = unqualify(ti.next);
2309     auto sizeelem = tinext.tsize;              // array element size
2310     void* result;
2311 
2312     debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %d, length = %d)\n", sizeelem, length);
2313     if (length == 0 || sizeelem == 0)
2314         result = null;
2315     else
2316     {
2317         auto allocsize = length * sizeelem;
2318         auto info = __arrayAlloc(allocsize, ti, tinext);
2319         auto isshared = typeid(ti) is typeid(TypeInfo_Shared);
2320         __setArrayAllocLength(info, allocsize, isshared, tinext);
2321         result = __arrayStart(info);
2322     }
2323     return result;
2324 }
2325 
2326 
2327 unittest
2328 {
2329     int[] a;
2330     int[] b;
2331     int i;
2332 
2333     a = new int[3];
2334     a[0] = 1; a[1] = 2; a[2] = 3;
2335     b = a.dup;
2336     assert(b.length == 3);
2337     for (i = 0; i < 3; i++)
2338         assert(b[i] == i + 1);
2339 
2340     // test slice appending
2341     b = a[0..1];
2342     b ~= 4;
2343     for (i = 0; i < 3; i++)
2344         assert(a[i] == i + 1);
2345 
2346     // test reserving
2347     char[] arr = new char[4093];
2348     for (i = 0; i < arr.length; i++)
2349         arr[i] = cast(char)(i % 256);
2350 
2351     // note that these two commands used to cause corruption, which may not be
2352     // detected.
2353     arr.reserve(4094);
2354     auto arr2 = arr ~ "123";
2355     assert(arr2[0..arr.length] == arr);
2356     assert(arr2[arr.length..$] == "123");
2357 
2358     // test postblit on array concat, append, length, etc.
2359     static struct S
2360     {
2361         int x;
2362         int pad;
2363         this(this)
2364         {
2365             ++x;
2366         }
2367     }
2368     void testPostBlit(T)()
2369     {
2370         auto sarr = new T[1];
2371         debug(SENTINEL) {} else
2372             assert(sarr.capacity == 1);
2373 
2374         // length extend
2375         auto sarr2 = sarr;
2376         assert(sarr[0].x == 0);
2377         sarr2.length += 1;
2378         assert(sarr2[0].x == 1);
2379         assert(sarr[0].x == 0);
2380 
2381         // append
2382         T s;
2383         sarr2 = sarr;
2384         sarr2 ~= s;
2385         assert(sarr2[0].x == 1);
2386         assert(sarr2[1].x == 1);
2387         assert(sarr[0].x == 0);
2388         assert(s.x == 0);
2389 
2390         // concat
2391         sarr2 = sarr ~ sarr;
2392         assert(sarr2[0].x == 1);
2393         assert(sarr2[1].x == 1);
2394         assert(sarr[0].x == 0);
2395 
2396         // concat multiple (calls different method)
2397         sarr2 = sarr ~ sarr ~ sarr;
2398         assert(sarr2[0].x == 1);
2399         assert(sarr2[1].x == 1);
2400         assert(sarr2[2].x == 1);
2401         assert(sarr[0].x == 0);
2402 
2403         // reserve capacity
2404         sarr2 = sarr;
2405         sarr2.reserve(2);
2406         assert(sarr2[0].x == 1);
2407         assert(sarr[0].x == 0);
2408     }
2409     testPostBlit!(S)();
2410     testPostBlit!(const(S))();
2411 }
2412 
2413 // cannot define structs inside unit test block, or they become nested structs.
2414 version (CoreUnittest)
2415 {
2416     struct S1
2417     {
2418         int x = 5;
2419     }
2420     struct S2
2421     {
2422         int x;
2423         this(int x) {this.x = x;}
2424     }
2425     struct S3
2426     {
2427         int[4] x;
2428         this(int x)
2429         {this.x[] = x;}
2430     }
2431     struct S4
2432     {
2433         int *x;
2434     }
2435 
2436 }
2437 
2438 unittest
2439 {
2440     auto s1 = new S1;
2441     assert(s1.x == 5);
2442     assert(GC.getAttr(s1) == BlkAttr.NO_SCAN);
2443 
2444     auto s2 = new S2(3);
2445     assert(s2.x == 3);
2446     assert(GC.getAttr(s2) == BlkAttr.NO_SCAN);
2447 
2448     auto s3 = new S3(1);
2449     assert(s3.x == [1,1,1,1]);
2450     assert(GC.getAttr(s3) == BlkAttr.NO_SCAN);
2451     debug(SENTINEL) {} else
2452         assert(GC.sizeOf(s3) == 16);
2453 
2454     auto s4 = new S4;
2455     assert(s4.x == null);
2456     assert(GC.getAttr(s4) == 0);
2457 }
2458 
2459 unittest
2460 {
2461     // Bugzilla 3454 - Inconsistent flag setting in GC.realloc()
2462     static void test(size_t multiplier)
2463     {
2464         auto p = GC.malloc(8 * multiplier, 0);
2465         assert(GC.getAttr(p) == 0);
2466 
2467         // no move, set attr
2468         p = GC.realloc(p, 8 * multiplier + 5, BlkAttr.NO_SCAN);
2469         assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
2470 
2471         // shrink, copy attr
2472         p = GC.realloc(p, 2 * multiplier, 0);
2473         assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
2474 
2475         // extend, copy attr
2476         p = GC.realloc(p, 8 * multiplier, 0);
2477         assert(GC.getAttr(p) == BlkAttr.NO_SCAN);
2478     }
2479     test(16);
2480     test(1024 * 1024);
2481 }
2482 
2483 unittest
2484 {
2485     import core.exception;
2486     try
2487     {
2488         size_t x = size_t.max;
2489         byte[] big_buf = new byte[x];
2490     }
2491     catch (OutOfMemoryError)
2492     {
2493     }
2494 }
2495 
2496 unittest
2497 {
2498     // bugzilla 13854
2499     auto arr = new ubyte[PAGESIZE]; // ensure page size
2500     auto info1 = GC.query(arr.ptr);
2501     assert(info1.base !is arr.ptr); // offset is required for page size or larger
2502 
2503     auto arr2 = arr[0..1];
2504     assert(arr2.capacity == 0); // cannot append
2505     arr2 ~= 0; // add a byte
2506     assert(arr2.ptr !is arr.ptr); // reallocated
2507     auto info2 = GC.query(arr2.ptr);
2508     assert(info2.base is arr2.ptr); // no offset, the capacity is small.
2509 
2510     // do the same via setting length
2511     arr2 = arr[0..1];
2512     assert(arr2.capacity == 0);
2513     arr2.length += 1;
2514     assert(arr2.ptr !is arr.ptr); // reallocated
2515     info2 = GC.query(arr2.ptr);
2516     assert(info2.base is arr2.ptr); // no offset, the capacity is small.
2517 
2518     // do the same for char[] since we need a type with an initializer to test certain runtime functions
2519     auto carr = new char[PAGESIZE];
2520     info1 = GC.query(carr.ptr);
2521     assert(info1.base !is carr.ptr); // offset is required for page size or larger
2522 
2523     auto carr2 = carr[0..1];
2524     assert(carr2.capacity == 0); // cannot append
2525     carr2 ~= 0; // add a byte
2526     assert(carr2.ptr !is carr.ptr); // reallocated
2527     info2 = GC.query(carr2.ptr);
2528     assert(info2.base is carr2.ptr); // no offset, the capacity is small.
2529 
2530     // do the same via setting length
2531     carr2 = carr[0..1];
2532     assert(carr2.capacity == 0);
2533     carr2.length += 1;
2534     assert(carr2.ptr !is carr.ptr); // reallocated
2535     info2 = GC.query(carr2.ptr);
2536     assert(info2.base is carr2.ptr); // no offset, the capacity is small.
2537 }
2538 
2539 unittest
2540 {
2541     // bugzilla 13878
2542     auto arr = new ubyte[1];
2543     auto info = GC.query(arr.ptr);
2544     assert(info.attr & BlkAttr.NO_SCAN); // should be NO_SCAN
2545     arr ~= 0; // ensure array is inserted into cache
2546     debug(SENTINEL) {} else
2547         assert(arr.ptr is info.base);
2548     GC.clrAttr(arr.ptr, BlkAttr.NO_SCAN); // remove the attribute
2549     auto arr2 = arr[0..1];
2550     assert(arr2.capacity == 0); // cannot append
2551     arr2 ~= 0;
2552     assert(arr2.ptr !is arr.ptr);
2553     info = GC.query(arr2.ptr);
2554     assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks
2555 
2556     // do the same via setting length
2557     arr = new ubyte[1];
2558     arr ~= 0; // ensure array is inserted into cache
2559     GC.clrAttr(arr.ptr, BlkAttr.NO_SCAN); // remove the attribute
2560     arr2 = arr[0..1];
2561     assert(arr2.capacity == 0);
2562     arr2.length += 1;
2563     assert(arr2.ptr !is arr.ptr); // reallocated
2564     info = GC.query(arr2.ptr);
2565     assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks
2566 
2567     // do the same for char[] since we need a type with an initializer to test certain runtime functions
2568     auto carr = new char[1];
2569     info = GC.query(carr.ptr);
2570     assert(info.attr & BlkAttr.NO_SCAN); // should be NO_SCAN
2571     carr ~= 0; // ensure array is inserted into cache
2572     debug(SENTINEL) {} else
2573         assert(carr.ptr is info.base);
2574     GC.clrAttr(carr.ptr, BlkAttr.NO_SCAN); // remove the attribute
2575     auto carr2 = carr[0..1];
2576     assert(carr2.capacity == 0); // cannot append
2577     carr2 ~= 0;
2578     assert(carr2.ptr !is carr.ptr);
2579     info = GC.query(carr2.ptr);
2580     assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks
2581 
2582     // do the same via setting length
2583     carr = new char[1];
2584     carr ~= 0; // ensure array is inserted into cache
2585     GC.clrAttr(carr.ptr, BlkAttr.NO_SCAN); // remove the attribute
2586     carr2 = carr[0..1];
2587     assert(carr2.capacity == 0);
2588     carr2.length += 1;
2589     assert(carr2.ptr !is carr.ptr); // reallocated
2590     info = GC.query(carr2.ptr);
2591     assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks
2592 }
2593 
2594 // test struct finalizers
2595 debug(SENTINEL) {} else
2596 deprecated unittest
2597 {
2598     __gshared int dtorCount;
2599     static struct S1
2600     {
2601         int x;
2602 
2603         ~this()
2604         {
2605             dtorCount++;
2606         }
2607     }
2608 
2609     dtorCount = 0;
2610     S1* s1 = new S1;
2611     _d_delstruct(cast(void**)&s1, typeid(typeof(*s1))); // delete s1;
2612     assert(dtorCount == 1);
2613 
2614     dtorCount = 0;
2615     S1[] arr1 = new S1[7];
2616     _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1;
2617     assert(dtorCount == 7);
2618 
2619     dtorCount = 0;
2620     S1* s2 = new S1;
2621     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2622     assert(dtorCount == 1);
2623     GC.free(s2);
2624 
2625     dtorCount = 0;
2626     const(S1)* s3 = new const(S1);
2627     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2628     assert(dtorCount == 1);
2629     GC.free(cast(void*)s3);
2630 
2631     dtorCount = 0;
2632     shared(S1)* s4 = new shared(S1);
2633     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2634     assert(dtorCount == 1);
2635     GC.free(cast(void*)s4);
2636 
2637     dtorCount = 0;
2638     const(S1)[] carr1 = new const(S1)[5];
2639     BlkInfo blkinf1 = GC.query(carr1.ptr);
2640     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2641     assert(dtorCount == 5);
2642     GC.free(blkinf1.base);
2643 
2644     dtorCount = 0;
2645     S1[] arr2 = new S1[10];
2646     arr2.length = 6;
2647     arr2.assumeSafeAppend;
2648     assert(dtorCount == 4); // destructors run explicitely?
2649 
2650     dtorCount = 0;
2651     BlkInfo blkinf = GC.query(arr2.ptr);
2652     GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2653     assert(dtorCount == 6);
2654     GC.free(blkinf.base);
2655 
2656     // associative arrays
2657     import rt.aaA : entryDtor;
2658     // throw away all existing AA entries with dtor
2659     GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
2660 
2661     S1[int] aa1;
2662     aa1[0] = S1(0);
2663     aa1[1] = S1(1);
2664     dtorCount = 0;
2665     aa1 = null;
2666     GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
2667     assert(dtorCount == 2);
2668 
2669     int[S1] aa2;
2670     aa2[S1(0)] = 0;
2671     aa2[S1(1)] = 1;
2672     aa2[S1(2)] = 2;
2673     dtorCount = 0;
2674     aa2 = null;
2675     GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
2676     assert(dtorCount == 3);
2677 
2678     S1[2][int] aa3;
2679     aa3[0] = [S1(0),S1(2)];
2680     aa3[1] = [S1(1),S1(3)];
2681     dtorCount = 0;
2682     aa3 = null;
2683     GC.runFinalizers((cast(char*)(&entryDtor))[0..1]);
2684     assert(dtorCount == 4);
2685 }
2686 
2687 // test struct dtor handling not causing false pointers
2688 unittest
2689 {
2690     // for 64-bit, allocate a struct of size 40
2691     static struct S
2692     {
2693         size_t[4] data;
2694         S* ptr4;
2695     }
2696     auto p1 = new S;
2697     auto p2 = new S;
2698     p2.ptr4 = p1;
2699 
2700     // a struct with a dtor with size 32, but the dtor will cause
2701     //  allocation to be larger by a pointer
2702     static struct A
2703     {
2704         size_t[3] data;
2705         S* ptr3;
2706 
2707         ~this() {}
2708     }
2709 
2710     GC.free(p2);
2711     auto a = new A; // reuse same memory
2712     if (cast(void*)a is cast(void*)p2) // reusage not guaranteed
2713     {
2714         auto ptr = cast(S**)(a + 1);
2715         assert(*ptr != p1); // still same data as p2.ptr4?
2716     }
2717 
2718     // small array
2719     static struct SArr
2720     {
2721         void*[10] data;
2722     }
2723     auto arr1 = new SArr;
2724     arr1.data[] = p1;
2725     GC.free(arr1);
2726 
2727     // allocates 2*A.sizeof + (void*).sizeof (TypeInfo) + 1 (array length)
2728     auto arr2 = new A[2];
2729     if (cast(void*)arr1 is cast(void*)arr2.ptr) // reusage not guaranteed
2730     {
2731         auto ptr = cast(S**)(arr2.ptr + 2);
2732         assert(*ptr != p1); // still same data as p2.ptr4?
2733     }
2734 
2735     // large array
2736     static struct LArr
2737     {
2738         void*[1023] data;
2739     }
2740     auto larr1 = new LArr;
2741     larr1.data[] = p1;
2742     GC.free(larr1);
2743 
2744     auto larr2 = new S[255];
2745     if (cast(void*)larr1 is cast(void*)larr2.ptr - LARGEPREFIX) // reusage not guaranteed
2746     {
2747         auto ptr = cast(S**)larr1;
2748         assert(ptr[0] != p1); // 16 bytes array header
2749         assert(ptr[1] != p1);
2750         version (D_LP64) {} else
2751         {
2752             assert(ptr[2] != p1);
2753             assert(ptr[3] != p1);
2754         }
2755     }
2756 }
2757 
2758 // test class finalizers exception handling
2759 unittest
2760 {
2761     bool test(E)()
2762     {
2763         import core.exception;
2764         static class C1
2765         {
2766             E exc;
2767             this(E exc) { this.exc = exc; }
2768             ~this() { throw exc; }
2769         }
2770 
2771         bool caught = false;
2772         C1 c = new C1(new E("test onFinalizeError"));
2773         try
2774         {
2775             GC.runFinalizers((cast(uint*)&C1.__dtor)[0..1]);
2776         }
2777         catch (FinalizeError err)
2778         {
2779             caught = true;
2780         }
2781         catch (E)
2782         {
2783         }
2784         GC.free(cast(void*)c);
2785         return caught;
2786     }
2787 
2788     assert( test!Exception);
2789     import core.exception : InvalidMemoryOperationError;
2790     assert(!test!InvalidMemoryOperationError);
2791 }
2792 
2793 // test struct finalizers exception handling
2794 debug(SENTINEL) {} else
2795 unittest
2796 {
2797     bool test(E)()
2798     {
2799         import core.exception;
2800         static struct S1
2801         {
2802             E exc;
2803             ~this() { throw exc; }
2804         }
2805 
2806         bool caught = false;
2807         S1* s = new S1(new E("test onFinalizeError"));
2808         try
2809         {
2810             GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]);
2811         }
2812         catch (FinalizeError err)
2813         {
2814             caught = true;
2815         }
2816         catch (E)
2817         {
2818         }
2819         GC.free(s);
2820         return caught;
2821     }
2822 
2823     assert( test!Exception);
2824     import core.exception : InvalidMemoryOperationError;
2825     assert(!test!InvalidMemoryOperationError);
2826 }
2827 
2828 // test bug 14126
2829 unittest
2830 {
2831     static struct S
2832     {
2833         S* thisptr;
2834         ~this() { assert(&this == thisptr); thisptr = null;}
2835     }
2836 
2837     S[] test14126 = new S[2048]; // make sure we allocate at least a PAGE
2838     foreach (ref s; test14126)
2839     {
2840         s.thisptr = &s;
2841     }
2842 }
2843