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