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 }