1 /++
2 `@nogc` exceptions and errors definitions.
3 +/
4 module mir.exception;
5 
6 /++
7 +/
8 class MirException : Exception
9 {
10     ///
11     mixin MirThrowableImpl;
12 }
13 
14 /// Generic style
15 @safe pure nothrow @nogc
16 unittest
17 {
18     import mir.exception;
19     try throw new MirException("Hi D", 2, "!");
20     catch(Exception e) assert(e.msg == "Hi D2!");
21 }
22 
23 /// C++ style
24 @safe pure nothrow @nogc
25 unittest
26 {
27     import mir.exception;
28     import mir.format;
29     try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData);
30     catch(Exception e) assert(e.msg == "Hi D2!");
31 }
32 
33 /// Low-level style
34 @safe pure nothrow @nogc
35 unittest
36 {
37     import mir.exception;
38     import mir.format;
39     stringBuf buf;
40     try throw new MirException(buf.print( "Hi D", 2, "!").data);
41     catch(Exception e) assert(e.msg == "Hi D2!");
42 }
43 
44 ///
45 @safe pure nothrow @nogc
46 unittest
47 {
48     @safe pure nothrow @nogc 
49     bool func(scope const(char)[] msg)
50     {
51         /// scope messages are copied
52         try throw new MirException(msg);
53         catch(Exception e) assert(e.msg == msg);
54 
55         /// immutable strings are not copied
56         static immutable char[] gmsg = "global msg";
57         try throw new MirException(gmsg);
58         catch(Exception e) assert(e.msg is gmsg);
59 
60         return __ctfe;
61     }
62 
63     assert(func("runtime-time check") == 0);
64 
65     static assert(func("compile-time check") == 1);
66 }
67 
68 // ///
69 // auto ref enforce(T, Args...)(scope auto return ref T arg, lazy @nogc Args args, string file = __FILE__, int line = __LINE__) @nogc
70 //     if (Args.length)
71 // {
72 //     import mir.utility: _expect;
73 //     static if (__traits(compiles, arg !is null))
74 //     {
75 //         if (_expect(arg !is null, true))
76 //             return arg;
77 //     }
78 //     else
79 //     {
80 //         if (_expect(cast(bool)arg, true))
81 //             return arg;
82 //     }
83 //     import mir.format;
84 //     stringBuf buf;
85 //     throw new MirException(buf.print(args).data, file, line);
86 // }
87 
88 // ///
89 // @safe pure nothrow @nogc
90 // unittest
91 // {
92 //     import mir.exception;
93 //     try enforce(false, "Hi D", 2, "!");
94 //     catch(Exception e) assert(e.msg == "Hi D2!");
95 // }
96 
97 // ///
98 // auto ref enforce(T)(scope auto return ref T arg, lazy scope const(char)[] msg, string file = __FILE__, int line = __LINE__) @nogc
99 // {
100 //     import mir.functional: forward;
101 //     import mir.utility: _expect;
102 //     static if (__traits(compiles, arg !is null))
103 //     {
104 //         if (_expect(arg !is null, true))
105 //             return forward!arg[0];
106 //     }
107 //     else
108 //     {
109 //         if (_expect(cast(bool)arg, true))
110 //             return forward!arg[0];
111 //     }
112 //     throw new MirException(msg, file, line);
113 // }
114 
115 // ///
116 // @safe pure nothrow @nogc
117 // unittest
118 // {
119 //     import mir.exception;
120 //     try enforce(false, "Msg");
121 //     catch(Exception e) assert(e.msg == "Msg");
122 // }
123 
124 ///
125 auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted
126 {
127     import mir.functional: forward;
128     import mir.utility: _expect;
129     static if (__traits(compiles, arg !is null))
130     {
131         if (_expect(arg !is null, true))
132             return forward!arg[0];
133     }
134     else
135     {
136         if (_expect(cast(bool)arg, true))
137             return forward!arg[0];
138     }
139     static immutable exception = new Exception(fmt, file, line);
140     throw exception;
141 }
142 
143 ///
144 @safe pure nothrow @nogc
145 unittest
146 {
147     import mir.exception;
148     try enforce!"Msg"(false);
149     catch(Exception e) assert(e.msg == "Msg");
150 }
151 
152 /++
153 +/
154 class MirError : Error
155 {
156     ///
157     mixin MirThrowableImpl;
158 }
159 
160 ///
161 @system pure nothrow @nogc
162 unittest
163 {
164     @system pure nothrow @nogc 
165     bool func(scope const(char)[] msg)
166     {
167         /// scope messages are copied
168         try throw new MirException(msg);
169         catch(Exception e) assert(e.msg == msg);
170     
171         /// immutable strings are not copied
172         static immutable char[] gmsg = "global msg";
173         try throw new MirError(gmsg);
174         catch(Error e) assert(e.msg is gmsg);
175     
176         return __ctfe;
177     }
178 
179     assert(func("runtime-time check") == 0);
180 
181     static assert(func("compile-time check") == 1);
182 }
183 
184 /++
185 +/
186 mixin template MirThrowableImpl()
187 {
188     private bool _global;
189     private char[maxMsgLen] _payload = void;
190 
191     /++
192     Params:
193         msg = message. No-scope `msg` is assumed to have the same lifetime as the throwable. scope strings are copied to internal buffer.
194         file = file name, zero terminated global string
195         line = line number
196         nextInChain = next exception in the chain (optional)
197     +/
198     @nogc @safe pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
199     {
200         super((() @trusted => cast(immutable) initilizePayload(_payload, msg))(), file, line, nextInChain);
201     }
202 
203     /// ditto
204     @nogc @safe pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
205     {
206         this(msg, file, line, nextInChain);
207     }
208 
209     /// ditto
210     @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
211     {
212         this._global = true;
213         super(msg, file, line, nextInChain);
214     }
215 
216     /// ditto
217     @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
218     {
219         this._global = true;
220         super(msg, file, line, nextInChain);
221     }
222 
223     ///
224     ~this() @trusted
225     {
226         import mir.internal.memory: free;
227         if (!_global && msg.ptr != _payload.ptr)
228             free(cast(void*)msg.ptr);
229     }
230 
231     /++
232     Generic multiargument overload.
233     Constructs a string using the `print` function.
234     +/
235     @nogc @safe pure nothrow this(Args...)(scope auto ref Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
236         if (Args.length > 1)
237     {
238         import mir.format;
239         stringBuf buf;
240         this(buf.print(args).data, file, line, nextInChain);
241     }
242 }
243 
244 private enum maxMsgLen = 447;
245 
246 pragma(inline, false)
247 pure nothrow @nogc @safe
248 private const(char)[] initilizePayload(ref return char[maxMsgLen] payload, scope const(char)[] msg)
249 {
250     import mir.internal.memory: malloc;
251     import core.stdc.string: memcpy;
252     if (msg.length > payload.length)
253     {
254         if (auto ret = (() @trusted
255             {
256                 if (__ctfe)
257                     return null;
258                 if (auto ptr = malloc(msg.length))
259                 {
260                     memcpy(ptr, msg.ptr, msg.length);
261                     return cast(const(char)[]) ptr[0 .. msg.length];
262                 }
263                 return null;
264             })())
265             return ret;
266         msg = msg[0 .. payload.length];
267         // remove tail UTF-8 symbol chunk if any
268         uint c = msg[$-1];
269         if (c > 0b_0111_1111)
270         {
271             do {
272                 c = msg[$-1];
273                 msg = msg[0 .. $ - 1];
274             }
275             while (msg.length && c < 0b_1100_0000);
276         }
277     }
278     if (__ctfe)
279         payload[][0 .. msg.length] = msg;
280     else
281         (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))();
282     return payload[0 .. msg.length];
283 }