1 module nxt.masking;
2 
3 /// Enumerates the possible units of a mask.
4 enum MaskKind {Byte, Nibble, Bit}
5 
6 /**
7  * Masks, at compile-time, a byte, a nibble or a bit in the argument.
8  *
9  * Params:
10  *      index = the position, 0-based, of the element to mask.
11  *      kind = the kind of the element to mask.
12  *      value = the value mask.
13  *
14  * Returns:
15  *      The input argument with the element masked.
16  */
17 auto mask(size_t index, MaskKind kind = MaskKind.Byte, T)(const T value)
18     nothrow @safe pure
19     if (   (kind == MaskKind.Byte && index <= T.sizeof)
20         || (kind == MaskKind.Nibble && index <= T.sizeof * 2)
21         || (kind == MaskKind.Bit && index <= T.sizeof * 8))
22 {
23     T _mask;
24     static if (kind == MaskKind.Byte)
25     {
26         _mask = T.min - 1 - (0xFF << index * 8);
27     }
28     else static if (kind == MaskKind.Nibble)
29     {
30         _mask = T.min - 1 - (0xF << index * 4);
31     }
32     else static if (kind == MaskKind.Bit)
33     {
34         _mask = T.min - 1 - (0x1 << index);
35     }
36     return value & _mask;
37 }
38 ///
39 nothrow @safe @nogc pure unittest
40 {
41     // MaskKind.Byte by default.
42     static assert(mask!1(0x12345678) == 0x12340078);
43     static assert(mask!(1,MaskKind.Nibble)(0x12345678) == 0x12345608);
44 }
45 
46 /// Compile-time $(D mask()) partially specialized for nibble-masking.
47 auto maskNibble(size_t index, T)(const T value) nothrow @safe pure
48 {
49     // note: aliasing prevents template parameter type deduction,
50     // e.g alias maskNibble(size_t index, T) = mask!(index, MaskKind.Nibble, T);
51     return mask!(index, MaskKind.Nibble)(value);
52 }
53 ///
54 nothrow @safe @nogc pure unittest
55 {
56     static assert(maskNibble!1(0x12345678) == 0x12345608);
57 }
58 
59 /// Compile-time $(D mask()) partially specialized for bit-masking.
60 auto maskBit(size_t index, T)(const T value) nothrow @safe pure
61 {
62     return mask!(index, MaskKind.Bit)(value);
63 }
64 ///
65 nothrow @safe @nogc pure unittest
66 {
67     static assert(maskBit!1(0b1111) == 0b1101);
68 }
69 
70 /**
71  * Masks, at run-time, a byte, a nibble or a bit in the argument.
72  *
73  * Params:
74  *      index = the position, 0-based, of the element to mask.
75  *      kind = the kind of the element to mask.
76  *      value = the value mask.
77  *
78  * Returns:
79  *      The input argument with the element masked.
80  */
81 auto mask(MaskKind kind = MaskKind.Byte, T)(const T value, size_t index)
82 nothrow @safe pure
83 {
84     static immutable byteMasker =
85     [
86         0xFFFFFFFFFFFFFF00,
87         0xFFFFFFFFFFFF00FF,
88         0xFFFFFFFFFF00FFFF,
89         0xFFFFFFFF00FFFFFF,
90         0xFFFFFF00FFFFFFFF,
91         0xFFFF00FFFFFFFFFF,
92         0xFF00FFFFFFFFFFFF,
93         0x00FFFFFFFFFFFFFF
94     ];
95 
96     static immutable nibbleMasker =
97     [
98         0xFFFFFFFFFFFFFFF0,
99         0xFFFFFFFFFFFFFF0F,
100         0xFFFFFFFFFFFFF0FF,
101         0xFFFFFFFFFFFF0FFF,
102         0xFFFFFFFFFFF0FFFF,
103         0xFFFFFFFFFF0FFFFF,
104         0xFFFFFFFFF0FFFFFF,
105         0xFFFFFFFF0FFFFFFF,
106         0xFFFFFFF0FFFFFFFF,
107         0xFFFFFF0FFFFFFFFF,
108         0xFFFFF0FFFFFFFFFF,
109         0xFFFF0FFFFFFFFFFF,
110         0xFFF0FFFFFFFFFFFF,
111         0xFF0FFFFFFFFFFFFF,
112         0xF0FFFFFFFFFFFFFF,
113         0x0FFFFFFFFFFFFFFF
114     ];
115     static if (kind == MaskKind.Byte)
116         return value & byteMasker[index];
117     else static if (kind == MaskKind.Nibble)
118         return value & nibbleMasker[index];
119     else
120         return value & (0xFFFFFFFFFFFFFFFF - (1UL << index));
121 }
122 ///
123 nothrow @safe @nogc pure unittest
124 {
125     // MaskKind.Byte by default.
126     assert(mask(0x12345678,1) == 0x12340078);
127     assert(mask!(MaskKind.Nibble)(0x12345678,1) == 0x12345608);
128 }
129 
130 /*
131 First version: less byte code but more latency do to memory access
132 This version: no memory access but similar latency due to more byte code.
133 auto mask(MaskKind kind = MaskKind.Byte, T)(const T value, size_t index) nothrow
134 {
135     static immutable T _max = - 1;
136     static if (kind == MaskKind.Byte)
137         return value & (_max - (0xFF << index * 8));
138     else static if (kind == MaskKind.Nibble)
139         return value & (_max - (0xF << index * 4));
140     else
141         return value & (_max - (0x1 << index));
142 }
143 */
144 
145 /// Run-time $(D mask()) partially specialized for nibble-masking.
146 auto maskNibble(T)(const T value, size_t index)
147 {
148     return mask!(MaskKind.Nibble)(value, index);
149 }
150 ///
151 nothrow @safe @nogc pure unittest
152 {
153     assert(maskNibble(0x12345678,1) == 0x12345608);
154 }
155 
156 /// Run-time $(D mask()) partially specialized for bit-masking.
157 auto maskBit(T)(const T value, size_t index) nothrow @safe pure
158 {
159     return mask!(MaskKind.Bit)(value, index);
160 }
161 ///
162 nothrow @safe pure @nogc unittest
163 {
164     assert(maskBit(0b1111,1) == 0b1101);
165 }
166 
167 nothrow @safe pure @nogc unittest
168 {
169     enum v0 = 0x44332211;
170     static assert(mask!0(v0) == 0x44332200);
171     static assert(mask!1(v0) == 0x44330011);
172     static assert(mask!2(v0) == 0x44002211);
173     static assert(mask!3(v0) == 0x00332211);
174 
175     assert(mask(v0,0) == 0x44332200);
176     assert(mask(v0,1) == 0x44330011);
177     assert(mask(v0,2) == 0x44002211);
178     assert(mask(v0,3) == 0x00332211);
179 
180     enum v1 = 0x87654321;
181     static assert(mask!(0, MaskKind.Nibble)(v1) == 0x87654320);
182     static assert(mask!(1, MaskKind.Nibble)(v1) == 0x87654301);
183     static assert(mask!(2, MaskKind.Nibble)(v1) == 0x87654021);
184     static assert(mask!(3, MaskKind.Nibble)(v1) == 0x87650321);
185     static assert(mask!(7, MaskKind.Nibble)(v1) == 0x07654321);
186 
187     assert(mask!(MaskKind.Nibble)(v1,0) == 0x87654320);
188     assert(mask!(MaskKind.Nibble)(v1,1) == 0x87654301);
189     assert(mask!(MaskKind.Nibble)(v1,2) == 0x87654021);
190     assert(mask!(MaskKind.Nibble)(v1,3) == 0x87650321);
191     assert(mask!(MaskKind.Nibble)(v1,7) == 0x07654321);
192 
193     enum v2 = 0b11111111;
194     static assert(mask!(0, MaskKind.Bit)(v2) == 0b11111110);
195     static assert(mask!(1, MaskKind.Bit)(v2) == 0b11111101);
196     static assert(mask!(7, MaskKind.Bit)(v2) == 0b01111111);
197 
198     assert(maskBit(v2,0) == 0b11111110);
199     assert(maskBit(v2,1) == 0b11111101);
200     assert(mask!(MaskKind.Bit)(v2,7) == 0b01111111);
201 }