Mercurial > projects > ldc
view runtime/internal/lifetime.d @ 942:55559220ad72
Reverted back to the LLVM_InlineAsm_X86 versions, a few more things still needs to be sorted out first.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Wed, 04 Feb 2009 20:47:27 +0100 |
parents | 16264a3973bf |
children | c4c6e5e9c23e |
line wrap: on
line source
/** * This module contains all functions related to an object's lifetime: * allocation, resizing, deallocation, and finalization. * * Copyright: Copyright (C) 2004-2007 Digital Mars, www.digitalmars.com. * All rights reserved. * License: * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, in both source and binary form, subject to the following * restrictions: * * o The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * o Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * o This notice may not be removed or altered from any source * distribution. * Authors: Walter Bright, Sean Kelly, Tomas Lindquist Olsen */ module lifetime; //debug=PRINTF; //debug=PRINTF2; private { import tango.stdc.stdlib; import tango.stdc.string; import tango.stdc.stdarg; debug(PRINTF) import tango.stdc.stdio; else debug(PRINTF2) import tango.stdc.stdio; } private { enum BlkAttr : uint { FINALIZE = 0b0000_0001, NO_SCAN = 0b0000_0010, NO_MOVE = 0b0000_0100, ALL_BITS = 0b1111_1111 } struct BlkInfo { void* base; size_t size; uint attr; } extern (C) uint gc_getAttr( void* p ); extern (C) uint gc_setAttr( void* p, uint a ); extern (C) uint gc_clrAttr( void* p, uint a ); extern (C) void* gc_malloc( size_t sz, uint ba = 0 ); extern (C) void* gc_calloc( size_t sz, uint ba = 0 ); extern (C) size_t gc_extend( void* p, size_t mx, size_t sz ); extern (C) void gc_free( void* p ); extern (C) void* gc_addrOf( void* p ); extern (C) size_t gc_sizeOf( void* p ); extern (C) BlkInfo gc_query( void* p ); extern (C) bool onCollectResource( Object o ); extern (C) void onFinalizeError( ClassInfo c, Exception e ); extern (C) void onOutOfMemoryError(); extern (C) void _d_monitordelete(Object h, bool det = true); enum { PAGESIZE = 4096 } alias bool function(Object) CollectHandler; CollectHandler collectHandler = null; } /** * */ extern (C) Object _d_allocclass(ClassInfo ci) { void* p; debug(PRINTF2) printf("_d_allocclass(ci = %p, %s)\n", ci, cast(char *)ci.name.ptr); /+ if (ci.flags & 1) // if COM object { /* COM objects are not garbage collected, they are reference counted * using AddRef() and Release(). They get free'd by C's free() * function called by Release() when Release()'s reference count goes * to zero. */ p = tango.stdc.stdlib.malloc(ci.init.length); if (!p) onOutOfMemoryError(); } else +/ { p = gc_malloc(ci.init.length, BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0)); debug(PRINTF2) printf(" p = %p\n", p); } debug(PRINTF2) { printf("p = %p\n", p); printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init.ptr, ci.init.length); printf("vptr = %p\n", *cast(void**) ci.init.ptr); printf("vtbl[0] = %p\n", (*cast(void***) ci.init.ptr)[0]); printf("vtbl[1] = %p\n", (*cast(void***) ci.init.ptr)[1]); printf("init[0] = %p\n", (cast(uint**) ci.init.ptr)[0]); printf("init[1] = %p\n", (cast(uint**) ci.init.ptr)[1]); printf("init[2] = %p\n", (cast(uint**) ci.init.ptr)[2]); printf("init[3] = %p\n", (cast(uint**) ci.init.ptr)[3]); printf("init[4] = %p\n", (cast(uint**) ci.init.ptr)[4]); } // initialize it // ldc does this inline //(cast(byte*) p)[0 .. ci.init.length] = ci.init[]; debug(PRINTF) printf("initialization done\n"); return cast(Object) p; } /** * */ extern (C) void _d_delinterface(void* p) { if (p) { Interface* pi = **cast(Interface ***)p; Object o = cast(Object)(p - pi.offset); _d_delclass(o); //*p = null; } } // used for deletion private extern (D) alias void function(Object) fp_t; /** * */ extern (C) void _d_delclass(Object p) { if (p) { debug(PRINTF) printf("_d_delclass(%p)\n", p); ClassInfo **pc = cast(ClassInfo **)p; if (*pc) { ClassInfo c = **pc; rt_finalize(cast(void*) p); if (c.deallocator) { fp_t fp = cast(fp_t)c.deallocator; (*fp)(p); // call deallocator //*p = null; return; } } else { rt_finalize(cast(void*) p); } gc_free(cast(void*) p); //*p = null; } } /+ /** * */ struct Array { size_t length; void* data; } +/ /** * Allocate a new array of length elements. * ti is the type of the resulting array, or pointer to element. * The resulting array is initialized to 0 */ extern (C) void* _d_newarrayT(TypeInfo ti, size_t length) { void* p; auto size = ti.next.tsize(); // array element size debug(PRINTF) printf("_d_newarrayT(length = %u, size = %d)\n", length, size); if (length == 0 || size == 0) return null; version (D_InlineAsm_X86) { asm { mov EAX,size ; mul EAX,length ; mov size,EAX ; jc Loverflow ; } } else size *= length; p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); debug(PRINTF) printf(" p = %p\n", p); memset(p, 0, size); return p; Loverflow: onOutOfMemoryError(); return null; } /** * As _d_newarrayT, but * for when the array has a non-zero initializer. */ extern (C) void* _d_newarrayiT(TypeInfo ti, size_t length) { void* result; auto size = ti.next.tsize(); // array element size debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size); if (length == 0 || size == 0) result = null; else { auto initializer = ti.next.init(); auto isize = initializer.length; auto q = initializer.ptr; version (D_InlineAsm_X86) { asm { mov EAX,size ; mul EAX,length ; mov size,EAX ; jc Loverflow ; } } else size *= length; auto p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); debug(PRINTF) printf(" p = %p\n", p); if (isize == 1) memset(p, *cast(ubyte*)q, size); else if (isize == int.sizeof) { int init = *cast(int*)q; size /= int.sizeof; for (size_t u = 0; u < size; u++) { (cast(int*)p)[u] = init; } } else { for (size_t u = 0; u < size; u += isize) { memcpy(p + u, q, isize); } } result = p; } return result; Loverflow: onOutOfMemoryError(); return null; } /** * As _d_newarrayT, but without initialization */ extern (C) void* _d_newarrayvT(TypeInfo ti, size_t length) { void* p; auto size = ti.next.tsize(); // array element size debug(PRINTF) printf("_d_newarrayvT(length = %u, size = %d)\n", length, size); if (length == 0 || size == 0) return null; version (D_InlineAsm_X86) { asm { mov EAX,size ; mul EAX,length ; mov size,EAX ; jc Loverflow ; } } else size *= length; p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); debug(PRINTF) printf(" p = %p\n", p); return p; Loverflow: onOutOfMemoryError(); return null; } /** * Allocate a new array of arrays of arrays of arrays ... * ti is the type of the resulting array. * ndims is the number of nested arrays. * dims it the array of dimensions, its size is ndims. * The resulting array is initialized to 0 */ extern (C) void* _d_newarraymT(TypeInfo ti, int ndims, size_t* dims) { void* result; debug(PRINTF) printf("_d_newarraymT(ndims = %d)\n", ndims); if (ndims == 0) result = null; else { static void[] foo(TypeInfo ti, size_t* pdim, int ndims) { size_t dim = *pdim; void[] p; debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims); if (ndims == 1) { auto r = _d_newarrayT(ti, dim); return r[0 .. dim]; } else { p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; for (int i = 0; i < dim; i++) { (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); } } return p; } result = foo(ti, dims, ndims).ptr; debug(PRINTF) printf("result = %p\n", result); version (none) { for (int i = 0; i < ndims; i++) { printf("index %d: %d\n", i, *dims++); } } } return result; } /** * As _d_newarraymT, but * for when the array has a non-zero initializer. */ extern (C) void* _d_newarraymiT(TypeInfo ti, int ndims, size_t* dims) { void* result; debug(PRINTF) printf("_d_newarraymiT(ndims = %d)\n", ndims); if (ndims == 0) result = null; else { static void[] foo(TypeInfo ti, size_t* pdim, int ndims) { size_t dim = *pdim; void[] p; if (ndims == 1) { auto r = _d_newarrayiT(ti, dim); p = r[0 .. dim]; } else { p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; for (int i = 0; i < dim; i++) { (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); } } return p; } result = foo(ti, dims, ndims).ptr; debug(PRINTF) printf("result = %p\n", result); version (none) { for (int i = 0; i < ndims; i++) { printf("index %d: %d\n", i, *dims++); printf("init = %d\n", *dims++); } } } return result; } /** * As _d_newarraymT, but without initialization */ extern (C) void* _d_newarraymvT(TypeInfo ti, int ndims, size_t* dims) { void* result; debug(PRINTF) printf("_d_newarraymvT(ndims = %d)\n", ndims); if (ndims == 0) result = null; else { static void[] foo(TypeInfo ti, size_t* pdim, int ndims) { size_t dim = *pdim; void[] p; debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims); if (ndims == 1) { auto r = _d_newarrayvT(ti, dim); return r[0 .. dim]; } else { p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; for (int i = 0; i < dim; i++) { (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); } } return p; } result = foo(ti, dims, ndims).ptr; debug(PRINTF) printf("result = %p\n", result); version (none) { for (int i = 0; i < ndims; i++) { printf("index %d: %d\n", i, *dims++); } } } return result; } /+ /** * */ void* _d_allocmemory(size_t nbytes) { return gc_malloc(nbytes); } +/ /** * for allocating a single POD value */ extern (C) void* _d_allocmemoryT(TypeInfo ti) { return gc_malloc(ti.tsize(), !(ti.flags() & 1) ? BlkAttr.NO_SCAN : 0); } /** * */ extern (C) void _d_delarray(size_t plength, void* pdata) { // if (p) // { // This assert on array consistency may fail with casts or in unions. // This function still does something sensible even if plength && !pdata. // assert(!plength || pdata); if (pdata) gc_free(pdata); // p.data = null; // p.length = 0; // } } /** * */ extern (C) void _d_delmemory(void* p) { if (p) { gc_free(p); //*p = null; } } /** * */ extern (C) void _d_callinterfacefinalizer(void *p) { if (p) { Interface *pi = **cast(Interface ***)p; Object o = cast(Object)(p - pi.offset); rt_finalize(cast(void*)o); } } /** * */ extern (C) void _d_callfinalizer(void* p) { rt_finalize( p ); } /** * */ extern (C) void rt_setCollectHandler(CollectHandler h) { collectHandler = h; } /** * */ extern (C) void rt_finalize(void* p, bool det = true) { debug(PRINTF) printf("rt_finalize(p = %p)\n", p); if (p) // not necessary if called from gc { ClassInfo** pc = cast(ClassInfo**)p; if (*pc) { ClassInfo c = **pc; try { if (det || collectHandler is null || collectHandler(cast(Object)p)) { do { if (c.destructor) { debug(PRINTF) printf("calling dtor of %.*s\n", c.name.length, c.name.ptr); fp_t fp = cast(fp_t)c.destructor; (*fp)(cast(Object)p); // call destructor } c = c.base; } while (c); } if ((cast(void**)p)[1]) // if monitor is not null _d_monitordelete(cast(Object)p, det); } catch (Exception e) { onFinalizeError(**pc, e); } finally { *pc = null; // zero vptr } } } } /** * Resize dynamic arrays with 0 initializers. */ extern (C) byte* _d_arraysetlengthT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata) in { assert(ti); // This assert on array consistency may fail with casts or in unions. // This function still does something sensible even if plength && !pdata. // assert(!plength || pdata); } body { byte* newdata; size_t sizeelem = ti.next.tsize(); debug(PRINTF) { printf("_d_arraysetlengthT(sizeelem = %d, newlength = %d)\n", sizeelem, newlength); printf("\tp.data = %p, p.length = %d\n", pdata, plength); } if (newlength) { version (D_InlineAsm_X86) { size_t newsize = void; asm { mov EAX, newlength; mul EAX, sizeelem; mov newsize, EAX; jc Loverflow; } } else { size_t newsize = sizeelem * newlength; if (newsize / newlength != sizeelem) goto Loverflow; } debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); if (pdata) { newdata = pdata; if (newlength > plength) { size_t size = plength * sizeelem; auto info = gc_query(pdata); if (info.size <= newsize || info.base != pdata) { if (info.size >= PAGESIZE && info.base == pdata) { // Try to extend in-place auto u = gc_extend(pdata, (newsize + 1) - info.size, (newsize + 1) - info.size); if (u) { goto L1; } } newdata = cast(byte *)gc_malloc(newsize + 1, info.attr); newdata[0 .. size] = pdata[0 .. size]; } L1: newdata[size .. newsize] = 0; } } else { newdata = cast(byte *)gc_calloc(newsize + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); } } else { newdata = pdata; } return newdata; Loverflow: onOutOfMemoryError(); return null; } /** * Resize arrays for non-zero initializers. * p pointer to array lvalue to be updated * newlength new .length property of array * sizeelem size of each element of array * initsize size of initializer * ... initializer */ extern (C) byte* _d_arraysetlengthiT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata) in { // This assert on array consistency may fail with casts or in unions. // This function still does something sensible even if plength && !pdata. // assert(!plength || pdata); } body { byte* newdata; TypeInfo tinext = ti.next; size_t sizeelem = tinext.tsize(); void[] initializer = tinext.init(); size_t initsize = initializer.length; assert(sizeelem); assert(initsize); assert(initsize <= sizeelem); assert((sizeelem / initsize) * initsize == sizeelem); debug(PRINTF) { printf("_d_arraysetlengthiT(sizeelem = %d, newlength = %d, initsize = %d)\n", sizeelem, newlength, initsize); printf("\tp.data = %p, p.length = %d\n", pdata, plength); } if (newlength) { version (D_InlineAsm_X86) { size_t newsize = void; asm { mov EAX,newlength ; mul EAX,sizeelem ; mov newsize,EAX ; jc Loverflow ; } } else { size_t newsize = sizeelem * newlength; if (newsize / newlength != sizeelem) goto Loverflow; } debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); size_t size = plength * sizeelem; if (pdata) { newdata = pdata; if (newlength > plength) { auto info = gc_query(pdata); if (info.size <= newsize || info.base != pdata) { if (info.size >= PAGESIZE && info.base == pdata) { // Try to extend in-place auto u = gc_extend(pdata, (newsize + 1) - info.size, (newsize + 1) - info.size); if (u) { goto L1; } } newdata = cast(byte *)gc_malloc(newsize + 1, info.attr); newdata[0 .. size] = pdata[0 .. size]; L1: ; } } } else { newdata = cast(byte *)gc_malloc(newsize + 1, !(tinext.flags() & 1) ? BlkAttr.NO_SCAN : 0); } auto q = initializer.ptr; // pointer to initializer if (newsize > size) { if (initsize == 1) { debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q); newdata[size .. newsize] = *(cast(byte*)q); } else { for (size_t u = size; u < newsize; u += initsize) { memcpy(newdata + u, q, initsize); } } } } else { newdata = pdata; } return newdata; Loverflow: onOutOfMemoryError(); return null; } /+ /** * Append y[] to array x[]. * size is size of each array element. */ extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y) { auto sizeelem = ti.next.tsize(); // array element size auto info = gc_query(px.data); auto length = px.length; auto newlength = length + y.length; auto newsize = newlength * sizeelem; if (info.size < newsize || info.base != px.data) { byte* newdata; if (info.size >= PAGESIZE && info.base == px.data) { // Try to extend in-place auto u = gc_extend(px.data, (newsize + 1) - info.size, (newsize + 1) - info.size); if (u) { goto L1; } } newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, info.attr); memcpy(newdata, px.data, length * sizeelem); px.data = newdata; } L1: px.length = newlength; memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem); return *cast(long*)px; } /** * */ size_t newCapacity(size_t newlength, size_t size) { version(none) { size_t newcap = newlength * size; } else { /* * Better version by Dave Fladebo: * This uses an inverse logorithmic algorithm to pre-allocate a bit more * space for larger arrays. * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most * common cases, memory allocation is 1 to 1. The small overhead added * doesn't affect small array perf. (it's virtually the same as * current). * - Larger arrays have some space pre-allocated. * - As the arrays grow, the relative pre-allocated space shrinks. * - The logorithmic algorithm allocates relatively more space for * mid-size arrays, making it very fast for medium arrays (for * mid-to-large arrays, this turns out to be quite a bit faster than the * equivalent realloc() code in C, on Linux at least. Small arrays are * just as fast as GCC). * - Perhaps most importantly, overall memory usage and stress on the GC * is decreased significantly for demanding environments. */ size_t newcap = newlength * size; size_t newext = 0; if (newcap > PAGESIZE) { //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0))); // redo above line using only integer math static int log2plus1(size_t c) { int i; if (c == 0) i = -1; else for (i = 1; c >>= 1; i++) { } return i; } /* The following setting for mult sets how much bigger * the new size will be over what is actually needed. * 100 means the same size, more means proportionally more. * More means faster but more memory consumption. */ //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap)); long mult = 100 + (1000L * size) / log2plus1(newcap); // testing shows 1.02 for large arrays is about the point of diminishing return if (mult < 102) mult = 102; newext = cast(size_t)((newcap * mult) / 100); newext -= newext % size; debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size); } newcap = newext > newcap ? newext : newcap; debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size); } return newcap; } /** * */ extern (C) byte[] _d_arrayappendcT(TypeInfo ti, inout byte[] x, ...) { auto sizeelem = ti.next.tsize(); // array element size auto info = gc_query(x.ptr); auto length = x.length; auto newlength = length + 1; auto newsize = newlength * sizeelem; assert(info.size == 0 || length * sizeelem <= info.size); debug(PRINTF) printf("_d_arrayappendcT(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size); if (info.size <= newsize || info.base != x.ptr) { byte* newdata; if (info.size >= PAGESIZE && info.base == x.ptr) { // Try to extend in-place auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size); if (u) { goto L1; } } debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size); auto newcap = newCapacity(newlength, sizeelem); assert(newcap >= newlength * sizeelem); newdata = cast(byte *)gc_malloc(newcap + 1, info.attr); memcpy(newdata, x.ptr, length * sizeelem); (cast(void**)(&x))[1] = newdata; } L1: byte *argp = cast(byte *)(&ti + 2); *cast(size_t *)&x = newlength; x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem]; assert((cast(size_t)x.ptr & 15) == 0); assert(gc_sizeOf(x.ptr) > x.length * sizeelem); return x; } /** * */ extern (C) byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y) out (result) { auto sizeelem = ti.next.tsize(); // array element size 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); assert(result.length == x.length + y.length); for (size_t i = 0; i < x.length * sizeelem; i++) assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); for (size_t i = 0; i < y.length * sizeelem; i++) assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); size_t cap = gc_sizeOf(result.ptr); assert(!cap || cap > result.length * sizeelem); } body { version (none) { /* Cannot use this optimization because: * char[] a, b; * char c = 'a'; * b = a ~ c; * c = 'b'; * will change the contents of b. */ if (!y.length) return x; if (!x.length) return y; } debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr); auto sizeelem = ti.next.tsize(); // array element size debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); size_t xlen = x.length * sizeelem; size_t ylen = y.length * sizeelem; size_t len = xlen + ylen; if (!len) return null; byte* p = cast(byte*)gc_malloc(len + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); memcpy(p, x.ptr, xlen); memcpy(p + xlen, y.ptr, ylen); p[len] = 0; return p[0 .. x.length + y.length]; } /** * */ extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...) { void* a; size_t length; byte[]* p; uint i; byte[] b; auto size = ti.next.tsize(); // array element size p = cast(byte[]*)(&n + 1); for (i = 0; i < n; i++) { b = *p++; length += b.length; } if (!length) return null; a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); p = cast(byte[]*)(&n + 1); uint j = 0; for (i = 0; i < n; i++) { b = *p++; if (b.length) { memcpy(a + j, b.ptr, b.length * size); j += b.length * size; } } byte[] result; *cast(int *)&result = length; // jam length (cast(void **)&result)[1] = a; // jam ptr return result; } /** * */ extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...) { auto sizeelem = ti.next.tsize(); // array element size void* result; debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length); if (length == 0 || sizeelem == 0) result = null; else { result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); va_list q; va_start!(size_t)(q, length); size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1); if (stacksize == sizeelem) { memcpy(result, q, length * sizeelem); } else { for (size_t i = 0; i < length; i++) { memcpy(result + i * sizeelem, q, sizeelem); q += stacksize; } } va_end(q); } return result; } +/ /** * Support for array.dup property. * The actual type is painted on the return value by the frontend * Given length is number of elements * Returned length is number of elements */ /** * */ extern (C) void[] _adDupT(TypeInfo ti, void[] a) out (result) { auto sizeelem = ti.next.tsize(); // array element size assert(memcmp(result.ptr, a.ptr, a.length * sizeelem) == 0); } body { void* ptr; if (a.length) { auto sizeelem = ti.next.tsize(); // array element size auto size = a.length * sizeelem; ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); memcpy(ptr, a.ptr, size); } return ptr[0 .. a.length]; } unittest { int[] a; int[] b; int i; a = new int[3]; a[0] = 1; a[1] = 2; a[2] = 3; b = a.dup; assert(b.length == 3); for (i = 0; i < 3; i++) assert(b[i] == i + 1); }