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 }