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 }