diff --git a/src/rt/profilegc.d b/src/rt/profilegc.d index 6bee9bc..31fab33 100644 --- a/src/rt/profilegc.d +++ b/src/rt/profilegc.d @@ -20,11 +20,12 @@ import core.stdc.stdlib; import core.stdc.string; import core.exception : onOutOfMemoryError; +import rt.util.hashtab; struct Entry { size_t count, size; } char[] buffer; -Entry[char[]] newCounts; +HashTab!(char[], Entry) newCounts; char[] logfilename = "profilegc.log"; /**** @@ -36,24 +37,27 @@ char[] logfilename = "profilegc.log"; extern (C) void profilegc_setlogfilename(char[] name) { - logfilename = name; + logfilename = name ~ "\0"; } - - -public void accumulate(char[] file, uint line, char[] funcname, char[] type, size_t sz) +public void accumulate(char[] file, uint line, char[] funcname, char[] type, + size_t sz) { + if (sz == 0) + return; + char[3 * line.sizeof + 1] buf; auto buflen = snprintf(buf.ptr, buf.length, "%u".ptr, line); - auto lngth = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen; - if (lngth > buffer.length) + auto ln = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen; + if (ln > buffer.length) { // Enlarge buffer[] so it is big enough - auto p = cast(char*)realloc(buffer.ptr, lngth); + assert(buffer.length > 0 || buffer.ptr is null); + auto p = cast(char*)realloc(buffer.ptr, ln + 1); if (!p) onOutOfMemoryError(); - buffer = p[0 .. lngth]; + buffer = p[0 .. ln + 1]; } // "type funcname file:line" @@ -67,14 +71,19 @@ public void accumulate(char[] file, uint line, char[] funcname, char[] type, siz 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]; + buffer[ln] = 0; - if (auto pcount = cast(char[])buffer[0 .. lngth] in newCounts) + if (auto pcount = buffer[0 .. ln] in newCounts) { // existing entry pcount.count++; pcount.size += sz; } else - newCounts[buffer[0..lngth].dup] = Entry(1, sz); // new entry + { + auto key = (cast(char*) malloc(char.sizeof * ln))[0 .. ln]; + key[] = buffer[0..ln]; + newCounts[key] = Entry(1, sz); // new entry + } } // Write report to stderr @@ -86,18 +95,27 @@ static ~this() Entry entry; // qsort() comparator to sort by count field - extern (C) static int qsort_cmp(in void *r1, in void *r2) + extern (C) static int qsort_cmp(void *r1, void *r2) { auto result1 = cast(Result*)r1; auto result2 = cast(Result*)r2; + ptrdiff_t cmp = result2.entry.size - result1.entry.size; if (cmp) return cmp < 0 ? -1 : 1; cmp = result2.entry.count - result1.entry.count; - return cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); + 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; } } - Result[] counts = new Result[newCounts.length]; + size_t size = newCounts.length; + Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size]; + scope(exit) + free(counts.ptr); size_t i; foreach (name, entry; newCounts) @@ -110,13 +128,10 @@ static ~this() if (counts.length) { qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp); - - FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename ~ - '\0').ptr, "w".ptr); + FILE* fp = logfilename.length == 0 ? stdout : fopen(logfilename.ptr, "w".ptr); if (fp) { - fprintf(fp, - "bytes allocated, allocations, type, function, file:line\n".ptr); + fprintf(fp, "bytes allocated, allocations, type, function, file:line\n".ptr); foreach (ref c; counts) { fprintf(fp, "%15llu\t%15llu\t%8.*s\n".ptr, @@ -127,7 +142,6 @@ static ~this() fclose(fp); } else - fprintf(stderr, "cannot write profilegc log file '%.*s'".ptr, - logfilename.length, logfilename.ptr); + fprintf(stderr, "cannot write profilegc log file '%.*s'".ptr, logfilename.length, logfilename.ptr); } } diff --git a/src/rt/util/array.d b/src/rt/util/array.d new file mode 100644 index 0000000..36ea3e4 --- /dev/null +++ b/src/rt/util/array.d @@ -0,0 +1,160 @@ +/** + * Array container for internal usage. + * + * Copyright: Copyright Martin Nowak 2013. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak + */ +module rt.util.array; + +static import common = rt.util.common; +import core.exception; + +struct Array(T) +{ + void reset() + { + length = 0; + } + + size_t length() + { + return _length; + } + + void length(size_t nlength) + { + size_t reqsize = T.sizeof * nlength; + 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; + } + + bool empty() + { + return !length; + } + + T* front() + in { assert(!empty); } + body + { + return &_ptr[0]; + } + + T* back() + in { assert(!empty); } + body + { + return &_ptr[_length - 1]; + } + + T opIndex(size_t idx) + in { assert(idx < length); } + body + { + return _ptr[idx]; + } + + void opIndexAssign(T value, size_t idx) + in { assert(idx < length); } + body + { + _ptr[idx] = value; + } + + T[] opSlice() + { + return _ptr[0 .. _length]; + } + + T[] opSlice(size_t a, size_t b) + in { assert(a < b && b <= length); } + body + { + return _ptr[a .. b]; + } + + void insertBack()(T val) + { + length = length + 1; + *back() = val; + } + + void popBack() + { + length = length - 1; + } + + void remove(size_t idx) + in { assert(idx < length); } + body + { + for (auto i = idx; i < length - 1; ++i) + _ptr[i] = _ptr[i+1]; + popBack(); + } + + void swap(ref Array other) + { + auto ptr = _ptr; + _ptr = other._ptr; + other._ptr = ptr; + auto len = _length; + _length = other._length; + other._length = len; + } + + invariant + { + assert(!_ptr == !_length); + } + +private: + T* _ptr; + size_t _length; +} + +unittest +{ + Array!(int) ary; + + assert(ary[] == null); + ary.insertBack(5); + assert(ary[] == [5]); + ary.popBack(); + assert(ary[] == null); + ary.insertBack(0); + ary.insertBack(1); + assert(ary[] == [0, 1]); + assert(ary[0 .. 1] == [0]); + assert(ary[1 .. 2] == [1]); + assert(ary[ary.length - 2 .. ary.length] == [0, 1]); + size_t idx; + foreach (val; ary[]) assert(idx++ == val); + foreach (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); + common.destroy(ary); + assert(ary.empty); + + Array!(int) ary2; + + ary2.insertBack(0); + assert(ary.empty); + assert(ary2[] == [0]); + ary.swap(ary2); + assert(ary[] == [0]); + assert(ary2.empty); +} diff --git a/src/rt/util/common.d b/src/rt/util/common.d new file mode 100644 index 0000000..e3a1c26 --- /dev/null +++ b/src/rt/util/common.d @@ -0,0 +1,54 @@ +/** + * Common code for writing containers. + * + * Copyright: Copyright Martin Nowak 2013. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak + */ +module rt.util.common; + +import core.exception; +import core.stdc.string; +import core.stdc.stdlib : malloc, realloc; +public import core.stdc.stdlib : free; + +void* xrealloc(void* ptr, size_t sz) +{ + if (!sz) { .free(ptr); return null; } + if (auto nptr = .realloc(ptr, sz)) return nptr; + .free(ptr); onOutOfMemoryError(); + assert(0); +} + +void* xmalloc(size_t sz) +{ + if (auto nptr = .malloc(sz)) + return nptr; + onOutOfMemoryError(); + assert(0); +} + +void destroy(T)(ref T t) +{ + static if (is(T == struct)) + { + t.reset(); + } + + t = T.init; +} + +void initialize(T)(ref T t) +{ + static if (is(T == struct)) + { + if (auto p = typeid(T).initializer().ptr) + memcpy(&t, p, T.sizeof); + else + memset(&t, 0, T.sizeof); + } + else + { + t = T.init; + } +} diff --git a/src/rt/util/hashtab.d b/src/rt/util/hashtab.d new file mode 100644 index 0000000..9919e06 --- /dev/null +++ b/src/rt/util/hashtab.d @@ -0,0 +1,245 @@ +/** + * HashTab container for internal usage. + * + * Copyright: Copyright Martin Nowak 2013. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Martin Nowak + */ +module rt.util.hashtab; + +import rt.util.array; +static import common = rt.util.common; + +struct HashTab(Key, Value) +{ + static struct Node + { + Key _key; + Value _value; + Node* _next; + + void 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; + } + + size_t length() + { + return _length; + } + + bool empty() + { + return !_length; + } + + void remove(in Key key) + in { assert(key in *this); } + body + { + ensureNotInOpApply(); + + auto hash = hashOf(key) & mask; + auto head = _buckets[hash]; + auto pp = &head; + 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); + } + + Value opIndex(Key key) + { + return *opIn_r(key); + } + + void opIndexAssign(Value value, Key key) + { + *get(key) = value; + } + + Value* opIn_r(Key key) + { + if (_buckets.length) + { + auto hash = hashOf(key) & mask; + for (auto p = _buckets[hash]; p !is null; p = p._next) + { + if (p._key == key) + return &p._value; + } + } + return null; + } + + int opApply(int delegate(ref Key, ref Value) dg) + { + auto 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; + + auto 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(ref Key key) + { + return typeid(Key).getHash(&key); + } + + hash_t mask() + { + return _buckets.length - 1; + } + + void grow() + in + { + assert(_buckets.length); + } + body + { + auto ocnt = _buckets.length; + auto nmask = 2 * ocnt - 1; + _buckets.length = 2 * ocnt; + for (size_t i = 0; i < ocnt; ++i) + { + auto head = _buckets[i]; + auto pp = &head; + while (*pp) + { + auto p = *pp; + + auto 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 + { + auto ocnt = _buckets.length; + auto ncnt = ocnt >> 1; + auto nmask = ncnt - 1; + + for (size_t i = ncnt; i < ocnt; ++i) + { + if (auto tail = _buckets[i]) + { + auto nidx = i & nmask; + auto head = _buckets[nidx]; + auto pp = &head; + 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!(char[], size_t) tab; + + tab["foo"] = 0; + assert(tab["foo"] == 0); + tab["foo"] = tab["foo"] + 1; + assert(tab["foo"] == 1); + + char[] s = "fo"; + s ~= "o"; + assert(tab[s] == 1); + assert(tab.length == 1); + auto x = tab[s]; + tab[s] = x - 1; + assert(tab[s] == 0); + tab["foo"] = 12; + assert(tab[s] == 12); + + tab.remove("foo"); + assert(tab.empty); +}