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 }