1 /** 2 Copyright: Copyright Thomas Stuart Bockman 2015 3 License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 4 Authors: Thomas Stuart Bockman 5 */ 6 7 module checkedint.flags; 8 9 import core.bitop, std.algorithm, std.array, std.format, std.range/+.primitives+/; 10 11 @safe: 12 13 // flags 14 struct IntFlag { 15 pure: nothrow: @nogc: /+pragma(inline, true):+/ 16 private: 17 uint index; 18 this(uint index) { 19 assert(index < strs.length); 20 this.index = index; 21 } 22 23 private static immutable strs = [ 24 "{NULL}", 25 "{undefined result}", 26 "{divide by zero}", 27 "{imaginary component}", 28 "{overflow}", 29 "{positive overflow}", 30 "{negative overflow}" 31 ]; 32 public: 33 static if(__VERSION__ >= 2067) { 34 version(GNU) { static assert(false); } 35 enum { 36 undef = IntFlag(1), 37 div0 = IntFlag(2), 38 imag = IntFlag(3), 39 over = IntFlag(4), 40 posOver = IntFlag(5), 41 negOver = IntFlag(6) 42 } 43 } else { 44 static @property { 45 auto undef() { 46 return IntFlag(1); } 47 auto div0() { 48 return IntFlag(2); } 49 auto imag() { 50 return IntFlag(3); } 51 auto over() { 52 return IntFlag(4); } 53 auto posOver() { 54 return IntFlag(5); } 55 auto negOver() { 56 return IntFlag(6); } 57 } 58 } 59 60 @property { 61 bool isNull() const { 62 return index == 0; } 63 IntFlags mask() const { 64 return IntFlags(1u << index); } 65 alias mask this; 66 67 string desc() const { 68 return strs[index][1 .. ($ - 1)]; } 69 } 70 void toString(Writer, Char)(Writer sink, FormatSpec!Char fmt = (FormatSpec!Char).init) const @trusted { 71 formatValue(sink, strs[index], fmt); } 72 string toString() const { 73 return strs[index]; } 74 } 75 76 struct IntFlags { 77 pure nothrow @nogc /+pragma(inline, true)+/ { 78 private: 79 uint bits = 0; 80 this(uint bits) { 81 this.bits = bits; 82 } 83 84 public: 85 this(IntFlags that) { 86 bits = that.bits; } 87 ref IntFlags opAssign(IntFlags that) { 88 bits = that.bits; 89 return this; 90 } 91 void clear() { 92 bits = 0; } 93 94 IntFlags opBinary(string op)(IntFlags that) const 95 if(op.among!("&", "|", "-")) 96 { 97 IntFlags ret = this; 98 return ret.opOpAssign!op(that); 99 } 100 ref IntFlags opOpAssign(string op)(IntFlags that) 101 if(op.among!("&", "|", "-")) 102 { 103 static if(op == "&") 104 bits &= that.bits; 105 else 106 static if(op == "|") 107 bits |= that.bits; 108 else 109 static if(op == "-") 110 bits &= ~(that.bits); 111 112 return this; 113 } 114 115 @property bool anySet() const { 116 return bits > 1; } 117 alias anySet this; 118 119 @property bool empty() const { 120 return bits <= 1; } 121 @property IntFlag front() const { 122 // bsr() is undefined for 0. 123 return IntFlag(bsr(bits | 1)); 124 } 125 ref IntFlags popFront() { 126 // bsr() is undefined for 0. 127 bits &= ~(1u << bsr(bits | 1)); 128 return this; 129 } 130 @property IntFlags save() const { 131 return this; } 132 @property uint length() const { 133 return popcnt(bits); } 134 135 unittest { 136 import std.range : isForwardRange, hasLength; 137 static assert(isForwardRange!IntFlags); 138 static assert(hasLength!IntFlags); 139 } 140 141 static IntFlags local; 142 enum string pushPop = r" 143 IntFlags outerIntFlags = IntFlags.local; 144 scope(exit) { 145 assert(IntFlags.local.empty); 146 IntFlags.local = outerIntFlags; 147 }"; 148 } 149 public { 150 void toString(Writer, Char)(Writer sink, FormatSpec!Char fmt) const @trusted { 151 put(sink, '{'); 152 153 bool first = true; 154 foreach(fd; this.save()) { 155 if(first) 156 first = false; 157 else 158 put(sink, ", "); 159 put(sink, fd.desc); 160 } 161 162 put(sink, '}'); 163 } 164 void toString(Writer)(Writer sink) const { 165 toString(sink, (FormatSpec!char).init); } 166 string toString() const { 167 if(length == 1) 168 return front.toString(); 169 else { 170 auto buff = appender!string(); 171 toString(buff); 172 return cast(immutable)(buff.data); 173 } 174 } 175 } 176 } 177 178 class CheckedIntException : Exception { 179 const IntFlags intFlags; 180 181 private: 182 enum msg0 = "Integer math exception(s): "; 183 private this(IntFlag flag, string fn = __FILE__, size_t ln = __LINE__) pure nothrow { 184 intFlags = flag; 185 super(msg0 ~ flag.toString(), fn, ln); 186 } 187 private this(IntFlags flags, string fn = __FILE__, size_t ln = __LINE__) pure nothrow { 188 intFlags = flags; 189 190 auto buff = appender(msg0); 191 flags.toString(buff); 192 193 super(cast(immutable)(buff.data), fn, ln); 194 } 195 } 196 197 /+pragma(inline, false) {+/ 198 /* The throwing versions of raise() must not be inlined, as doing so tends to 199 prevent the caller from being inlined, at least on DMD. */ 200 201 void raise(bool throws)(IntFlag flag) 202 if(throws) 203 { 204 throw new CheckedIntException(flag); 205 } 206 void raise(bool throws)(IntFlags flags) 207 if(throws) 208 { 209 if(flags.anySet) 210 throw new CheckedIntException(flags); 211 } 212 /+} 213 pragma(inline, true) {+/ 214 void raise(bool throws)(IntFlag flag) 215 if(!throws) 216 { 217 IntFlags.local |= flag; 218 } 219 void raise(IntFlag flag) { 220 raise!true(flag); } 221 222 void raise(bool throws = true)(IntFlags flags) 223 if(!throws) 224 { 225 IntFlags.local |= flags; 226 } 227 void raise(IntFlags flags) { 228 raise!true(flags); } 229 /+}+/