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