1 /// Utilities module 2 module gfx.core.util; 3 4 /// Down cast of a reference to a child class reference 5 /// runtime check is disabled in release build 6 /// If T is a base class of U, this does a blind cast to the same address. 7 /// If T is an interface implemented by U, it first offset address to the object base address 8 /// and does a blind cast to that address. 9 template unsafeCast(U) if (!is(U == const)) 10 { 11 static assert (!is(U == interface), "unsafeCast cannot be used to return interfaces"); 12 static assert (is(U == class), "unsafeCast can only be used with objects"); 13 14 @system 15 U unsafeCast(T)(T obj) 16 if ((is(T==class) || is(T==interface)) && is(U : T)) 17 { 18 if (!obj) return null; 19 debug { 20 auto uObj = cast(U)obj; 21 assert(uObj, "unsafeCast from "~T.stringof~" to "~U.stringof~" failed"); 22 return uObj; 23 } 24 else { 25 static if (is(T == interface)) { 26 return cast(U)(cast(void*)(cast(Object)obj)); 27 } 28 else { 29 return cast(U)(cast(void*)obj); 30 } 31 } 32 } 33 34 } 35 36 /// ditto 37 template unsafeCast(U) if (is(U == const)) 38 { 39 static assert (!is(U == interface), "unsafeCast cannot be used to return interfaces"); 40 static assert (is(U == class), "unsafeCast can only be used with objects"); 41 42 @system 43 U unsafeCast(T)(const(T) obj) 44 if ((is(T==class) || is(T==interface)) && is(U : const(T))) 45 { 46 if (!obj) return null; 47 debug { 48 auto uObj = cast(U)obj; 49 assert(uObj, "unsafeCast from "~T.stringof~" to "~U.stringof~" failed"); 50 return uObj; 51 } 52 else { 53 static if (is(T == interface)) { 54 return cast(U)(cast(const(void*))(cast(const(Object))obj)); 55 } 56 else { 57 return cast(U)(cast(const(void*))obj); 58 } 59 } 60 } 61 } 62 63 64 /// Return a bit by bit unchecked identical representation of the passed argument 65 template transmute(U) 66 { 67 import std.traits : isDynamicArray; 68 69 static if (!isDynamicArray!U) { 70 @system @nogc nothrow pure 71 U transmute(T)(in T value) 72 { 73 static assert(T.sizeof == U.sizeof, "can only transmute to identical type size"); 74 static assert(!is(T == class) && !is(T == interface), "cannot used transmute on objects"); 75 static assert(!is(U == class) && !is(U == interface), "cannot used transmute on objects"); 76 77 return *cast(U*)cast(void*)&value; 78 } 79 } 80 else { 81 @system @nogc nothrow pure 82 U transmute(T)(T[] value) 83 { 84 import std.traits : isMutable; 85 86 alias V = typeof(U.init[0]); 87 88 static assert(T.sizeof == V.sizeof, "can only transmute to identical type size"); 89 static assert(!is(T == class) && !is(T == interface), "cannot used transmute on objects"); 90 static assert(!is(V == class) && !is(V == interface), "cannot used transmute on objects"); 91 static if (!isMutable!V) { 92 static assert(!isMutable!T, "transmute is not meant to cast constness away"); 93 } 94 95 return (cast(V*)cast(void*)value.ptr)[0 .. value.length]; 96 } 97 } 98 } 99 100 101 /// Utility to represent and retrieve the current stack-trace 102 struct StackTrace 103 { 104 /// represent a single frame 105 struct Frame { 106 /// address of the frame 107 void* addr; 108 /// binary containing code 109 string binary; 110 /// function name and signature (with return type and arguments) 111 string symbol; 112 /// offset within the frame 113 size_t offset; 114 } 115 116 /// options when retrieving stack frames 117 enum Options { 118 /// no option 119 none = 0, 120 /// Strip module qualifiers for names in symbols, keeping only the last components. 121 /// Follows the common convention that module names are lowercase and type names are camelcase. 122 unqualNames = 1, 123 /// replace immutable(char)[] by string (and same for wchar and dchar) 124 renameString = 2, 125 126 /// sugar for both unqualNames and renameString 127 all = unqualNames | renameString, 128 } 129 130 /// frames of the stack trace 131 Frame[] frames; 132 133 // TODO: index, slice, toString... 134 135 static StackTrace obtain(in size_t maxFrames=0, in Options opts=Options.all) 136 { 137 version(linux) { 138 import core.sys.linux.execinfo : backtrace, backtrace_symbols; 139 import core.stdc.stdlib : free; 140 import std.array : replace; 141 import std.conv : parse; 142 import std.demangle : demangle; 143 import std.exception : enforce; 144 import std.regex : ctRegex, matchFirst, replaceAll; 145 import std..string : fromStringz; 146 147 int sz = cast(int)maxFrames; 148 const getAll = sz == 0; 149 if (!sz) sz = 64; 150 auto buf = new void*[sz]; 151 auto num = backtrace(&buf[0], sz); 152 while (getAll && num == sz) { 153 sz *= 2; 154 buf = new void*[sz]; 155 num = backtrace(&buf[0], sz); 156 } 157 auto syms = backtrace_symbols(&buf[0], num); 158 scope(exit) free(syms); 159 160 auto linuxBtReg = ctRegex!(`([\w\./]+)\(([^\s\)\+]+)(\+0x([0-9a-fA-F]+))?\)`); 161 auto unqualReg = ctRegex!(`([a-z]\w*\.)+(([A-Z]\w*\.)?\w+)`); 162 163 auto frames = new Frame[num]; 164 foreach (i; 0 .. num) { 165 auto sym = fromStringz(syms[i]).idup; 166 auto captures = enforce(matchFirst(sym, linuxBtReg), "could not retrieve stack-trace"); 167 168 frames[i].addr = buf[i]; 169 frames[i].binary = captures[1]; 170 string symbol = demangle(captures[2]); 171 if ((opts & Options.unqualNames) != Options.none) { 172 symbol = replaceAll(symbol, unqualReg, "$2"); 173 } 174 if ((opts & Options.renameString) != Options.none) { 175 symbol = symbol.replace("immutable(char)[]", "string"); 176 symbol = symbol.replace("immutable(wchar)[]", "wstring"); 177 symbol = symbol.replace("immutable(dchar)[]", "dstring"); 178 } 179 frames[i].symbol = symbol; 180 string off = captures[4]; 181 if (off.length) frames[i].offset = parse!size_t(off, 16); 182 183 } 184 return StackTrace(frames); 185 } 186 else { 187 assert(false, "not implemented"); 188 } 189 } 190 191 } 192