diff options
Diffstat (limited to 'libphobos/libdruntime/rt')
34 files changed, 1610 insertions, 3766 deletions
diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d index f665702..bea441f 100644 --- a/libphobos/libdruntime/rt/aApply.d +++ b/libphobos/libdruntime/rt/aApply.d @@ -4,13 +4,13 @@ * of those. * * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright - * Source: $(DRUNTIMESRC src/rt/_aApply.d) + * Source: $(DRUNTIMESRC rt/_aApply.d) */ module rt.aApply; -private import rt.util.utf : decode, toUTF8; +import core.internal.utf : decode, toUTF8; /**********************************************/ /* 1 argument versions */ diff --git a/libphobos/libdruntime/rt/aApplyR.d b/libphobos/libdruntime/rt/aApplyR.d index b29d370..6db6530 100644 --- a/libphobos/libdruntime/rt/aApplyR.d +++ b/libphobos/libdruntime/rt/aApplyR.d @@ -4,8 +4,9 @@ * of those. * * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright, Sean Kelly + * Source: $(DRUNTIMESRC rt/_aApplyR.d) */ /* Copyright Digital Mars 2004 - 2010. @@ -20,7 +21,7 @@ module rt.aApplyR; * and dchar, and 2 of each of those. */ -private import rt.util.utf; +import core.internal.utf; /**********************************************/ /* 1 argument versions */ diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index 0ccf902..0181053 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -2,8 +2,9 @@ * Implementation of associative arrays. * * Copyright: Copyright Digital Mars 2000 - 2015. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Martin Nowak + * Source: $(DRUNTIMESRC rt/_aaA.d) */ module rt.aaA; @@ -11,6 +12,7 @@ module rt.aaA; extern (C) immutable int _aaVersion = 1; import core.memory : GC; +import core.internal.util.math : min, max; // grow threshold private enum GROW_NUM = 4; @@ -48,13 +50,12 @@ struct AA private struct Impl { private: - this(in TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) + this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS) { keysz = cast(uint) ti.key.tsize; valsz = cast(uint) ti.value.tsize; buckets = allocBuckets(sz); firstUsed = cast(uint) buckets.length; - entryTI = fakeEntryTI(ti.key, ti.value); valoff = cast(uint) talign(keysz, ti.value.talign); import rt.lifetime : hasPostblit, unqualify; @@ -63,6 +64,8 @@ private: flags |= Flags.keyHasPostblit; if ((ti.key.flags | ti.value.flags) & 1) flags |= Flags.hasPointers; + + entryTI = fakeEntryTI(this, ti.key, ti.value); } Bucket[] buckets; @@ -110,7 +113,7 @@ private: } // lookup a key - inout(Bucket)* findSlotLookup(size_t hash, in void* pkey, in TypeInfo keyti) inout + inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout { for (size_t i = hash & mask, j = 1;; ++j) { @@ -122,7 +125,7 @@ private: } } - void grow(in TypeInfo keyti) + void grow(scope const TypeInfo keyti) { // If there are so many deleted entries, that growing would push us // below the shrink threshold, we just purge deleted entries instead. @@ -132,7 +135,7 @@ private: resize(GROW_FAC * dim); } - void shrink(in TypeInfo keyti) + void shrink(scope const TypeInfo keyti) { if (dim > INIT_NUM_BUCKETS) resize(dim / GROW_FAC); @@ -200,7 +203,7 @@ Bucket[] allocBuckets(size_t dim) @trusted pure nothrow // Entry //------------------------------------------------------------------------------ -private void* allocEntry(in Impl* aa, in void* pkey) +private void* allocEntry(scope const Impl* aa, scope const void* pkey) { import rt.lifetime : _d_newitemU; import core.stdc.string : memcpy, memset; @@ -243,19 +246,45 @@ private bool hasDtor(const TypeInfo ti) return false; } +private immutable(void)* getRTInfo(const TypeInfo ti) +{ + // classes are references + const isNoClass = ti && typeid(ti) !is typeid(TypeInfo_Class); + return isNoClass ? ti.rtInfo() : rtinfoHasPointers; +} + // build type info for Entry with additional key and value fields -TypeInfo_Struct fakeEntryTI(const TypeInfo keyti, const TypeInfo valti) +TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) { import rt.lifetime : unqualify; auto kti = unqualify(keyti); auto vti = unqualify(valti); - if (!hasDtor(kti) && !hasDtor(vti)) + + // figure out whether RTInfo has to be generated (indicated by rtisize > 0) + enum pointersPerWord = 8 * (void*).sizeof * (void*).sizeof; + auto rtinfo = rtinfoNoPointers; + size_t rtisize = 0; + immutable(size_t)* keyinfo = void; + immutable(size_t)* valinfo = void; + if (aa.flags & Impl.Flags.hasPointers) + { + // classes are references + keyinfo = cast(immutable(size_t)*) getRTInfo(keyti); + valinfo = cast(immutable(size_t)*) getRTInfo(valti); + + if (keyinfo is rtinfoHasPointers && valinfo is rtinfoHasPointers) + rtinfo = rtinfoHasPointers; + else + rtisize = 1 + (aa.valoff + aa.valsz + pointersPerWord - 1) / pointersPerWord; + } + bool entryHasDtor = hasDtor(kti) || hasDtor(vti); + if (rtisize == 0 && !entryHasDtor) return null; // save kti and vti after type info for struct enum sizeti = __traits(classInstanceSize, TypeInfo_Struct); - void* p = GC.malloc(sizeti + 2 * (void*).sizeof); + void* p = GC.malloc(sizeti + (2 + rtisize) * (void*).sizeof); import core.stdc.string : memcpy; memcpy(p, typeid(TypeInfo_Struct).initializer().ptr, sizeti); @@ -265,26 +294,146 @@ TypeInfo_Struct fakeEntryTI(const TypeInfo keyti, const TypeInfo valti) extra[0] = cast() kti; extra[1] = cast() vti; - static immutable tiName = __MODULE__ ~ ".Entry!(...)"; - ti.name = tiName; + static immutable tiMangledName = "S2rt3aaA__T5EntryZ"; + ti.mangledName = tiMangledName; + + ti.m_RTInfo = rtisize > 0 ? rtinfoEntry(aa, keyinfo, valinfo, cast(size_t*)(extra + 2), rtisize) : rtinfo; + ti.m_flags = ti.m_RTInfo is rtinfoNoPointers ? cast(TypeInfo_Struct.StructFlags)0 : TypeInfo_Struct.StructFlags.hasPointers; // we don't expect the Entry objects to be used outside of this module, so we have control // over the non-usage of the callback methods and other entries and can keep these null // xtoHash, xopEquals, xopCmp, xtoString and xpostblit - ti.m_RTInfo = rtinfoNoPointers; - immutable entrySize = talign(kti.tsize, vti.talign) + vti.tsize; + immutable entrySize = aa.valoff + aa.valsz; ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr - // xdtor needs to be built from the dtors of key and value for the GC - ti.xdtorti = &entryDtor; + if (entryHasDtor) + { + // xdtor needs to be built from the dtors of key and value for the GC + ti.xdtorti = &entryDtor; + ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType; + } - ti.m_flags = TypeInfo_Struct.StructFlags.isDynamicType; - ti.m_flags |= (keyti.flags | valti.flags) & TypeInfo_Struct.StructFlags.hasPointers; ti.m_align = cast(uint) max(kti.talign, vti.talign); return ti; } +// build appropriate RTInfo at runtime +immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo, immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) +{ + enum bitsPerWord = 8 * size_t.sizeof; + + rtinfoData[0] = aa.valoff + aa.valsz; + rtinfoData[1..rtinfoSize] = 0; + + void copyKeyInfo(string src)() + { + size_t pos = 1; + size_t keybits = aa.keysz / (void*).sizeof; + while (keybits >= bitsPerWord) + { + rtinfoData[pos] = mixin(src); + keybits -= bitsPerWord; + pos++; + } + if (keybits > 0) + rtinfoData[pos] = mixin(src) & ((cast(size_t) 1 << keybits) - 1); + } + + if (keyinfo is rtinfoHasPointers) + copyKeyInfo!"~cast(size_t) 0"(); + else if (keyinfo !is rtinfoNoPointers) + copyKeyInfo!"keyinfo[pos]"(); + + void copyValInfo(string src)() + { + size_t bitpos = aa.valoff / (void*).sizeof; + size_t pos = 1; + size_t dstpos = 1 + bitpos / bitsPerWord; + size_t begoff = bitpos % bitsPerWord; + size_t valbits = aa.valsz / (void*).sizeof; + size_t endoff = (bitpos + valbits) % bitsPerWord; + for (;;) + { + const bits = bitsPerWord - begoff; + size_t s = mixin(src); + rtinfoData[dstpos] |= s << begoff; + if (begoff > 0 && valbits > bits) + rtinfoData[dstpos+1] |= s >> bits; + if (valbits < bitsPerWord) + break; + valbits -= bitsPerWord; + dstpos++; + pos++; + } + if (endoff > 0) + rtinfoData[dstpos] &= ((cast(size_t) 1 << endoff) - 1); + } + + if (valinfo is rtinfoHasPointers) + copyValInfo!"~cast(size_t) 0"(); + else if (valinfo !is rtinfoNoPointers) + copyValInfo!"valinfo[pos]"(); + + return cast(immutable(void)*) rtinfoData; +} + +unittest +{ + void test(K, V)() + { + static struct Entry + { + K key; + V val; + } + auto keyti = typeid(K); + auto valti = typeid(V); + auto valrti = getRTInfo(valti); + auto keyrti = getRTInfo(keyti); + + auto impl = new Impl(typeid(V[K])); + if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers) + { + assert(!(impl.flags & Impl.Flags.hasPointers)); + assert(impl.entryTI is null); + } + else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers) + { + assert(impl.flags & Impl.Flags.hasPointers); + assert(impl.entryTI is null); + } + else + { + auto rtInfo = cast(size_t*) impl.entryTI.rtInfo(); + auto refInfo = cast(size_t*) typeid(Entry).rtInfo(); + assert(rtInfo[0] == refInfo[0]); // size + enum bytesPerWord = 8 * size_t.sizeof * (void*).sizeof; + size_t words = (rtInfo[0] + bytesPerWord - 1) / bytesPerWord; + foreach (i; 0 .. words) + assert(rtInfo[1 + i] == refInfo[i + 1]); + } + } + test!(long, int)(); + test!(string, string); + test!(ubyte[16], Object); + + static struct Small + { + ubyte[16] guid; + string name; + } + test!(string, Small); + + static struct Large + { + ubyte[1024] data; + string[412] names; + ubyte[1024] moredata; + } + test!(Large, Large); +} + //============================================================================== // Helper functions //------------------------------------------------------------------------------ @@ -307,14 +456,14 @@ private size_t mix(size_t h) @safe pure nothrow @nogc return h; } -private size_t calcHash(in void* pkey, in TypeInfo keyti) +private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) { immutable hash = keyti.getHash(pkey); // highest bit is set to distinguish empty/deleted from filled buckets return mix(hash) | HASH_FILLED_MARK; } -private size_t nextpow2(in size_t n) pure nothrow @nogc +private size_t nextpow2(const size_t n) pure nothrow @nogc { import core.bitop : bsr; @@ -332,22 +481,12 @@ pure nothrow @nogc unittest assert(nextpow2(n) == pow2); } -private T min(T)(T a, T b) pure nothrow @nogc -{ - return a < b ? a : b; -} - -private T max(T)(T a, T b) pure nothrow @nogc -{ - return b < a ? a : b; -} - //============================================================================== // API Implementation //------------------------------------------------------------------------------ /// Determine number of entries in associative array. -extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc +extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc { return aa ? aa.length : 0; } @@ -356,7 +495,7 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc * Lookup *pkey in aa. * Called only from implementation of (aa[key]) expressions when value is mutable. * Params: - * aa = associative array opaque pointer + * paa = associative array opaque pointer * ti = TypeInfo for the associative array * valsz = ignored * pkey = pointer to the key value @@ -365,18 +504,18 @@ extern (C) size_t _aaLen(in AA aa) pure nothrow @nogc * If key was not in the aa, a mutable pointer to newly inserted value which * is set to all zeros */ -extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, - in size_t valsz, in void* pkey) +extern (C) void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, + const size_t valsz, scope const void* pkey) { bool found; - return _aaGetX(aa, ti, valsz, pkey, found); + return _aaGetX(paa, ti, valsz, pkey, found); } /****************************** * Lookup *pkey in aa. * Called only from implementation of require * Params: - * aa = associative array opaque pointer + * paa = associative array opaque pointer * ti = TypeInfo for the associative array * valsz = ignored * pkey = pointer to the key value @@ -386,12 +525,16 @@ extern (C) void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, * If key was not in the aa, a mutable pointer to newly inserted value which * is set to all zeros */ -extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti, - in size_t valsz, in void* pkey, out bool found) +extern (C) void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, + const size_t valsz, scope const void* pkey, out bool found) { // lazily alloc implementation - if (aa.impl is null) - aa.impl = new Impl(ti); + AA aa = *paa; + if (aa is null) + { + aa = new Impl(ti); + *paa = aa; + } // get hash and bucket for key immutable hash = calcHash(pkey, ti.key); @@ -417,7 +560,7 @@ extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti, // update search cache and allocate entry aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr)); p.hash = hash; - p.entry = allocEntry(aa.impl, pkey); + p.entry = allocEntry(aa, pkey); // postblit for key if (aa.flags & Impl.Flags.keyHasPostblit) { @@ -440,8 +583,8 @@ extern (C) void* _aaGetX(AA* aa, const TypeInfo_AssociativeArray ti, * Returns: * pointer to value if present, null otherwise */ -extern (C) inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, - in void* pkey) +extern (C) inout(void)* _aaGetRvalueX(inout AA aa, scope const TypeInfo keyti, const size_t valsz, + scope const void* pkey) { return _aaInX(aa, keyti, pkey); } @@ -456,7 +599,7 @@ extern (C) inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t * Returns: * pointer to value if present, null otherwise */ -extern (C) inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey) +extern (C) inout(void)* _aaInX(inout AA aa, scope const TypeInfo keyti, scope const void* pkey) { if (aa.empty) return null; @@ -467,8 +610,8 @@ extern (C) inout(void)* _aaInX(inout AA aa, in TypeInfo keyti, in void* pkey) return null; } -/// Delete entry in AA, return true if it was present -extern (C) bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) +/// Delete entry scope const AA, return true if it was present +extern (C) bool _aaDelX(AA aa, scope const TypeInfo keyti, scope const void* pkey) { if (aa.empty) return false; @@ -481,7 +624,9 @@ extern (C) bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) p.entry = null; ++aa.deleted; - if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM) + // `shrink` reallocates, and allocating from a finalizer leads to + // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442 + if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM && !GC.inFinalizer()) aa.shrink(keyti); return true; @@ -494,20 +639,21 @@ extern (C) void _aaClear(AA aa) pure nothrow { if (!aa.empty) { - aa.impl.clear(); + aa.clear(); } } /// Rehash AA -extern (C) void* _aaRehash(AA* paa, in TypeInfo keyti) pure nothrow +extern (C) void* _aaRehash(AA* paa, scope const TypeInfo keyti) pure nothrow { - if (!paa.empty) - paa.resize(nextpow2(INIT_DEN * paa.length / INIT_NUM)); - return *paa; + AA aa = *paa; + if (!aa.empty) + aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM)); + return aa; } /// Return a GC allocated array of all values -extern (C) inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz, +extern (C) inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow { if (aa.empty) @@ -531,7 +677,7 @@ extern (C) inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz } /// Return a GC allocated array of all keys -extern (C) inout(void[]) _aaKeys(inout AA aa, in size_t keysz, const TypeInfo tiKeyArray) pure nothrow +extern (C) inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow { if (aa.empty) return null; @@ -557,7 +703,7 @@ extern (D) alias dg_t = int delegate(void*); extern (D) alias dg2_t = int delegate(void*, void*); /// foreach opApply over all values -extern (C) int _aaApply(AA aa, in size_t keysz, dg_t dg) +extern (C) int _aaApply(AA aa, const size_t keysz, dg_t dg) { if (aa.empty) return 0; @@ -574,7 +720,7 @@ extern (C) int _aaApply(AA aa, in size_t keysz, dg_t dg) } /// foreach opApply over all key/value pairs -extern (C) int _aaApply2(AA aa, in size_t keysz, dg2_t dg) +extern (C) int _aaApply2(AA aa, const size_t keysz, dg2_t dg) { if (aa.empty) return 0; @@ -639,9 +785,9 @@ extern (C) Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void } /// compares 2 AAs for equality -extern (C) int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2) +extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2) { - if (aa1.impl is aa2.impl) + if (aa1 is aa2) return true; immutable len = _aaLen(aa1); @@ -669,8 +815,10 @@ extern (C) int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2) } /// compute a hash -extern (C) hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow +extern (C) hash_t _aaGetHash(scope const AA* paa, scope const TypeInfo tiRaw) nothrow { + const AA aa = *paa; + if (aa.empty) return 0; @@ -707,7 +855,7 @@ struct Range extern (C) pure nothrow @nogc @safe { - Range _aaRange(AA aa) + Range _aaRange(return AA aa) { if (!aa) return Range(); @@ -715,7 +863,7 @@ extern (C) pure nothrow @nogc @safe foreach (i; aa.firstUsed .. aa.dim) { if (aa.buckets[i].filled) - return Range(aa.impl, i); + return Range(aa, i); } return Range(aa, aa.dim); } @@ -756,7 +904,7 @@ extern (C) pure nothrow @nogc @safe } } -// Most tests are now in in test_aa.d +// Most tests are now in test_aa.d // test postblit for AA literals unittest diff --git a/libphobos/libdruntime/rt/adi.d b/libphobos/libdruntime/rt/adi.d index 44f0e15..ea5a78f 100644 --- a/libphobos/libdruntime/rt/adi.d +++ b/libphobos/libdruntime/rt/adi.d @@ -6,7 +6,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright - * Source: $(DRUNTIMESRC src/rt/_adi.d) + * Source: $(DRUNTIMESRC rt/_adi.d) */ module rt.adi; @@ -16,66 +16,6 @@ module rt.adi; private { debug(adi) import core.stdc.stdio; - import core.stdc.string; - import core.stdc.stdlib; - import core.memory; - import rt.util.utf; - - extern (C) void[] _adSort(void[] a, TypeInfo ti); -} - -private dchar[] mallocUTF32(C)(in C[] s) -{ - size_t j = 0; - auto p = cast(dchar*)malloc(dchar.sizeof * s.length); - auto r = p[0..s.length]; // r[] will never be longer than s[] - foreach (dchar c; s) - r[j++] = c; - return r[0 .. j]; -} - -/********************************************** - * Sort array of chars. - */ - -extern (C) char[] _adSortChar(char[] a) -{ - if (a.length > 1) - { - auto da = mallocUTF32(a); - _adSort(*cast(void[]*)&da, typeid(da[0])); - size_t i = 0; - foreach (dchar d; da) - { char[4] buf; - auto t = toUTF8(buf, d); - a[i .. i + t.length] = t[]; - i += t.length; - } - free(da.ptr); - } - return a; -} - -/********************************************** - * Sort array of wchars. - */ - -extern (C) wchar[] _adSortWchar(wchar[] a) -{ - if (a.length > 1) - { - auto da = mallocUTF32(a); - _adSort(*cast(void[]*)&da, typeid(da[0])); - size_t i = 0; - foreach (dchar d; da) - { wchar[2] buf; - auto t = toUTF16(buf, d); - a[i .. i + t.length] = t[]; - i += t.length; - } - free(da.ptr); - } - return a; } /*************************************** @@ -85,27 +25,6 @@ extern (C) wchar[] _adSortWchar(wchar[] a) * 0 not equal */ -extern (C) int _adEq(void[] a1, void[] a2, TypeInfo ti) -{ - debug(adi) printf("_adEq(a1.length = %d, a2.length = %d)\n", a1.length, a2.length); - if (a1.length != a2.length) - return 0; // not equal - auto sz = ti.tsize; - auto p1 = a1.ptr; - auto p2 = a2.ptr; - - if (sz == 1) - // We should really have a ti.isPOD() check for this - return (memcmp(p1, p2, a1.length) == 0); - - for (size_t i = 0; i < a1.length; i++) - { - if (!ti.equals(p1 + i * sz, p2 + i * sz)) - return 0; // not equal - } - return 1; // equal -} - extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) { debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length); @@ -115,218 +34,43 @@ extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti) return 0; return 1; } -unittest + +@safe unittest { debug(adi) printf("array.Eq unittest\n"); - auto a = "hello"c; + struct S(T) { T val; } + alias String = S!string; + alias Float = S!float; + + String[1] a = [String("hello"c)]; - assert(a != "hel"); - assert(a != "helloo"); - assert(a != "betty"); - assert(a == "hello"); - assert(a != "hxxxx"); + assert(a != [String("hel")]); + assert(a != [String("helloo")]); + assert(a != [String("betty")]); + assert(a == [String("hello")]); + assert(a != [String("hxxxx")]); - float[] fa = [float.nan]; + Float[1] fa = [Float(float.nan)]; assert(fa != fa); } -/*************************************** - * Support for array compare test. - */ - -extern (C) int _adCmp(void[] a1, void[] a2, TypeInfo ti) +unittest { - debug(adi) printf("adCmp()\n"); - auto len = a1.length; - if (a2.length < len) - len = a2.length; - auto sz = ti.tsize; - void *p1 = a1.ptr; - void *p2 = a2.ptr; + debug(adi) printf("struct.Eq unittest\n"); - if (sz == 1) - { // We should really have a ti.isPOD() check for this - auto c = memcmp(p1, p2, len); - if (c) - return c; - } - else + static struct TestStruct { - for (size_t i = 0; i < len; i++) + int value; + + bool opEquals(const TestStruct rhs) const { - auto c = ti.compare(p1 + i * sz, p2 + i * sz); - if (c) - return c; + return value == rhs.value; } } - if (a1.length == a2.length) - return 0; - return (a1.length > a2.length) ? 1 : -1; -} - -extern (C) int _adCmp2(void[] a1, void[] a2, TypeInfo ti) -{ - debug(adi) printf("_adCmp2(a1.length = %d, a2.length = %d)\n", a1.length, a2.length); - return ti.compare(&a1, &a2); -} -unittest -{ - debug(adi) printf("array.Cmp unittest\n"); - - auto a = "hello"c; - - assert(a > "hel"); - assert(a >= "hel"); - assert(a < "helloo"); - assert(a <= "helloo"); - assert(a > "betty"); - assert(a >= "betty"); - assert(a == "hello"); - assert(a <= "hello"); - assert(a >= "hello"); - assert(a < "я"); -} - -/*************************************** - * Support for array compare test. - */ - -extern (C) int _adCmpChar(void[] a1, void[] a2) -{ - version (D_InlineAsm_X86) - { - asm - { naked ; - - push EDI ; - push ESI ; - - mov ESI,a1+4[4+ESP] ; - mov EDI,a2+4[4+ESP] ; - - mov ECX,a1[4+ESP] ; - mov EDX,a2[4+ESP] ; - - cmp ECX,EDX ; - jb GotLength ; - - mov ECX,EDX ; - -GotLength: - cmp ECX,4 ; - jb DoBytes ; - - // Do alignment if neither is dword aligned - test ESI,3 ; - jz Aligned ; - - test EDI,3 ; - jz Aligned ; -DoAlign: - mov AL,[ESI] ; //align ESI to dword bounds - mov DL,[EDI] ; - - cmp AL,DL ; - jnz Unequal ; - - inc ESI ; - inc EDI ; - - test ESI,3 ; - - lea ECX,[ECX-1] ; - jnz DoAlign ; -Aligned: - mov EAX,ECX ; - - // do multiple of 4 bytes at a time - - shr ECX,2 ; - jz TryOdd ; - - repe ; - cmpsd ; - - jnz UnequalQuad ; - -TryOdd: - mov ECX,EAX ; -DoBytes: - // if still equal and not end of string, do up to 3 bytes slightly - // slower. - - and ECX,3 ; - jz Equal ; - - repe ; - cmpsb ; - - jnz Unequal ; -Equal: - mov EAX,a1[4+ESP] ; - mov EDX,a2[4+ESP] ; - - sub EAX,EDX ; - pop ESI ; - - pop EDI ; - ret ; - -UnequalQuad: - mov EDX,[EDI-4] ; - mov EAX,[ESI-4] ; - - cmp AL,DL ; - jnz Unequal ; - - cmp AH,DH ; - jnz Unequal ; - - shr EAX,16 ; - - shr EDX,16 ; - - cmp AL,DL ; - jnz Unequal ; - - cmp AH,DH ; -Unequal: - sbb EAX,EAX ; - pop ESI ; - - or EAX,1 ; - pop EDI ; - - ret ; - } - } - else - { - debug(adi) printf("adCmpChar()\n"); - auto len = a1.length; - if (a2.length < len) - len = a2.length; - auto c = memcmp(cast(char *)a1.ptr, cast(char *)a2.ptr, len); - if (!c) - c = cast(int)a1.length - cast(int)a2.length; - return c; - } -} - -unittest -{ - debug(adi) printf("array.CmpChar unittest\n"); - - auto a = "hello"c; - assert(a > "hel"); - assert(a >= "hel"); - assert(a < "helloo"); - assert(a <= "helloo"); - assert(a > "betty"); - assert(a >= "betty"); - assert(a == "hello"); - assert(a <= "hello"); - assert(a >= "hello"); + TestStruct[] b = [TestStruct(5)]; + TestStruct[] c = [TestStruct(6)]; + assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&c, typeid(TestStruct[])) == false); + assert(_adEq2(*cast(void[]*)&b, *cast(void[]*)&b, typeid(TestStruct[])) == true); } diff --git a/libphobos/libdruntime/rt/arrayassign.d b/libphobos/libdruntime/rt/arrayassign.d index 389ff92..21d50b0 100644 --- a/libphobos/libdruntime/rt/arrayassign.d +++ b/libphobos/libdruntime/rt/arrayassign.d @@ -6,14 +6,14 @@ * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * Authors: Walter Bright, Kenji Hara - * Source: $(DRUNTIMESRC src/rt/_arrayassign.d) + * Source: $(DRUNTIMESRC rt/_arrayassign.d) */ module rt.arrayassign; private { - import rt.util.array; + import core.internal.util.array; import core.stdc.string; import core.stdc.stdlib; debug(PRINTF) import core.stdc.stdio; diff --git a/libphobos/libdruntime/rt/arraycast.d b/libphobos/libdruntime/rt/arraycast.d deleted file mode 100644 index d16d30d..0000000 --- a/libphobos/libdruntime/rt/arraycast.d +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Implementation of array cast support routines. - * - * Copyright: Copyright Digital Mars 2004 - 2016. - * License: Distributed under the - * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). - * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_arraycast.d) - */ - -module rt.arraycast; - -/****************************************** - * Runtime helper to convert dynamic array of one - * type to dynamic array of another. - * Adjusts the length of the array. - * Throws an error if new length is not aligned. - */ - -extern (C) - -@trusted nothrow -void[] _d_arraycast(size_t tsize, size_t fsize, void[] a) -{ - auto length = a.length; - - auto nbytes = length * fsize; - if (nbytes % tsize != 0) - { - throw new Error("array cast misalignment"); - } - length = nbytes / tsize; - *cast(size_t *)&a = length; // jam new length - return a; -} - -unittest -{ - byte[int.sizeof * 3] b; - int[] i; - short[] s; - - i = cast(int[])b; - assert(i.length == 3); - - s = cast(short[])b; - assert(s.length == 6); - - s = cast(short[])i; - assert(s.length == 6); -} - diff --git a/libphobos/libdruntime/rt/arraycat.d b/libphobos/libdruntime/rt/arraycat.d index f3f05c3..d879480 100644 --- a/libphobos/libdruntime/rt/arraycat.d +++ b/libphobos/libdruntime/rt/arraycat.d @@ -5,7 +5,7 @@ * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_arraycat.d) + * Source: $(DRUNTIMESRC rt/_arraycat.d) */ module rt.arraycat; @@ -13,7 +13,7 @@ module rt.arraycat; private { import core.stdc.string; - import rt.util.array; + import core.internal.util.array; debug(PRINTF) import core.stdc.stdio; } diff --git a/libphobos/libdruntime/rt/cast_.d b/libphobos/libdruntime/rt/cast_.d index f34d82e..dcb4438 100644 --- a/libphobos/libdruntime/rt/cast_.d +++ b/libphobos/libdruntime/rt/cast_.d @@ -2,8 +2,9 @@ * Implementation of array assignment support routines. * * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright, Sean Kelly + * Source: $(DRUNTIMESRC rt/_cast_.d) */ /* Copyright Digital Mars 2004 - 2010. @@ -14,6 +15,19 @@ module rt.cast_; extern (C): +@nogc: +nothrow: +pure: + +// Needed because ClassInfo.opEquals(Object) does a dynamic cast, +// but we are trying to implement dynamic cast. +extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe +{ + if (a is b) + return true; + // take care of potential duplicates across binaries + return a.name == b.name; +} /****************************************** * Given a pointer: @@ -22,7 +36,7 @@ extern (C): * If it is null, return null. * Else, undefined crash */ -Object _d_toObject(void* p) +Object _d_toObject(return void* p) { if (!p) return null; @@ -74,21 +88,21 @@ void* _d_dynamic_cast(Object o, ClassInfo c) return res; } -int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset) +int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe { - if (oc is c) + if (areClassInfosEqual(oc, c)) return true; do { - if (oc.base is c) + if (oc.base && areClassInfosEqual(oc.base, c)) return true; // Bugzilla 2013: Use depth-first search to calculate offset // from the derived (oc) to the base (c). foreach (iface; oc.interfaces) { - if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset)) + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset)) { offset += iface.offset; return true; @@ -101,19 +115,19 @@ int _d_isbaseof2(ClassInfo oc, ClassInfo c, ref size_t offset) return false; } -int _d_isbaseof(ClassInfo oc, ClassInfo c) +int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe { - if (oc is c) + if (areClassInfosEqual(oc, c)) return true; do { - if (oc.base is c) + if (oc.base && areClassInfosEqual(oc.base, c)) return true; foreach (iface; oc.interfaces) { - if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c)) + if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c)) return true; } @@ -122,20 +136,3 @@ int _d_isbaseof(ClassInfo oc, ClassInfo c) return false; } - -/********************************* - * Find the vtbl[] associated with Interface ic. - */ -void* _d_interface_vtbl(ClassInfo ic, Object o) -{ - debug(cast_) printf("__d_interface_vtbl(o = %p, ic = %p)\n", o, ic); - - assert(o); - - foreach (iface; typeid(o).interfaces) - { - if (iface.classinfo is ic) - return cast(void*) iface.vtbl; - } - assert(0); -} diff --git a/libphobos/libdruntime/rt/config.d b/libphobos/libdruntime/rt/config.d index 904f721..f7682f3 100644 --- a/libphobos/libdruntime/rt/config.d +++ b/libphobos/libdruntime/rt/config.d @@ -1,68 +1,73 @@ /** -* Configuration options for druntime -* -* Copyright: Copyright Digital Mars 2014. -* License: Distributed under the -* $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). -* (See accompanying file LICENSE) -* Authors: Rainer Schuetze -* Source: $(DRUNTIMESRC src/rt/_config.d) +Configuration options for druntime. + +The default way to configure the runtime is by passing command line arguments +starting with `--DRT-` and followed by the option name, e.g. `--DRT-gcopt` to +configure the GC. +When command line parsing is enabled, command line options starting +with `--DRT-` are filtered out before calling main, so the program +will not see them. They are still available via `rt_args()`. + +Configuration via the command line can be disabled by declaring a variable for the +linker to pick up before using it's default from the runtime: + +--- +extern(C) __gshared bool rt_cmdline_enabled = false; +--- + +Likewise, declare a boolean rt_envvars_enabled to enable configuration via the +environment variable `DRT_` followed by the option name, e.g. `DRT_GCOPT`: + +--- +extern(C) __gshared bool rt_envvars_enabled = true; +--- + +Setting default configuration properties in the executable can be done by specifying an +array of options named `rt_options`: + +--- +extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"]; +--- + +Evaluation order of options is `rt_options`, then environment variables, then command +line arguments, i.e. if command line arguments are not disabled, they can override +options specified through the environment or embedded in the executable. + +Copyright: Copyright Digital Mars 2014. +License: Distributed under the + $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + (See accompanying file LICENSE) +Authors: Rainer Schuetze +Source: $(DRUNTIMESRC rt/_config.d) */ module rt.config; -// The default way to configure the runtime is by passing command line arguments -// starting with "--DRT-" and followed by the option name, e.g. "--DRT-gcopt" to -// configure the GC. -// Command line options starting with "--DRT-" are filtered out before calling main, -// so the program will not see them. They are still available via rt_args(). -// -// Configuration via the command line can be disabled by declaring a variable for the -// linker to pick up before using it's default from the runtime: -// -// extern(C) __gshared bool rt_cmdline_enabled = false; -// -// Likewise, declare a boolean rt_envvars_enabled to enable configuration via the -// environment variable "DRT_" followed by the option name, e.g. "DRT_GCOPT": -// -// extern(C) __gshared bool rt_envvars_enabled = true; -// -// Setting default configuration properties in the executable can be done by specifying an -// array of options named rt_options: -// -// extern(C) __gshared string[] rt_options = [ "gcopt=precise:1 profile:1"]; -// -// Evaluation order of options is rt_options, then environment variables, then command -// line arguments, i.e. if command line arguments are not disabled, they can override -// options specified through the environment or embedded in the executable. - -import core.demangle : cPrefix; - // put each variable in its own COMDAT by making them template instances template rt_envvars_enabled() { - pragma(mangle, cPrefix ~ "rt_envvars_enabled") __gshared bool rt_envvars_enabled = false; + extern(C) pragma(mangle, "rt_envvars_enabled") __gshared bool rt_envvars_enabled = false; } template rt_cmdline_enabled() { - pragma(mangle, cPrefix ~ "rt_cmdline_enabled") __gshared bool rt_cmdline_enabled = true; + extern(C) pragma(mangle, "rt_cmdline_enabled") __gshared bool rt_cmdline_enabled = true; } template rt_options() { - pragma(mangle, cPrefix ~ "rt_options") __gshared string[] rt_options = []; + extern(C) pragma(mangle, "rt_options") __gshared string[] rt_options = []; } import core.stdc.ctype : toupper; import core.stdc.stdlib : getenv; import core.stdc.string : strlen; -extern extern(C) string[] rt_args() @nogc nothrow; +extern extern(C) string[] rt_args() @nogc nothrow @system; alias rt_configCallBack = string delegate(string) @nogc nothrow; /** * get a druntime config option using standard configuration options -* opt name of the option to retreive +* opt name of the option to retrieve * dg if non-null, passes the option through this * delegate and only returns its return value if non-null * reverse reverse the default processing order cmdline/envvar/rt_options diff --git a/libphobos/libdruntime/rt/critical_.d b/libphobos/libdruntime/rt/critical_.d index 9404261..ae18122 100644 --- a/libphobos/libdruntime/rt/critical_.d +++ b/libphobos/libdruntime/rt/critical_.d @@ -2,8 +2,9 @@ * Implementation of support routines for synchronized blocks. * * Copyright: Copyright Digital Mars 2000 - 2011. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright, Sean Kelly + * Source: $(DRUNTIMESRC rt/_critical_.d) */ /* Copyright Digital Mars 2000 - 2011. diff --git a/libphobos/libdruntime/rt/deh.d b/libphobos/libdruntime/rt/deh.d index 440e242..695f2ce 100644 --- a/libphobos/libdruntime/rt/deh.d +++ b/libphobos/libdruntime/rt/deh.d @@ -1,12 +1,37 @@ /** - * Implementation of exception handling support routines. + * Entry point for exception handling support routines. * - * Copyright: Copyright Digital Mars 1999 - 2013. + * There are three style of exception handling being supported by DMD: + * DWARF, Win32, and Win64. The Win64 code also supports POSIX. + * Support for those scheme is in `rt.dwarfeh`, `rt.deh_win32`, and + * `rt.deh_win64_posix`, respectively, and publicly imported here. + * + * When an exception is thrown by the user, the compiler translates + * code like `throw e;` into either `_d_throwdwarf` (for DWARF exceptions) + * or `_d_throwc` (Win32 / Win64), with the `Exception` object as argument. + * + * During those functions' handling, they eventually call `_d_createTrace`, + * which will store inside the `Exception` object the return of + * `_d_traceContext`, which is an object implementing `Throwable.TraceInfo`. + * `_d_traceContext` is a configurable hook, and by default will call + * `core.runtime : defaultTraceHandler`, which itself will call `backtrace` + * or something similar to store an array of stack frames (`void*` pointers) + * in the object it returns. + * Note that `defaultTraceHandler` returns a GC-allocated instance, + * hence a GC allocation can happen in the middle of throwing an `Exception`. + * + * The `Throwable.TraceInfo`-implementing should not resolves function names, + * file and line number until its `opApply` function is called, avoiding the + * overhead of reading the debug infos until the user call `toString`. + * If the user only calls `Throwable.message` (or use `Throwable.msg` directly), + * only the overhead of `backtrace` will be paid, which is minimal enouh. + * + * Copyright: Copyright Digital Mars 1999 - 2020. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright - * Source: $(DRUNTIMESRC src/rt/deh.d) + * Source: $(DRUNTIMESRC rt/deh.d) */ /* NOTE: This file has been patched from the original DMD distribution to @@ -17,10 +42,8 @@ module rt.deh; extern (C) { Throwable.TraceInfo _d_traceContext(void* ptr = null); - void _d_createTrace(Object o, void* context) + void _d_createTrace(Throwable t, void* context) { - auto t = cast(Throwable) o; - if (t !is null && t.info is null && cast(byte*) t !is typeid(t).initializer.ptr) { @@ -39,4 +62,3 @@ else version (Posix) public import rt.deh_win64_posix; else static assert (0, "Unsupported architecture"); - diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index e6acbd5..328452e 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -1,12 +1,12 @@ /** * Contains druntime startup and shutdown routines. * - * Copyright: Copyright Digital Mars 2000 - 2013. + * Copyright: Copyright Digital Mars 2000 - 2018. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_dmain2.d) + * Source: $(DRUNTIMESRC rt/_dmain2.d) */ /* NOTE: This file has been patched from the original DMD distribution to @@ -14,22 +14,27 @@ */ module rt.dmain2; -private -{ - import rt.memory; - import rt.sections; - import core.atomic; - import core.stdc.stddef; - import core.stdc.stdlib; - import core.stdc.string; - import core.stdc.stdio; // for printf() - import core.stdc.errno : errno; -} +import rt.memory; +import rt.sections; +import core.atomic; +import core.stdc.stddef; +import core.stdc.stdlib; +import core.stdc.string; +import core.stdc.stdio; // for printf() +import core.stdc.errno : errno; version (Windows) { - private import core.stdc.wchar_; - private import core.sys.windows.windows; + import core.stdc.wchar_; + import core.sys.windows.basetsd : HANDLE; + import core.sys.windows.shellapi : CommandLineToArgvW; + import core.sys.windows.winbase : FreeLibrary, GetCommandLineW, GetProcAddress, + IsDebuggerPresent, LoadLibraryW, LocalFree, WriteFile; + import core.sys.windows.wincon : CONSOLE_SCREEN_BUFFER_INFO, GetConsoleOutputCP, + GetConsoleScreenBufferInfo; + import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar, WideCharToMultiByte; + import core.sys.windows.winnt : WCHAR; + import core.sys.windows.winuser : MB_ICONERROR, MessageBoxW; pragma(lib, "shell32.lib"); // needed for CommandLineToArgvW } @@ -47,27 +52,33 @@ version (DragonFlyBSD) import core.stdc.fenv; } +// not sure why we can't define this in one place, but this is to keep this +// module from importing core.runtime. +struct UnitTestResult +{ + size_t executed; + size_t passed; + bool runMain; + bool summarize; +} + extern (C) void _d_monitor_staticctor(); extern (C) void _d_monitor_staticdtor(); extern (C) void _d_critical_init(); extern (C) void _d_critical_term(); extern (C) void gc_init(); extern (C) void gc_term(); +extern (C) void thread_init() @nogc; +extern (C) void thread_term() @nogc; extern (C) void lifetime_init(); extern (C) void rt_moduleCtor(); extern (C) void rt_moduleTlsCtor(); extern (C) void rt_moduleDtor(); extern (C) void rt_moduleTlsDtor(); extern (C) void thread_joinAll(); -extern (C) bool runModuleUnitTests(); +extern (C) UnitTestResult runModuleUnitTests(); extern (C) void _d_initMonoTime(); -version (OSX) -{ - // The bottom of the stack - extern (C) __gshared void* __osx_stack_end = cast(void*)0xC0000000; -} - version (CRuntime_Microsoft) { extern(C) void init_msvc(); @@ -83,9 +94,6 @@ extern (C) string[] rt_args() return _d_args; } -// make arguments passed to main available for being filtered by runtime initializers -extern(C) __gshared char[][] _d_main_args = null; - // This variable is only ever set by a debugger on initialization so it should // be fine to leave it as __gshared. extern (C) __gshared bool rt_trapExceptions = true; @@ -123,7 +131,8 @@ extern (C) int rt_init() // this initializes mono time before anything else to allow usage // in other druntime systems. _d_initMonoTime(); - gc_init(); + thread_init(); + // TODO: fixme - calls GC.addRange -> Initializes GC initStaticDataGC(); lifetime_init(); rt_moduleCtor(); @@ -132,7 +141,7 @@ extern (C) int rt_init() } catch (Throwable t) { - _initCount = 0; + atomicStore!(MemoryOrder.raw)(_initCount, 0); _d_print_throwable(t); } _d_critical_term(); @@ -145,7 +154,7 @@ extern (C) int rt_init() */ extern (C) int rt_term() { - if (!_initCount) return 0; // was never initialized + if (atomicLoad!(MemoryOrder.raw)(_initCount) == 0) return 0; // was never initialized if (atomicOp!"-="(_initCount, 1)) return 1; try @@ -154,6 +163,7 @@ extern (C) int rt_term() thread_joinAll(); rt_moduleDtor(); gc_term(); + thread_term(); return 1; } catch (Throwable t) @@ -234,74 +244,22 @@ extern (C) CArgs rt_cArgs() @nogc return _cArgs; } -/*********************************** - * Run the given main function. - * Its purpose is to wrap the D main() - * function and catch any unhandled exceptions. - */ +/// Type of the D main() function (`_Dmain`). private alias extern(C) int function(char[][] args) MainFunc; -extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) +/** + * Sets up the D char[][] command-line args, initializes druntime, + * runs embedded unittests and then runs the given D main() function, + * optionally catching and printing any unhandled exceptions. + */ +extern (C) int _d_run_main(int argc, char** argv, MainFunc mainFunc) { + // Set up _cArgs and array of D char[] slices, then forward to _d_run_main2 + // Remember the original C argc/argv _cArgs.argc = argc; _cArgs.argv = argv; - int result; - - version (OSX) - { /* OSX does not provide a way to get at the top of the - * stack, except for the magic value 0xC0000000. - * But as far as the gc is concerned, argv is at the top - * of the main thread's stack, so save the address of that. - */ - __osx_stack_end = cast(void*)&argv; - } - - version (FreeBSD) version (D_InlineAsm_X86) - { - /* - * FreeBSD/i386 sets the FPU precision mode to 53 bit double. - * Make it 64 bit extended. - */ - ushort fpucw; - asm - { - fstsw fpucw; - or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw fpucw; - } - } - version (CRuntime_Microsoft) - { - // enable full precision for reals - version (D_InlineAsm_X86_64) - { - asm - { - push RAX; - fstcw word ptr [RSP]; - or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw word ptr [RSP]; - pop RAX; - } - } - else version (D_InlineAsm_X86) - { - asm - { - push EAX; - fstcw word ptr [ESP]; - or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision - // 111111: mask all FP exceptions - fldcw word ptr [ESP]; - pop EAX; - } - } - } - version (Windows) { /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that, @@ -309,10 +267,10 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) * Then, reparse into wargc/wargs, and then use Windows API to convert * to UTF-8. */ - const wchar_t* wCommandLine = GetCommandLineW(); + const wCommandLine = GetCommandLineW(); immutable size_t wCommandLineLength = wcslen(wCommandLine); int wargc; - wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc); + auto wargs = CommandLineToArgvW(wCommandLine, &wargc); // assert(wargc == argc); /* argc can be broken by Unicode arguments */ // Allocate args[] on the stack - use wargc @@ -357,6 +315,114 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) else static assert(0); + return _d_run_main2(args, totalArgsLength, mainFunc); +} + +/** + * Windows-specific version for wide command-line arguments, e.g., + * from a wmain/wWinMain C entry point. + * This wide version uses the specified arguments, unlike narrow + * _d_run_main which uses the actual (wide) process arguments instead. + */ +version (Windows) +extern (C) int _d_wrun_main(int argc, wchar** wargv, MainFunc mainFunc) +{ + // Allocate args[] on the stack + char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; + + // 1st pass: compute each argument's length as UTF-16 and UTF-8 + size_t totalArgsLength = 0; + foreach (i; 0 .. argc) + { + const warg = wargv[i]; + const size_t wlen = wcslen(warg) + 1; // incl. terminating null + assert(wlen <= cast(size_t) int.max, "wlen cannot exceed int.max"); + const int len = WideCharToMultiByte(CP_UTF8, 0, warg, cast(int) wlen, null, 0, null, null); + args[i] = (cast(char*) wlen)[0 .. len]; // args[i].ptr = wlen, args[i].length = len + totalArgsLength += len; + } + + // Allocate a single buffer for all (null-terminated) argument strings in UTF-8 on the stack + char* utf8Buffer = cast(char*) alloca(totalArgsLength); + + // 2nd pass: convert to UTF-8 and finalize `args` + char* utf8 = utf8Buffer; + foreach (i; 0 .. argc) + { + const wlen = cast(int) args[i].ptr; + const len = cast(int) args[i].length; + WideCharToMultiByte(CP_UTF8, 0, wargv[i], wlen, utf8, len, null, null); + args[i] = utf8[0 .. len-1]; // excl. terminating null + utf8 += len; + } + + // Set C argc/argv; argv is a new stack-allocated array of UTF-8 C strings + char*[] argv = (cast(char**) alloca(argc * (char*).sizeof))[0 .. argc]; + foreach (i, ref arg; argv) + arg = args[i].ptr; + _cArgs.argc = argc; + _cArgs.argv = argv.ptr; + + totalArgsLength -= argc; // excl. null terminator per arg + return _d_run_main2(args, totalArgsLength, mainFunc); +} + +private extern (C) int _d_run_main2(char[][] args, size_t totalArgsLength, MainFunc mainFunc) +{ + int result; + + version (FreeBSD) version (D_InlineAsm_X86) + { + /* + * FreeBSD/i386 sets the FPU precision mode to 53 bit double. + * Make it 64 bit extended. + */ + ushort fpucw; + asm + { + fstsw fpucw; + or fpucw, 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw fpucw; + } + } + version (CRuntime_Microsoft) + { + // enable full precision for reals + version (D_InlineAsm_X86_64) + { + asm + { + push RAX; + fstcw word ptr [RSP]; + or [RSP], 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw word ptr [RSP]; + pop RAX; + } + } + else version (D_InlineAsm_X86) + { + asm + { + push EAX; + fstcw word ptr [ESP]; + or [ESP], 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + fldcw word ptr [ESP]; + pop EAX; + } + } + else version (GNU_InlineAsm) + { + size_t fpu_cw; + asm { "fstcw %0" : "=m" (fpu_cw); } + fpu_cw |= 0b11_00_111111; // 11: use 64 bit extended-precision + // 111111: mask all FP exceptions + asm { "fldcw %0" : "=m" (fpu_cw); } + } + } + /* Create a copy of args[] on the stack to be used for main, so that rt_args() * cannot be modified by the user. * Note that when this function returns, _d_args will refer to garbage. @@ -368,28 +434,33 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) char[][] argsCopy = buff[0 .. args.length]; auto argBuff = cast(char*) (buff + args.length); size_t j = 0; + import rt.config : rt_cmdline_enabled; + bool parseOpts = rt_cmdline_enabled!(); foreach (arg; args) { - if (arg.length < 6 || arg[0..6] != "--DRT-") // skip D runtime options - { - argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); - argBuff += arg.length; - } + // Do not pass Druntime options to the program + if (parseOpts && arg.length >= 6 && arg[0 .. 6] == "--DRT-") + continue; + // https://issues.dlang.org/show_bug.cgi?id=20459 + if (arg == "--") + parseOpts = false; + argsCopy[j++] = (argBuff[0 .. arg.length] = arg[]); + argBuff += arg.length; } args = argsCopy[0..j]; } - bool trapExceptions = rt_trapExceptions; + auto useExceptionTrap = parseExceptionOptions(); version (Windows) { if (IsDebuggerPresent()) - trapExceptions = false; + useExceptionTrap = false; } void tryExec(scope void delegate() dg) { - if (trapExceptions) + if (useExceptionTrap) { try { @@ -417,8 +488,34 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) // thrown during cleanup, however, will abort the cleanup process. void runAll() { - if (rt_init() && runModuleUnitTests()) - tryExec({ result = mainFunc(args); }); + if (rt_init()) + { + auto utResult = runModuleUnitTests(); + assert(utResult.passed <= utResult.executed); + if (utResult.passed == utResult.executed) + { + if (utResult.summarize) + { + if (utResult.passed == 0) + .fprintf(.stderr, "No unittests run\n"); + else + .fprintf(.stderr, "%d modules passed unittests\n", + cast(int)utResult.passed); + } + if (utResult.runMain) + tryExec({ result = mainFunc(args); }); + else + result = EXIT_SUCCESS; + } + else + { + if (utResult.summarize) + .fprintf(.stderr, "%d/%d modules FAILED unittests\n", + cast(int)(utResult.executed - utResult.passed), + cast(int)utResult.executed); + result = EXIT_FAILURE; + } + } else result = EXIT_FAILURE; @@ -441,17 +538,17 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) return result; } -private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothrow sink) +private void formatThrowable(Throwable t, scope void delegate(const scope char[] s) nothrow sink) { - for (; t; t = t.next) + foreach (u; t) { - t.toString(sink); sink("\n"); + u.toString(sink); sink("\n"); - auto e = cast(Error)t; + auto e = cast(Error)u; if (e is null || e.bypassedException is null) continue; sink("=== Bypassed ===\n"); - for (auto t2 = e.bypassedException; t2; t2 = t2.next) + foreach (t2; e.bypassedException) { t2.toString(sink); sink("\n"); } @@ -459,6 +556,18 @@ private void formatThrowable(Throwable t, scope void delegate(in char[] s) nothr } } +private auto parseExceptionOptions() +{ + import rt.config : rt_configOption; + import core.internal.parseoptions : rt_parseOption; + const optName = "trapExceptions"; + auto option = rt_configOption(optName); + auto trap = rt_trapExceptions; + if (option.length) + rt_parseOption(optName, option, trap, ""); + return trap; +} + extern (C) void _d_print_throwable(Throwable t) { // On Windows, a console may not be present to print the output to. @@ -468,17 +577,17 @@ extern (C) void _d_print_throwable(Throwable t) { static struct WSink { - wchar_t* ptr; size_t len; + WCHAR* ptr; size_t len; - void sink(in char[] s) scope nothrow + void sink(const scope char[] s) scope nothrow { if (!s.length) return; int swlen = MultiByteToWideChar( CP_UTF8, 0, s.ptr, cast(int)s.length, null, 0); if (!swlen) return; - auto newPtr = cast(wchar_t*)realloc(ptr, - (this.len + swlen + 1) * wchar_t.sizeof); + auto newPtr = cast(WCHAR*)realloc(ptr, + (this.len + swlen + 1) * WCHAR.sizeof); if (!newPtr) return; ptr = newPtr; auto written = MultiByteToWideChar( @@ -486,7 +595,7 @@ extern (C) void _d_print_throwable(Throwable t) len += written; } - wchar_t* get() { if (ptr) ptr[len] = 0; return ptr; } + typeof(ptr) get() { if (ptr) ptr[len] = 0; return ptr; } void free() { .free(ptr); } } @@ -558,7 +667,7 @@ extern (C) void _d_print_throwable(Throwable t) } } - void sink(in char[] buf) scope nothrow + void sink(const scope char[] buf) scope nothrow { fprintf(stderr, "%.*s", cast(int)buf.length, buf.ptr); } diff --git a/libphobos/libdruntime/rt/dylib_fixes.c b/libphobos/libdruntime/rt/dylib_fixes.c index 7bcf34b..e484fed 100644 --- a/libphobos/libdruntime/rt/dylib_fixes.c +++ b/libphobos/libdruntime/rt/dylib_fixes.c @@ -2,7 +2,7 @@ * OS X support for dynamic libraries. * * Copyright: Copyright Digital Mars 2010 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright */ diff --git a/libphobos/libdruntime/rt/ehalloc.d b/libphobos/libdruntime/rt/ehalloc.d new file mode 100644 index 0000000..1dcd69a --- /dev/null +++ b/libphobos/libdruntime/rt/ehalloc.d @@ -0,0 +1,125 @@ +/** + * Exception allocation, cloning, and release compiler support routines. + * + * Copyright: Copyright (c) 2017 by D Language Foundation + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Walter Bright + * Source: $(DRUNTIMESRC rt/_ehalloc.d) + */ + +module rt.ehalloc; + +//debug = PRINTF; + +debug(PRINTF) +{ + import core.stdc.stdio; +} + +/********************************************** + * Allocate an exception of type `ci` from the exception pool. + * It has the same interface as `rt.lifetime._d_newclass()`. + * The class type must be Throwable or derived from it, + * and cannot be a COM or C++ class. The compiler must enforce + * this. + * Returns: + * default initialized instance of the type + */ + +extern (C) Throwable _d_newThrowable(const TypeInfo_Class ci) +{ + debug(PRINTF) printf("_d_newThrowable(ci = %p, %s)\n", ci, cast(char *)ci.name); + + assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass)); + assert(!(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass)); + + import core.stdc.stdlib : malloc; + auto init = ci.initializer; + void* p = malloc(init.length); + if (!p) + { + import core.exception : onOutOfMemoryError; + onOutOfMemoryError(); + } + + debug(PRINTF) printf(" p = %p\n", p); + + // initialize it + p[0 .. init.length] = init[]; + + if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)) + { + // Inform the GC about the pointers in the object instance + import core.memory : GC; + + GC.addRange(p, init.length, ci); + } + + debug(PRINTF) printf("initialization done\n"); + Throwable t = cast(Throwable)p; + t.refcount() = 1; + return t; +} + + +/******************************************** + * Delete exception instance `t` from the exception pool. + * Must have been allocated with `_d_newThrowable()`. + * This is meant to be called at the close of a catch block. + * It's nothrow because otherwise any function with a catch block could + * not be nothrow. + * Input: + * t = Throwable + */ + +nothrow extern (C) void _d_delThrowable(Throwable t) +{ + if (t) + { + debug(PRINTF) printf("_d_delThrowable(%p)\n", t); + + /* If allocated by the GC, don't free it. + * Let the GC handle it. + * Supporting this is necessary while transitioning + * to this new scheme for allocating exceptions. + */ + auto refcount = t.refcount(); + if (refcount == 0) + return; // it was allocated by the GC + + if (refcount == 1) + assert(0); // no zombie objects + + t.refcount() = --refcount; + if (refcount > 1) + return; + + TypeInfo_Class **pc = cast(TypeInfo_Class **)t; + if (*pc) + { + TypeInfo_Class ci = **pc; + + if (!(ci.m_flags & TypeInfo_Class.ClassFlags.noPointers)) + { + // Inform the GC about the pointers in the object instance + import core.memory : GC; + GC.removeRange(cast(void*) t); + } + } + + try + { + import rt.lifetime : rt_finalize; + rt_finalize(cast(void*) t); + } + catch (Throwable t) + { + assert(0); // should never happen since Throwable.~this() is nothrow + } + import core.stdc.stdlib : free; + debug(PRINTF) printf("free(%p)\n", t); + free(cast(void*) t); + } +} diff --git a/libphobos/libdruntime/rt/invariant.d b/libphobos/libdruntime/rt/invariant.d index 4dddfad..e536196 100644 --- a/libphobos/libdruntime/rt/invariant.d +++ b/libphobos/libdruntime/rt/invariant.d @@ -2,8 +2,9 @@ * Implementation of invariant support routines. * * Copyright: Copyright Digital Mars 2007 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright + * Source: $(DRUNTIMESRC rt/_invariant.d) */ /* Copyright Digital Mars 2007 - 2010. diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index 6a6eb50..f1a9d87 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -7,22 +7,18 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly, Steven Schveighoffer - * Source: $(DRUNTIMESRC src/rt/_lifetime.d) + * Source: $(DRUNTIMESRC rt/_lifetime.d) */ module rt.lifetime; -import core.stdc.stdlib; -import core.stdc.string; -import core.stdc.stdarg; -import core.bitop; +import core.attribute : weak; import core.memory; debug(PRINTF) import core.stdc.stdio; static import rt.tlsgc; alias BlkInfo = GC.BlkInfo; alias BlkAttr = GC.BlkAttr; -import core.exception : onOutOfMemoryError, onFinalizeError, onInvalidMemoryOperationError; private { @@ -52,7 +48,7 @@ extern (C) void lifetime_init() /** * */ -extern (C) void* _d_allocmemory(size_t sz) +extern (C) void* _d_allocmemory(size_t sz) @weak { return GC.malloc(sz); } @@ -60,9 +56,12 @@ extern (C) void* _d_allocmemory(size_t sz) /** * */ -extern (C) Object _d_newclass(const ClassInfo ci) +extern (C) Object _d_newclass(const ClassInfo ci) @weak { + import core.stdc.stdlib; + import core.exception : onOutOfMemoryError; void* p; + auto init = ci.initializer; debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name); if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass) @@ -71,7 +70,7 @@ extern (C) Object _d_newclass(const ClassInfo ci) * function called by Release() when Release()'s reference count goes * to zero. */ - p = malloc(ci.initializer.length); + p = malloc(init.length); if (!p) onOutOfMemoryError(); } @@ -85,26 +84,26 @@ extern (C) Object _d_newclass(const ClassInfo ci) attr |= BlkAttr.FINALIZE; if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers) attr |= BlkAttr.NO_SCAN; - p = GC.malloc(ci.initializer.length, attr, ci); + p = GC.malloc(init.length, attr, ci); debug(PRINTF) printf(" p = %p\n", p); } debug(PRINTF) { printf("p = %p\n", p); - printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, ci.initializer.ptr, cast(ulong)ci.initializer.length); - printf("vptr = %p\n", *cast(void**) ci.initializer); - printf("vtbl[0] = %p\n", (*cast(void***) ci.initializer)[0]); - printf("vtbl[1] = %p\n", (*cast(void***) ci.initializer)[1]); - printf("init[0] = %x\n", (cast(uint*) ci.initializer)[0]); - printf("init[1] = %x\n", (cast(uint*) ci.initializer)[1]); - printf("init[2] = %x\n", (cast(uint*) ci.initializer)[2]); - printf("init[3] = %x\n", (cast(uint*) ci.initializer)[3]); - printf("init[4] = %x\n", (cast(uint*) ci.initializer)[4]); + printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, init.ptr, cast(ulong)init.length); + printf("vptr = %p\n", *cast(void**) init); + printf("vtbl[0] = %p\n", (*cast(void***) init)[0]); + printf("vtbl[1] = %p\n", (*cast(void***) init)[1]); + printf("init[0] = %x\n", (cast(uint*) init)[0]); + printf("init[1] = %x\n", (cast(uint*) init)[1]); + printf("init[2] = %x\n", (cast(uint*) init)[2]); + printf("init[3] = %x\n", (cast(uint*) init)[3]); + printf("init[4] = %x\n", (cast(uint*) init)[4]); } // initialize it - p[0 .. ci.initializer.length] = ci.initializer[]; + p[0 .. init.length] = init[]; debug(PRINTF) printf("initialization done\n"); return cast(Object) p; @@ -134,7 +133,7 @@ private extern (D) alias void function (Object) fp_t; /** * */ -extern (C) void _d_delclass(Object* p) +extern (C) void _d_delclass(Object* p) @weak { if (*p) { @@ -169,7 +168,7 @@ extern (C) void _d_delclass(Object* p) * being deleted is a pointer to a struct with a destructor * but doesn't have an overloaded delete operator. */ -extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) +extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) @weak { if (*p) { @@ -182,7 +181,7 @@ extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) } // strip const/immutable/shared/inout from type info -inout(TypeInfo) unqualify(inout(TypeInfo) cti) pure nothrow @nogc +inout(TypeInfo) unqualify(return inout(TypeInfo) cti) pure nothrow @nogc { TypeInfo ti = cast() cti; while (ti) @@ -382,7 +381,7 @@ size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow /** get the start of the array for the given block */ -void *__arrayStart(BlkInfo info) nothrow pure +void *__arrayStart(return BlkInfo info) nothrow pure { return info.base + ((info.size & BIGLENGTHMASK) ? LARGEPREFIX : 0); } @@ -398,10 +397,26 @@ size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted } /** + clear padding that might not be zeroed by the GC (it assumes it is within the + requested size from the start, but it is actually at the end of the allocated block) + */ +private void __arrayClearPad(ref BlkInfo info, size_t arrsize, size_t padsize) nothrow pure +{ + import core.stdc.string; + if (padsize > MEDPAD && !(info.attr & BlkAttr.NO_SCAN) && info.base) + { + if (info.size < PAGESIZE) + memset(info.base + arrsize, 0, padsize); + else + memset(info.base, 0, LARGEPREFIX); + } +} + +/** allocate an array memory block by applying the proper padding and assigning block attributes if not inherited from the existing block */ -BlkInfo __arrayAlloc(size_t arrsize, const TypeInfo ti, const TypeInfo tinext) nothrow pure +BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure { import core.checkedint; @@ -417,24 +432,30 @@ BlkInfo __arrayAlloc(size_t arrsize, const TypeInfo ti, const TypeInfo tinext) n uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE; if (typeInfoSize) attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; - return GC.qalloc(padded_size, attr, ti); + + auto bi = GC.qalloc(padded_size, attr, tinext); + __arrayClearPad(bi, arrsize, padsize); + return bi; } -BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const TypeInfo ti, const TypeInfo tinext) +BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) { import core.checkedint; if (!info.base) return __arrayAlloc(arrsize, ti, tinext); + immutable padsize = __arrayPad(arrsize, tinext); bool overflow; - auto padded_size = addu(arrsize, __arrayPad(arrsize, tinext), overflow); + auto padded_size = addu(arrsize, padsize, overflow); if (overflow) { return BlkInfo(); } - return GC.qalloc(padded_size, info.attr, ti); + auto bi = GC.qalloc(padded_size, info.attr, tinext); + __arrayClearPad(bi, arrsize, padsize); + return bi; } /** @@ -468,6 +489,8 @@ else { if (!__blkcache_storage) { + import core.stdc.stdlib; + import core.stdc.string; // allocate the block cache for the first time immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; __blkcache_storage = cast(BlkInfo *)malloc(size); @@ -482,6 +505,7 @@ static ~this() // free the blkcache if (__blkcache_storage) { + import core.stdc.stdlib; free(__blkcache_storage); __blkcache_storage = null; } @@ -670,7 +694,10 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ // Note: Since we "assume" the append is safe, it means it is not shared. // Since it is not shared, we also know it won't throw (no lock). if (!__setArrayAllocLength(info, newsize, false, tinext)) + { + import core.exception : onInvalidMemoryOperationError; onInvalidMemoryOperationError(); + } // cache the block if not already done. if (!isshared && !bic) @@ -720,14 +747,17 @@ void __doPostblit(void *ptr, size_t len, const TypeInfo ti) * of 0 to get the current capacity. Returns the number of elements that can * actually be stored once the resizing is done. */ -extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) +extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak in { assert(ti); assert(!(*p).length || (*p).ptr); } -body +do { + import core.stdc.string; + import core.exception : onOutOfMemoryError; + // step 1, get the block auto isshared = typeid(ti) is typeid(TypeInfo_Shared); auto bic = isshared ? null : __getBlkInfo((*p).ptr); @@ -890,8 +920,10 @@ Lcontinue: * Allocate a new uninitialized array of length elements. * ti is the type of the resulting array, or pointer to element. */ -extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow +extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak { + import core.exception : onOutOfMemoryError; + auto tinext = unqualify(ti.next); auto size = tinext.tsize; @@ -949,8 +981,10 @@ Lcontinue: * ti is the type of the resulting array, or pointer to element. * (For when the array is initialized to 0) */ -extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow +extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak { + import core.stdc.string; + void[] result = _d_newarrayU(ti, length); auto tinext = unqualify(ti.next); auto size = tinext.tsize; @@ -962,7 +996,7 @@ extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow /** * For when the array has a non-zero initializer. */ -extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow +extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak { import core.internal.traits : AliasSeq; @@ -983,6 +1017,7 @@ extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow default: { + import core.stdc.string; immutable sz = init.length; for (size_t u = 0; u < size * length; u += sz) memcpy(result.ptr + u, init.ptr, sz); @@ -1036,7 +1071,7 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) /** * */ -extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) +extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length); @@ -1052,7 +1087,7 @@ extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) /** * */ -extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) +extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak { debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length); @@ -1068,12 +1103,13 @@ extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) * Allocate an uninitialized non-array item. * This is an optimization to avoid things needed for arrays like the __arrayPad(size). */ -extern (C) void* _d_newitemU(in TypeInfo _ti) +extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak { auto ti = unqualify(_ti); auto flags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0; immutable tiSize = structTypeInfoSize(ti); - immutable size = ti.tsize + tiSize; + immutable itemSize = ti.tsize; + immutable size = itemSize + tiSize; if (tiSize) flags |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; @@ -1081,22 +1117,27 @@ extern (C) void* _d_newitemU(in TypeInfo _ti) auto p = blkInf.base; if (tiSize) + { + *cast(TypeInfo*)(p + itemSize) = null; // the GC might not have cleared this area *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti; + } return p; } /// Same as above, zero initializes the item. -extern (C) void* _d_newitemT(in TypeInfo _ti) +extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak { + import core.stdc.string; auto p = _d_newitemU(_ti); memset(p, 0, _ti.tsize); return p; } /// Same as above, for item with non-zero initializer. -extern (C) void* _d_newitemiT(in TypeInfo _ti) +extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak { + import core.stdc.string; auto p = _d_newitemU(_ti); auto init = _ti.initializer(); assert(init.length <= _ti.tsize); @@ -1113,15 +1154,6 @@ struct Array byte* data; } - -/** - * This function has been replaced by _d_delarray_t - */ -extern (C) void _d_delarray(void[]* p) -{ - _d_delarray_t(p, null); -} - debug(PRINTF) { extern(C) void printArrayCache() @@ -1138,7 +1170,7 @@ debug(PRINTF) /** * */ -extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) +extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) @weak { if (p) { @@ -1164,7 +1196,7 @@ extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti) } } -unittest +deprecated unittest { __gshared size_t countDtor = 0; struct S @@ -1176,7 +1208,7 @@ unittest auto x = new S[10000]; void* p = x.ptr; assert(GC.addrOf(p) != null); - delete x; + _d_delarray_t(cast(void[]*)&x, typeid(typeof(x[0]))); // delete x; assert(GC.addrOf(p) == null); assert(countDtor == 10000); @@ -1185,7 +1217,7 @@ unittest auto z = y[200 .. 300]; p = z.ptr; assert(GC.addrOf(p) != null); - delete z; + _d_delarray_t(cast(void[]*)&z, typeid(typeof(z[0]))); // delete z; assert(GC.addrOf(p) == null); assert(countDtor == 10000 + 400); } @@ -1193,7 +1225,7 @@ unittest /** * */ -extern (C) void _d_delmemory(void* *p) +extern (C) void _d_delmemory(void* *p) @weak { if (*p) { @@ -1206,7 +1238,7 @@ extern (C) void _d_delmemory(void* *p) /** * */ -extern (C) void _d_callinterfacefinalizer(void *p) +extern (C) void _d_callinterfacefinalizer(void *p) @weak { if (p) { @@ -1220,7 +1252,7 @@ extern (C) void _d_callinterfacefinalizer(void *p) /** * */ -extern (C) void _d_callfinalizer(void* p) +extern (C) void _d_callfinalizer(void* p) @weak { rt_finalize( p ); } @@ -1324,6 +1356,7 @@ void finalize_array2(void* p, size_t size) nothrow } catch (Exception e) { + import core.exception : onFinalizeError; onFinalizeError(si, e); } } @@ -1353,6 +1386,7 @@ void finalize_struct(void* p, size_t size) nothrow } catch (Exception e) { + import core.exception : onFinalizeError; onFinalizeError(ti, e); } } @@ -1393,6 +1427,7 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) } catch (Exception e) { + import core.exception : onFinalizeError; onFinalizeError(*pc, e); } finally @@ -1401,12 +1436,12 @@ extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) } } -extern (C) void rt_finalize(void* p, bool det = true) +extern (C) void rt_finalize(void* p, bool det = true) nothrow { rt_finalize2(p, det, true); } -extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) +extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow { // to verify: reset memory necessary? if (!(attr & BlkAttr.STRUCTFINAL)) @@ -1421,14 +1456,17 @@ extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) /** * Resize dynamic arrays with 0 initializers. */ -extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) +extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { assert(ti); assert(!(*p).length || (*p).ptr); } -body +do { + import core.stdc.string; + import core.exception : onOutOfMemoryError; + debug(PRINTF) { //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); @@ -1436,170 +1474,179 @@ body printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); } - void* newdata = void; - if (newlength) + if (newlength <= (*p).length) { - if (newlength <= (*p).length) + *p = (*p)[0 .. newlength]; + void* newdata = (*p).ptr; + return newdata[0 .. newlength]; + } + auto tinext = unqualify(ti.next); + size_t sizeelem = tinext.tsize; + + /* Calculate: newsize = newlength * sizeelem + */ + bool overflow = false; + version (D_InlineAsm_X86) + { + size_t newsize = void; + + asm pure nothrow @nogc { - *p = (*p)[0 .. newlength]; - newdata = (*p).ptr; - return newdata[0 .. newlength]; + mov EAX, newlength; + mul EAX, sizeelem; + mov newsize, EAX; + setc overflow; } - auto tinext = unqualify(ti.next); - size_t sizeelem = tinext.tsize; - version (D_InlineAsm_X86) - { - size_t newsize = void; + } + else version (D_InlineAsm_X86_64) + { + size_t newsize = void; - asm pure nothrow @nogc - { - mov EAX, newlength; - mul EAX, sizeelem; - mov newsize, EAX; - jc Loverflow; - } - } - else version (D_InlineAsm_X86_64) + asm pure nothrow @nogc { - size_t newsize = void; - - asm pure nothrow @nogc - { - mov RAX, newlength; - mul RAX, sizeelem; - mov newsize, RAX; - jc Loverflow; - } + mov RAX, newlength; + mul RAX, sizeelem; + mov newsize, RAX; + setc overflow; } - else - { - import core.checkedint : mulu; + } + else + { + import core.checkedint : mulu; + const size_t newsize = mulu(sizeelem, newlength, overflow); + } + if (overflow) + { + onOutOfMemoryError(); + assert(0); + } - bool overflow = false; - size_t newsize = mulu(sizeelem, newlength, overflow); - if (overflow) - goto Loverflow; - } + debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); - debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); + const isshared = typeid(ti) is typeid(TypeInfo_Shared); + + if (!(*p).ptr) + { + // pointer was null, need to allocate + auto info = __arrayAlloc(newsize, ti, tinext); + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); + } + __setArrayAllocLength(info, newsize, isshared, tinext); + if (!isshared) + __insertBlkInfoCache(info, null); + void* newdata = cast(byte *)__arrayStart(info); + memset(newdata, 0, newsize); + *p = newdata[0 .. newlength]; + return *p; + } - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); + const size_t size = (*p).length * sizeelem; + auto bic = isshared ? null : __getBlkInfo((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); - if ((*p).ptr) + /* Attempt to extend past the end of the existing array. + * If not possible, allocate new space for entire array and copy. + */ + bool allocateAndCopy = false; + void* newdata = (*p).ptr; + if (info.base && (info.attr & BlkAttr.APPENDABLE)) + { + // calculate the extent of the array given the base. + const size_t offset = (*p).ptr - __arrayStart(info); + if (info.size >= PAGESIZE) { - newdata = (*p).ptr; - if (newlength > (*p).length) + // size of array is at the front of the block + if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - size_t size = (*p).length * sizeelem; - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) + // check to see if it failed because there is not + // enough space + if (*(cast(size_t*)info.base) == size + offset) { - // calculate the extent of the array given the base. - size_t offset = (*p).ptr - __arrayStart(info); - if (info.size >= PAGESIZE) + // not enough space, try extending + auto extendsize = newsize + offset + LARGEPAD - info.size; + auto u = GC.extend(info.base, extendsize, extendsize); + if (u) { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - goto L1; - } - } - } - - // couldn't do it, reallocate - goto L2; - } - else if (!isshared && !bic) + // extend worked, now try setting the length + // again. + info.size = u; + if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); + if (!isshared) + __insertBlkInfoCache(info, bic); + memset(newdata + size, 0, newsize - size); + *p = newdata[0 .. newlength]; + return *p; } } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - goto L2; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } } - else - { - if (info.base) - { - L2: - if (bic) - { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; - } - info = __arrayAlloc(newsize, info, ti, tinext); - } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - if (info.base is null) - goto Loverflow; - - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); - newdata[0 .. size] = (*p).ptr[0 .. size]; - - // do postblit processing - __doPostblit(newdata, size, tinext); - } - L1: - memset(newdata + size, 0, newsize - size); + // couldn't do it, reallocate + allocateAndCopy = true; + } + else if (!isshared && !bic) + { + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } - else + else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // pointer was null, need to allocate - auto info = __arrayAlloc(newsize, ti, tinext); - if (info.base is null) - goto Loverflow; - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, null); - newdata = cast(byte *)__arrayStart(info); - memset(newdata, 0, newsize); + // could not resize in place + allocateAndCopy = true; + } + else if (!isshared && !bic) + { + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } else + allocateAndCopy = true; + + if (allocateAndCopy) { - newdata = (*p).ptr; + if (info.base) + { + if (bic) + { + // a chance that flags have changed since this was cached, we should fetch the most recent flags + info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; + } + info = __arrayAlloc(newsize, info, ti, tinext); + } + else + { + info = __arrayAlloc(newsize, ti, tinext); + } + + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); + } + + __setArrayAllocLength(info, newsize, isshared, tinext); + if (!isshared) + __insertBlkInfoCache(info, bic); + newdata = cast(byte *)__arrayStart(info); + newdata[0 .. size] = (*p).ptr[0 .. size]; + + /* Do postblit processing, as we are making a copy and the + * original array may have references. + * Note that this may throw. + */ + __doPostblit(newdata, size, tinext); } + // Zero the unused portion of the newly allocated space + memset(newdata + size, 0, newsize - size); + *p = newdata[0 .. newlength]; return *p; - -Loverflow: - onOutOfMemoryError(); - assert(0); } @@ -1611,205 +1658,221 @@ Loverflow: * initsize size of initializer * ... initializer */ -extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) +extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak in { assert(!(*p).length || (*p).ptr); } -body +do { - void* newdata; - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; - auto initializer = tinext.initializer(); - auto initsize = initializer.length; - - assert(sizeelem); - assert(initsize); - assert(initsize <= sizeelem); - assert((sizeelem / initsize) * initsize == sizeelem); + import core.stdc.string; + import core.exception : onOutOfMemoryError; debug(PRINTF) { - printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d, initsize = %d)\n", p, sizeelem, newlength, initsize); + //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); if (p) - printf("\tp.data = %p, p.length = %d\n", (*p).ptr, (*p).length); + printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); } - if (newlength) + if (newlength <= (*p).length) { - version (D_InlineAsm_X86) - { - size_t newsize = void; + *p = (*p)[0 .. newlength]; + void* newdata = (*p).ptr; + return newdata[0 .. newlength]; + } + auto tinext = unqualify(ti.next); + size_t sizeelem = tinext.tsize; - asm - { - mov EAX,newlength ; - mul EAX,sizeelem ; - mov newsize,EAX ; - jc Loverflow ; - } + /* Calculate: newsize = newlength * sizeelem + */ + bool overflow = false; + version (D_InlineAsm_X86) + { + size_t newsize = void; + + asm pure nothrow @nogc + { + mov EAX, newlength; + mul EAX, sizeelem; + mov newsize, EAX; + setc overflow; } - else version (D_InlineAsm_X86_64) + } + else version (D_InlineAsm_X86_64) + { + size_t newsize = void; + + asm pure nothrow @nogc { - size_t newsize = void; + mov RAX, newlength; + mul RAX, sizeelem; + mov newsize, RAX; + setc overflow; + } + } + else + { + import core.checkedint : mulu; + const size_t newsize = mulu(sizeelem, newlength, overflow); + } + if (overflow) + { + onOutOfMemoryError(); + assert(0); + } - asm - { - mov RAX,newlength ; - mul RAX,sizeelem ; - mov newsize,RAX ; - jc Loverflow ; - } + debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); + + const isshared = typeid(ti) is typeid(TypeInfo_Shared); + + static void doInitialize(void *start, void *end, const void[] initializer) + { + if (initializer.length == 1) + { + memset(start, *(cast(ubyte*)initializer.ptr), end - start); } else { - import core.checkedint : mulu; + auto q = initializer.ptr; + immutable initsize = initializer.length; + for (; start < end; start += initsize) + { + memcpy(start, q, initsize); + } + } + } - bool overflow = false; - size_t newsize = mulu(sizeelem, newlength, overflow); - if (overflow) - goto Loverflow; + if (!(*p).ptr) + { + // pointer was null, need to allocate + auto info = __arrayAlloc(newsize, ti, tinext); + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); } - debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); + __setArrayAllocLength(info, newsize, isshared, tinext); + if (!isshared) + __insertBlkInfoCache(info, null); + void* newdata = cast(byte *)__arrayStart(info); + doInitialize(newdata, newdata + newsize, tinext.initializer); + *p = newdata[0 .. newlength]; + return *p; + } + const size_t size = (*p).length * sizeelem; + auto bic = isshared ? null : __getBlkInfo((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); - size_t size = (*p).length * sizeelem; - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - if ((*p).ptr) + /* Attempt to extend past the end of the existing array. + * If not possible, allocate new space for entire array and copy. + */ + bool allocateAndCopy = false; + void* newdata = (*p).ptr; + + if (info.base && (info.attr & BlkAttr.APPENDABLE)) + { + // calculate the extent of the array given the base. + const size_t offset = (*p).ptr - __arrayStart(info); + if (info.size >= PAGESIZE) { - newdata = (*p).ptr; - if (newlength > (*p).length) + // size of array is at the front of the block + if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - auto bic = isshared ? null : __getBlkInfo((*p).ptr); - auto info = bic ? *bic : GC.query((*p).ptr); - - // calculate the extent of the array given the base. - size_t offset = (*p).ptr - __arrayStart(info); - if (info.base && (info.attr & BlkAttr.APPENDABLE)) - { - if (info.size >= PAGESIZE) - { - // size of array is at the front of the block - if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // check to see if it failed because there is not - // enough space - if (*(cast(size_t*)info.base) == size + offset) - { - // not enough space, try extending - auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = GC.extend(info.base, extendsize, extendsize); - if (u) - { - // extend worked, now try setting the length - // again. - info.size = u; - if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - if (!isshared) - __insertBlkInfoCache(info, bic); - goto L1; - } - } - } - - // couldn't do it, reallocate - goto L2; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) - { - // could not resize in place - goto L2; - } - else if (!isshared && !bic) - { - // add this to the cache, it wasn't present previously. - __insertBlkInfoCache(info, null); - } - } - else + // check to see if it failed because there is not + // enough space + if (*(cast(size_t*)info.base) == size + offset) { - // not appendable or not part of the heap yet. - if (info.base) + // not enough space, try extending + auto extendsize = newsize + offset + LARGEPAD - info.size; + auto u = GC.extend(info.base, extendsize, extendsize); + if (u) { - L2: - if (bic) + // extend worked, now try setting the length + // again. + info.size = u; + if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // a chance that flags have changed since this was cached, we should fetch the most recent flags - info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; + if (!isshared) + __insertBlkInfoCache(info, bic); + doInitialize(newdata + size, newdata + newsize, tinext.initializer); + *p = newdata[0 .. newlength]; + return *p; } - info = __arrayAlloc(newsize, info, ti, tinext); } - else - { - info = __arrayAlloc(newsize, ti, tinext); - } - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, bic); - newdata = cast(byte *)__arrayStart(info); - newdata[0 .. size] = (*p).ptr[0 .. size]; - - // do postblit processing - __doPostblit(newdata, size, tinext); } - L1: ; + + // couldn't do it, reallocate + allocateAndCopy = true; + } + else if (!isshared && !bic) + { + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } - else + else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) { - // length was zero, need to allocate - auto info = __arrayAlloc(newsize, ti, tinext); - __setArrayAllocLength(info, newsize, isshared, tinext); - if (!isshared) - __insertBlkInfoCache(info, null); - newdata = cast(byte *)__arrayStart(info); + // could not resize in place + allocateAndCopy = true; } - - auto q = initializer.ptr; // pointer to initializer - - if (newsize > size) + else if (!isshared && !bic) { - if (initsize == 1) - { - debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q); - memset(newdata + size, *cast(byte*)q, newsize - size); - } - else - { - for (size_t u = size; u < newsize; u += initsize) - { - memcpy(newdata + u, q, initsize); - } - } + // add this to the cache, it wasn't present previously. + __insertBlkInfoCache(info, null); } } else + allocateAndCopy = true; + + if (allocateAndCopy) { - newdata = (*p).ptr; + if (info.base) + { + if (bic) + { + // a chance that flags have changed since this was cached, we should fetch the most recent flags + info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; + } + info = __arrayAlloc(newsize, info, ti, tinext); + } + else + { + info = __arrayAlloc(newsize, ti, tinext); + } + + if (info.base is null) + { + onOutOfMemoryError(); + assert(0); + } + + __setArrayAllocLength(info, newsize, isshared, tinext); + if (!isshared) + __insertBlkInfoCache(info, bic); + newdata = cast(byte *)__arrayStart(info); + newdata[0 .. size] = (*p).ptr[0 .. size]; + + /* Do postblit processing, as we are making a copy and the + * original array may have references. + * Note that this may throw. + */ + __doPostblit(newdata, size, tinext); } + // Initialize the unused portion of the newly allocated space + doInitialize(newdata + size, newdata + newsize, tinext.initializer); *p = newdata[0 .. newlength]; return *p; - -Loverflow: - onOutOfMemoryError(); - assert(0); } - /** * Append y[] to array x[] */ -extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) +extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y) @weak { + import core.stdc.string; auto length = x.length; auto tinext = unqualify(ti.next); auto sizeelem = tinext.tsize; // array element size @@ -1879,6 +1942,7 @@ size_t newCapacity(size_t newlength, size_t size) */ //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap)); //long mult = 100 + (1000L * size) / log2plus1(newcap); + import core.bitop; long mult = 100 + (1000L) / (bsr(newcap) + 1); // testing shows 1.02 for large arrays is about the point of diminishing return @@ -1908,8 +1972,9 @@ size_t newCapacity(size_t newlength, size_t size) * Caller must initialize those elements. */ extern (C) -byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) +byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak { + import core.stdc.string; // This is a cut&paste job from _d_arrayappendT(). Should be refactored. // only optimize array append where ti is not a shared type @@ -2011,28 +2076,28 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) /** * Append dchar to char[] */ -extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) +extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 4 characters char[4] buf = void; - byte[] appendthis; // passed to appendT + char[] appendthis; // passed to appendT if (c <= 0x7F) { buf.ptr[0] = cast(char)c; - appendthis = (cast(byte *)buf.ptr)[0..1]; + appendthis = buf[0..1]; } else if (c <= 0x7FF) { buf.ptr[0] = cast(char)(0xC0 | (c >> 6)); buf.ptr[1] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..2]; + appendthis = buf[0..2]; } else if (c <= 0xFFFF) { buf.ptr[0] = cast(char)(0xE0 | (c >> 12)); buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf.ptr[2] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..3]; + appendthis = buf[0..3]; } else if (c <= 0x10FFFF) { @@ -2040,7 +2105,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); buf.ptr[3] = cast(char)(0x80 | (c & 0x3F)); - appendthis = (cast(byte *)buf.ptr)[0..4]; + appendthis = buf[0..4]; } else { @@ -2053,7 +2118,12 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) // get a typeinfo from the compiler. Assuming shared is the safest option. // Once the compiler is fixed, the proper typeinfo should be forwarded. // - return _d_arrayappendT(typeid(shared char[]), x, appendthis); + + // Hack because _d_arrayappendT takes `x` as a reference + auto xx = cast(shared(char)[])x; + object._d_arrayappendTImpl!(shared(char)[])._d_arrayappendT(xx, cast(shared(char)[])appendthis); + x = cast(byte[])xx; + return x; } unittest @@ -2088,25 +2158,21 @@ unittest /** * Append dchar to wchar[] */ -extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) +extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak { // c could encode into from 1 to 2 w characters wchar[2] buf = void; - byte[] appendthis; // passed to appendT + wchar[] appendthis; // passed to appendT if (c <= 0xFFFF) { buf.ptr[0] = cast(wchar) c; - // note that although we are passing only 1 byte here, appendT - // interprets this as being an array of wchar, making the necessary - // casts. - appendthis = (cast(byte *)buf.ptr)[0..1]; + appendthis = buf[0..1]; } else { buf.ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); buf.ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); - // ditto from above. - appendthis = (cast(byte *)buf.ptr)[0..2]; + appendthis = buf[0..2]; } // @@ -2114,14 +2180,18 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) // get a typeinfo from the compiler. Assuming shared is the safest option. // Once the compiler is fixed, the proper typeinfo should be forwarded. // - return _d_arrayappendT(typeid(shared wchar[]), x, appendthis); + + auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length]; + object._d_arrayappendTImpl!(shared(wchar)[])._d_arrayappendT(xx, cast(shared(wchar)[])appendthis); + x = (cast(byte*)xx.ptr)[0 .. xx.length]; + return x; } /** * */ -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) +extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak out (result) { auto tinext = unqualify(ti.next); @@ -2142,8 +2212,9 @@ out (result) size_t cap = GC.sizeOf(result.ptr); assert(!cap || cap > result.length * sizeelem); } -body +do { + import core.stdc.string; version (none) { /* Cannot use this optimization because: @@ -2186,8 +2257,10 @@ body /** * */ -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) +extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak { + import core.stdc.string; + size_t length; auto tinext = unqualify(ti.next); auto size = tinext.tsize; // array element size @@ -2225,7 +2298,7 @@ extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) * Allocate the array, rely on the caller to do the initialization of the array. */ extern (C) -void* _d_arrayliteralTX(const TypeInfo ti, size_t length) +void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak { auto tinext = unqualify(ti.next); auto sizeelem = tinext.tsize; // array element size @@ -2333,7 +2406,7 @@ unittest } // cannot define structs inside unit test block, or they become nested structs. -version (unittest) +version (CoreUnittest) { struct S1 { @@ -2383,12 +2456,22 @@ unittest // Bugzilla 3454 - Inconsistent flag setting in GC.realloc() static void test(size_t multiplier) { - auto p = GC.malloc(8 * multiplier, BlkAttr.NO_SCAN); + auto p = GC.malloc(8 * multiplier, 0); + assert(GC.getAttr(p) == 0); + + // no move, set attr + p = GC.realloc(p, 8 * multiplier + 5, BlkAttr.NO_SCAN); assert(GC.getAttr(p) == BlkAttr.NO_SCAN); - p = GC.realloc(p, 2 * multiplier, BlkAttr.NO_SCAN); + + // shrink, copy attr + p = GC.realloc(p, 2 * multiplier, 0); + assert(GC.getAttr(p) == BlkAttr.NO_SCAN); + + // extend, copy attr + p = GC.realloc(p, 8 * multiplier, 0); assert(GC.getAttr(p) == BlkAttr.NO_SCAN); } - test(1); + test(16); test(1024 * 1024); } @@ -2505,7 +2588,7 @@ unittest // test struct finalizers debug(SENTINEL) {} else -unittest +deprecated unittest { __gshared int dtorCount; static struct S1 @@ -2520,12 +2603,12 @@ unittest dtorCount = 0; S1* s1 = new S1; - delete s1; + _d_delstruct(cast(void**)&s1, typeid(typeof(*s1))); // delete s1; assert(dtorCount == 1); dtorCount = 0; S1[] arr1 = new S1[7]; - delete arr1; + _d_delarray_t(cast(void[]*)&arr1, typeid(typeof(arr1[0]))); // delete arr1; assert(dtorCount == 7); dtorCount = 0; @@ -2596,6 +2679,77 @@ unittest assert(dtorCount == 4); } +// test struct dtor handling not causing false pointers +unittest +{ + // for 64-bit, allocate a struct of size 40 + static struct S + { + size_t[4] data; + S* ptr4; + } + auto p1 = new S; + auto p2 = new S; + p2.ptr4 = p1; + + // a struct with a dtor with size 32, but the dtor will cause + // allocation to be larger by a pointer + static struct A + { + size_t[3] data; + S* ptr3; + + ~this() {} + } + + GC.free(p2); + auto a = new A; // reuse same memory + if (cast(void*)a is cast(void*)p2) // reusage not guaranteed + { + auto ptr = cast(S**)(a + 1); + assert(*ptr != p1); // still same data as p2.ptr4? + } + + // small array + static struct SArr + { + void*[10] data; + } + auto arr1 = new SArr; + arr1.data[] = p1; + GC.free(arr1); + + // allocates 2*A.sizeof + (void*).sizeof (TypeInfo) + 1 (array length) + auto arr2 = new A[2]; + if (cast(void*)arr1 is cast(void*)arr2.ptr) // reusage not guaranteed + { + auto ptr = cast(S**)(arr2.ptr + 2); + assert(*ptr != p1); // still same data as p2.ptr4? + } + + // large array + static struct LArr + { + void*[1023] data; + } + auto larr1 = new LArr; + larr1.data[] = p1; + GC.free(larr1); + + auto larr2 = new S[255]; + if (cast(void*)larr1 is cast(void*)larr2.ptr - LARGEPREFIX) // reusage not guaranteed + { + auto ptr = cast(S**)larr1; + assert(ptr[0] != p1); // 16 bytes array header + assert(ptr[1] != p1); + version (D_LP64) {} else + { + assert(ptr[2] != p1); + assert(ptr[3] != p1); + } + } +} + // test class finalizers exception handling unittest { diff --git a/libphobos/libdruntime/rt/memory.d b/libphobos/libdruntime/rt/memory.d index 220b3d2..99b00c0 100644 --- a/libphobos/libdruntime/rt/memory.d +++ b/libphobos/libdruntime/rt/memory.d @@ -7,7 +7,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_memory.d) + * Source: $(DRUNTIMESRC rt/_memory.d) */ module rt.memory; diff --git a/libphobos/libdruntime/rt/minfo.d b/libphobos/libdruntime/rt/minfo.d index 4722866..0d5cd22 100644 --- a/libphobos/libdruntime/rt/minfo.d +++ b/libphobos/libdruntime/rt/minfo.d @@ -7,7 +7,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_minfo.d) + * Source: $(DRUNTIMESRC rt/_minfo.d) */ module rt.minfo; @@ -165,7 +165,7 @@ struct ModuleGroup void sortCtors(string cycleHandling) { import core.bitop : bts, btr, bt, BitRange; - import rt.util.container.hashtab; + import core.internal.container.hashtab; enum OnCycle { @@ -287,7 +287,7 @@ struct ModuleGroup else enum EOL = "\n"; - sink("Cyclic dependency between module "); + sink("Cyclic dependency between module constructors/destructors of "); sink(_modules[sourceIdx].name); sink(" and "); sink(_modules[cycleIdx].name); @@ -544,7 +544,7 @@ struct ModuleGroup * behavior. * * Params: - * edges - The module edges as found in the `importedModules` member of + * edges = The module edges as found in the `importedModules` member of * each ModuleInfo. Generated in sortCtors. * Returns: * true if no cycle is found, false if one was. @@ -566,7 +566,7 @@ struct ModuleGroup } auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len]; - // TODO: reuse GCBits by moving it to rt.util.container or core.internal + // TODO: reuse GCBits by moving it to core.internal.container immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof); auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof); diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d index 8cb3c3a..6bfce63 100644 --- a/libphobos/libdruntime/rt/monitor_.d +++ b/libphobos/libdruntime/rt/monitor_.d @@ -2,8 +2,9 @@ * Contains the implementation for object monitors. * * Copyright: Copyright Digital Mars 2000 - 2015. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Walter Bright, Sean Kelly, Martin Nowak + * Source: $(DRUNTIMESRC rt/_monitor_.d) */ /* NOTE: This file has been patched from the original DMD distribution to @@ -25,7 +26,7 @@ in { assert(ownee.__monitor is null); } -body +do { auto m = ensureMonitor(cast(Object) owner); if (m.impl is null) @@ -81,7 +82,7 @@ in { assert(h !is null, "Synchronized object must not be null."); } -body +do { auto m = cast(Monitor*) ensureMonitor(h); auto i = m.impl; @@ -185,6 +186,7 @@ version (GNU) version (SingleThreaded) { +@nogc: alias Mutex = int; void initMutex(Mutex* mtx) @@ -262,7 +264,7 @@ struct Monitor private: -@property ref shared(Monitor*) monitor(Object h) pure nothrow @nogc +@property ref shared(Monitor*) monitor(return Object h) pure nothrow @nogc { return *cast(shared Monitor**)&h.__monitor; } diff --git a/libphobos/libdruntime/rt/obj.d b/libphobos/libdruntime/rt/obj.d deleted file mode 100644 index 97dfbb5..0000000 --- a/libphobos/libdruntime/rt/obj.d +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Contains object comparator functions called by generated code. - * - * Copyright: Copyright Digital Mars 2002 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright - */ - -/* Copyright Digital Mars 2000 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.obj; - -extern (C): - -/******************************** - * Compiler helper for operator == for class objects. - */ - -int _d_obj_eq(Object o1, Object o2) -{ - return o1 is o2 || (o1 && o1.opEquals(o2)); -} - - -/******************************** - * Compiler helper for operator <, <=, >, >= for class objects. - */ - -int _d_obj_cmp(Object o1, Object o2) -{ - return o1.opCmp(o2); -} diff --git a/libphobos/libdruntime/rt/profilegc.d b/libphobos/libdruntime/rt/profilegc.d new file mode 100644 index 0000000..45e0d51 --- /dev/null +++ b/libphobos/libdruntime/rt/profilegc.d @@ -0,0 +1,170 @@ +/* + * Data collection and report generation for + * -profile=gc + * switch + * + * Copyright: Copyright Digital Mars 2015 - 2015. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Andrei Alexandrescu and Walter Bright + * Source: $(DRUNTIMESRC rt/_profilegc.d) + */ + +module rt.profilegc; + +private: + +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; + +import core.exception : onOutOfMemoryError; +import core.internal.container.hashtab; + +struct Entry { ulong count, size; } + +char[] buffer; +HashTab!(const(char)[], Entry) newCounts; + +__gshared +{ + HashTab!(const(char)[], Entry) globalNewCounts; + string logfilename = "profilegc.log"; +} + +/**** + * Set file name for output. + * A file name of "" means write results to stdout. + * Params: + * name = file name + */ + +extern (C) void profilegc_setlogfilename(string name) +{ + logfilename = name ~ "\0"; +} + +public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow +{ + if (sz == 0) + return; + + char[3 * line.sizeof + 1] buf = void; + auto buflen = snprintf(buf.ptr, buf.length, "%u", line); + + auto length = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen; + if (length > buffer.length) + { + // Enlarge buffer[] so it is big enough + assert(buffer.length > 0 || buffer.ptr is null); + auto p = cast(char*)realloc(buffer.ptr, length); + if (!p) + onOutOfMemoryError(); + buffer = p[0 .. length]; + } + + // "type funcname file:line" + buffer[0 .. type.length] = type[]; + buffer[type.length] = ' '; + buffer[type.length + 1 .. + type.length + 1 + funcname.length] = funcname[]; + buffer[type.length + 1 + funcname.length] = ' '; + buffer[type.length + 1 + funcname.length + 1 .. + type.length + 1 + funcname.length + 1 + file.length] = file[]; + buffer[type.length + 1 + funcname.length + 1 + file.length] = ':'; + buffer[type.length + 1 + funcname.length + 1 + file.length + 1 .. + type.length + 1 + funcname.length + 1 + file.length + 1 + buflen] = buf[0 .. buflen]; + + if (auto pcount = cast(string)buffer[0 .. length] in newCounts) + { // existing entry + pcount.count++; + pcount.size += sz; + } + else + { + auto key = (cast(char*) malloc(char.sizeof * length))[0 .. length]; + key[] = buffer[0..length]; + newCounts[key] = Entry(1, sz); // new entry + } +} + +// Merge thread local newCounts into globalNewCounts +static ~this() +{ + if (newCounts.length) + { + synchronized + { + foreach (name, entry; newCounts) + { + if (!(name in globalNewCounts)) + globalNewCounts[name] = Entry.init; + + globalNewCounts[name].count += entry.count; + globalNewCounts[name].size += entry.size; + } + } + newCounts.reset(); + } + free(buffer.ptr); + buffer = null; +} + +// Write report to stderr +shared static ~this() +{ + static struct Result + { + const(char)[] name; + Entry entry; + + // qsort() comparator to sort by count field + extern (C) static int qsort_cmp(scope const void *r1, scope const void *r2) @nogc nothrow + { + auto result1 = cast(Result*)r1; + auto result2 = cast(Result*)r2; + long cmp = result2.entry.size - result1.entry.size; + if (cmp) return cmp < 0 ? -1 : 1; + cmp = result2.entry.count - result1.entry.count; + if (cmp) return cmp < 0 ? -1 : 1; + if (result2.name == result1.name) return 0; + // ascending order for names reads better + return result2.name > result1.name ? -1 : 1; + } + } + + size_t size = globalNewCounts.length; + Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size]; + scope(exit) + free(counts.ptr); + + size_t i; + foreach (name, entry; globalNewCounts) + { + counts[i].name = name; + counts[i].entry = entry; + ++i; + } + + if (counts.length) + { + qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp); + + FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w"); + if (fp) + { + fprintf(fp, "bytes allocated, allocations, type, function, file:line\n"); + foreach (ref c; counts) + { + fprintf(fp, "%15llu\t%15llu\t%8.*s\n", + cast(ulong)c.entry.size, cast(ulong)c.entry.count, + cast(int) c.name.length, c.name.ptr); + } + if (logfilename.length) + fclose(fp); + } + else + fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr); + } +} diff --git a/libphobos/libdruntime/rt/qsort.d b/libphobos/libdruntime/rt/qsort.d deleted file mode 100644 index 079a0f5..0000000 --- a/libphobos/libdruntime/rt/qsort.d +++ /dev/null @@ -1,166 +0,0 @@ -/** - * This is a public domain version of qsort.d. All it does is call C's - * qsort(). - * - * Copyright: Copyright Digital Mars 2000 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Martin Nowak - */ - -/* Copyright Digital Mars 2000 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.qsort; - -//debug=qsort; - -private import core.stdc.stdlib; - -version (OSX) - version = Darwin; -else version (iOS) - version = Darwin; -else version (TVOS) - version = Darwin; -else version (WatchOS) - version = Darwin; - -// qsort_r was added in glibc in 2.8. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88127 -version (CRuntime_Glibc) -{ - version (GNU) - { - import gcc.config : Have_Qsort_R; - enum Glibc_Qsort_R = Have_Qsort_R; - } - else - { - enum Glibc_Qsort_R = true; - } -} -else -{ - enum Glibc_Qsort_R = false; -} - -static if (Glibc_Qsort_R) -{ - alias extern (C) int function(scope const void *, scope const void *, scope void *) Cmp; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, Cmp cmp, scope void *arg); - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti) - { - return (cast(TypeInfo)ti).compare(p1, p2); - } - qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti); - return a; - } -} -else version (FreeBSD) -{ - alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp); - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2) - { - return (cast(TypeInfo)ti).compare(p1, p2); - } - qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp); - return a; - } -} -else version (DragonFlyBSD) -{ - alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp); - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2) - { - return (cast(TypeInfo)ti).compare(p1, p2); - } - qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp); - return a; - } -} -else version (Darwin) -{ - alias extern (C) int function(scope void *, scope const void *, scope const void *) Cmp; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, scope void *thunk, Cmp cmp); - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope void* ti, scope const void* p1, scope const void* p2) - { - return (cast(TypeInfo)ti).compare(p1, p2); - } - qsort_r(a.ptr, a.length, ti.tsize, cast(void*)ti, &cmp); - return a; - } -} -else version (CRuntime_UClibc) -{ - alias extern (C) int function(scope const void *, scope const void *, scope void *) __compar_d_fn_t; - extern (C) void qsort_r(scope void *base, size_t nmemb, size_t size, __compar_d_fn_t cmp, scope void *arg); - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope const void* p1, scope const void* p2, scope void* ti) - { - return (cast(TypeInfo)ti).compare(p1, p2); - } - qsort_r(a.ptr, a.length, ti.tsize, &cmp, cast(void*)ti); - return a; - } -} -else -{ - private TypeInfo tiglobal; - - extern (C) void[] _adSort(return scope void[] a, TypeInfo ti) - { - extern (C) int cmp(scope const void* p1, scope const void* p2) - { - return tiglobal.compare(p1, p2); - } - tiglobal = ti; - qsort(a.ptr, a.length, ti.tsize, &cmp); - return a; - } -} - - - -unittest -{ - debug(qsort) printf("array.sort.unittest()\n"); - - int[] a = new int[10]; - - a[0] = 23; - a[1] = 1; - a[2] = 64; - a[3] = 5; - a[4] = 6; - a[5] = 5; - a[6] = 17; - a[7] = 3; - a[8] = 0; - a[9] = -1; - - _adSort(*cast(void[]*)&a, typeid(a[0])); - - for (int i = 0; i < a.length - 1; i++) - { - //printf("i = %d", i); - //printf(" %d %d\n", a[i], a[i + 1]); - assert(a[i] <= a[i + 1]); - } -} diff --git a/libphobos/libdruntime/rt/sections.d b/libphobos/libdruntime/rt/sections.d index 6009a79..006d48d 100644 --- a/libphobos/libdruntime/rt/sections.d +++ b/libphobos/libdruntime/rt/sections.d @@ -5,7 +5,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly, Martin Nowak - * Source: $(DRUNTIMESRC src/rt/_sections.d) + * Source: $(DRUNTIMESRC rt/_sections.d) */ /* NOTE: This file has been patched from the original DMD distribution to @@ -26,10 +26,23 @@ version (GNU) public import gcc.sections; else version (CRuntime_Glibc) public import rt.sections_elf_shared; +else version (CRuntime_Musl) + public import rt.sections_elf_shared; else version (FreeBSD) public import rt.sections_elf_shared; else version (NetBSD) public import rt.sections_elf_shared; +else version (OpenBSD) +{ + /** + * OpenBSD is missing support needed for elf_shared. + * See the top of sections_solaris.d for more info. + */ + + public import rt.sections_solaris; +} +else version (DragonFlyBSD) + public import rt.sections_elf_shared; else version (Solaris) public import rt.sections_solaris; else version (Darwin) @@ -47,6 +60,8 @@ else version (CRuntime_Microsoft) public import rt.sections_win64; else version (CRuntime_Bionic) public import rt.sections_android; +else version (CRuntime_UClibc) + public import rt.sections_elf_shared; else static assert(0, "unimplemented"); diff --git a/libphobos/libdruntime/rt/switch_.d b/libphobos/libdruntime/rt/switch_.d deleted file mode 100644 index 73ad636..0000000 --- a/libphobos/libdruntime/rt/switch_.d +++ /dev/null @@ -1,424 +0,0 @@ -/** - * Contains support code for switch blocks using string constants. - * - * Copyright: Copyright Digital Mars 2004 - 2010. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Sean Kelly - */ - -/* Copyright Digital Mars 2004 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.switch_; - -private import core.stdc.string; - -/****************************************************** - * Support for switch statements switching on strings. - * Input: - * table[] sorted array of strings generated by compiler - * ca string to look up in table - * Output: - * result index of match in table[] - * -1 if not in table - */ - -extern (C): - -int _d_switch_string(char[][] table, char[] ca) -in -{ - //printf("in _d_switch_string()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (size_t j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - int ci; - - ci = memcmp(table[j - 1].ptr, table[j].ptr, len1); - assert(ci < 0); // ci==0 means a duplicate - } - } -} -out (result) -{ - int cj; - - //printf("out _d_switch_string()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { cj = memcmp(table[i].ptr, ca.ptr, ca.length); - assert(cj != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - cj = memcmp(table[i].ptr, ca.ptr, ca.length); - if (cj == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_string(%.*s)\n", ca.length, ca.ptr); - size_t low = 0; - size_t high = table.length; - - version (none) - { - // Print table - printf("ca[] = '%s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - printf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - if (high && - ca.length >= table[0].length && - ca.length <= table[high - 1].length) - { - // Looking for 0 length string, which would only be at the beginning - if (ca.length == 0) - return 0; - - char c1 = ca[0]; - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = cast(ubyte)c1 - cast(ubyte)pca[0]; - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - } - - //printf("not found\n"); - return -1; // not found -} - -unittest -{ - switch (cast(char []) "c") - { - case "coo": - default: - break; - } - - int bug5381(string s) - { - switch (s) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"); - assert(rc == 3); -} - -/********************************** - * Same thing, but for wide chars. - */ - -int _d_switch_ustring(wchar[][] table, wchar[] ca) -in -{ - //printf("in _d_switch_ustring()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (size_t j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - int c; - - c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * wchar.sizeof); - assert(c < 0); // c==0 means a duplicate - } - } -} -out (result) -{ - int c; - - //printf("out _d_switch_ustring()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof); - assert(c != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - c = memcmp(table[i].ptr, ca.ptr, ca.length * wchar.sizeof); - if (c == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_ustring()\n"); - size_t low = 0; - auto high = table.length; - - version (none) - { - // Print table - wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length * wchar.sizeof); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - //printf("not found\n"); - return -1; // not found -} - - -unittest -{ - switch (cast(wchar []) "c") - { - case "coo": - default: - break; - } - - int bug5381(wstring ws) - { - switch (ws) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"w); - assert(rc == 3); -} - -/********************************** - * Same thing, but for wide chars. - */ - -int _d_switch_dstring(dchar[][] table, dchar[] ca) -in -{ - //printf("in _d_switch_dstring()\n"); - assert(table.length >= 0); - assert(ca.length >= 0); - - // Make sure table[] is sorted correctly - for (auto j = 1u; j < table.length; j++) - { - auto len1 = table[j - 1].length; - auto len2 = table[j].length; - - assert(len1 <= len2); - if (len1 == len2) - { - auto c = memcmp(table[j - 1].ptr, table[j].ptr, len1 * dchar.sizeof); - assert(c < 0); // c==0 means a duplicate - } - } -} -out (result) -{ - //printf("out _d_switch_dstring()\n"); - if (result == -1) - { - // Not found - for (auto i = 0u; i < table.length; i++) - { - if (table[i].length == ca.length) - { auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof); - assert(c != 0); - } - } - } - else - { - assert(0 <= result && cast(size_t)result < table.length); - for (auto i = 0u; 1; i++) - { - assert(i < table.length); - if (table[i].length == ca.length) - { - auto c = memcmp(table[i].ptr, ca.ptr, ca.length * dchar.sizeof); - if (c == 0) - { - assert(i == result); - break; - } - } - } - } -} -body -{ - //printf("body _d_switch_dstring()\n"); - size_t low = 0; - auto high = table.length; - - version (none) - { - // Print table - wprintf("ca[] = '%.*s'\n", ca.length, ca.ptr); - for (auto i = 0; i < high; i++) - { - auto pca = table[i]; - wprintf("table[%d] = %d, '%.*s'\n", i, pca.length, pca.length, pca.ptr); - } - } - - // Do binary search - while (low < high) - { - auto mid = (low + high) >> 1; - auto pca = table[mid]; - auto c = cast(sizediff_t)(ca.length - pca.length); - if (c == 0) - { - c = memcmp(ca.ptr, pca.ptr, ca.length * dchar.sizeof); - if (c == 0) - { //printf("found %d\n", mid); - return cast(int)mid; - } - } - if (c < 0) - { - high = mid; - } - else - { - low = mid + 1; - } - } - //printf("not found\n"); - return -1; // not found -} - - -unittest -{ - switch (cast(dchar []) "c") - { - case "coo": - default: - break; - } - - int bug5381(dstring ds) - { - switch (ds) - { - case "unittest": return 1; - case "D_Version2": return 2; - case "none": return 3; - case "all": return 4; - default: return 5; - } - } - int rc = bug5381("none"d); - assert(rc == 3); -} diff --git a/libphobos/libdruntime/rt/tlsgc.d b/libphobos/libdruntime/rt/tlsgc.d index db7347f..b13a1b3 100644 --- a/libphobos/libdruntime/rt/tlsgc.d +++ b/libphobos/libdruntime/rt/tlsgc.d @@ -1,8 +1,9 @@ /** * * Copyright: Copyright Digital Mars 2011 - 2012. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors: Martin Nowak + * Source: $(DRUNTIMESRC rt/tlsgc.d) */ /* Copyright Digital Mars 2011. diff --git a/libphobos/libdruntime/rt/util/array.d b/libphobos/libdruntime/rt/util/array.d deleted file mode 100644 index b2cfb8d..0000000 --- a/libphobos/libdruntime/rt/util/array.d +++ /dev/null @@ -1,72 +0,0 @@ -/** -Array utilities. - -Copyright: Denis Shelomovskij 2013 -License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). -Authors: Denis Shelomovskij -Source: $(DRUNTIMESRC src/rt/util/_array.d) -*/ -module rt.util.array; - - -import core.internal.string; -import core.stdc.stdint; - - -@safe /* pure dmd @@@BUG11461@@@ */ nothrow: - -void enforceTypedArraysConformable(T)(const char[] action, - const T[] a1, const T[] a2, in bool allowOverlap = false) -{ - _enforceSameLength(action, a1.length, a2.length); - if (!allowOverlap) - _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), T.sizeof * a1.length); -} - -void enforceRawArraysConformable(const char[] action, in size_t elementSize, - const void[] a1, const void[] a2, in bool allowOverlap = false) -{ - _enforceSameLength(action, a1.length, a2.length); - if (!allowOverlap) - _enforceNoOverlap(action, arrayToPtr(a1), arrayToPtr(a2), elementSize * a1.length); -} - -private void _enforceSameLength(const char[] action, - in size_t length1, in size_t length2) -{ - if (length1 == length2) - return; - - UnsignedStringBuf tmpBuff = void; - string msg = "Array lengths don't match for "; - msg ~= action; - msg ~= ": "; - msg ~= length1.unsignedToTempString(tmpBuff, 10); - msg ~= " != "; - msg ~= length2.unsignedToTempString(tmpBuff, 10); - throw new Error(msg); -} - -private void _enforceNoOverlap(const char[] action, - uintptr_t ptr1, uintptr_t ptr2, in size_t bytes) -{ - const d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1; - if (d >= bytes) - return; - const overlappedBytes = bytes - d; - - UnsignedStringBuf tmpBuff = void; - string msg = "Overlapping arrays in "; - msg ~= action; - msg ~= ": "; - msg ~= overlappedBytes.unsignedToTempString(tmpBuff, 10); - msg ~= " byte(s) overlap of "; - msg ~= bytes.unsignedToTempString(tmpBuff, 10); - throw new Error(msg); -} - -private uintptr_t arrayToPtr(const void[] array) @trusted -{ - // Ok because the user will never dereference the pointer - return cast(uintptr_t)array.ptr; -} diff --git a/libphobos/libdruntime/rt/util/container/array.d b/libphobos/libdruntime/rt/util/container/array.d deleted file mode 100644 index f5aa3d7..0000000 --- a/libphobos/libdruntime/rt/util/container/array.d +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Array container for internal usage. - * - * Copyright: Copyright Martin Nowak 2013. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - */ -module rt.util.container.array; - -static import common = rt.util.container.common; - -import core.exception : onOutOfMemoryErrorNoGC; - -struct Array(T) -{ -nothrow: - @disable this(this); - - ~this() - { - reset(); - } - - void reset() - { - length = 0; - } - - @property size_t length() const - { - return _length; - } - - @property void length(size_t nlength) - { - import core.checkedint : mulu; - - bool overflow = false; - size_t reqsize = mulu(T.sizeof, nlength, overflow); - if (!overflow) - { - if (nlength < _length) - foreach (ref val; _ptr[nlength .. _length]) common.destroy(val); - _ptr = cast(T*)common.xrealloc(_ptr, reqsize); - if (nlength > _length) - foreach (ref val; _ptr[_length .. nlength]) common.initialize(val); - _length = nlength; - } - else - onOutOfMemoryErrorNoGC(); - - } - - @property bool empty() const - { - return !length; - } - - @property ref inout(T) front() inout - in { assert(!empty); } - body - { - return _ptr[0]; - } - - @property ref inout(T) back() inout - in { assert(!empty); } - body - { - return _ptr[_length - 1]; - } - - ref inout(T) opIndex(size_t idx) inout - in { assert(idx < length); } - body - { - return _ptr[idx]; - } - - inout(T)[] opSlice() inout - { - return _ptr[0 .. _length]; - } - - inout(T)[] opSlice(size_t a, size_t b) inout - in { assert(a < b && b <= length); } - body - { - return _ptr[a .. b]; - } - - alias length opDollar; - - void insertBack()(auto ref T val) - { - import core.checkedint : addu; - - bool overflow = false; - size_t newlength = addu(length, 1, overflow); - if (!overflow) - { - length = newlength; - back = val; - } - else - onOutOfMemoryErrorNoGC(); - } - - void popBack() - { - length = length - 1; - } - - void remove(size_t idx) - in { assert(idx < length); } - body - { - foreach (i; idx .. length - 1) - _ptr[i] = _ptr[i+1]; - popBack(); - } - - void swap(ref Array other) - { - auto ptr = _ptr; - _ptr = other._ptr; - other._ptr = ptr; - immutable len = _length; - _length = other._length; - other._length = len; - } - - invariant - { - assert(!_ptr == !_length); - } - -private: - T* _ptr; - size_t _length; -} - -unittest -{ - Array!size_t ary; - - assert(ary[] == []); - ary.insertBack(5); - assert(ary[] == [5]); - assert(ary[$-1] == 5); - ary.popBack(); - assert(ary[] == []); - ary.insertBack(0); - ary.insertBack(1); - assert(ary[] == [0, 1]); - assert(ary[0 .. 1] == [0]); - assert(ary[1 .. 2] == [1]); - assert(ary[$ - 2 .. $] == [0, 1]); - size_t idx; - foreach (val; ary) assert(idx++ == val); - foreach_reverse (val; ary) assert(--idx == val); - foreach (i, val; ary) assert(i == val); - foreach_reverse (i, val; ary) assert(i == val); - - ary.insertBack(2); - ary.remove(1); - assert(ary[] == [0, 2]); - - assert(!ary.empty); - ary.reset(); - assert(ary.empty); - ary.insertBack(0); - assert(!ary.empty); - destroy(ary); - assert(ary.empty); - - // not copyable - static assert(!__traits(compiles, { Array!size_t ary2 = ary; })); - Array!size_t ary2; - static assert(!__traits(compiles, ary = ary2)); - static void foo(Array!size_t copy) {} - static assert(!__traits(compiles, foo(ary))); - - ary2.insertBack(0); - assert(ary.empty); - assert(ary2[] == [0]); - ary.swap(ary2); - assert(ary[] == [0]); - assert(ary2.empty); -} - -unittest -{ - alias RC = common.RC!(); - Array!RC ary; - - size_t cnt; - assert(cnt == 0); - ary.insertBack(RC(&cnt)); - assert(cnt == 1); - ary.insertBack(RC(&cnt)); - assert(cnt == 2); - ary.back = ary.front; - assert(cnt == 2); - ary.popBack(); - assert(cnt == 1); - ary.popBack(); - assert(cnt == 0); -} - -unittest -{ - import core.exception; - try - { - // Overflow ary.length. - auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -1); - ary.insertBack(0); - } - catch (OutOfMemoryError) - { - } - try - { - // Overflow requested memory size for common.xrealloc(). - auto ary = Array!size_t(cast(size_t*)0xdeadbeef, -2); - ary.insertBack(0); - } - catch (OutOfMemoryError) - { - } -} diff --git a/libphobos/libdruntime/rt/util/container/common.d b/libphobos/libdruntime/rt/util/container/common.d deleted file mode 100644 index 9e6c013..0000000 --- a/libphobos/libdruntime/rt/util/container/common.d +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Common code for writing containers. - * - * Copyright: Copyright Martin Nowak 2013. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - */ -module rt.util.container.common; - -import core.stdc.stdlib : malloc, realloc; -public import core.stdc.stdlib : free; -import core.internal.traits : dtorIsNothrow; -nothrow: - -void* xrealloc(void* ptr, size_t sz) nothrow @nogc -{ - import core.exception; - - if (!sz) { .free(ptr); return null; } - if (auto nptr = .realloc(ptr, sz)) return nptr; - .free(ptr); onOutOfMemoryErrorNoGC(); - assert(0); -} - -void* xmalloc(size_t sz) nothrow @nogc -{ - import core.exception; - if (auto nptr = .malloc(sz)) - return nptr; - onOutOfMemoryErrorNoGC(); - assert(0); -} - -void destroy(T)(ref T t) if (is(T == struct) && dtorIsNothrow!T) -{ - scope (failure) assert(0); // nothrow hack - object.destroy(t); -} - -void destroy(T)(ref T t) if (!is(T == struct)) -{ - t = T.init; -} - -void initialize(T)(ref T t) if (is(T == struct)) -{ - import core.stdc.string; - if (auto p = typeid(T).initializer().ptr) - memcpy(&t, p, T.sizeof); - else - memset(&t, 0, T.sizeof); -} - -void initialize(T)(ref T t) if (!is(T == struct)) -{ - t = T.init; -} - -version (unittest) struct RC() -{ -nothrow: - this(size_t* cnt) { ++*(_cnt = cnt); } - ~this() { if (_cnt) --*_cnt; } - this(this) { if (_cnt) ++*_cnt; } - size_t* _cnt; -} diff --git a/libphobos/libdruntime/rt/util/container/hashtab.d b/libphobos/libdruntime/rt/util/container/hashtab.d deleted file mode 100644 index fd9f0f7..0000000 --- a/libphobos/libdruntime/rt/util/container/hashtab.d +++ /dev/null @@ -1,329 +0,0 @@ -/** - * HashTab container for internal usage. - * - * Copyright: Copyright Martin Nowak 2013. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Martin Nowak - */ -module rt.util.container.hashtab; - -import rt.util.container.array; -static import common = rt.util.container.common; - -struct HashTab(Key, Value) -{ - static struct Node - { - Key _key; - Value _value; - Node* _next; - } - - @disable this(this); - - ~this() - { - reset(); - } - - void reset() - { - foreach (p; _buckets) - { - while (p !is null) - { - auto pn = p._next; - common.destroy(*p); - common.free(p); - p = pn; - } - } - _buckets.reset(); - _length = 0; - } - - @property size_t length() const - { - return _length; - } - - @property bool empty() const - { - return !_length; - } - - void remove(in Key key) - in { assert(key in this); } - body - { - ensureNotInOpApply(); - - immutable hash = hashOf(key) & mask; - auto pp = &_buckets[hash]; - while (*pp) - { - auto p = *pp; - if (p._key == key) - { - *pp = p._next; - common.destroy(*p); - common.free(p); - if (--_length < _buckets.length && _length >= 4) - shrink(); - return; - } - else - { - pp = &p._next; - } - } - assert(0); - } - - ref inout(Value) opIndex(Key key) inout - { - return *opIn_r(key); - } - - void opIndexAssign(Value value, Key key) - { - *get(key) = value; - } - - inout(Value)* opIn_r(in Key key) inout - { - if (_buckets.length) - { - immutable hash = hashOf(key) & mask; - for (inout(Node)* p = _buckets[hash]; p !is null; p = p._next) - { - if (p._key == key) - return &p._value; - } - } - return null; - } - - int opApply(scope int delegate(ref Key, ref Value) dg) - { - immutable save = _inOpApply; - _inOpApply = true; - scope (exit) _inOpApply = save; - foreach (p; _buckets) - { - while (p !is null) - { - if (auto res = dg(p._key, p._value)) - return res; - p = p._next; - } - } - return 0; - } - -private: - - Value* get(Key key) - { - if (auto p = opIn_r(key)) - return p; - - ensureNotInOpApply(); - - if (!_buckets.length) - _buckets.length = 4; - - immutable hash = hashOf(key) & mask; - auto p = cast(Node*)common.xmalloc(Node.sizeof); - common.initialize(*p); - p._key = key; - p._next = _buckets[hash]; - _buckets[hash] = p; - if (++_length >= 2 * _buckets.length) - grow(); - return &p._value; - } - - static hash_t hashOf(in ref Key key) @trusted - { - static if (is(Key U : U[])) - return .hashOf(key, 0); - else - return .hashOf((&key)[0 .. 1], 0); - } - - @property hash_t mask() const - { - return _buckets.length - 1; - } - - void grow() - in - { - assert(_buckets.length); - } - body - { - immutable ocnt = _buckets.length; - immutable nmask = 2 * ocnt - 1; - _buckets.length = 2 * ocnt; - for (size_t i = 0; i < ocnt; ++i) - { - auto pp = &_buckets[i]; - while (*pp) - { - auto p = *pp; - - immutable nidx = hashOf(p._key) & nmask; - if (nidx != i) - { - *pp = p._next; - p._next = _buckets[nidx]; - _buckets[nidx] = p; - } - else - { - pp = &p._next; - } - } - } - } - - void shrink() - in - { - assert(_buckets.length >= 2); - } - body - { - immutable ocnt = _buckets.length; - immutable ncnt = ocnt >> 1; - immutable nmask = ncnt - 1; - - for (size_t i = ncnt; i < ocnt; ++i) - { - if (auto tail = _buckets[i]) - { - immutable nidx = i & nmask; - auto pp = &_buckets[nidx]; - while (*pp) - pp = &(*pp)._next; - *pp = tail; - _buckets[i] = null; - } - } - _buckets.length = ncnt; - } - - void ensureNotInOpApply() - { - if (_inOpApply) - assert(0, "Invalid HashTab manipulation during opApply iteration."); - } - - Array!(Node*) _buckets; - size_t _length; - bool _inOpApply; -} - -unittest -{ - HashTab!(int, int) tab; - - foreach (i; 0 .. 100) - tab[i] = 100 - i; - - foreach (i; 0 .. 100) - assert(tab[i] == 100 - i); - - foreach (k, v; tab) - assert(v == 100 - k); - - foreach (i; 0 .. 50) - tab.remove(2 * i); - - assert(tab.length == 50); - - foreach (i; 0 .. 50) - assert(tab[2 * i + 1] == 100 - 2 * i - 1); - - assert(tab.length == 50); - - tab.reset(); - assert(tab.empty); - tab[0] = 0; - assert(!tab.empty); - destroy(tab); - assert(tab.empty); - - // not copyable - static assert(!__traits(compiles, { HashTab!(int, int) tab2 = tab; })); - HashTab!(int, int) tab2; - static assert(!__traits(compiles, tab = tab2)); - static void foo(HashTab!(int, int) copy) {} - static assert(!__traits(compiles, foo(tab))); -} - -unittest -{ - HashTab!(string, size_t) tab; - - tab["foo"] = 0; - assert(tab["foo"] == 0); - ++tab["foo"]; - assert(tab["foo"] == 1); - tab["foo"]++; - assert(tab["foo"] == 2); - - auto s = "fo"; - s ~= "o"; - assert(tab[s] == 2); - assert(tab.length == 1); - tab[s] -= 2; - assert(tab[s] == 0); - tab["foo"] = 12; - assert(tab[s] == 12); - - tab.remove("foo"); - assert(tab.empty); -} - -unittest -{ - alias RC = common.RC!(); - HashTab!(size_t, RC) tab; - - size_t cnt; - assert(cnt == 0); - tab[0] = RC(&cnt); - assert(cnt == 1); - tab[1] = tab[0]; - assert(cnt == 2); - tab.remove(0); - assert(cnt == 1); - tab.remove(1); - assert(cnt == 0); -} - -unittest -{ - import core.exception; - - HashTab!(uint, uint) tab; - foreach (i; 0 .. 5) - tab[i] = i; - bool thrown; - foreach (k, v; tab) - { - try - { - if (k == 3) tab.remove(k); - } - catch (AssertError e) - { - thrown = true; - } - } - assert(thrown); - assert(tab[3] == 3); -} diff --git a/libphobos/libdruntime/rt/util/container/treap.d b/libphobos/libdruntime/rt/util/container/treap.d deleted file mode 100644 index f0c04fd..0000000 --- a/libphobos/libdruntime/rt/util/container/treap.d +++ /dev/null @@ -1,338 +0,0 @@ -/** - * Treap container for internal usage. - * - * Copyright: Copyright Digital Mars 2014 - 2014. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - */ -module rt.util.container.treap; - -static import common = rt.util.container.common; -import rt.util.random; -import rt.qsort; - -struct Treap(E) -{ -nothrow: - static struct Node - { - Node* left, right; - E element; - uint priority; - } - - @disable this(this); - - ~this() - { - removeAll(); - } - - void initialize() - { - rand48.defaultSeed(); - } - - void insert(E element) @nogc - { - root = insert(root, element); - } - - void remove(E element) - { - remove(&root, element); - } - - int opApply(scope int delegate(ref E) nothrow dg) - { - return (cast(const)&this).opApply((ref const E e) => dg(*cast(E*)&e)); - } - - int opApply(scope int delegate(ref const E) nothrow dg) const - { - return opApplyHelper(root, dg); - } - - version (unittest) - bool opEquals(E[] elements) - { - size_t i; - foreach (e; this) - { - if (i >= elements.length) - return false; - if (e != elements[i++]) - return false; - } - return i == elements.length; - } - - void removeAll() - { - removeAll(root); - root = null; - } - - version (unittest) - bool valid() - { - return valid(root); - } - - - version (none) - uint height() - { - static uint height(Node* node) - { - if (!node) - return 0; - auto left = height(node.left); - auto right = height(node.right); - return 1 + (left > right ? left : right); - } - return height(root); - } - - version (none) - size_t count() - { - static size_t count(Node* node) - { - if (!node) - return 0; - return count(node.left) + count(node.right) + 1; - } - return count(root); - } - - -private: - Node* root; - Rand48 rand48; - - Node* allocNode(E element) @nogc - { - Node* node = cast(Node*)common.xmalloc(Node.sizeof); - node.left = node.right = null; - node.priority = rand48(); - node.element = element; - return node; - } - - Node* insert(Node* node, E element) @nogc - { - if (!node) - return allocNode(element); - else if (element < node.element) - { - node.left = insert(node.left, element); - if (node.left.priority < node.priority) - node = rotateR(node); - } - else if (element > node.element) - { - node.right = insert(node.right, element); - if (node.right.priority < node.priority) - node = rotateL(node); - } - else - {} // ignore duplicate - - return node; - } - -static: - - void freeNode(Node* node) - { - common.free(node); - } - - Node* rotateL(Node* root) - { - auto pivot = root.right; - root.right = pivot.left; - pivot.left = root; - return pivot; - } - - Node* rotateR(Node* root) - { - auto pivot = root.left; - root.left = pivot.right; - pivot.right = root; - return pivot; - } - - void remove(Node** ppnode, E element) - { - Node* node = *ppnode; - if (!node) - return; // element not in treap - - if (element < node.element) - { - remove(&node.left, element); - } - else if (element > node.element) - { - remove(&node.right, element); - } - else - { - while (node.left && node.right) - { - if (node.left.priority < node.right.priority) - { - *ppnode = rotateR(node); - ppnode = &(*ppnode).right; - } - else - { - *ppnode = rotateL(node); - ppnode = &(*ppnode).left; - } - } - if (!node.left) - *ppnode = node.right; - else - *ppnode = node.left; - freeNode(node); - } - } - - void removeAll(Node* node) - { - if (!node) - return; - removeAll(node.left); - removeAll(node.right); - freeNode(node); - } - - int opApplyHelper(const Node* node, scope int delegate(ref const E) nothrow dg) - { - if (!node) - return 0; - - int result = opApplyHelper(node.left, dg); - if (result) - return result; - result = dg(node.element); - if (result) - return result; - return opApplyHelper(node.right, dg); - } - - version (unittest) - bool valid(Node* node) - { - if (!node) - return true; - - if (node.left) - { - if (node.left.priority < node.priority) - return false; - if (node.left.element > node.element) - return false; - } - if (node.right) - { - if (node.right.priority < node.priority) - return false; - if (node.right.element < node.element) - return false; - } - return valid(node.left) && valid(node.right); - } -} - -unittest -{ - // randomized unittest for randomized data structure - import /*cstdlib = */core.stdc.stdlib : rand, srand; - import /*ctime = */core.stdc.time : time; - - enum OP { add, remove } - enum initialSize = 1000; - enum randOps = 1000; - - Treap!uint treap; - OP[] ops; - uint[] opdata; - - treap.initialize(); - srand(cast(uint)time(null)); - - uint[] data; -initialLoop: - foreach (i; 0 .. initialSize) - { - data ~= rand(); - treap.insert(data[$-1]); - foreach (e; data[0..$-1]) - if (e == data[$-1]) - { - data = data[0..$-1]; - continue initialLoop; - } - } - _adSort(*cast(void[]*)&data, typeid(data[0])); - assert(treap == data); - assert(treap.valid()); - - for (int i = randOps; i > 0; --i) - { - ops ~= cast(OP)(rand() < uint.max / 2 ? OP.add: OP.remove); - opdata ~= rand(); - } - - foreach (op; ops) - { - if (op == OP.add) - { - treap.insert(opdata[0]); - - size_t i; - for (i = 0; i < data.length; ++i) - if (data[i] >= opdata[0]) - break; - - if (i == data.length || data[i] != opdata[0]) - { // not a duplicate - data.length++; - uint tmp = opdata[0]; - for (; i < data.length; ++i) - { - uint tmp2 = data[i]; - data[i] = tmp; - tmp = tmp2; - } - } - } - else if (!data.length) // nothing to remove - { - opdata = opdata[1..$]; - continue; - } - else - { - uint tmp = data[opdata[0]%data.length]; - treap.remove(tmp); - size_t i; - for (i = 0; data[i] < tmp; ++i) - {} - for (; i < data.length-1; ++i) - data[i] = data[i+1]; - data.length--; - } - assert(treap.valid()); - assert(treap == data); - opdata = opdata[1..$]; - } - - treap.removeAll(); - data.length = 0; - assert(treap == data); -} diff --git a/libphobos/libdruntime/rt/util/random.d b/libphobos/libdruntime/rt/util/random.d deleted file mode 100644 index 69e4cfe..0000000 --- a/libphobos/libdruntime/rt/util/random.d +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Random number generators for internal usage. - * - * Copyright: Copyright Digital Mars 2014. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - */ -module rt.util.random; - -struct Rand48 -{ - private ulong rng_state; - -@safe @nogc nothrow: - - void defaultSeed() - { - import ctime = core.stdc.time : time; - seed(cast(uint)ctime.time(null)); - } - -pure: - - void seed(uint seedval) - { - assert(seedval); - rng_state = cast(ulong)seedval << 16 | 0x330e; - popFront(); - } - - auto opCall() - { - auto result = front; - popFront(); - return result; - } - - @property uint front() - { - return cast(uint)(rng_state >> 16); - } - - void popFront() - { - immutable ulong a = 25214903917; - immutable ulong c = 11; - immutable ulong m_mask = (1uL << 48uL) - 1; - rng_state = (a*rng_state+c) & m_mask; - } - - enum empty = false; -} diff --git a/libphobos/libdruntime/rt/util/typeinfo.d b/libphobos/libdruntime/rt/util/typeinfo.d index 31770a0..d06254c 100644 --- a/libphobos/libdruntime/rt/util/typeinfo.d +++ b/libphobos/libdruntime/rt/util/typeinfo.d @@ -4,8 +4,10 @@ * Copyright: Copyright Kenji Hara 2014-. * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. * Authors: Kenji Hara + * Source: $(DRUNTIMESRC rt/util/_typeinfo.d) */ module rt.util.typeinfo; +import rt.util.utility : d_cfloat, d_cdouble, d_creal, isComplex; static import core.internal.hash; template Floating(T) @@ -35,14 +37,16 @@ if (is(T == float) || is(T == double) || is(T == real)) public alias hashOf = core.internal.hash.hashOf; } + +// @@@DEPRECATED_2.105@@@ template Floating(T) -if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) +if (isComplex!T) { pure nothrow @safe: bool equals(T f1, T f2) { - return f1 == f2; + return f1.re == f2.re && f1.im == f2.im; } int compare(T f1, T f2) @@ -62,12 +66,14 @@ if (is(T == cfloat) || is(T == cdouble) || is(T == creal)) return result; } - public alias hashOf = core.internal.hash.hashOf; + size_t hashOf(scope const T val) + { + return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im)); + } } template Array(T) -if (is(T == float) || is(T == double) || is(T == real) || - is(T == cfloat) || is(T == cdouble) || is(T == creal)) +if (is(T == float) || is(T == double) || is(T == real)) { pure nothrow @safe: @@ -94,17 +100,56 @@ if (is(T == float) || is(T == double) || is(T == real) || if (int c = Floating!T.compare(s1[u], s2[u])) return c; } - if (s1.length < s2.length) - return -1; - else if (s1.length > s2.length) - return 1; - return 0; + return (s1.length > s2.length) - (s1.length < s2.length); } public alias hashOf = core.internal.hash.hashOf; } -version (unittest) +// @@@DEPRECATED_2.105@@@ +template Array(T) +if (isComplex!T) +{ + pure nothrow @safe: + + bool equals(T[] s1, T[] s2) + { + size_t len = s1.length; + if (len != s2.length) + return false; + for (size_t u = 0; u < len; u++) + { + if (!Floating!T.equals(s1[u], s2[u])) + return false; + } + return true; + } + + int compare(T[] s1, T[] s2) + { + size_t len = s1.length; + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + if (int c = Floating!T.compare(s1[u], s2[u])) + return c; + } + return (s1.length > s2.length) - (s1.length < s2.length); + } + + size_t hashOf(scope const T[] val) + { + size_t hash = 0; + foreach (ref o; val) + { + hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash); + } + return hash; + } +} + +version (CoreUnittest) { alias TypeTuple(T...) = T; } @@ -162,109 +207,6 @@ unittest ti = typeid(S[3]); assert(ti.getHash(&sa1) == ti.getHash(&sa2)); }(); - - // imaginary types - foreach (F; TypeTuple!(ifloat, idouble, ireal)) - (){ // workaround #2396 - alias S = SX!F; - F f1 = +0.0i, - f2 = -0.0i; - - assert(f1 == f2); - assert(f1 !is f2); - ti = typeid(F); - assert(ti.getHash(&f1) == ti.getHash(&f2)); - - F[] a1 = [f1, f1, f1]; - F[] a2 = [f2, f2, f2]; - assert(a1 == a2); - assert(a1 !is a2); - ti = typeid(F[]); - assert(ti.getHash(&a1) == ti.getHash(&a2)); - - F[][] aa1 = [a1, a1, a1]; - F[][] aa2 = [a2, a2, a2]; - assert(aa1 == aa2); - assert(aa1 !is aa2); - ti = typeid(F[][]); - assert(ti.getHash(&aa1) == ti.getHash(&aa2)); - - S s1 = {f1}, - s2 = {f2}; - assert(s1 == s2); - assert(s1 !is s2); - ti = typeid(S); - assert(ti.getHash(&s1) == ti.getHash(&s2)); - - S[] da1 = [S(f1), S(f1), S(f1)], - da2 = [S(f2), S(f2), S(f2)]; - assert(da1 == da2); - assert(da1 !is da2); - ti = typeid(S[]); - assert(ti.getHash(&da1) == ti.getHash(&da2)); - - S[3] sa1 = {f1}, - sa2 = {f2}; - assert(sa1 == sa2); - assert(sa1[] !is sa2[]); - ti = typeid(S[3]); - assert(ti.getHash(&sa1) == ti.getHash(&sa2)); - }(); - - // complex types - foreach (F; TypeTuple!(cfloat, cdouble, creal)) - (){ // workaround #2396 - alias S = SX!F; - F[4] f = [+0.0 + 0.0i, - +0.0 - 0.0i, - -0.0 + 0.0i, - -0.0 - 0.0i]; - - foreach (i, f1; f) foreach (j, f2; f) if (i != j) - { - assert(f1 == 0 + 0i); - - assert(f1 == f2); - assert(f1 !is f2); - ti = typeid(F); - assert(ti.getHash(&f1) == ti.getHash(&f2)); - - F[] a1 = [f1, f1, f1]; - F[] a2 = [f2, f2, f2]; - assert(a1 == a2); - assert(a1 !is a2); - ti = typeid(F[]); - assert(ti.getHash(&a1) == ti.getHash(&a2)); - - F[][] aa1 = [a1, a1, a1]; - F[][] aa2 = [a2, a2, a2]; - assert(aa1 == aa2); - assert(aa1 !is aa2); - ti = typeid(F[][]); - assert(ti.getHash(&aa1) == ti.getHash(&aa2)); - - S s1 = {f1}, - s2 = {f2}; - assert(s1 == s2); - assert(s1 !is s2); - ti = typeid(S); - assert(ti.getHash(&s1) == ti.getHash(&s2)); - - S[] da1 = [S(f1), S(f1), S(f1)], - da2 = [S(f2), S(f2), S(f2)]; - assert(da1 == da2); - assert(da1 !is da2); - ti = typeid(S[]); - assert(ti.getHash(&da1) == ti.getHash(&da2)); - - S[3] sa1 = {f1}, - sa2 = {f2}; - assert(sa1 == sa2); - assert(sa1[] !is sa2[]); - ti = typeid(S[3]); - assert(ti.getHash(&sa1) == ti.getHash(&sa2)); - } - }(); } // Reduces to `T` if `cond` is `true` or `U` otherwise. @@ -279,7 +221,7 @@ TypeInfo information for built-in types. A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: -`float` and `ifloat` or `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap +`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect during compilation whether they have different signedness and override appropriately. For initializer, we detect if we need to override. The overriding initializer should be nonzero. @@ -296,7 +238,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) static if (is(T == Base)) override size_t getHash(scope const void* p) { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) return Floating!T.hashOf(*cast(T*)p); else return hashOf(*cast(const T *)p); @@ -306,7 +248,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) static if (is(T == Base)) override bool equals(in void* p1, in void* p2) { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) return Floating!T.equals(*cast(T*)p1, *cast(T*)p2); else return *cast(T *)p1 == *cast(T *)p2; @@ -316,7 +258,7 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) override int compare(in void* p1, in void* p2) { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) { return Floating!T.compare(*cast(T*)p1, *cast(T*)p2); } @@ -375,9 +317,12 @@ if (T.sizeof == Base.sizeof && T.alignof == Base.alignof) } static if (is(T == Base)) - static if (__traits(isFloating, T) && T.mant_dig != 64) + { + static if ((__traits(isFloating, T) && T.mant_dig != 64) || + (isComplex!T && T.re.mant_dig != 64)) // FP types except 80-bit X87 are passed in SIMD register. override @property uint flags() const { return 2; } + } } unittest @@ -414,7 +359,7 @@ TypeInfo information for arrays of built-in types. A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example: -`float` and `ifloat` or `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap +`char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect during compilation whether they have different signedness and override appropriately. For initializer, we detect if we need to override. The overriding initializer should be nonzero. @@ -429,7 +374,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf static if (is(T == Base)) override size_t getHash(scope const void* p) @trusted const { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) return Array!T.hashOf(*cast(T[]*)p); else return hashOf(*cast(const T[]*) p); @@ -438,7 +383,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf static if (is(T == Base)) override bool equals(in void* p1, in void* p2) const { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) { return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2); } @@ -455,7 +400,7 @@ private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInf static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) override int compare(in void* p1, in void* p2) const { - static if (__traits(isFloating, T)) + static if (__traits(isFloating, T) || isComplex!T) { return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2); } @@ -519,12 +464,12 @@ class TypeInfo_v : TypeInfoGeneric!ubyte { return 1; } +} - unittest - { - assert(typeid(void).toString == "void"); - assert(typeid(void).flags == 1); - } +unittest +{ + assert(typeid(void).toString == "void"); + assert(typeid(void).flags == 1); } // All integrals. @@ -545,17 +490,36 @@ static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {} // All simple floating-point types. class TypeInfo_f : TypeInfoGeneric!float {} -class TypeInfo_o : TypeInfoGeneric!(ifloat, float) {} class TypeInfo_d : TypeInfoGeneric!double {} -class TypeInfo_p : TypeInfoGeneric!(idouble, double) {} class TypeInfo_e : TypeInfoGeneric!real {} -class TypeInfo_j : TypeInfoGeneric!(ireal, real) {} + +// All imaginary floating-point types. + +// ifloat @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_o : TypeInfoGeneric!float +{ + override string toString() const pure nothrow @safe { return "ifloat"; } +} + +// idouble @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_p : TypeInfoGeneric!double +{ + override string toString() const pure nothrow @safe { return "idouble"; } +} + +// ireal @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_j : TypeInfoGeneric!real +{ + override string toString() const pure nothrow @safe { return "ireal"; } +} // All complex floating-point types. -// cfloat -class TypeInfo_q : TypeInfoGeneric!cfloat +// cfloat @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_q : TypeInfoGeneric!d_cfloat { + override string toString() const pure nothrow @safe { return "cfloat"; } + const: nothrow: pure: @trusted: static if (__traits(hasMember, TypeInfo, "argTypes")) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @@ -565,9 +529,11 @@ class TypeInfo_q : TypeInfoGeneric!cfloat } } -// cdouble -class TypeInfo_r : TypeInfoGeneric!cdouble +// cdouble @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_r : TypeInfoGeneric!d_cdouble { + override string toString() const pure nothrow @safe { return "cdouble"; } + const: nothrow: pure: @trusted: static if (__traits(hasMember, TypeInfo, "argTypes")) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @@ -578,9 +544,11 @@ class TypeInfo_r : TypeInfoGeneric!cdouble } } -// creal -class TypeInfo_c : TypeInfoGeneric!creal +// creal @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_c : TypeInfoGeneric!d_creal { + override string toString() const pure nothrow @safe { return "creal"; } + const: nothrow: pure: @trusted: static if (__traits(hasMember, TypeInfo, "argTypes")) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) @@ -591,18 +559,6 @@ class TypeInfo_c : TypeInfoGeneric!creal } } -static if (__traits(hasMember, TypeInfo, "argTypes")) - unittest - { - TypeInfo t1, t2; - assert(typeid(cfloat).argTypes(t1, t2) == 0 && t1 == typeid(double) && - t2 is null); - assert(typeid(cdouble).argTypes(t1, t2) == 0 && t1 == typeid(double) && - t2 == typeid(double)); - assert(typeid(creal).argTypes(t1, t2) == 0 && t1 == typeid(real) && - t2 == typeid(real)); - } - // Arrays of all integrals. class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {} class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {} @@ -623,7 +579,7 @@ class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {} class TypeInfo_Am : TypeInfoArrayGeneric!ulong {} class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {} -version (unittest) +version (CoreUnittest) private extern (C) void[] _adSort(void[] a, TypeInfo ti); unittest @@ -662,16 +618,50 @@ unittest assert(!(a1 < b1 && b1 < a1)); // Original failing case } -// Arrays of all floating point types. +// Arrays of all simple floating-point types. class TypeInfo_Af : TypeInfoArrayGeneric!float {} -class TypeInfo_Ao : TypeInfoArrayGeneric!(ifloat, float) {} class TypeInfo_Ad : TypeInfoArrayGeneric!double {} -class TypeInfo_Ap : TypeInfoArrayGeneric!(idouble, double) {} class TypeInfo_Ae : TypeInfoArrayGeneric!real {} -class TypeInfo_Aj : TypeInfoArrayGeneric!(ireal, real) {} -class TypeInfo_Aq : TypeInfoArrayGeneric!cfloat {} -class TypeInfo_Ar : TypeInfoArrayGeneric!cdouble {} -class TypeInfo_Ac : TypeInfoArrayGeneric!creal {} + +// Arrays of all imaginary floating-point types. + +// ifloat @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Ao : TypeInfoArrayGeneric!float +{ + override string toString() const pure nothrow @safe { return "ifloat[]"; } +} + +// idouble @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Ap : TypeInfoArrayGeneric!double +{ + override string toString() const pure nothrow @safe { return "idouble[]"; } +} + +// ireal @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Aj : TypeInfoArrayGeneric!real +{ + override string toString() const pure nothrow @safe { return "ireal[]"; } +} + +// Arrays of all complex floating-point types. + +// cfloat @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Aq : TypeInfoArrayGeneric!d_cfloat +{ + override string toString() const pure nothrow @safe { return "cfloat[]"; } +} + +// cdouble @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Ar : TypeInfoArrayGeneric!d_cdouble +{ + override string toString() const pure nothrow @safe { return "cdouble[]"; } +} + +// creal @@@DEPRECATED_2.105@@@ +deprecated class TypeInfo_Ac : TypeInfoArrayGeneric!d_creal +{ + override string toString() const pure nothrow @safe { return "creal[]"; } +} // void[] is a bit different, behaves like ubyte[] for comparison purposes. class TypeInfo_Av : TypeInfo_Ah diff --git a/libphobos/libdruntime/rt/util/utf.d b/libphobos/libdruntime/rt/util/utf.d deleted file mode 100644 index 55869b3..0000000 --- a/libphobos/libdruntime/rt/util/utf.d +++ /dev/null @@ -1,920 +0,0 @@ -/******************************************** - * Encode and decode UTF-8, UTF-16 and UTF-32 strings. - * - * For Win32 systems, the C wchar_t type is UTF-16 and corresponds to the D - * wchar type. - * For Posix systems, the C wchar_t type is UTF-32 and corresponds to - * the D utf.dchar type. - * - * UTF character support is restricted to (\u0000 <= character <= \U0010FFFF). - * - * See_Also: - * $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)<br> - * $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)<br> - * $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335) - * - * Copyright: Copyright Digital Mars 2003 - 2016. - * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). - * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/util/_utf.d) - */ - -module rt.util.utf; - -extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @safe pure; - -/******************************* - * Test if c is a valid UTF-32 character. - * - * \uFFFE and \uFFFF are considered valid by this function, - * as they are permitted for internal use by an application, - * but they are not allowed for interchange by the Unicode standard. - * - * Returns: true if it is, false if not. - */ - -@safe @nogc pure nothrow -bool isValidDchar(dchar c) -{ - /* Note: FFFE and FFFF are specifically permitted by the - * Unicode standard for application internal use, but are not - * allowed for interchange. - * (thanks to Arcane Jill) - */ - - return c < 0xD800 || - (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/); -} - -unittest -{ - debug(utf) printf("utf.isValidDchar.unittest\n"); - assert(isValidDchar(cast(dchar)'a') == true); - assert(isValidDchar(cast(dchar)0x1FFFFF) == false); -} - - - -static immutable UTF8stride = -[ - cast(ubyte) - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF, -]; - -/** - * stride() returns the length of a UTF-8 sequence starting at index i - * in string s. - * Returns: - * The number of bytes in the UTF-8 sequence or - * 0xFF meaning s[i] is not the start of of UTF-8 sequence. - */ -@safe @nogc pure nothrow -uint stride(in char[] s, size_t i) -{ - return UTF8stride[s[i]]; -} - -/** - * stride() returns the length of a UTF-16 sequence starting at index i - * in string s. - */ -@safe @nogc pure nothrow -uint stride(in wchar[] s, size_t i) -{ uint u = s[i]; - return 1 + (u >= 0xD800 && u <= 0xDBFF); -} - -/** - * stride() returns the length of a UTF-32 sequence starting at index i - * in string s. - * Returns: The return value will always be 1. - */ -@safe @nogc pure nothrow -uint stride(in dchar[] s, size_t i) -{ - return 1; -} - -/******************************************* - * Given an index i into an array of characters s[], - * and assuming that index i is at the start of a UTF character, - * determine the number of UCS characters up to that index i. - */ -@safe pure -size_t toUCSindex(in char[] s, size_t i) -{ - size_t n; - size_t j; - - for (j = 0; j < i; ) - { - j += stride(s, j); - n++; - } - if (j > i) - { - onUnicodeError("invalid UTF-8 sequence", j); - } - return n; -} - -/** ditto */ -@safe pure -size_t toUCSindex(in wchar[] s, size_t i) -{ - size_t n; - size_t j; - - for (j = 0; j < i; ) - { - j += stride(s, j); - n++; - } - if (j > i) - { - onUnicodeError("invalid UTF-16 sequence", j); - } - return n; -} - -/** ditto */ -@safe @nogc pure nothrow -size_t toUCSindex(in dchar[] s, size_t i) -{ - return i; -} - -/****************************************** - * Given a UCS index n into an array of characters s[], return the UTF index. - */ -@safe pure -size_t toUTFindex(in char[] s, size_t n) -{ - size_t i; - - while (n--) - { - uint j = UTF8stride[s[i]]; - if (j == 0xFF) - onUnicodeError("invalid UTF-8 sequence", i); - i += j; - } - return i; -} - -/** ditto */ -@safe @nogc pure nothrow -size_t toUTFindex(in wchar[] s, size_t n) -{ - size_t i; - - while (n--) - { wchar u = s[i]; - - i += 1 + (u >= 0xD800 && u <= 0xDBFF); - } - return i; -} - -/** ditto */ -@safe @nogc pure nothrow -size_t toUTFindex(in dchar[] s, size_t n) -{ - return n; -} - -/* =================== Decode ======================= */ - -/*************** - * Decodes and returns character starting at s[idx]. idx is advanced past the - * decoded character. If the character is not well formed, a UtfException is - * thrown and idx remains unchanged. - */ -@safe pure -dchar decode(in char[] s, ref size_t idx) - in - { - assert(idx >= 0 && idx < s.length); - } - out (result) - { - assert(isValidDchar(result)); - } - body - { - size_t len = s.length; - dchar V; - size_t i = idx; - char u = s[i]; - - if (u & 0x80) - { uint n; - char u2; - - /* The following encodings are valid, except for the 5 and 6 byte - * combinations: - * 0xxxxxxx - * 110xxxxx 10xxxxxx - * 1110xxxx 10xxxxxx 10xxxxxx - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - */ - for (n = 1; ; n++) - { - if (n > 4) - goto Lerr; // only do the first 4 of 6 encodings - if (((u << n) & 0x80) == 0) - { - if (n == 1) - goto Lerr; - break; - } - } - - // Pick off (7 - n) significant bits of B from first byte of octet - V = cast(dchar)(u & ((1 << (7 - n)) - 1)); - - if (i + (n - 1) >= len) - goto Lerr; // off end of string - - /* The following combinations are overlong, and illegal: - * 1100000x (10xxxxxx) - * 11100000 100xxxxx (10xxxxxx) - * 11110000 1000xxxx (10xxxxxx 10xxxxxx) - * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) - * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) - */ - u2 = s[i + 1]; - if ((u & 0xFE) == 0xC0 || - (u == 0xE0 && (u2 & 0xE0) == 0x80) || - (u == 0xF0 && (u2 & 0xF0) == 0x80) || - (u == 0xF8 && (u2 & 0xF8) == 0x80) || - (u == 0xFC && (u2 & 0xFC) == 0x80)) - goto Lerr; // overlong combination - - for (uint j = 1; j != n; j++) - { - u = s[i + j]; - if ((u & 0xC0) != 0x80) - goto Lerr; // trailing bytes are 10xxxxxx - V = (V << 6) | (u & 0x3F); - } - if (!isValidDchar(V)) - goto Lerr; - i += n; - } - else - { - V = cast(dchar) u; - i++; - } - - idx = i; - return V; - - Lerr: - onUnicodeError("invalid UTF-8 sequence", i); - return V; // dummy return - } - -unittest -{ size_t i; - dchar c; - - debug(utf) printf("utf.decode.unittest\n"); - - static s1 = "abcd"c; - i = 0; - c = decode(s1, i); - assert(c == cast(dchar)'a'); - assert(i == 1); - c = decode(s1, i); - assert(c == cast(dchar)'b'); - assert(i == 2); - - static s2 = "\xC2\xA9"c; - i = 0; - c = decode(s2, i); - assert(c == cast(dchar)'\u00A9'); - assert(i == 2); - - static s3 = "\xE2\x89\xA0"c; - i = 0; - c = decode(s3, i); - assert(c == cast(dchar)'\u2260'); - assert(i == 3); - - static s4 = - [ "\xE2\x89"c[], // too short - "\xC0\x8A", - "\xE0\x80\x8A", - "\xF0\x80\x80\x8A", - "\xF8\x80\x80\x80\x8A", - "\xFC\x80\x80\x80\x80\x8A", - ]; - - for (int j = 0; j < s4.length; j++) - { - try - { - i = 0; - c = decode(s4[j], i); - assert(0); - } - catch (Throwable o) - { - i = 23; - } - assert(i == 23); - } -} - -/** ditto */ -@safe pure -dchar decode(in wchar[] s, ref size_t idx) - in - { - assert(idx >= 0 && idx < s.length); - } - out (result) - { - assert(isValidDchar(result)); - } - body - { - string msg; - dchar V; - size_t i = idx; - uint u = s[i]; - - if (u & ~0x7F) - { if (u >= 0xD800 && u <= 0xDBFF) - { uint u2; - - if (i + 1 == s.length) - { msg = "surrogate UTF-16 high value past end of string"; - goto Lerr; - } - u2 = s[i + 1]; - if (u2 < 0xDC00 || u2 > 0xDFFF) - { msg = "surrogate UTF-16 low value out of range"; - goto Lerr; - } - u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00); - i += 2; - } - else if (u >= 0xDC00 && u <= 0xDFFF) - { msg = "unpaired surrogate UTF-16 value"; - goto Lerr; - } - else if (u == 0xFFFE || u == 0xFFFF) - { msg = "illegal UTF-16 value"; - goto Lerr; - } - else - i++; - } - else - { - i++; - } - - idx = i; - return cast(dchar)u; - - Lerr: - onUnicodeError(msg, i); - return cast(dchar)u; // dummy return - } - -/** ditto */ -@safe pure -dchar decode(in dchar[] s, ref size_t idx) - in - { - assert(idx >= 0 && idx < s.length); - } - body - { - size_t i = idx; - dchar c = s[i]; - - if (!isValidDchar(c)) - goto Lerr; - idx = i + 1; - return c; - - Lerr: - onUnicodeError("invalid UTF-32 value", i); - return c; // dummy return - } - - -/* =================== Encode ======================= */ - -/******************************* - * Encodes character c and appends it to array s[]. - */ -@safe pure nothrow -void encode(ref char[] s, dchar c) - in - { - assert(isValidDchar(c)); - } - body - { - char[] r = s; - - if (c <= 0x7F) - { - r ~= cast(char) c; - } - else - { - char[4] buf; - uint L; - - if (c <= 0x7FF) - { - buf[0] = cast(char)(0xC0 | (c >> 6)); - buf[1] = cast(char)(0x80 | (c & 0x3F)); - L = 2; - } - else if (c <= 0xFFFF) - { - buf[0] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[2] = cast(char)(0x80 | (c & 0x3F)); - L = 3; - } - else if (c <= 0x10FFFF) - { - buf[0] = cast(char)(0xF0 | (c >> 18)); - buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[3] = cast(char)(0x80 | (c & 0x3F)); - L = 4; - } - else - { - assert(0); - } - r ~= buf[0 .. L]; - } - s = r; - } - -unittest -{ - debug(utf) printf("utf.encode.unittest\n"); - - char[] s = "abcd".dup; - encode(s, cast(dchar)'a'); - assert(s.length == 5); - assert(s == "abcda"); - - encode(s, cast(dchar)'\u00A9'); - assert(s.length == 7); - assert(s == "abcda\xC2\xA9"); - //assert(s == "abcda\u00A9"); // BUG: fix compiler - - encode(s, cast(dchar)'\u2260'); - assert(s.length == 10); - assert(s == "abcda\xC2\xA9\xE2\x89\xA0"); -} - -/** ditto */ -@safe pure nothrow -void encode(ref wchar[] s, dchar c) - in - { - assert(isValidDchar(c)); - } - body - { - wchar[] r = s; - - if (c <= 0xFFFF) - { - r ~= cast(wchar) c; - } - else - { - wchar[2] buf; - - buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); - r ~= buf; - } - s = r; - } - -/** ditto */ -@safe pure nothrow -void encode(ref dchar[] s, dchar c) - in - { - assert(isValidDchar(c)); - } - body - { - s ~= c; - } - -/** -Returns the code length of $(D c) in the encoding using $(D C) as a -code point. The code is returned in character count, not in bytes. - */ -@safe pure nothrow @nogc -ubyte codeLength(C)(dchar c) -{ - static if (C.sizeof == 1) - { - if (c <= 0x7F) return 1; - if (c <= 0x7FF) return 2; - if (c <= 0xFFFF) return 3; - if (c <= 0x10FFFF) return 4; - assert(false); - } - else static if (C.sizeof == 2) - { - return c <= 0xFFFF ? 1 : 2; - } - else - { - static assert(C.sizeof == 4); - return 1; - } -} - -/* =================== Validation ======================= */ - -/*********************************** -Checks to see if string is well formed or not. $(D S) can be an array - of $(D char), $(D wchar), or $(D dchar). Throws a $(D UtfException) - if it is not. Use to check all untrusted input for correctness. - */ -@safe pure -void validate(S)(in S s) -{ - auto len = s.length; - for (size_t i = 0; i < len; ) - { - decode(s, i); - } -} - -/* =================== Conversion to UTF8 ======================= */ - -@safe pure nothrow @nogc -char[] toUTF8(char[] buf, dchar c) - in - { - assert(isValidDchar(c)); - } - body - { - if (c <= 0x7F) - { - buf[0] = cast(char) c; - return buf[0 .. 1]; - } - else if (c <= 0x7FF) - { - buf[0] = cast(char)(0xC0 | (c >> 6)); - buf[1] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 2]; - } - else if (c <= 0xFFFF) - { - buf[0] = cast(char)(0xE0 | (c >> 12)); - buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[2] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 3]; - } - else if (c <= 0x10FFFF) - { - buf[0] = cast(char)(0xF0 | (c >> 18)); - buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); - buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); - buf[3] = cast(char)(0x80 | (c & 0x3F)); - return buf[0 .. 4]; - } - assert(0); - } - -/******************* - * Encodes string s into UTF-8 and returns the encoded string. - */ -@safe pure nothrow -string toUTF8(string s) - in - { - validate(s); - } - body - { - return s; - } - -/** ditto */ -@trusted pure -string toUTF8(in wchar[] s) -{ - char[] r; - size_t i; - size_t slen = s.length; - - r.length = slen; - - for (i = 0; i < slen; i++) - { wchar c = s[i]; - - if (c <= 0x7F) - r[i] = cast(char)c; // fast path for ascii - else - { - r.length = i; - foreach (dchar ch; s[i .. slen]) - { - encode(r, ch); - } - break; - } - } - return cast(string)r; -} - -/** ditto */ -@trusted pure -string toUTF8(in dchar[] s) -{ - char[] r; - size_t i; - size_t slen = s.length; - - r.length = slen; - - for (i = 0; i < slen; i++) - { dchar c = s[i]; - - if (c <= 0x7F) - r[i] = cast(char)c; // fast path for ascii - else - { - r.length = i; - foreach (dchar d; s[i .. slen]) - { - encode(r, d); - } - break; - } - } - return cast(string)r; -} - -/* =================== Conversion to UTF16 ======================= */ - -@safe pure nothrow @nogc -wchar[] toUTF16(wchar[] buf, dchar c) - in - { - assert(isValidDchar(c)); - } - body - { - if (c <= 0xFFFF) - { - buf[0] = cast(wchar) c; - return buf[0 .. 1]; - } - else - { - buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); - buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); - return buf[0 .. 2]; - } - } - -/**************** - * Encodes string s into UTF-16 and returns the encoded string. - * toUTF16z() is suitable for calling the 'W' functions in the Win32 API that take - * an LPWSTR or LPCWSTR argument. - */ -@trusted pure -wstring toUTF16(in char[] s) -{ - wchar[] r; - size_t slen = s.length; - - r.length = slen; - r.length = 0; - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c <= 0x7F) - { - i++; - r ~= cast(wchar)c; - } - else - { - c = decode(s, i); - encode(r, c); - } - } - return cast(wstring)r; -} - -alias const(wchar)* wptr; -/** ditto */ -@safe pure -wptr toUTF16z(in char[] s) -{ - wchar[] r; - size_t slen = s.length; - - r.length = slen + 1; - r.length = 0; - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c <= 0x7F) - { - i++; - r ~= cast(wchar)c; - } - else - { - c = decode(s, i); - encode(r, c); - } - } - r ~= '\000'; - return &r[0]; -} - -/** ditto */ -@safe pure nothrow -wstring toUTF16(wstring s) - in - { - validate(s); - } - body - { - return s; - } - -/** ditto */ -@trusted pure nothrow -wstring toUTF16(in dchar[] s) -{ - wchar[] r; - size_t slen = s.length; - - r.length = slen; - r.length = 0; - for (size_t i = 0; i < slen; i++) - { - encode(r, s[i]); - } - return cast(wstring)r; -} - -/* =================== Conversion to UTF32 ======================= */ - -/***** - * Encodes string s into UTF-32 and returns the encoded string. - */ -@trusted pure -dstring toUTF32(in char[] s) -{ - dchar[] r; - size_t slen = s.length; - size_t j = 0; - - r.length = slen; // r[] will never be longer than s[] - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c >= 0x80) - c = decode(s, i); - else - i++; // c is ascii, no need for decode - r[j++] = c; - } - return cast(dstring)r[0 .. j]; -} - -/** ditto */ -@trusted pure -dstring toUTF32(in wchar[] s) -{ - dchar[] r; - size_t slen = s.length; - size_t j = 0; - - r.length = slen; // r[] will never be longer than s[] - for (size_t i = 0; i < slen; ) - { - dchar c = s[i]; - if (c >= 0x80) - c = decode(s, i); - else - i++; // c is ascii, no need for decode - r[j++] = c; - } - return cast(dstring)r[0 .. j]; -} - -/** ditto */ -@safe pure nothrow -dstring toUTF32(dstring s) - in - { - validate(s); - } - body - { - return s; - } - -/* ================================ tests ================================== */ - -unittest -{ - debug(utf) printf("utf.toUTF.unittest\n"); - - auto c = "hello"c[]; - auto w = toUTF16(c); - assert(w == "hello"); - auto d = toUTF32(c); - assert(d == "hello"); - - c = toUTF8(w); - assert(c == "hello"); - d = toUTF32(w); - assert(d == "hello"); - - c = toUTF8(d); - assert(c == "hello"); - w = toUTF16(d); - assert(w == "hello"); - - - c = "hel\u1234o"; - w = toUTF16(c); - assert(w == "hel\u1234o"); - d = toUTF32(c); - assert(d == "hel\u1234o"); - - c = toUTF8(w); - assert(c == "hel\u1234o"); - d = toUTF32(w); - assert(d == "hel\u1234o"); - - c = toUTF8(d); - assert(c == "hel\u1234o"); - w = toUTF16(d); - assert(w == "hel\u1234o"); - - - c = "he\U000BAAAAllo"; - w = toUTF16(c); - //foreach (wchar c; w) printf("c = x%x\n", c); - //foreach (wchar c; cast(wstring)"he\U000BAAAAllo") printf("c = x%x\n", c); - assert(w == "he\U000BAAAAllo"); - d = toUTF32(c); - assert(d == "he\U000BAAAAllo"); - - c = toUTF8(w); - assert(c == "he\U000BAAAAllo"); - d = toUTF32(w); - assert(d == "he\U000BAAAAllo"); - - c = toUTF8(d); - assert(c == "he\U000BAAAAllo"); - w = toUTF16(d); - assert(w == "he\U000BAAAAllo"); - - wchar[2] buf; - auto ret = toUTF16(buf, '\U000BAAAA'); - assert(ret == "\U000BAAAA"); -} diff --git a/libphobos/libdruntime/rt/util/utility.d b/libphobos/libdruntime/rt/util/utility.d new file mode 100644 index 0000000..b1796fd --- /dev/null +++ b/libphobos/libdruntime/rt/util/utility.d @@ -0,0 +1,44 @@ +/** + * Contains various utility functions used by the runtime implementation. + * + * Copyright: Copyright Digital Mars 2016. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Jacob Carlborg + * Source: $(DRUNTIMESRC rt/util/_utility.d) + */ +module rt.util.utility; + +/** + * Asserts that the given condition is `true`. + * + * The assertion is independent from -release, by abort()ing. Regular assertions + * throw an AssertError and thus require an initialized GC, which might not be + * the case (yet or anymore) for the startup/shutdown code in this package + * (called by CRT ctors/dtors etc.). + */ +package(rt) void safeAssert( + bool condition, scope string msg, scope string file = __FILE__, size_t line = __LINE__ +) nothrow @nogc @safe +{ + import core.internal.abort; + condition || abort(msg, file, line); +} + +// @@@DEPRECATED_2.105@@@ +// Remove this when complex types have been removed from the language. +package(rt) +{ + private struct _Complex(T) { T re; T im; } + + enum __c_complex_float : _Complex!float; + enum __c_complex_double : _Complex!double; + enum __c_complex_real : _Complex!real; // This is why we don't use stdc.config + + alias d_cfloat = __c_complex_float; + alias d_cdouble = __c_complex_double; + alias d_creal = __c_complex_real; + + enum isComplex(T) = is(T == d_cfloat) || is(T == d_cdouble) || is(T == d_creal); +} |