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