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