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