1 #!/usr/bin/env rdmd-dev-module
2 
3 /** Various extensions to core.bitop and std.bitmanip.
4     Copyright: Per Nordlöw 2014-.
5     License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
6     Authors: $(WEB Per Nordlöw)
7 */
8 module bitop_ex;
9 
10 import std.traits: isIntegral;
11 import std.meta: allSatisfy;
12 
13 /** Get an Unsigned Type of size as $(D T) if possible. */
14 template UnsignedOfSameSizeAs(T)
15 {
16     enum nBits = 8*T.sizeof;
17     static      if (nBits ==  8) alias UnsignedOfSameSizeAs = ubyte;
18     else static if (nBits == 16) alias UnsignedOfSameSizeAs = ushort;
19     else static if (nBits == 32) alias UnsignedOfSameSizeAs = uint;
20     else static if (nBits == 64) alias UnsignedOfSameSizeAs = ulong;
21     else static if (nBits == 128) alias UnsignedOfSameSizeAs = ucent;
22     else {
23         import std.conv: to;
24         static assert(false, "No Unsigned type of size " ~ to!string(nBits) ~ " found");
25     }
26 }
27 
28 /** Returns: Zero Instance T with $(D bix):th Bit set. */
29 T makeBit(T, I...)(I bixs) @safe @nogc pure nothrow if (isIntegral!T &&
30                                                         allSatisfy!(isIntegral, I))
31     in { foreach (bix; bixs) { assert(0 <= bix && bix < 8*T.sizeof); } }
32 body {
33     typeof(return) x;
34     foreach (bix; bixs)
35         x |= cast(T)((cast(T)1) << bix);
36     return x;
37 }
38 alias btm = makeBit;
39 unittest {
40     assert(makeBit!int(2) == 4);
41     assert(makeBit!int(2, 3) == 12);
42 }
43 
44 /** Returns: Check if all $(D bix):th Bits Of $(D a) are set. */
45 bool getBit(T, I...)(in T a, I bixs) @safe @nogc pure nothrow if (isIntegral!T &&
46                                                                   allSatisfy!(isIntegral, I))
47 {
48     return a & makeBit!T(bixs) ? true : false;
49 }
50 /** Returns: Check if all $(D bix):th Bits Of $(D a) are set. */
51 bool getBit(T, I)(in T a, I bix) @trusted @nogc pure nothrow if ((!(isIntegral!T)) &&
52                                                                  allSatisfy!(isIntegral, I))
53 {
54     return (*(cast(UnsignedOfSameSizeAs!T*)&a)).getBit(bix); // reuse integer variant
55 }
56 alias bt = getBit;
57 void testGetBit(T)()
58 {
59     const mn = T.min, mx = T.max;
60     enum nBits = 8*T.sizeof;
61     foreach (ix; 0..nBits-1)
62     {
63         assert(!mn.bt(ix));
64     }
65     assert(mn.bt(nBits - 1));
66     foreach (ix; 0..T.sizeof)
67     {
68         assert(mx.bt(ix));
69     }
70 }
71 unittest {
72     testGetBit!byte;
73     testGetBit!short;
74     testGetBit!int;
75     testGetBit!long;
76 }
77 
78 /** Test and sets the $(D bix):th Bit Of $(D a) to one.
79     Returns: A non-zero value if the bit was set, and a zero if it was clear.
80 */
81 void setBit(T, I...)(ref T a, I bixs) @safe @nogc pure nothrow if (isIntegral!T &&
82                                                                    allSatisfy!(isIntegral, I))
83 {
84     a |= makeBit!T(bixs);
85 }
86 
87 /** Returns: Check if all $(D bix):th Bits Of $(D a) are set. */
88 void setBit(T, I...)(ref T a, I bixs) @trusted @nogc pure nothrow if ((!(isIntegral!T)) &&
89                                                                       allSatisfy!(isIntegral, I))
90 {
91     alias U = UnsignedOfSameSizeAs!T;
92     (*(cast(U*)&a)) |= makeBit!U(bixs); // reuse integer variant
93 }
94 
95 alias bts = setBit;
96 
97 /* alias btc = complementBit; */
98 /* alias btr = resetBit; */
99 
100 void setLowestBit(T)(ref T a) @safe @nogc pure nothrow if (isIntegral!T)
101 {
102     setBit(a, 0);
103 }
104 alias setBottomBit = setLowestBit;
105 
106 void setHighestBit(T)(ref T a) @safe @nogc pure nothrow if (isIntegral!T)
107 {
108     setBit(a, 8*T.sizeof - 1);
109 }
110 alias setTopBit = setHighestBit;
111 
112 bool getLowBit(T)(T a) @safe @nogc pure nothrow if (isIntegral!T)
113 {
114     return (a & (1 << 0)) != 0;
115 }
116 alias getBottomBit = getLowBit;
117 
118 bool getHighBit(T)(T a) @safe @nogc pure nothrow if (isIntegral!T)
119 {
120     return (a & (1 << 8*T.sizeof - 1)) != 0;
121 }
122 alias getTopBit = getHighBit;
123 
124 unittest
125 {
126     const ubyte x = 1;
127     assert(!x.getTopBit);
128     assert(x.getLowBit);
129 }
130 
131 unittest
132 {
133     const ubyte x = 128;
134     assert(x.getTopBit);
135     assert(!x.getLowBit);
136 }
137 
138 void resetBit(T, I...)(ref T a, I bixs) @safe @nogc pure nothrow if (isIntegral!T &&
139                                                                    allSatisfy!(isIntegral, I))
140 {
141     a &= ~makeBit!T(bixs);
142 }
143 
144 void resetBit(T, I...)(ref T a, I bixs) @trusted @nogc pure nothrow if ((!(isIntegral!T)) &&
145                                                                       allSatisfy!(isIntegral, I))
146 {
147     alias U = UnsignedOfSameSizeAs!T;
148     (*(cast(U*)&a)) &= ~makeBit!U(bixs); // reuse integer variant
149 }
150 
151 void resetLowestBit(T)(ref T a) @safe @nogc pure nothrow if (isIntegral!T)
152 {
153     resetBit(a, 0);
154 }
155 alias resetBottomBit = resetLowestBit;
156 
157 void resetHighestBit(T)(ref T a) @safe @nogc pure nothrow if (isIntegral!T)
158 {
159     resetBit(a, 8*T.sizeof - 1);
160 }
161 alias resetTopBit = resetHighestBit;
162 
163 alias btr = resetBit;
164 
165 unittest
166 {
167     alias T = int;
168     enum nBits = 8*T.sizeof;
169     T a = 0;
170 
171     a.bts(0); assert(a == 1);
172     a.bts(1); assert(a == 3);
173     a.bts(2); assert(a == 7);
174 
175     a.btr(0); assert(a == 6);
176     a.btr(1); assert(a == 4);
177     a.btr(2); assert(a == 0);
178 
179     a.bts(8*T.sizeof - 1); assert(a != 0);
180     a.btr(8*T.sizeof - 1); assert(a == 0);
181 
182     T b = 0;
183     b.bts(nBits - 1);
184     assert(b == T.min);
185 }
186 
187 unittest
188 {
189     static void test(T)()
190     {
191         enum nBits = 8*T.sizeof;
192         T x = 0;
193         x.bts(0);
194     }
195 
196     test!float;
197     test!double;
198 }