1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * Bit manipulation.
7  *
8  * Copyright: Eugene Wissner 2018-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/bitmanip.d,
13  *                 tanya/bitmanip.d)
14  */
15 module tanya.bitmanip;
16 
17 import tanya.meta.metafunction;
18 import tanya.meta.trait;
19 import tanya.meta.transform;
20 
21 /**
22  * Determines whether $(D_PARAM E) is a $(D_KEYWORD enum), whose members can be
23  * used as bit flags.
24  *
25  * This is the case if all members of $(D_PARAM E) are integral numbers that
26  * are either 0 or positive integral powers of 2.
27  *
28  * Params:
29  *  E = Some $(D_KEYWORD enum).
30  *
31  * Returns: $(D_KEYWORD true) if $(D_PARAM E) contains only bit flags,
32  *          $(D_KEYWORD false) otherwise.
33  */
34 template isBitFlagEnum(E)
35 {
36     enum bool isValid(OriginalType!E x) = x == 0
37                                        || (x > 0 && ((x & (x - 1)) == 0));
38     static if (isIntegral!E)
39     {
40         enum bool isBitFlagEnum = allSatisfy!(isValid, EnumMembers!E);
41     }
42     else
43     {
44         enum bool isBitFlagEnum = false;
45     }
46 }
47 
48 ///
49 @nogc nothrow pure @safe unittest
50 {
51     enum Valid
52     {
53         none = 0,
54         one = 1 << 0,
55         two = 1 << 1,
56     }
57     static assert(isBitFlagEnum!Valid);
58 
59     enum Invalid
60     {
61         one,
62         two,
63         three,
64         four,
65     }
66     static assert(!isBitFlagEnum!Invalid);
67 
68     enum Negative
69     {
70         one = -1,
71         two = -2,
72     }
73     static assert(!isBitFlagEnum!Negative);
74 }
75 
76 /**
77  * Validates that $(D_PARAM field) contains only bits from $(D_PARAM E).
78  *
79  * Params:
80  *  E     = Some $(D_KEYWORD enum).
81  *  field = Bit field.
82  *
83  * Returns: $(D_KEYWORD true) if $(D_PARAM field) is valid, $(D_KEYWORD false)
84  *          otherwise.
85  */
86 bool containsBitFlags(E)(E field)
87 if (isBitFlagEnum!E)
88 {
89     OriginalType!E fillField()
90     {
91         typeof(return) full;
92         static foreach (member; EnumMembers!E)
93         {
94             full |= member;
95         }
96         return full;
97     }
98     enum OriginalType!E full = fillField();
99     return (field & ~full) == OriginalType!E.init;
100 }
101 
102 ///
103 @nogc nothrow pure @safe unittest
104 {
105     enum E
106     {
107         one,
108         two,
109         three,
110     }
111     assert(containsBitFlags(E.one | E.two));
112     assert(!containsBitFlags(cast(E) 0x8));
113 }
114 
115 /**
116  * Allows to use $(D_KEYWORD enum) values as a set of bit flags.
117  *
118  * $(D_PSYMBOL BitFlags) behaves the same as a bit field of type $(D_PARAM E),
119  * but does additional cheks to ensure that the bit field contains only valid
120  * values, this is only values from $(D_PARAM E).
121  *
122  * Params:
123  *  E = Some $(D_KEYWORD enum).
124  */
125 struct BitFlags(E)
126 if (isBitFlagEnum!E)
127 {
128     private OriginalType!E field;
129 
130     /**
131      * Constructs $(D_PSYMBOL BitFlags) from $(D_PARAM field).
132      *
133      * Params:
134      *  field = Bits to be set.
135      */
136     this(E field)
137     {
138         this.field = field;
139     }
140 
141     /**
142      * Converts $(D_PSYMBOL BitFlags) to a boolean.
143      *
144      * It is $(D_KEYWORD true) if any bit is set, $(D_KEYWORD false) otherwise.
145      *
146      * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL BitFlags) contains any
147      *          set bits, $(D_KEYWORD false) otherwise.
148      */
149     bool opCast(T : bool)()
150     {
151         return this.field != 0;
152     }
153 
154     /**
155      * Converts to the original type of $(D_PARAM E) ($(D_KEYWORD int) by
156      * default).
157      *
158      * Returns: $(D_KEYWORD this) as $(D_INLINECODE OriginalType!T).
159      */
160     OriginalType!E opCast(T : OriginalType!E)() const
161     {
162         return this.field;
163     }
164 
165     /**
166      * Tests (&), sets (|) or toggles (^) bits.
167      *
168      * Params:
169      *  op   = Operation.
170      *  that = 0 or more bit flags.
171      *
172      * Returns: New $(D_PSYMBOL BitFlags) object.
173      */
174     BitFlags opBinary(string op)(E that) const
175     if (op == "&" || op == "|" || op == "^")
176     {
177         BitFlags result = this;
178         mixin("return result " ~ op ~ "= that;");
179     }
180 
181     /// ditto
182     BitFlags opBinary(string op)(BitFlags that) const
183     if (op == "&" || op == "|" || op == "^")
184     {
185         BitFlags result = this;
186         mixin("return result " ~ op ~ "= that;");
187     }
188 
189     /// ditto
190     BitFlags opBinaryRight(string op)(E that) const
191     if (op == "&" || op == "|" || op == "^")
192     {
193         BitFlags result = this;
194         mixin("return result " ~ op ~ "= that;");
195     }
196 
197     /**
198      * Tests (&), sets (|) or toggles (^) bits.
199      *
200      * Params:
201      *  op   = Operation.
202      *  that = 0 or more bit flags.
203      *
204      * Returns: $(D_KEYWORD this).
205      */
206     ref BitFlags opOpAssign(string op)(E that)
207     if (op == "&" || op == "|" || op == "^")
208     {
209         mixin("this.field " ~ op ~ "= that;");
210         return this;
211     }
212 
213     /// ditto
214     ref BitFlags opOpAssign(string op)(BitFlags that)
215     if (op == "&" || op == "|" || op == "^")
216     {
217         mixin("this.field " ~ op ~ "= that.field;");
218         return this;
219     }
220 
221     /**
222      * Inverts all bit flags.
223      *
224      * Returns: New $(D_PSYMBOL BitFlags) object with all bits inverted.
225      */
226     BitFlags opUnary(string op : "~")() const
227     {
228         BitFlags result;
229         result.field  = ~this.field;
230         return result;
231     }
232 
233     /**
234      * Assigns a bit field.
235      *
236      * Params:
237      *  that = Bit field of type $(D_PARAM E).
238      *
239      * Returns: $(D_KEYWORD this).
240      */
241     ref BitFlags opAssign(E that)
242     {
243         this.field = that;
244         return this;
245     }
246 
247     /**
248      * Compares this $(D_PSYMBOL BitFlags) object to another bit field.
249      *
250      * Params:
251      *  that = $(D_PSYMBOL BitFlags) object or a bit field of type
252      *         $(D_PARAM E).
253      *
254      * Returns: $(D_KEYWORD true) if $(D_KEYWORD this) and $(D_PARAM that)
255      *          contain the same bits ,$(D_KEYWORD false) otherwise.
256      */
257     bool opEquals(E that) const
258     {
259         return this.field == that;
260     }
261 
262     /// ditto
263     bool opEquals(BitFlags that) const
264     {
265         return this.field == that.field;
266     }
267 
268     /**
269      * Generates a hash value of this object.
270      *
271      * Returns: Hash value.
272      */
273     size_t toHash() const
274     {
275         return cast(size_t) this.field;
276     }
277 }
278 
279 /**
280  * Creates a $(D_PSYMBOL BitFlags) object initialized with $(D_PARAM field).
281  *
282  * Params:
283  *  E     = Some $(D_KEYWORD enum).
284  *  field = Bits to be set.
285  */
286 BitFlags!E bitFlags(E)(E field)
287 if (isBitFlagEnum!E)
288 {
289     return BitFlags!E(field);
290 }
291 
292 ///
293 @nogc nothrow pure @safe unittest
294 {
295     enum E
296     {
297         one = 1 << 0,
298         two = 1 << 1,
299         three = 1 << 2,
300     }
301     // Construct with E.one and E.two set
302     auto flags = bitFlags(E.one | E.two);
303 
304     // Test wheter E.one is set
305     assert(flags & E.one);
306 
307     // Toggle E.one
308     flags ^= E.one;
309     assert(!(flags & E.one));
310 
311     // Set E.three
312     flags |= E.three;
313     assert(flags & E.three);
314 
315     // Clear E.three
316     flags &= ~E.three;
317     assert(!(flags & E.three));
318 }