1 /** 2 * Bit manipulation. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 * Simon Arlott 13 */ 14 15 module ae.utils.bitmanip; 16 17 import std.bitmanip; 18 import std.traits; 19 20 /// Stores `T` in big-endian byte order. 21 struct BigEndian(T) 22 { 23 private ubyte[T.sizeof] _endian_bytes; 24 @property T _endian_value() const { return cast(T)bigEndianToNative!(OriginalType!T)(_endian_bytes); } 25 @property void _endian_value(T value) { _endian_bytes = nativeToBigEndian(OriginalType!T(value)); } 26 alias _endian_value this; 27 alias opAssign = _endian_value; /// 28 this(T value) { _endian_value(value); } /// 29 } 30 31 /// Stores `T` in little-endian byte order. 32 struct LittleEndian(T) 33 { 34 private ubyte[T.sizeof] _endian_bytes; 35 @property T _endian_value() const { return cast(T)littleEndianToNative!(OriginalType!T)(_endian_bytes); } 36 @property void _endian_value(T value) { _endian_bytes = nativeToLittleEndian(OriginalType!T(value)); } 37 alias _endian_value this; 38 alias opAssign = _endian_value; /// 39 this(T value) { _endian_value(value); } /// 40 } 41 42 /// 43 unittest 44 { 45 union U 46 { 47 BigEndian!ushort be; 48 LittleEndian!ushort le; 49 ubyte[2] bytes; 50 } 51 U u; 52 53 u.be = 0x1234; 54 assert(u.bytes == [0x12, 0x34]); 55 56 u.le = 0x1234; 57 assert(u.bytes == [0x34, 0x12]); 58 59 u.bytes = [0x56, 0x78]; 60 assert(u.be == 0x5678); 61 assert(u.le == 0x7856); 62 } 63 64 unittest 65 { 66 enum E : uint { e } 67 BigEndian!E be; 68 } 69 70 unittest 71 { 72 const e = BigEndian!int(1); 73 assert(e == 1); 74 } 75 76 // ---------------------------------------------------------------------------- 77 78 /// Set consisting of members of the given enum. 79 /// Each unique enum member can be present or absent. 80 /// Accessing members can be done by name or with indexing. 81 struct EnumBitSet(E) 82 if (is(E == enum)) 83 { 84 private enum bits = size_t.sizeof * 8; 85 private size_t[(cast(size_t)E.max + bits - 1) / bits] representation = 0; 86 87 /// Construct an instance populated with a single member. 88 this(E e) 89 { 90 this[e] = true; 91 } 92 93 /// Access by indexing (runtime value). 94 bool opIndex(E e) const 95 { 96 return (representation[e / bits] >> (e % bits)) & 1; 97 } 98 99 /// ditto 100 bool opIndexAssign(bool value, E e) 101 { 102 auto shift = e % bits; 103 representation[e / bits] &= ~(1 << shift); 104 representation[e / bits] |= value << shift; 105 return value; 106 } 107 108 /// Access by name (compile-time value). 109 template opDispatch(string name) 110 if (__traits(hasMember, E, name)) 111 { 112 enum E e = __traits(getMember, E, name); 113 114 @property bool opDispatch() const 115 { 116 return (representation[e / bits] >> (e % bits)) & 1; 117 } 118 119 @property bool opDispatch(bool value) 120 { 121 auto shift = e % bits; 122 representation[e / bits] &= ~(1 << shift); 123 representation[e / bits] |= value << shift; 124 return value; 125 } 126 } 127 128 /// Enumeration of set members. Returns a range. 129 auto opSlice() 130 { 131 alias set = this; 132 133 struct R 134 { 135 size_t pos = 0; 136 137 private void advance() 138 { 139 while (pos <= cast(size_t)E.max && !set[front]) 140 pos++; 141 } 142 143 @property E front() { return cast(E)pos; } 144 145 bool empty() const { return pos > cast(size_t)E.max; } 146 void popFront() { pos++; advance(); } 147 } 148 R r; 149 r.advance(); 150 return r; 151 } 152 153 /// Filling / clearing. 154 /// Caution: filling with `true` will also set members with no corresponding enum member, 155 /// if the enum is not contiguous. 156 void opSliceAssign(bool value) 157 { 158 representation[] = value ? 0xFF : 0x00; 159 } 160 161 /// Set operations. 162 EnumBitSet opUnary(string op)() const 163 if (op == "~") 164 { 165 EnumBitSet result = this; 166 foreach (ref b; result.representation) 167 b = ~b; 168 return result; 169 } 170 171 EnumBitSet opBinary(string op)(auto ref const EnumBitSet b) const 172 if (op == "|" || op == "&" || op == "^") 173 { 174 EnumBitSet result = this; 175 mixin(q{result }~op~q{= b;}); 176 return result; 177 } 178 179 ref EnumBitSet opOpAssign(string op)(auto ref const EnumBitSet o) 180 if (op == "|" || op == "&" || op == "^") 181 { 182 foreach (i; 0 .. representation.length) 183 mixin(q{representation[i] }~op~q{= o.representation[i];}); 184 return this; 185 } 186 187 T opCast(T)() const 188 if (is(T == bool)) 189 { 190 return this !is typeof(this).init; 191 } 192 } 193 194 unittest 195 { 196 import std.algorithm.comparison : equal; 197 198 enum E { a, b, c } 199 alias S = EnumBitSet!E; 200 201 { 202 S s; 203 204 assert(!s[E.a]); 205 s[E.a] = true; 206 assert(s[E.a]); 207 s[E.a] = false; 208 assert(!s[E.a]); 209 210 assert(!s.b); 211 s.b = true; 212 assert(s.b); 213 s.b = false; 214 assert(!s.b); 215 216 assert(s[].empty); 217 s.b = true; 218 assert(equal(s[], [E.b])); 219 } 220 221 { 222 auto a = S(E.a); 223 auto c = S(E.c); 224 a |= c; 225 assert(a[].equal([E.a, E.c])); 226 227 a[] = false; 228 assert(a[].empty); 229 } 230 }