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