mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
775 lines
16 KiB
D
775 lines
16 KiB
D
|
|
/*
|
|
* Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com
|
|
* Written by Walter Bright
|
|
*
|
|
* 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, 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.
|
|
*/
|
|
|
|
|
|
// Storage allocation
|
|
|
|
module std.gc;
|
|
|
|
//debug = PRINTF;
|
|
|
|
import std.c.stdarg;
|
|
import std.c.stdlib;
|
|
import std.c.string;
|
|
import gcx;
|
|
import std.outofmemory;
|
|
import gcstats;
|
|
import std.thread;
|
|
|
|
version=GCCLASS;
|
|
|
|
version (GCCLASS)
|
|
alias GC gc_t;
|
|
else
|
|
alias GC* gc_t;
|
|
|
|
gc_t _gc;
|
|
|
|
void addRoot(void *p) { _gc.addRoot(p); }
|
|
void removeRoot(void *p) { _gc.removeRoot(p); }
|
|
void addRange(void *pbot, void *ptop) { _gc.addRange(pbot, ptop); }
|
|
void removeRange(void *pbot) { _gc.removeRange(pbot); }
|
|
void fullCollect() { _gc.fullCollect(); }
|
|
void fullCollectNoStack() { _gc.fullCollectNoStack(); }
|
|
void genCollect() { _gc.genCollect(); }
|
|
void minimize() { _gc.minimize(); }
|
|
void disable() { _gc.disable(); }
|
|
void enable() { _gc.enable(); }
|
|
void getStats(out GCStats stats) { _gc.getStats(stats); }
|
|
|
|
void* getGCHandle()
|
|
{
|
|
return cast(void*)_gc;
|
|
}
|
|
|
|
void setGCHandle(void* p)
|
|
{
|
|
void* oldp = getGCHandle();
|
|
gc_t g = cast(gc_t)p;
|
|
if (g.gcversion != gcx.GCVERSION)
|
|
throw new Error("incompatible gc versions");
|
|
|
|
// Add our static data to the new gc
|
|
GC.scanStaticData(g);
|
|
|
|
_gc = g;
|
|
// return oldp;
|
|
}
|
|
|
|
void endGCHandle()
|
|
{
|
|
GC.unscanStaticData(_gc);
|
|
}
|
|
|
|
extern (C)
|
|
{
|
|
|
|
void _d_monitorrelease(Object h);
|
|
|
|
|
|
void gc_init()
|
|
{
|
|
version (GCCLASS)
|
|
{ void* p;
|
|
ClassInfo ci = GC.classinfo;
|
|
|
|
p = std.c.stdlib.malloc(ci.init.length);
|
|
(cast(byte*)p)[0 .. ci.init.length] = ci.init[];
|
|
_gc = cast(GC)p;
|
|
}
|
|
else
|
|
{
|
|
_gc = cast(GC *) std.c.stdlib.calloc(1, GC.sizeof);
|
|
}
|
|
_gc.initialize();
|
|
GC.scanStaticData(_gc);
|
|
std.thread.Thread.thread_init();
|
|
}
|
|
|
|
void gc_term()
|
|
{
|
|
_gc.fullCollectNoStack();
|
|
}
|
|
|
|
Object _d_newclass(ClassInfo ci)
|
|
{
|
|
void *p;
|
|
|
|
debug(PRINTF) printf("_d_newclass(ci = %p)\n", ci);
|
|
if (ci.flags & 1) // if COM object
|
|
{
|
|
p = cast(Object)std.c.stdlib.malloc(ci.init.length);
|
|
if (!p)
|
|
_d_OutOfMemory();
|
|
}
|
|
else
|
|
{
|
|
p = _gc.malloc(ci.init.length);
|
|
debug(PRINTF) printf(" p = %p\n", p);
|
|
_gc.setFinalizer(p, &new_finalizer);
|
|
}
|
|
|
|
debug (PRINTF)
|
|
{
|
|
printf("p = %p\n", p);
|
|
printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length);
|
|
printf("vptr = %p\n", *cast(void **)ci.init);
|
|
printf("vtbl[0] = %p\n", (*cast(void ***)ci.init)[0]);
|
|
printf("vtbl[1] = %p\n", (*cast(void ***)ci.init)[1]);
|
|
printf("init[0] = %x\n", (cast(uint *)ci.init)[0]);
|
|
printf("init[1] = %x\n", (cast(uint *)ci.init)[1]);
|
|
printf("init[2] = %x\n", (cast(uint *)ci.init)[2]);
|
|
printf("init[3] = %x\n", (cast(uint *)ci.init)[3]);
|
|
printf("init[4] = %x\n", (cast(uint *)ci.init)[4]);
|
|
}
|
|
|
|
|
|
// Initialize it
|
|
(cast(byte*)p)[0 .. ci.init.length] = ci.init[];
|
|
|
|
//printf("initialization done\n");
|
|
return cast(Object)p;
|
|
}
|
|
|
|
extern (D) alias void (*fp_t)(Object); // generic function pointer
|
|
|
|
void _d_delinterface(void** p)
|
|
{
|
|
if (*p)
|
|
{
|
|
Interface *pi = **cast(Interface ***)*p;
|
|
Object o;
|
|
|
|
o = cast(Object)(*p - pi.offset);
|
|
_d_delclass(&o);
|
|
*p = null;
|
|
}
|
|
}
|
|
|
|
void _d_delclass(Object *p)
|
|
{
|
|
if (*p)
|
|
{
|
|
debug (PRINTF) printf("_d_delclass(%p)\n", *p);
|
|
version(0)
|
|
{
|
|
ClassInfo **pc = cast(ClassInfo **)*p;
|
|
if (*pc)
|
|
{
|
|
ClassInfo c = **pc;
|
|
|
|
if (c.deallocator)
|
|
{
|
|
_d_callfinalizer(*p);
|
|
fp_t fp = cast(fp_t)c.deallocator;
|
|
(*fp)(*p); // call deallocator
|
|
*p = null;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
_gc.free(*p);
|
|
*p = null;
|
|
}
|
|
}
|
|
|
|
ulong _d_new(size_t length, size_t size)
|
|
{
|
|
void *p;
|
|
ulong result;
|
|
|
|
debug(PRINTF) printf("_d_new(length = %d, size = %d)\n", length, size);
|
|
if (length == 0 || size == 0)
|
|
result = 0;
|
|
else
|
|
{
|
|
p = _gc.malloc(length * size + 1);
|
|
debug(PRINTF) printf(" p = %p\n", p);
|
|
memset(p, 0, length * size);
|
|
result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ulong _d_newarrayi(size_t length, size_t size, ...)
|
|
{
|
|
void *p;
|
|
ulong result;
|
|
|
|
//debug(PRINTF) printf("_d_newarrayi(length = %d, size = %d)\n", length, size);
|
|
if (length == 0 || size == 0)
|
|
result = 0;
|
|
else
|
|
{
|
|
//void* q = cast(void*)(&size + 1); // pointer to initializer
|
|
va_list q;
|
|
va_start!(size_t)(q, size); // q is pointer to ... initializer
|
|
p = _gc.malloc(length * size + 1);
|
|
debug(PRINTF) printf(" p = %p\n", p);
|
|
if (size == 1)
|
|
memset(p, *cast(ubyte*)q, length);
|
|
else
|
|
{
|
|
for (uint u = 0; u < length; u++)
|
|
{
|
|
memcpy(p + u * size, q, size);
|
|
}
|
|
}
|
|
va_end(q);
|
|
result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ulong _d_newbitarray(size_t length, bit value)
|
|
{
|
|
void *p;
|
|
ulong result;
|
|
|
|
debug(PRINTF) printf("_d_newbitarray(length = %d, value = %d)\n", length, value);
|
|
if (length == 0)
|
|
result = 0;
|
|
else
|
|
{ size_t size = (length + 8) >> 3; // number of bytes
|
|
ubyte fill = value ? 0xFF : 0;
|
|
|
|
p = _gc.malloc(size);
|
|
debug(PRINTF) printf(" p = %p\n", p);
|
|
memset(p, fill, size);
|
|
result = cast(ulong)length + (cast(ulong)cast(uint)p << 32);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
struct Array
|
|
{
|
|
size_t length;
|
|
byte *data;
|
|
};
|
|
|
|
// Perhaps we should get a a size argument like _d_new(), so we
|
|
// can zero out the array?
|
|
|
|
void _d_delarray(Array *p)
|
|
{
|
|
if (p)
|
|
{
|
|
assert(!p.length || p.data);
|
|
if (p.data)
|
|
_gc.free(p.data);
|
|
p.data = null;
|
|
p.length = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void _d_delmemory(void* *p)
|
|
{
|
|
if (*p)
|
|
{
|
|
_gc.free(*p);
|
|
*p = null;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void new_finalizer(void *p, void *dummy)
|
|
{
|
|
//printf("new_finalizer(p = %p)\n", p);
|
|
_d_callfinalizer(p);
|
|
}
|
|
|
|
extern (C)
|
|
void _d_callfinalizer(void *p)
|
|
{
|
|
//printf("_d_callfinalizer(p = %p)\n", p);
|
|
if (p) // not necessary if called from gc
|
|
{
|
|
ClassInfo **pc = cast(ClassInfo **)p;
|
|
if (*pc)
|
|
{
|
|
ClassInfo c = **pc;
|
|
|
|
try
|
|
{
|
|
do
|
|
{
|
|
if (c.destructor)
|
|
{
|
|
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_monitorrelease(cast(Object)p);
|
|
}
|
|
finally
|
|
{
|
|
*pc = null; // zero vptr
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/+ ------------------------------------------------ +/
|
|
|
|
|
|
/******************************
|
|
* Resize dynamic arrays other than bit[].
|
|
*/
|
|
|
|
extern (C)
|
|
byte[] _d_arraysetlength(size_t newlength, size_t sizeelem, Array *p)
|
|
in
|
|
{
|
|
assert(sizeelem);
|
|
assert(!p.length || p.data);
|
|
}
|
|
body
|
|
{
|
|
byte* newdata;
|
|
|
|
debug(PRINTF)
|
|
{
|
|
printf("_d_arraysetlength(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
|
|
if (p)
|
|
printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
|
|
}
|
|
|
|
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;
|
|
}
|
|
//printf("newsize = %x, newlength = %x\n", newsize, newlength);
|
|
|
|
if (p.length)
|
|
{
|
|
newdata = p.data;
|
|
if (newlength > p.length)
|
|
{
|
|
size_t size = p.length * sizeelem;
|
|
size_t cap = _gc.capacity(p.data);
|
|
|
|
if (cap <= newsize)
|
|
{
|
|
newdata = cast(byte *)_gc.malloc(newsize + 1);
|
|
newdata[0 .. size] = p.data[0 .. size];
|
|
}
|
|
newdata[size .. newsize] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = cast(byte *)_gc.calloc(newsize + 1, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = null;
|
|
}
|
|
|
|
p.data = newdata;
|
|
p.length = newlength;
|
|
return newdata[0 .. newlength];
|
|
|
|
Loverflow:
|
|
_d_OutOfMemory();
|
|
}
|
|
|
|
/**
|
|
* For non-zero initializers
|
|
*/
|
|
extern (C)
|
|
byte[] _d_arraysetlength2(size_t newlength, size_t sizeelem, Array *p, ...)
|
|
in
|
|
{
|
|
assert(sizeelem);
|
|
assert(!p.length || p.data);
|
|
}
|
|
body
|
|
{
|
|
byte* newdata;
|
|
|
|
debug(PRINTF)
|
|
{
|
|
printf("_d_arraysetlength2(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength);
|
|
if (p)
|
|
printf("\tp.data = %p, p.length = %d\n", p.data, p.length);
|
|
}
|
|
|
|
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;
|
|
}
|
|
//printf("newsize = %x, newlength = %x\n", newsize, newlength);
|
|
|
|
size_t size = p.length * sizeelem;
|
|
if (p.length)
|
|
{
|
|
newdata = p.data;
|
|
if (newlength > p.length)
|
|
{
|
|
size_t cap = _gc.capacity(p.data);
|
|
|
|
if (cap <= newsize)
|
|
{
|
|
newdata = cast(byte *)_gc.malloc(newsize + 1);
|
|
newdata[0 .. size] = p.data[0 .. size];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = cast(byte *)_gc.malloc(newsize + 1);
|
|
}
|
|
|
|
va_list q;
|
|
va_start!(Array *)(q, p); // q is pointer to initializer
|
|
|
|
if (newsize > size)
|
|
{
|
|
if (sizeelem == 1)
|
|
newdata[size .. newsize] = *(cast(byte*)q);
|
|
else
|
|
{
|
|
for (size_t u = size; u < newsize; u += sizeelem)
|
|
{
|
|
memcpy(newdata + u, q, sizeelem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = null;
|
|
}
|
|
|
|
p.data = newdata;
|
|
p.length = newlength;
|
|
return newdata[0 .. newlength];
|
|
|
|
Loverflow:
|
|
_d_OutOfMemory();
|
|
}
|
|
|
|
/***************************
|
|
* Resize bit[] arrays.
|
|
*/
|
|
|
|
version (none)
|
|
{
|
|
extern (C)
|
|
bit[] _d_arraysetlengthb(size_t newlength, Array *p)
|
|
{
|
|
byte* newdata;
|
|
size_t newsize;
|
|
|
|
debug (PRINTF)
|
|
printf("p = %p, newlength = %d\n", p, newlength);
|
|
|
|
assert(!p.length || p.data);
|
|
if (newlength)
|
|
{
|
|
newsize = ((newlength + 31) >> 5) * 4; // # bytes rounded up to uint
|
|
if (p.length)
|
|
{ size_t size = ((p.length + 31) >> 5) * 4;
|
|
|
|
newdata = p.data;
|
|
if (newsize > size)
|
|
{
|
|
size_t cap = _gc.capacity(p.data);
|
|
if (cap <= newsize)
|
|
{
|
|
newdata = cast(byte *)_gc.malloc(newsize + 1);
|
|
newdata[0 .. size] = p.data[0 .. size];
|
|
}
|
|
newdata[size .. newsize] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = cast(byte *)_gc.calloc(newsize + 1, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newdata = null;
|
|
}
|
|
|
|
p.data = newdata;
|
|
p.length = newlength;
|
|
return (cast(bit *)newdata)[0 .. newlength];
|
|
}
|
|
}
|
|
|
|
/****************************************
|
|
* Append y[] to array x[].
|
|
* size is size of each array element.
|
|
*/
|
|
|
|
extern (C)
|
|
long _d_arrayappend(Array *px, byte[] y, size_t size)
|
|
{
|
|
|
|
size_t cap = _gc.capacity(px.data);
|
|
size_t length = px.length;
|
|
size_t newlength = length + y.length;
|
|
if (newlength * size > cap)
|
|
{ byte* newdata;
|
|
|
|
newdata = cast(byte *)_gc.malloc(newCapacity(newlength, size) + 1);
|
|
memcpy(newdata, px.data, length * size);
|
|
px.data = newdata;
|
|
}
|
|
px.length = newlength;
|
|
memcpy(px.data + length * size, y, y.length * size);
|
|
return *cast(long*)px;
|
|
}
|
|
|
|
extern (C)
|
|
long _d_arrayappendb(Array *px, bit[] y)
|
|
{
|
|
|
|
size_t cap = _gc.capacity(px.data);
|
|
size_t length = px.length;
|
|
size_t newlength = length + y.length;
|
|
size_t newsize = (newlength + 7) / 8;
|
|
if (newsize > cap)
|
|
{ void* newdata;
|
|
|
|
//newdata = _gc.malloc(newlength * size);
|
|
newdata = _gc.malloc(newCapacity(newsize, 1) + 1);
|
|
memcpy(newdata, px.data, (length + 7) / 8);
|
|
px.data = cast(byte*)newdata;
|
|
}
|
|
px.length = newlength;
|
|
if ((length & 7) == 0)
|
|
// byte aligned, straightforward copy
|
|
memcpy(px.data + length / 8, y, (y.length + 7) / 8);
|
|
else
|
|
{ bit* x = cast(bit*)px.data;
|
|
|
|
for (size_t u = 0; u < y.length; u++)
|
|
{
|
|
x[length + u] = y[u];
|
|
}
|
|
}
|
|
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 4096 bytes are left as-is, so for the most
|
|
* common cases, memory allocation is 1 to 1. The small overhead added
|
|
* doesn't effect small array perf. (it's virutally 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 > 4096)
|
|
{
|
|
//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;
|
|
//printf("mult: %2.2f, mult2: %2.2f, alloc: %2.2f\n",mult/100.0,mult2,newext / cast(double)size);
|
|
}
|
|
newcap = newext > newcap ? newext : newcap;
|
|
//printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size);
|
|
}
|
|
return newcap;
|
|
}
|
|
|
|
extern (C)
|
|
byte[] _d_arrayappendc(inout byte[] x, in size_t size, ...)
|
|
{
|
|
size_t cap = _gc.capacity(x);
|
|
size_t length = x.length;
|
|
size_t newlength = length + 1;
|
|
|
|
assert(cap == 0 || length * size <= cap);
|
|
|
|
//printf("_d_arrayappendc(size = %d, ptr = %p, length = %d, cap = %d)\n", size, x.ptr, x.length, cap);
|
|
|
|
if (newlength * size >= cap)
|
|
{ byte* newdata;
|
|
|
|
//printf("_d_arrayappendc(size = %d, newlength = %d, cap = %d)\n", size, newlength, cap);
|
|
cap = newCapacity(newlength, size);
|
|
assert(cap >= newlength * size);
|
|
newdata = cast(byte *)_gc.malloc(cap + 1);
|
|
memcpy(newdata, x, length * size);
|
|
(cast(void **)(&x))[1] = newdata;
|
|
}
|
|
byte *argp = cast(byte *)(&size + 1);
|
|
|
|
*cast(size_t *)&x = newlength;
|
|
(cast(byte *)x)[length * size .. newlength * size] = argp[0 .. size];
|
|
assert((cast(size_t)x.ptr & 15) == 0);
|
|
assert(_gc.capacity(x.ptr) > x.length * size);
|
|
return x;
|
|
}
|
|
|
|
extern (C)
|
|
byte[] _d_arraycat(byte[] x, byte[] y, size_t size)
|
|
out (result)
|
|
{
|
|
//printf("_d_arraycat(%d,%p ~ %d,%p size = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, size, result.length, result.ptr);
|
|
assert(result.length == x.length + y.length);
|
|
for (size_t i = 0; i < x.length * size; i++)
|
|
assert((cast(byte*)result)[i] == (cast(byte*)x)[i]);
|
|
for (size_t i = 0; i < y.length * size; i++)
|
|
assert((cast(byte*)result)[x.length * size + i] == (cast(byte*)y)[i]);
|
|
|
|
size_t cap = _gc.capacity(result.ptr);
|
|
assert(!cap || cap > result.length * size);
|
|
}
|
|
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;
|
|
}
|
|
|
|
size_t xlen = x.length * size;
|
|
size_t ylen = y.length * size;
|
|
size_t len = xlen + ylen;
|
|
if (!len)
|
|
return null;
|
|
|
|
byte* p = cast(byte*)_gc.malloc(len + 1);
|
|
memcpy(p, x, xlen);
|
|
memcpy(p + xlen, y, ylen);
|
|
p[len] = 0;
|
|
|
|
return p[0 .. x.length + y.length];
|
|
}
|
|
|
|
|
|
|
|
extern (C)
|
|
bit[] _d_arrayappendcb(inout bit[] x, bit b)
|
|
{
|
|
if (x.length & 7)
|
|
{
|
|
*cast(size_t *)&x = x.length + 1;
|
|
}
|
|
else
|
|
{
|
|
x.length = x.length + 1;
|
|
}
|
|
x[x.length - 1] = b;
|
|
return x;
|
|
}
|