diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-02-28 15:47:52 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2022-02-28 17:49:01 +0100 |
commit | 1027dc459204894f4503f713a3d73826e4bbab15 (patch) | |
tree | 606564b15e0978c77c76d0e618c00c25a78aaf38 /libphobos/libdruntime | |
parent | 430c89274d7f82810724126637ffdc5507d442f0 (diff) | |
download | gcc-1027dc459204894f4503f713a3d73826e4bbab15.zip gcc-1027dc459204894f4503f713a3d73826e4bbab15.tar.gz gcc-1027dc459204894f4503f713a3d73826e4bbab15.tar.bz2 |
d: Merge upstream dmd cf63dd8e5, druntime caf14b0f, phobos 41aaf8c26.
D front-end changes:
- Import dmd v2.099.0-rc.1.
- The `main' can now return type `noreturn' and supports return
inference.
D Runtime changes:
- Import druntime v2.099.0-rc.1.
- C bindings for stat_t on powerpc-linux has been fixed.
Phobos changes:
- Import phobos v2.099.0-rc.1.
gcc/d/ChangeLog:
* d-target.cc (Target::_init): Initialize C type size fields.
* dmd/MERGE: Merge upstream dmd cf63dd8e5.
* dmd/VERSION: Update version to v2.099.0-rc.1.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime caf14b0f.
* src/MERGE: Merge upstream phobos 41aaf8c26.
gcc/testsuite/ChangeLog:
* gdc.dg/torture/simd7413a.d: Update.
* gdc.dg/ubsan/pr88957.d: Update.
* gdc.dg/simd18489.d: New test.
* gdc.dg/torture/simd21727.d: New test.
Diffstat (limited to 'libphobos/libdruntime')
-rw-r--r-- | libphobos/libdruntime/MERGE | 2 | ||||
-rw-r--r-- | libphobos/libdruntime/core/gc/gcinterface.d | 4 | ||||
-rw-r--r-- | libphobos/libdruntime/core/internal/gc/bits.d | 12 | ||||
-rw-r--r-- | libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d | 257 | ||||
-rw-r--r-- | libphobos/libdruntime/core/internal/gc/pooltable.d | 29 | ||||
-rw-r--r-- | libphobos/libdruntime/core/internal/gc/proxy.d | 4 | ||||
-rw-r--r-- | libphobos/libdruntime/core/memory.d | 4 | ||||
-rw-r--r-- | libphobos/libdruntime/core/stdcpp/string.d | 8 | ||||
-rw-r--r-- | libphobos/libdruntime/core/sys/posix/sys/stat.d | 85 | ||||
-rw-r--r-- | libphobos/libdruntime/core/time.d | 158 | ||||
-rw-r--r-- | libphobos/libdruntime/object.d | 13 |
11 files changed, 329 insertions, 247 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 49f6ae2..7c0bb57 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -55528bd1e963d858eaa63901fc818b957c349fbc +caf14b0f4ebbae4157aac89368d6278332ee2aa1 The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d index e8cdf11..5560c62 100644 --- a/libphobos/libdruntime/core/gc/gcinterface.d +++ b/libphobos/libdruntime/core/gc/gcinterface.d @@ -141,13 +141,13 @@ interface GC * Retrieve statistics about garbage collection. * Useful for debugging and tuning. */ - core.memory.GC.Stats stats() nothrow; + core.memory.GC.Stats stats() @safe nothrow @nogc; /** * Retrieve profile statistics about garbage collection. * Useful for debugging and tuning. */ - core.memory.GC.ProfileStats profileStats() nothrow @safe; + core.memory.GC.ProfileStats profileStats() @safe nothrow @nogc; /** * add p to list of roots diff --git a/libphobos/libdruntime/core/internal/gc/bits.d b/libphobos/libdruntime/core/internal/gc/bits.d index d50c38f..3c1bb54 100644 --- a/libphobos/libdruntime/core/internal/gc/bits.d +++ b/libphobos/libdruntime/core/internal/gc/bits.d @@ -60,7 +60,7 @@ struct GCBits onOutOfMemoryError(); } - wordtype test(size_t i) const nothrow + wordtype test(size_t i) const scope @trusted pure nothrow @nogc in { assert(i < nbits); @@ -70,7 +70,7 @@ struct GCBits return core.bitop.bt(data, i); } - int set(size_t i) nothrow + int set(size_t i) scope @trusted pure nothrow @nogc in { assert(i < nbits); @@ -80,7 +80,7 @@ struct GCBits return core.bitop.bts(data, i); } - int clear(size_t i) nothrow + int clear(size_t i) scope @trusted pure nothrow @nogc in { assert(i <= nbits); @@ -91,7 +91,7 @@ struct GCBits } // return non-zero if bit already set - size_t setLocked(size_t i) nothrow + size_t setLocked(size_t i) scope @trusted pure nothrow @nogc { version (GNU) { @@ -112,7 +112,7 @@ struct GCBits } else version (D_InlineAsm_X86) { - asm @nogc nothrow { + asm pure @nogc nothrow { mov EAX, this; mov ECX, data[EAX]; mov EDX, i; @@ -123,7 +123,7 @@ struct GCBits } else version (D_InlineAsm_X86_64) { - asm @nogc nothrow { + asm pure @nogc nothrow { mov RAX, this; mov RAX, data[RAX]; mov RDX, i; diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d index 87c45fb..aa51867 100644 --- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d +++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d @@ -38,6 +38,8 @@ import core.gc.config; import core.gc.gcinterface; import core.internal.container.treap; +import core.internal.spinlock; +import core.internal.gc.pooltable; import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; import core.stdc.string : memcpy, memset, memmove; @@ -145,7 +147,6 @@ class ConservativeGC : GC Gcx *gcx; // implementation - import core.internal.spinlock; static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy); static bool _inFinalizer; __gshared bool isPrecise = false; @@ -155,7 +156,7 @@ class ConservativeGC : GC * * Throws: InvalidMemoryOperationError on recursive locking during finalization. */ - static void lockNR() @nogc nothrow + static void lockNR() @safe @nogc nothrow { if (_inFinalizer) onInvalidMemoryOperationError(); @@ -685,7 +686,7 @@ class ConservativeGC : GC else if (pagenum + newsz <= pool.npages) { // Attempt to expand in place (TODO: merge with extend) - if (lpool.pagetable[pagenum + psz] != B_FREE) + if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return doMalloc(); auto newPages = newsz - psz; @@ -695,7 +696,7 @@ class ConservativeGC : GC debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize); debug (PRINTF) printFreeInfo(pool); - memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newPages); + memset(&lpool.pagetable[pagenum + psz], Bins.B_PAGEPLUS, newPages); lpool.bPageOffsets[pagenum] = cast(uint) newsz; for (auto offset = psz; offset < newsz; offset++) lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; @@ -766,7 +767,7 @@ class ConservativeGC : GC auto lpool = cast(LargeObjectPool*) pool; size_t pagenum = lpool.pagenumOf(p); - if (lpool.pagetable[pagenum] != B_PAGE) + if (lpool.pagetable[pagenum] != Bins.B_PAGE) return 0; size_t psz = lpool.bPageOffsets[pagenum]; @@ -777,7 +778,7 @@ class ConservativeGC : GC if (pagenum + psz >= lpool.npages) return 0; - if (lpool.pagetable[pagenum + psz] != B_FREE) + if (lpool.pagetable[pagenum + psz] != Bins.B_FREE) return 0; size_t freesz = lpool.bPageOffsets[pagenum + psz]; @@ -785,7 +786,7 @@ class ConservativeGC : GC return 0; size_t sz = freesz > maxsz ? maxsz : freesz; debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE); - memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz); + memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz); lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz); for (auto offset = psz; offset < psz + sz; offset++) lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; @@ -874,11 +875,11 @@ class ConservativeGC : GC debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]); - bin = cast(Bins)pool.pagetable[pagenum]; + bin = pool.pagetable[pagenum]; // Verify that the pointer is at the beginning of a block, // no action should be taken if p is an interior pointer - if (bin > B_PAGE) // B_PAGEPLUS or B_FREE + if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE return; size_t off = (sentinel_sub(p) - pool.baseAddr); size_t base = baseOffset(off, bin); @@ -893,7 +894,7 @@ class ConservativeGC : GC if (pool.isLargeObject) // if large alloc { biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large; - assert(bin == B_PAGE); + assert(bin == Bins.B_PAGE); auto lpool = cast(LargeObjectPool*) pool; // Free pages @@ -1094,13 +1095,13 @@ class ConservativeGC : GC pool = gcx.findPool(p); assert(pool); pagenum = pool.pagenumOf(p); - bin = cast(Bins)pool.pagetable[pagenum]; - assert(bin <= B_PAGE); + bin = pool.pagetable[pagenum]; + assert(bin <= Bins.B_PAGE); assert(p == cast(void*)baseOffset(cast(size_t)p, bin)); debug (PTRCHECK2) { - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { // Check that p is not on a free list List *list; @@ -1299,7 +1300,7 @@ class ConservativeGC : GC } - core.memory.GC.Stats stats() nothrow + core.memory.GC.Stats stats() @safe nothrow @nogc { typeof(return) ret; @@ -1332,13 +1333,20 @@ class ConservativeGC : GC // // Implementation of getStats // - private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow + private void getStatsNoSync(out core.memory.GC.Stats stats) @trusted nothrow @nogc { - foreach (pool; gcx.pooltable[0 .. gcx.npools]) + // This function is trusted for two reasons: `pool.pagetable` is a pointer, + // which is being sliced in the below foreach, and so is `binPageChain`, + // also sliced a bit later in this function. + // However, both usages are safe as long as the assumption that `npools` + // defines the limit for `pagetable`'s length holds true (see allocation). + // The slicing happens at __LINE__ + 4 and __LINE__ + 24. + // `@trusted` delegates are not used to prevent any performance issue. + foreach (pool; gcx.pooltable[]) { foreach (bin; pool.pagetable[0 .. pool.npages]) { - if (bin == B_FREE) + if (bin == Bins.B_FREE) stats.freeSize += PAGESIZE; else stats.usedSize += PAGESIZE; @@ -1346,13 +1354,13 @@ class ConservativeGC : GC } size_t freeListSize; - foreach (n; 0 .. B_PAGE) + foreach (n; 0 .. Bins.B_PAGE) { immutable sz = binsize[n]; for (List *list = gcx.bucket[n]; list; list = list.next) freeListSize += sz; - foreach (pool; gcx.pooltable[0 .. gcx.npools]) + foreach (pool; gcx.pooltable[]) { if (pool.isLargeObject) continue; @@ -1381,7 +1389,7 @@ enum } -enum +enum Bins : ubyte { B_16, B_32, @@ -1405,10 +1413,6 @@ enum B_MAX, } - -alias ubyte Bins; - - struct List { List *next; @@ -1416,12 +1420,12 @@ struct List } // non power of two sizes optimized for small remainder within page (<= 64 bytes) -immutable short[B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ]; -immutable short[PAGESIZE / 16][B_NUMSMALL + 1] binbase = calcBinBase(); +immutable short[Bins.B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ]; +immutable short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] binbase = calcBinBase(); -short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase() +short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase() { - short[PAGESIZE / 16][B_NUMSMALL + 1] bin; + short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] bin; foreach (i, size; binsize) { @@ -1440,7 +1444,7 @@ short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase() size_t baseOffset(size_t offset, Bins bin) @nogc nothrow { - assert(bin <= B_PAGE); + assert(bin <= Bins.B_PAGE); return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4]; } @@ -1448,9 +1452,9 @@ alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD]; static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0); // bitmask with bits set at base offsets of objects -immutable PageBits[B_NUMSMALL] baseOffsetBits = (){ - PageBits[B_NUMSMALL] bits; - foreach (bin; 0..B_NUMSMALL) +immutable PageBits[Bins.B_NUMSMALL] baseOffsetBits = (){ + PageBits[Bins.B_NUMSMALL] bits; + foreach (bin; 0 .. Bins.B_NUMSMALL) { size_t size = binsize[bin]; const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned @@ -1475,7 +1479,6 @@ private void set(ref PageBits bits, size_t i) @nogc pure nothrow struct Gcx { - import core.internal.spinlock; auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); Treap!Root roots; @@ -1491,11 +1494,9 @@ struct Gcx debug(INVARIANT) bool inCollection; uint disabled; // turn off collections if >0 - import core.internal.gc.pooltable; - private @property size_t npools() pure const nothrow { return pooltable.length; } PoolTable!Pool pooltable; - List*[B_NUMSMALL] bucket; // free list for each small size + List*[Bins.B_NUMSMALL] bucket; // free list for each small size // run a collection when reaching those thresholds (number of used pages) float smallCollectThreshold, largeCollectThreshold; @@ -1508,7 +1509,7 @@ struct Gcx else alias leakDetector = LeakDetector; - SmallObjectPool*[B_NUMSMALL] recoverPool; + SmallObjectPool*[Bins.B_NUMSMALL] recoverPool; version (Posix) __gshared Gcx* instance; void initialize() @@ -1592,9 +1593,8 @@ struct Gcx debug(INVARIANT) initialized = false; - for (size_t i = 0; i < npools; i++) + foreach (Pool* pool; this.pooltable[]) { - Pool *pool = pooltable[i]; mappedPages -= pool.npages; pool.Dtor(); cstdlib.free(pool); @@ -1635,7 +1635,7 @@ struct Gcx if (!inCollection) (cast()rangesLock).unlock(); - for (size_t i = 0; i < B_NUMSMALL; i++) + for (size_t i = 0; i < Bins.B_NUMSMALL; i++) { size_t j = 0; List* prev, pprev, ppprev; // keep a short history to inspect in the debugger @@ -1752,7 +1752,7 @@ struct Gcx ConservativeGC._inFinalizer = true; scope (failure) ConservativeGC._inFinalizer = false; - foreach (pool; pooltable[0 .. npools]) + foreach (pool; this.pooltable[]) { if (!pool.finals.nbits) continue; @@ -1816,18 +1816,18 @@ struct Gcx /** * Computes the bin table using CTFE. */ - static byte[2049] ctfeBins() nothrow + static Bins[2049] ctfeBins() nothrow { - byte[2049] ret; + Bins[2049] ret; size_t p = 0; - for (Bins b = B_16; b <= B_2048; b++) + for (Bins b = Bins.B_16; b <= Bins.B_2048; b++) for ( ; p <= binsize[b]; p++) ret[p] = b; return ret; } - static const byte[2049] binTable = ctfeBins(); + static immutable Bins[2049] binTable = ctfeBins(); /** * Allocate a new pool of at least size bytes. @@ -1994,7 +1994,7 @@ struct Gcx bool tryAlloc() nothrow { - foreach (p; pooltable[0 .. npools]) + foreach (p; this.pooltable[]) { if (!p.isLargeObject || p.freepages < npages) continue; @@ -2094,10 +2094,11 @@ struct Gcx } // Allocate successively larger pools up to 8 megs - if (npools) - { size_t n; + if (this.pooltable.length) + { + size_t n; - n = config.minPoolSize + config.incPoolSize * npools; + n = config.minPoolSize + config.incPoolSize * this.pooltable.length; if (n > config.maxPoolSize) n = config.maxPoolSize; // cap pool size n /= PAGESIZE; // convert bytes to pages @@ -2139,9 +2140,8 @@ struct Gcx List* allocPage(Bins bin) nothrow { //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin); - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { - Pool* pool = pooltable[n]; if (pool.isLargeObject) continue; if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin)) @@ -2275,7 +2275,7 @@ struct Gcx // let dmd allocate a register for this.pools auto pools = pooltable.pools; - const highpool = pooltable.npools - 1; + const highpool = pooltable.length - 1; const minAddr = pooltable.minAddr; size_t memSize = pooltable.maxAddr - minAddr; Pool* pool = null; @@ -2300,7 +2300,6 @@ struct Gcx bitpos -= rng.bmplength; rng.pbase += rng.bmplength; } - import core.bitop; if (!core.bitop.bt(rng.ptrbmp, bitpos)) { debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n"); @@ -2335,7 +2334,7 @@ struct Gcx printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin); // Adjust bit to be at start of allocated memory block - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { // We don't care abou setting pointsToBase correctly // because it's ignored for small object pools anyhow. @@ -2356,7 +2355,7 @@ struct Gcx goto LaddRange; } } - else if (bin == B_PAGE) + else if (bin == Bins.B_PAGE) { biti = offset >> Pool.ShiftBy.Large; //debug(PRINTF) printf("\t\tbiti = x%x\n", biti); @@ -2376,7 +2375,7 @@ struct Gcx goto LaddLargeRange; } } - else if (bin == B_PAGEPLUS) + else if (bin == Bins.B_PAGEPLUS) { pn -= pool.bPageOffsets[pn]; biti = pn * (PAGESIZE >> Pool.ShiftBy.Large); @@ -2429,7 +2428,7 @@ struct Gcx else { // Don't mark bits in B_FREE pages - assert(bin == B_FREE); + assert(bin == Bins.B_FREE); } } LnextPtr: @@ -2526,9 +2525,8 @@ struct Gcx { debug(COLLECT_PRINTF) printf("preparing mark.\n"); - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { - Pool* pool = pooltable[n]; if (pool.isLargeObject) pool.mark.zero(); else @@ -2598,10 +2596,9 @@ struct Gcx size_t freedLargePages; size_t freedSmallPages; size_t freed; - for (size_t n = 0; n < npools; n++) + foreach (Pool* pool; this.pooltable[]) { size_t pn; - Pool* pool = pooltable[n]; if (pool.isLargeObject) { @@ -2612,12 +2609,12 @@ struct Gcx { npages = pool.bPageOffsets[pn]; Bins bin = cast(Bins)pool.pagetable[pn]; - if (bin == B_FREE) + if (bin == Bins.B_FREE) { numFree += npages; continue; } - assert(bin == B_PAGE); + assert(bin == Bins.B_PAGE); size_t biti = pn; if (!pool.mark.test(biti)) @@ -2637,7 +2634,7 @@ struct Gcx debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p); leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA)); - pool.pagetable[pn..pn+npages] = B_FREE; + pool.pagetable[pn..pn+npages] = Bins.B_FREE; if (pn < pool.searchStart) pool.searchStart = pn; freedLargePages += npages; pool.freepages += npages; @@ -2671,7 +2668,7 @@ struct Gcx { Bins bin = cast(Bins)pool.pagetable[pn]; - if (bin < B_PAGE) + if (bin < Bins.B_PAGE) { auto freebitsdata = pool.freebits.data + pn * PageBits.length; auto markdata = pool.mark.data + pn * PageBits.length; @@ -2767,7 +2764,7 @@ struct Gcx { pool.freeAllPageBits(pn); - pool.pagetable[pn] = B_FREE; + pool.pagetable[pn] = Bins.B_FREE; // add to free chain pool.binPageChain[pn] = cast(uint) pool.searchStart; pool.searchStart = pn; @@ -2789,7 +2786,8 @@ struct Gcx assert(freedLargePages <= usedLargePages); usedLargePages -= freedLargePages; - debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools); + debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", + freed, freedLargePages, this.pooltable.length); assert(freedSmallPages <= usedSmallPages); usedSmallPages -= freedSmallPages; @@ -2854,12 +2852,12 @@ struct Gcx private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow { Pool* pool; - while (poolIndex < npools && - ((pool = pooltable[poolIndex]).isLargeObject || + while (poolIndex < this.pooltable.length && + ((pool = this.pooltable[poolIndex]).isLargeObject || pool.recoverPageFirst[bin] >= pool.npages)) poolIndex++; - return recoverPool[bin] = poolIndex < npools ? cast(SmallObjectPool*)pool : null; + return recoverPool[bin] = poolIndex < this.pooltable.length ? cast(SmallObjectPool*)pool : null; } version (COLLECT_FORK) @@ -2928,7 +2926,6 @@ struct Gcx import core.stdc.stdlib : _Exit; debug (PRINTF_TO_FILE) { - import core.stdc.stdio : fflush; fflush(null); // avoid duplicated FILE* output } version (OSX) @@ -3153,7 +3150,7 @@ Lmark: // init bucket lists bucket[] = null; - foreach (Bins bin; 0..B_NUMSMALL) + foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL) setNextRecoverPool(bin, 0); stop = currTime; @@ -3188,24 +3185,24 @@ Lmark: auto pn = offset / PAGESIZE; auto bins = cast(Bins)pool.pagetable[pn]; size_t biti = void; - if (bins < B_PAGE) + if (bins < Bins.B_PAGE) { biti = baseOffset(offset, bins) >> pool.ShiftBy.Small; // doesn't need to check freebits because no pointer must exist // to a block that was free before starting the collection } - else if (bins == B_PAGE) + else if (bins == Bins.B_PAGE) { biti = pn * (PAGESIZE >> pool.ShiftBy.Large); } - else if (bins == B_PAGEPLUS) + else if (bins == Bins.B_PAGEPLUS) { pn -= pool.bPageOffsets[pn]; biti = pn * (PAGESIZE >> pool.ShiftBy.Large); } - else // bins == B_FREE + else // bins == Bins.B_FREE { - assert(bins == B_FREE); + assert(bins == Bins.B_FREE); return IsMarked.no; } return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no; @@ -3262,8 +3259,11 @@ Lmark: /* ============================ Parallel scanning =============================== */ version (COLLECT_PARALLEL): - import core.sync.event; + import core.atomic; + import core.cpuid; + import core.sync.event; + private: // disable invariants for background threads static struct ScanThreadData @@ -3334,7 +3334,6 @@ Lmark: int maxParallelThreads() nothrow { - import core.cpuid; auto threads = threadsPerCPU(); if (threads == 0) @@ -3512,7 +3511,7 @@ struct Pool GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only) size_t npages; size_t freepages; // The number of pages not in use. - ubyte* pagetable; + Bins* pagetable; bool isLargeObject; @@ -3541,7 +3540,7 @@ struct Pool enum PageRecovered = uint.max; // first of chain of pages to recover (SmallObjectPool only) - uint[B_NUMSMALL] recoverPageFirst; + uint[Bins.B_NUMSMALL] recoverPageFirst; // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only) immutable(size_t)** rtinfo; @@ -3611,7 +3610,7 @@ struct Pool noscan.alloc(nbits); appendable.alloc(nbits); - pagetable = cast(ubyte*)cstdlib.malloc(npages); + pagetable = cast(Bins*)cstdlib.malloc(npages * Bins.sizeof); if (!pagetable) onOutOfMemoryErrorNoGC(); @@ -3635,7 +3634,7 @@ struct Pool } } - memset(pagetable, B_FREE, npages); + memset(pagetable, Bins.B_FREE, npages); this.npages = npages; this.freepages = npages; @@ -3852,7 +3851,7 @@ struct Pool } public - @property bool isFree() const pure nothrow + @property bool isFree() const scope @safe pure nothrow @nogc { return npages == freepages; } @@ -3883,10 +3882,10 @@ struct Pool { size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; // Adjust bit to be at start of allocated memory block - if (bin < B_NUMSMALL) + if (bin < Bins.B_NUMSMALL) { auto baseOff = baseOffset(offset, bin); const biti = baseOff >> Pool.ShiftBy.Small; @@ -3894,11 +3893,11 @@ struct Pool return null; return baseAddr + baseOff; } - if (bin == B_PAGE) + if (bin == Bins.B_PAGE) { return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); } - if (bin == B_PAGEPLUS) + if (bin == Bins.B_PAGEPLUS) { size_t pageOffset = bPageOffsets[pn]; offset -= pageOffset * PAGESIZE; @@ -3907,7 +3906,7 @@ struct Pool return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); } // we are in a B_FREE page - assert(bin == B_FREE); + assert(bin == Bins.B_FREE); return null; } @@ -3944,8 +3943,8 @@ struct Pool { for (size_t i = 0; i < npages; i++) { - Bins bin = cast(Bins)pagetable[i]; - assert(bin < B_MAX); + Bins bin = pagetable[i]; + assert(bin < Bins.B_MAX); } } } @@ -4053,19 +4052,19 @@ struct LargeObjectPool uint np = bPageOffsets[n]; assert(np > 0 && np <= npages - n); - if (pagetable[n] == B_PAGE) + if (pagetable[n] == Bins.B_PAGE) { for (uint p = 1; p < np; p++) { - assert(pagetable[n + p] == B_PAGEPLUS); + assert(pagetable[n + p] == Bins.B_PAGEPLUS); assert(bPageOffsets[n + p] == p); } } - else if (pagetable[n] == B_FREE) + else if (pagetable[n] == Bins.B_FREE) { for (uint p = 1; p < np; p++) { - assert(pagetable[n + p] == B_FREE); + assert(pagetable[n + p] == Bins.B_FREE); } assert(bPageOffsets[n + np - 1] == np); } @@ -4086,17 +4085,17 @@ struct LargeObjectPool //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n); size_t largest = 0; - if (pagetable[searchStart] == B_PAGEPLUS) + if (pagetable[searchStart] == Bins.B_PAGEPLUS) { searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE searchStart += bPageOffsets[searchStart]; } - while (searchStart < npages && pagetable[searchStart] == B_PAGE) + while (searchStart < npages && pagetable[searchStart] == Bins.B_PAGE) searchStart += bPageOffsets[searchStart]; for (size_t i = searchStart; i < npages; ) { - assert(pagetable[i] == B_FREE); + assert(pagetable[i] == Bins.B_FREE); auto p = bPageOffsets[i]; if (p > n) @@ -4107,11 +4106,11 @@ struct LargeObjectPool if (p == n) { L_found: - pagetable[i] = B_PAGE; + pagetable[i] = Bins.B_PAGE; bPageOffsets[i] = cast(uint) n; if (n > 1) { - memset(&pagetable[i + 1], B_PAGEPLUS, n - 1); + memset(&pagetable[i + 1], Bins.B_PAGEPLUS, n - 1); for (auto offset = 1; offset < n; offset++) bPageOffsets[i + offset] = cast(uint) offset; } @@ -4122,7 +4121,7 @@ struct LargeObjectPool largest = p; i += p; - while (i < npages && pagetable[i] == B_PAGE) + while (i < npages && pagetable[i] == Bins.B_PAGE) { // we have the size information, so we skip a whole bunch of pages. i += bPageOffsets[i]; @@ -4145,8 +4144,8 @@ struct LargeObjectPool for (size_t i = pagenum; i < npages + pagenum; i++) { - assert(pagetable[i] < B_FREE); - pagetable[i] = B_FREE; + assert(pagetable[i] < Bins.B_FREE); + pagetable[i] = Bins.B_FREE; } freepages += npages; largestFree = freepages; // invalidate @@ -4157,8 +4156,8 @@ struct LargeObjectPool */ void setFreePageOffsets(size_t page, size_t num) nothrow @nogc { - assert(pagetable[page] == B_FREE); - assert(pagetable[page + num - 1] == B_FREE); + assert(pagetable[page] == Bins.B_FREE); + assert(pagetable[page + num - 1] == Bins.B_FREE); bPageOffsets[page] = cast(uint)num; if (num > 1) bPageOffsets[page + num - 1] = cast(uint)num; @@ -4168,7 +4167,7 @@ struct LargeObjectPool { static if (bwd) { - if (page > 0 && pagetable[page - 1] == B_FREE) + if (page > 0 && pagetable[page - 1] == Bins.B_FREE) { auto sz = bPageOffsets[page - 1]; page -= sz; @@ -4177,7 +4176,7 @@ struct LargeObjectPool } static if (fwd) { - if (page + num < npages && pagetable[page + num] == B_FREE) + if (page + num < npages && pagetable[page + num] == Bins.B_FREE) num += bPageOffsets[page + num]; } setFreePageOffsets(page, num); @@ -4197,8 +4196,8 @@ struct LargeObjectPool if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer return 0; size_t pagenum = pagenumOf(p); - Bins bin = cast(Bins)pagetable[pagenum]; - if (bin != B_PAGE) + Bins bin = pagetable[pagenum]; + if (bin != Bins.B_PAGE) return 0; return bPageOffsets[pagenum]; } @@ -4208,7 +4207,7 @@ struct LargeObjectPool */ size_t getSize(size_t pn) const nothrow @nogc { - assert(pagetable[pn] == B_PAGE); + assert(pagetable[pn] == Bins.B_PAGE); return cast(size_t) bPageOffsets[pn] * PAGESIZE; } @@ -4221,11 +4220,11 @@ struct LargeObjectPool size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; - if (bin == B_PAGEPLUS) + if (bin == Bins.B_PAGEPLUS) pn -= bPageOffsets[pn]; - else if (bin != B_PAGE) + else if (bin != Bins.B_PAGE) return info; // no info for free pages info.base = baseAddr + pn * PAGESIZE; @@ -4238,8 +4237,8 @@ struct LargeObjectPool { foreach (pn; 0 .. npages) { - Bins bin = cast(Bins)pagetable[pn]; - if (bin > B_PAGE) + Bins bin = pagetable[pn]; + if (bin > Bins.B_PAGE) continue; size_t biti = pn; @@ -4265,7 +4264,7 @@ struct LargeObjectPool size_t n = 1; for (; pn + n < npages; ++n) - if (pagetable[pn + n] != B_PAGEPLUS) + if (pagetable[pn + n] != Bins.B_PAGEPLUS) break; debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE); freePages(pn, n); @@ -4285,7 +4284,7 @@ struct SmallObjectPool { //base.Invariant(); uint cntRecover = 0; - foreach (Bins bin; 0 .. B_NUMSMALL) + foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL) { for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn]) { @@ -4296,7 +4295,7 @@ struct SmallObjectPool uint cntFree = 0; for (auto pn = searchStart; pn < npages; pn = binPageChain[pn]) { - assert(pagetable[pn] == B_FREE); + assert(pagetable[pn] == Bins.B_FREE); cntFree++; } assert(cntFree == freepages); @@ -4315,8 +4314,8 @@ struct SmallObjectPool do { size_t pagenum = pagenumOf(p); - Bins bin = cast(Bins)pagetable[pagenum]; - assert(bin < B_PAGE); + Bins bin = pagetable[pagenum]; + assert(bin < Bins.B_PAGE); if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer return 0; const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small; @@ -4330,9 +4329,9 @@ struct SmallObjectPool BlkInfo info; size_t offset = cast(size_t)(p - baseAddr); size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pagetable[pn]; + Bins bin = pagetable[pn]; - if (bin >= B_PAGE) + if (bin >= Bins.B_PAGE) return info; auto base = cast(void*)baseOffset(cast(size_t)p, bin); @@ -4352,8 +4351,8 @@ struct SmallObjectPool { foreach (pn; 0 .. npages) { - Bins bin = cast(Bins)pagetable[pn]; - if (bin >= B_PAGE) + Bins bin = pagetable[pn]; + if (bin >= Bins.B_PAGE) continue; immutable size = binsize[bin]; @@ -4404,13 +4403,13 @@ struct SmallObjectPool if (searchStart >= npages) return null; - assert(pagetable[searchStart] == B_FREE); + assert(pagetable[searchStart] == Bins.B_FREE); L1: size_t pn = searchStart; searchStart = binPageChain[searchStart]; binPageChain[pn] = Pool.PageRecovered; - pagetable[pn] = cast(ubyte)bin; + pagetable[pn] = bin; freepages--; // Convert page to free list @@ -4537,7 +4536,7 @@ debug(PRINTF) void printFreeInfo(Pool* pool) nothrow { uint nReallyFree; foreach (i; 0..pool.npages) { - if (pool.pagetable[i] >= B_FREE) nReallyFree++; + if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++; } printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages); @@ -4770,7 +4769,7 @@ debug (LOGGING) size_t offset = cast(size_t)(p - pool.baseAddr); size_t biti; size_t pn = offset / PAGESIZE; - Bins bin = cast(Bins)pool.pagetable[pn]; + Bins bin = pool.pagetable[pn]; biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy; debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti); } @@ -4921,7 +4920,7 @@ unittest assert(p + (260 << 20) == q); assert(q + (65 << 20) == r); GC.free(q); - // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q: + // should trigger "assert(bin == Bins.B_FREE);" in mark due to dangling pointer q: GC.collect(); // should trigger "break;" in extendNoSync: size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited) diff --git a/libphobos/libdruntime/core/internal/gc/pooltable.d b/libphobos/libdruntime/core/internal/gc/pooltable.d index 5924f9c..0966338 100644 --- a/libphobos/libdruntime/core/internal/gc/pooltable.d +++ b/libphobos/libdruntime/core/internal/gc/pooltable.d @@ -13,15 +13,14 @@ struct PoolTable(Pool) { import core.stdc.string : memmove; -nothrow: - void Dtor() + void Dtor() nothrow @nogc { cstdlib.free(pools); pools = null; npools = 0; } - bool insert(Pool* pool) + bool insert(Pool* pool) nothrow @nogc { auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof); if (!newpools) @@ -51,25 +50,31 @@ nothrow: return true; } - @property size_t length() pure const + @property size_t length() const scope @safe pure nothrow @nogc { return npools; } - ref inout(Pool*) opIndex(size_t idx) inout pure + ref inout(Pool*) opIndex(size_t idx) inout return @trusted pure nothrow @nogc in { assert(idx < length); } do { return pools[idx]; } - inout(Pool*)[] opSlice(size_t a, size_t b) inout pure + inout(Pool*)[] opSlice(size_t a, size_t b) inout return @trusted pure nothrow @nogc in { assert(a <= length && b <= length); } do { return pools[a .. b]; } + /// Returns: A slice over all pools in this `PoolTable` + inout(Pool*)[] opSlice() inout return @trusted pure nothrow @nogc + { + return this.pools[0 .. this.length]; + } + alias opDollar = length; /** @@ -77,7 +82,7 @@ nothrow: * Return null if not in a Pool. * Assume pooltable[] is sorted. */ - Pool *findPool(void *p) nothrow + Pool *findPool(void *p) nothrow @nogc { if (p >= minAddr && p < maxAddr) { @@ -109,7 +114,7 @@ nothrow: } // semi-stable partition, returns right half for which pred is false - Pool*[] minimize() pure + Pool*[] minimize() pure nothrow @nogc { static void swap(ref Pool* a, ref Pool* b) { @@ -151,7 +156,7 @@ nothrow: return pools[npools .. len]; } - void Invariant() const + void Invariant() const nothrow @nogc { if (!npools) return; @@ -165,8 +170,8 @@ nothrow: assert(_maxAddr == pools[npools - 1].topAddr); } - @property const(void)* minAddr() pure const { return _minAddr; } - @property const(void)* maxAddr() pure const { return _maxAddr; } + @property const(void)* minAddr() const @safe pure nothrow @nogc { return _minAddr; } + @property const(void)* maxAddr() const @safe pure nothrow @nogc { return _maxAddr; } package: Pool** pools; @@ -184,7 +189,7 @@ unittest { byte* baseAddr, topAddr; size_t freepages, npages, ptIndex; - @property bool isFree() const pure nothrow { return freepages == npages; } + @property bool isFree() const scope pure nothrow @nogc { return freepages == npages; } } PoolTable!MockPool pooltable; diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d index 2c89472..695ef06 100644 --- a/libphobos/libdruntime/core/internal/gc/proxy.d +++ b/libphobos/libdruntime/core/internal/gc/proxy.d @@ -209,12 +209,12 @@ extern (C) return instance.query( p ); } - core.memory.GC.Stats gc_stats() nothrow + core.memory.GC.Stats gc_stats() @safe nothrow @nogc { return instance.stats(); } - core.memory.GC.ProfileStats gc_profileStats() nothrow @safe + core.memory.GC.ProfileStats gc_profileStats() @safe nothrow @nogc { return instance.profileStats(); } diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d index 6ba569a..f25ba6f 100644 --- a/libphobos/libdruntime/core/memory.d +++ b/libphobos/libdruntime/core/memory.d @@ -133,7 +133,7 @@ private } extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow; - extern (C) GC.Stats gc_stats ( ) nothrow @nogc; + extern (C) GC.Stats gc_stats ( ) @safe nothrow @nogc; extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe; } @@ -766,7 +766,7 @@ extern(D): * Returns runtime stats for currently active GC implementation * See `core.memory.GC.Stats` for list of available metrics. */ - static Stats stats() nothrow + static Stats stats() @safe nothrow @nogc { return gc_stats(); } diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d index 1cdb0f4..dfec1ec 100644 --- a/libphobos/libdruntime/core/stdcpp/string.d +++ b/libphobos/libdruntime/core/stdcpp/string.d @@ -343,7 +343,7 @@ extern(D): /// inout(T)* data() inout @safe { return _Get_data()._Myptr; } /// - inout(T)[] as_array() inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } + inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } @@ -1920,7 +1920,7 @@ extern(D): /// inout(T)* data() inout @safe { return __get_pointer(); } /// - inout(T)[] as_array() inout nothrow @trusted { return __get_pointer()[0 .. size()]; } + inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; } /// ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } @@ -2497,8 +2497,8 @@ extern(C++, (StdNamespace)): extern(D) @safe @nogc: pragma(inline, true) { - ref inout(Alloc) _Getal() inout pure nothrow { return _Mypair._Myval1; } - ref inout(ValTy) _Get_data() inout pure nothrow { return _Mypair._Myval2; } + ref inout(Alloc) _Getal() return inout pure nothrow { return _Mypair._Myval1; } + ref inout(ValTy) _Get_data() return inout pure nothrow { return _Mypair._Myval2; } } void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); } diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d index 22f4df6..1fb4e44 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/stat.d +++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d @@ -388,50 +388,93 @@ version (linux) { struct stat_t { - c_ulong st_dev; - ino_t st_ino; + dev_t st_dev; + static if (!__USE_FILE_OFFSET64) + { + ushort __pad1; + ino_t st_ino; + } + else + ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; - c_ulong st_rdev; + dev_t st_rdev; + ushort __pad2; off_t st_size; - c_ulong st_blksize; - c_ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; + blksize_t st_blksize; + blkcnt_t st_blocks; + static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700) + { + timespec st_atim; + timespec st_mtim; + timespec st_ctim; + extern(D) @safe @property inout pure nothrow + { + ref inout(time_t) st_atime() return { return st_atim.tv_sec; } + ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } + ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } + } + } + else + { + time_t st_atime; + c_ulong st_atimensec; + time_t st_mtime; + c_ulong st_mtimensec; + time_t st_ctime; + c_ulong st_ctimensec; + } c_ulong __unused4; c_ulong __unused5; } + static if (__USE_FILE_OFFSET64) + static assert(stat_t.sizeof == 104); + else + static assert(stat_t.sizeof == 88); } else version (PPC64) { struct stat_t { - c_ulong st_dev; + dev_t st_dev; ino_t st_ino; nlink_t st_nlink; mode_t st_mode; uid_t st_uid; gid_t st_gid; - c_ulong st_rdev; + int __pad2; + dev_t st_rdev; off_t st_size; - c_ulong st_blksize; - c_ulong st_blocks; - c_ulong st_atime; - c_ulong st_atime_nsec; - c_ulong st_mtime; - c_ulong st_mtime_nsec; - c_ulong st_ctime; - c_ulong st_ctime_nsec; + blksize_t st_blksize; + blkcnt_t st_blocks; + static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700) + { + timespec st_atim; + timespec st_mtim; + timespec st_ctim; + extern(D) @safe @property inout pure nothrow + { + ref inout(time_t) st_atime() return { return st_atim.tv_sec; } + ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; } + ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; } + } + } + else + { + time_t st_atime; + c_ulong st_atimensec; + time_t st_mtime; + c_ulong st_mtimensec; + time_t st_ctime; + c_ulong st_ctimensec; + } c_ulong __unused4; c_ulong __unused5; c_ulong __unused6; } + static assert(stat_t.sizeof == 144); } else version (RISCV_Any) { diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d index 0ddf62f..91f218e 100644 --- a/libphobos/libdruntime/core/time.d +++ b/libphobos/libdruntime/core/time.d @@ -496,6 +496,81 @@ assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == +/ struct Duration { + /++ + Converts this `Duration` to a `string`. + + The string is meant to be human readable, not machine parseable (e.g. + whether there is an `'s'` on the end of the unit name usually depends on + whether it's plural or not, and empty units are not included unless the + Duration is `zero`). Any code needing a specific string format should + use `total` or `split` to get the units needed to create the desired + string format and create the string itself. + + The format returned by toString may or may not change in the future. + + Params: + sink = A sink object, expected to be a delegate or aggregate + implementing `opCall` that accepts a `scope const(char)[]` + as argument. + +/ + void toString (SinkT) (scope SinkT sink) const scope + { + static immutable units = [ + "weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs" + ]; + + static void appListSep(SinkT sink, uint pos, bool last) + { + if (pos == 0) + return; + if (!last) + sink(", "); + else + sink(pos == 1 ? " and " : ", and "); + } + + static void appUnitVal(string units)(SinkT sink, long val) + { + immutable plural = val != 1; + string unit; + static if (units == "seconds") + unit = plural ? "secs" : "sec"; + else static if (units == "msecs") + unit = "ms"; + else static if (units == "usecs") + unit = "μs"; + else + unit = plural ? units : units[0 .. $-1]; + sink(signedToTempString(val)); + sink(" "); + sink(unit); + } + + if (_hnsecs == 0) + { + sink("0 hnsecs"); + return; + } + + long hnsecs = _hnsecs; + uint pos; + static foreach (unit; units) + { + if (auto val = splitUnitsFromHNSecs!unit(hnsecs)) + { + appListSep(sink, pos++, hnsecs == 0); + appUnitVal!unit(sink, val); + } + if (hnsecs == 0) + return; + } + if (hnsecs != 0) + { + appListSep(sink, pos++, true); + appUnitVal!"hnsecs"(sink, hnsecs); + } + } + @safe pure: public: @@ -1539,71 +1614,12 @@ public: } } - - /++ - Converts this `Duration` to a `string`. - - The string is meant to be human readable, not machine parseable (e.g. - whether there is an `'s'` on the end of the unit name usually depends on - whether it's plural or not, and empty units are not included unless the - Duration is `zero`). Any code needing a specific string format should - use `total` or `split` to get the units needed to create the desired - string format and create the string itself. - - The format returned by toString may or may not change in the future. - +/ - string toString() const nothrow pure @safe + /// Ditto + string toString() const scope nothrow { - static void appListSep(ref string res, uint pos, bool last) - { - if (pos == 0) - return; - if (!last) - res ~= ", "; - else - res ~= pos == 1 ? " and " : ", and "; - } - - static void appUnitVal(string units)(ref string res, long val) - { - immutable plural = val != 1; - string unit; - static if (units == "seconds") - unit = plural ? "secs" : "sec"; - else static if (units == "msecs") - unit = "ms"; - else static if (units == "usecs") - unit = "μs"; - else - unit = plural ? units : units[0 .. $-1]; - res ~= signedToTempString(val); - res ~= " "; - res ~= unit; - } - - if (_hnsecs == 0) - return "0 hnsecs"; - - template TT(T...) { alias T TT; } - alias units = TT!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs"); - - long hnsecs = _hnsecs; string res; uint pos; - foreach (unit; units) - { - if (auto val = splitUnitsFromHNSecs!unit(hnsecs)) - { - appListSep(res, pos++, hnsecs == 0); - appUnitVal!unit(res, val); - } - if (hnsecs == 0) - break; - } - if (hnsecs != 0) - { - appListSep(res, pos++, true); - appUnitVal!"hnsecs"(res, hnsecs); - } - return res; + string result; + this.toString((in char[] data) { result ~= data; }); + return result; } /// @@ -1731,6 +1747,20 @@ unittest assert(myTime == 123.msecs); } +// Ensure `toString` doesn't allocate if the sink doesn't +version (CoreUnittest) @safe pure nothrow @nogc unittest +{ + char[256] buffer; size_t len; + scope sink = (in char[] data) { + assert(data.length + len <= buffer.length); + buffer[len .. len + data.length] = data[]; + len += data.length; + }; + auto dur = Duration(-12_096_020_900_003); + dur.toString(sink); + assert(buffer[0 .. len] == "-2 weeks, -2 secs, -90 ms, and -3 hnsecs"); +} + /++ Converts a $(D TickDuration) to the given units as either an integral value or a floating point value. diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index cf7cf96..56a2efe 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -2649,13 +2649,18 @@ class Throwable : Object /** * Get the message describing the error. - * Base behavior is to return the `Throwable.msg` field. - * Override to return some other error message. + * + * This getter is an alternative way to access the Exception's message, + * with the added advantage of being override-able in subclasses. + * Subclasses are hence free to do their own memory managements without + * being tied to the requirement of providing a `string` in a field. + * + * The default behavior is to return the `Throwable.msg` field. * * Returns: - * Error message + * A message representing the cause of the `Throwable` */ - @__future const(char)[] message() const + @__future const(char)[] message() const @safe nothrow { return this.msg; } |