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 alias NetworkByteOrder = BigEndian; 43 44 /// 45 debug(ae_unittest) unittest 46 { 47 union U 48 { 49 BigEndian!ushort be; 50 LittleEndian!ushort le; 51 ubyte[2] bytes; 52 } 53 U u; 54 55 u.be = 0x1234; 56 assert(u.bytes == [0x12, 0x34]); 57 58 u.le = 0x1234; 59 assert(u.bytes == [0x34, 0x12]); 60 61 u.bytes = [0x56, 0x78]; 62 assert(u.be == 0x5678); 63 assert(u.le == 0x7856); 64 } 65 66 debug(ae_unittest) unittest 67 { 68 enum E : uint { e } 69 BigEndian!E be; 70 } 71 72 debug(ae_unittest) unittest 73 { 74 const e = BigEndian!int(1); 75 assert(e == 1); 76 } 77 78 // ---------------------------------------------------------------------------- 79 80 /// Set consisting of members of the given enum. 81 /// Each unique enum member can be present or absent. 82 /// Accessing members can be done by name or with indexing. 83 struct EnumBitSet(E) 84 if (is(E == enum)) 85 { 86 private enum bits = size_t.sizeof * 8; 87 private size_t[(cast(size_t)E.max + bits - 1) / bits] representation = 0; 88 89 /// Construct an instance populated with a single member. 90 this(E e) 91 { 92 this[e] = true; 93 } 94 95 /// Access by indexing (runtime value). 96 bool opIndex(E e) const 97 { 98 return (representation[e / bits] >> (e % bits)) & 1; 99 } 100 101 /// ditto 102 bool opIndexAssign(bool value, E e) 103 { 104 auto shift = e % bits; 105 representation[e / bits] &= ~(1 << shift); 106 representation[e / bits] |= value << shift; 107 return value; 108 } 109 110 /// Access by name (compile-time value). 111 template opDispatch(string name) 112 if (__traits(hasMember, E, name)) 113 { 114 enum E e = __traits(getMember, E, name); 115 116 @property bool opDispatch() const 117 { 118 return (representation[e / bits] >> (e % bits)) & 1; 119 } 120 121 @property bool opDispatch(bool value) 122 { 123 auto shift = e % bits; 124 representation[e / bits] &= ~(1 << shift); 125 representation[e / bits] |= value << shift; 126 return value; 127 } 128 } 129 130 /// Enumeration of set members. Returns a range. 131 auto opSlice() 132 { 133 alias set = this; 134 135 struct R 136 { 137 size_t pos = 0; 138 139 private void advance() 140 { 141 while (pos <= cast(size_t)E.max && !set[front]) 142 pos++; 143 } 144 145 @property E front() { return cast(E)pos; } 146 147 bool empty() const { return pos > cast(size_t)E.max; } 148 void popFront() { pos++; advance(); } 149 } 150 R r; 151 r.advance(); 152 return r; 153 } 154 155 /// Filling / clearing. 156 /// Caution: filling with `true` will also set members with no corresponding enum member, 157 /// if the enum is not contiguous. 158 void opSliceAssign(bool value) 159 { 160 representation[] = value ? 0xFF : 0x00; 161 } 162 163 /// Set operations. 164 EnumBitSet opUnary(string op)() const 165 if (op == "~") 166 { 167 EnumBitSet result = this; 168 foreach (ref b; result.representation) 169 b = ~b; 170 return result; 171 } 172 173 EnumBitSet opBinary(string op)(auto ref const EnumBitSet b) const 174 if (op == "|" || op == "&" || op == "^") 175 { 176 EnumBitSet result = this; 177 mixin(q{result }~op~q{= b;}); 178 return result; 179 } 180 181 ref EnumBitSet opOpAssign(string op)(auto ref const EnumBitSet o) 182 if (op == "|" || op == "&" || op == "^") 183 { 184 foreach (i; 0 .. representation.length) 185 mixin(q{representation[i] }~op~q{= o.representation[i];}); 186 return this; 187 } 188 189 T opCast(T)() const 190 if (is(T == bool)) 191 { 192 return this !is typeof(this).init; 193 } 194 } 195 196 debug(ae_unittest) unittest 197 { 198 import std.algorithm.comparison : equal; 199 200 enum E { a, b, c } 201 alias S = EnumBitSet!E; 202 203 { 204 S s; 205 206 assert(!s[E.a]); 207 s[E.a] = true; 208 assert(s[E.a]); 209 s[E.a] = false; 210 assert(!s[E.a]); 211 212 assert(!s.b); 213 s.b = true; 214 assert(s.b); 215 s.b = false; 216 assert(!s.b); 217 218 assert(s[].empty); 219 s.b = true; 220 assert(equal(s[], [E.b])); 221 } 222 223 { 224 auto a = S(E.a); 225 auto c = S(E.c); 226 a |= c; 227 assert(a[].equal([E.a, E.c])); 228 229 a[] = false; 230 assert(a[].empty); 231 } 232 }