The OpenD Programming Language

1 /++
2 This module contains a collection of bit-level operations.
3 
4 Authors: Ilia Ki, Phobos & LDC Authors (original Phobos unittests, docs, conventions).
5 +/
6 module mir.bitop;
7 
8 version(LDC)
9     import ldc.intrinsics;
10 version(GNU)
11     import gcc.builtins;
12 
13 import mir.math.common: fastmath;
14 
15 /// Right shift vallue for bit index to get element's index (5 for `uint`).
16 enum uint bitElemShift(T : ubyte) = 3;
17 /// ditto
18 enum uint bitElemShift(T : byte) = 3;
19 /// ditto
20 enum uint bitElemShift(T : ushort) = 4;
21 /// ditto
22 enum uint bitElemShift(T : short) = 4;
23 /// ditto
24 enum uint bitElemShift(T : uint) = 5;
25 /// ditto
26 enum uint bitElemShift(T : int) = 5;
27 /// ditto
28 enum uint bitElemShift(T : ulong) = 6;
29 /// ditto
30 enum uint bitElemShift(T : long) = 6;
31 static if (is(ucent))
32 /// ditto
33 enum uint bitElemShift(T : ucent) = 7;
34 /// ditto
35 static if (is(cent))
36 enum uint bitElemShift(T : cent) = 7;
37 
38 /// Bit mask for bit index to get element's bit shift (31 for uint).
39 enum uint bitShiftMask(T : ubyte) = 7;
40 /// ditto
41 enum uint bitShiftMask(T : byte) = 7;
42 /// ditto
43 enum uint bitShiftMask(T : ushort) = 15;
44 /// ditto
45 enum uint bitShiftMask(T : short) = 15;
46 /// ditto
47 enum uint bitShiftMask(T : uint) = 31;
48 /// ditto
49 enum uint bitShiftMask(T : int) = 31;
50 /// ditto
51 enum uint bitShiftMask(T : ulong) = 63;
52 /// ditto
53 enum uint bitShiftMask(T : long) = 63;
54 static if (is(ucent))
55 /// ditto
56 enum uint bitShiftMask(T : ucent) = 127;
57 static if (is(cent))
58 /// ditto
59 enum uint bitShiftMask(T : cent) = 127;
60 
61 // no effect on this function, but better for optimization of other @fastmath code that uses this
62 @fastmath:
63 
64 
65 /++
66 +/
67 T nTrailingBitsToCount(T)(in T value, in T popcnt)
68     if (__traits(isUnsigned, T))
69 {
70     import std.traits;
71     import mir.internal.utility: Iota;
72     alias S = Signed!(CommonType!(int, T));
73     S mask = S(-1) << T.sizeof * 4;
74     foreach_reverse (s; Iota!(bitElemShift!T - 1))
75     {{
76         enum shift = 1 << s;
77         if (S(popcnt) > S(ctpop(cast(T)(value & ~mask))))
78             mask <<= shift;
79         else
80             mask >>= shift;
81     }}
82     return cttz(cast(T)mask) + (S(popcnt) != ctpop(cast(T)(value & ~mask)));
83 }
84 
85 ///
86 version(mir_core_test) unittest
87 {
88     assert(nTrailingBitsToCount(0xF0u, 3u) == 7);
89     assert(nTrailingBitsToCount(0xE00u, 3u) == 12);
90 
91     foreach(uint i; 1 .. 32)
92         assert(nTrailingBitsToCount(uint.max, i) == i);
93 }
94 
95 /++
96 +/
97 T nLeadingBitsToCount(T)(in T value, in T popcnt)
98     if (__traits(isUnsigned, T))
99 {
100     import std.traits;
101     import mir.internal.utility: Iota;
102     alias S = Signed!(CommonType!(int, T));
103     S mask = S(-1) << T.sizeof * 4;
104     foreach_reverse (s; Iota!(bitElemShift!T - 1))
105     {{
106         enum shift = 1 << s;
107         if (S(popcnt) > S(ctpop(cast(T)(value & mask))))
108             mask >>= shift;
109         else
110             mask <<= shift;
111     }}
112     return ctlz(cast(T)~mask) + (S(popcnt) != ctpop(cast(T)(value & mask)));
113 }
114 
115 ///
116 version(mir_core_test) unittest
117 {
118     assert(nLeadingBitsToCount(0xF0u, 3u) == 32 - 5);
119     assert(nLeadingBitsToCount(0x700u, 3u) == 32 - 8);
120 
121     foreach(uint i; 1 .. 32)
122         assert(nLeadingBitsToCount(uint.max, i) == i);
123 }
124 
125 /++
126 Tests the bit.
127 Returns:
128      A non-zero value if the bit was set, and a zero
129      if it was clear.
130 +/
131 auto bt(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
132     if (__traits(isUnsigned, T))
133 {
134     auto index = bitnum >> bitElemShift!T;
135     auto mask = T(1) << (bitnum & bitShiftMask!T);
136     return p[index] & mask;
137 }
138 
139 ///
140 @system pure version(mir_core_test) unittest
141 {
142     size_t[2] array;
143 
144     array[0] = 2;
145     array[1] = 0x100;
146 
147     assert(bt(array.ptr, 1));
148     assert(array[0] == 2);
149     assert(array[1] == 0x100);
150 }
151 
152 /++
153 Tests and assign the bit.
154 Returns:
155      A non-zero value if the bit was set, and a zero if it was clear.
156 +/
157 auto bta(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum, bool value)
158     if (__traits(isUnsigned, T))
159 {
160     auto index = bitnum >> bitElemShift!T;
161     auto shift = bitnum & bitShiftMask!T;
162     auto mask = T(1) << shift;
163     static if (__traits(compiles, &p[size_t.init]))
164     {
165         auto qp = &p[index];
166         auto q = *qp;
167         auto ret = q & mask;
168         *qp = cast(T)((q & ~mask) ^ (T(value) << shift));
169     }
170     else
171     {
172         auto q = p[index];
173         auto ret = q & mask;
174         p[index] = cast(T)((q & ~mask) ^ (T(value) << shift));
175     }
176     return ret;    
177 }
178 
179 /++
180 Tests and complements the bit.
181 Returns:
182      A non-zero value if the bit was set, and a zero if it was clear.
183 +/
184 auto btc(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
185     if (__traits(isUnsigned, T))
186 {
187     auto index = bitnum >> bitElemShift!T;
188     auto mask = T(1) << (bitnum & bitShiftMask!T);
189     static if (__traits(compiles, &p[size_t.init]))
190     {
191         auto qp = &p[index];
192         auto q = *qp;
193         auto ret = q & mask;
194         *qp = cast(T)(q ^ mask);
195     }
196     else
197     {
198         auto q = p[index];
199         auto ret = q & mask;
200         p[index] = cast(T)(q ^ mask);
201     }
202     return ret;
203 }
204 
205 /++
206 Tests and resets (sets to 0) the bit.
207 Returns:
208      A non-zero value if the bit was set, and a zero if it was clear.
209 +/
210 auto btr(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
211     if (__traits(isUnsigned, T))
212 {
213     auto index = bitnum >> bitElemShift!T;
214     auto mask = T(1) << (bitnum & bitShiftMask!T);
215     static if (__traits(compiles, &p[size_t.init]))
216     {
217         auto qp = &p[index];
218         auto q = *qp;
219         auto ret = q & mask;
220         *qp = cast(T)(q & ~mask);
221     }
222     else
223     {
224         auto q = p[index];
225         auto ret = q & mask;
226         p[index] = cast(T)(q & ~mask);
227     }
228     return ret;
229 }
230 
231 /++
232 Tests and sets the bit.
233 Params:
234 p = a non-NULL field / pointer to an array of unsigned integers.
235 bitnum = a bit number, starting with bit 0 of p[0],
236 and progressing. It addresses bits like the expression:
237 ---
238 p[index / (T.sizeof*8)] & (1 << (index & ((T.sizeof*8) - 1)))
239 ---
240 Returns:
241      A non-zero value if the bit was set, and a zero if it was clear.
242 +/
243 auto bts(Field, T = typeof(Field.init[size_t.init]))(auto ref Field p, size_t bitnum)
244     if (__traits(isUnsigned, T))
245 {
246     auto index = bitnum >> bitElemShift!T;
247     auto mask = T(1) << (bitnum & bitShiftMask!T);
248     static if (__traits(compiles, &p[size_t.init]))
249     {
250         auto qp = &p[index];
251         auto q = *qp;
252         auto ret = q & mask;
253         *qp = cast(T)(q | mask);
254     }
255     else
256     {
257         auto q = p[index];
258         auto ret = q & mask;
259         p[index] = cast(T)(q | mask);
260     }
261     return ret;
262 }
263 
264 ///
265 @system pure version(mir_core_test) unittest
266 {
267     size_t[2] array;
268 
269     array[0] = 2;
270     array[1] = 0x100;
271 
272     assert(btc(array.ptr, 35) == 0);
273     if (size_t.sizeof == 8)
274     {
275         assert(array[0] == 0x8_0000_0002);
276         assert(array[1] == 0x100);
277     }
278     else
279     {
280         assert(array[0] == 2);
281         assert(array[1] == 0x108);
282     }
283 
284     assert(btc(array.ptr, 35));
285     assert(array[0] == 2);
286     assert(array[1] == 0x100);
287 
288     assert(bts(array.ptr, 35) == 0);
289     if (size_t.sizeof == 8)
290     {
291         assert(array[0] == 0x8_0000_0002);
292         assert(array[1] == 0x100);
293     }
294     else
295     {
296         assert(array[0] == 2);
297         assert(array[1] == 0x108);
298     }
299 
300     assert(btr(array.ptr, 35));
301     assert(array[0] == 2);
302     assert(array[1] == 0x100);
303 }
304 
305 /// The 'ctpop' family of intrinsics counts the number of bits set in a value.
306 T ctpop(T)(in T src)
307     if (__traits(isUnsigned, T))
308 {
309     version(LDC) if (!__ctfe)
310         return llvm_ctpop(src);
311     version(GNU) if (!__ctfe)
312     {
313         static if (T.sizeof < __builtin_clong.sizeof)
314             return cast(T) __builtin_popcount(src);
315         else static if (T.sizeof <= __builtin_clong.sizeof)
316             return cast(T) __builtin_popcountl(src);
317         else
318             return cast(T) __builtin_popcountll(src);
319     }
320     import core.bitop: popcnt;
321     return cast(T) popcnt(src);
322 }
323 
324 /++
325 The 'ctlz' family of intrinsic functions counts the number of leading zeros in a variable.
326 Result is undefined if the argument is zero.
327 +/
328 T ctlz(T)(in T src)
329     if (__traits(isUnsigned, T))
330 {
331     version(LDC) if (!__ctfe)
332         return llvm_ctlz(src, true);
333     version(GNU) if (!__ctfe)
334     {
335         // Do not zero-extend when counting leading zeroes.
336         static if (T.sizeof < __builtin_clong.sizeof && T.sizeof >= uint.sizeof)
337             return cast(T) __builtin_clz(src);
338         else static if (T.sizeof == __builtin_clong.sizeof)
339             return cast(T) __builtin_clzl(src);
340         else static if (T.sizeof > __builtin_clong.sizeof)
341             return cast(T) __builtin_clzll(src);
342     }
343     import core.bitop: bsr;
344     return cast(T)(T.sizeof * 8  - 1 - bsr(src));
345 }
346 
347 ///
348 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
349 {
350     assert(ctlz(cast(ubyte) 0b0011_1111) == 2);
351     assert(ctlz(cast(ushort) 0b0000_0001_1111_1111) == 7);
352 }
353 
354 /++
355 The 'ctlzp' family of intrinsic functions counts the number of leading zeros in a variable.
356 Result is properly defined if the argument is zero.
357 +/
358 T ctlzp(T)(in T src)
359     if (__traits(isUnsigned, T))
360 {
361     version(LDC) if (!__ctfe)
362         return llvm_ctlz(src, false);
363     return src ? ctlz(src) : T.sizeof * 8;
364 }
365 
366 ///
367 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
368 {
369     assert(ctlzp(cast(ubyte) 0b0000_0000) == 8);
370     assert(ctlzp(cast(ubyte) 0b0011_1111) == 2);
371     assert(ctlzp(cast(ushort) 0b0000_0001_1111_1111) == 7);
372     assert(ctlzp(cast(ushort) 0) == 16);
373     assert(ctlzp(cast(ulong) 0) == 64);
374 }
375 
376 /++
377 The 'cttz' family of intrinsic functions counts the number of trailing zeros.
378 Result is undefined if the argument is zero.
379 +/
380 T cttz(T)(in T src)
381     if (__traits(isUnsigned, T))
382 {
383     version(LDC) if (!__ctfe)
384         return llvm_cttz(src, true);
385     version(GNU) if (!__ctfe)
386     {
387         static if (T.sizeof <__builtin_clong.sizeof)
388             return cast(T) __builtin_ctz(src);
389         else static if (T.sizeof <=__builtin_clong.sizeof)
390             return cast(T) __builtin_ctzl(src);
391         else
392             return cast(T) __builtin_ctzll(src);
393     }
394     import core.bitop: bsf;
395     return cast(T) bsf(src);
396 }
397 
398 ///
399 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
400 {
401     assert(cttzp(cast(ubyte) 0b11111100) == 2);
402     assert(cttzp(cast(ushort) 0b1111111110000000) == 7);
403 }
404 
405 /++
406 The 'cttz' family of intrinsic functions counts the number of trailing zeros.
407 Result is properly defined if the argument is zero.
408 +/
409 T cttzp(T)(in T src)
410     if (__traits(isUnsigned, T))
411 {
412     version(LDC) if (!__ctfe)
413         return llvm_cttz(src, false);
414     return src ? cttz(src) : T.sizeof * 8;
415 }
416 
417 ///
418 version (mir_core_test) @nogc nothrow pure @safe version(mir_core_test) unittest
419 {
420     assert(cttzp(cast(ubyte) 0b0000_0000) == 8);
421     assert(cttzp(cast(ubyte) 0b11111100) == 2);
422     assert(cttzp(cast(ushort) 0b1111111110000000) == 7);
423     assert(cttzp(cast(ushort) 0) == 16);
424     assert(cttzp(cast(ulong) 0) == 64);
425 }