1 module lmpl4d.common;
2 
3 import
4 	core.bitop,
5 	std.container.array;
6 
7 package import
8 	std.meta,
9 	std.traits,
10 	std.typecons;
11 
12 version(EnableReal)
13 	enum EnableReal = true;
14 else
15 	enum EnableReal = false;
16 
17 version(NoPackingStruct) {}
18 else {
19 	struct nonPacked {}
20 
21 	package enum isPackedField(alias field) = staticIndexOf!(nonPacked, __traits(getAttributes, field)) == -1
22 		&& !isSomeFunction!(typeof(field));
23 
24 	/**
25 	 * Get the number of member to serialize.
26 	 */
27 	template NumOfSerializingMembers(Classes...)
28 	{
29 		static if (Classes.length)
30 			enum NumOfSerializingMembers = Filter!(isPackedField, Classes[0].tupleof).length +
31 				NumOfSerializingMembers!(Classes[1..$]);
32 		else
33 			enum NumOfSerializingMembers = 0;
34 	}
35 }
36 
37 package:
38 
39 enum isSomeArray(T) = (isArray!T || isInstanceOf!(Array, T)) && !is(Unqual!T == enum);
40 
41 static if (real.sizeof == double.sizeof) {
42 	// for 80bit real inter-operation on non-x86 CPU
43 	version = NonX86;
44 
45 	import std.numeric;
46 }
47 
48 /**
49  * For float/double type (de)serialization
50  */
51 union _f { float f; uint i; }
52 
53 union _d { double f; ulong i; }
54 
55 /**
56  * For real type (de)serialization
57  *
58  * 80-bit real is padded to 12 bytes(Linux) and 16 bytes(Mac).
59  * http://lists.puremagic.com/pipermail/digitalmars-d/2010-June/077394.html
60  */
61 union _r
62 {
63 	real f;
64 
65 	struct
66 	{
67 		ulong  fraction;
68 		ushort exponent;  // includes sign
69 	}
70 }
71 
72 enum isRawByte(T) = is(Unqual!T : ubyte) || isSomeChar!T;
73 
74 /**
75  * Gets asterisk string from pointer type
76  */
77 template AsteriskOf(T)
78 {
79 	static if (is(T P == U*, U))
80 		enum AsteriskOf = "*" ~ AsteriskOf!U;
81 	else
82 		enum AsteriskOf = "";
83 }
84 
85 version(unittest)
86 void fillData(T)(ref T data)
87 {
88 	import core.stdc.stdlib;
89 	import std.datetime.systime;
90 
91 	srand(cast(uint)Clock.currStdTime);
92 	foreach(ref x; data)
93 		x = cast(ubyte)rand();
94 }
95 
96 public:
97 
98 version (LittleEndian)
99 {
100 	/*
101 	 * Converts $(value) to different Endian.
102 	 *
103 	 * Params:
104 	 *  value = the LittleEndian value to convert.
105 	 *
106 	 * Returns:
107 	 *  the converted value.
108 	 */
109 	@trusted
110 	ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16)
111 	{
112 		return byteswap(cast(ushort)value);
113 	}
114 
115 	// ditto
116 	@trusted
117 	uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32)
118 	{
119 		return bswap(cast(uint)value);
120 	}
121 
122 	// ditto
123 	@trusted
124 	ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64)
125 	{
126 		return bswap(value);
127 	}
128 
129 	unittest
130 	{
131 		assert(convertEndianTo!16(0x0123)             == 0x2301);
132 		assert(convertEndianTo!32(0x01234567)         == 0x67452301);
133 		assert(convertEndianTo!64(0x0123456789abcdef) == 0xefcdab8967452301);
134 	}
135 
136 	/*
137 	 * Comapatible for BigEndian environment.
138 	 */
139 	ubyte take8from(size_t bit = 8, T)(T value)
140 	if (bit == 8 || bit == 16 || bit == 32 || bit == 64)
141 	{
142 		return (cast(ubyte*)&value)[0];
143 	}
144 }
145 else
146 {
147 	/*
148 	 * Comapatible for LittleEndian environment.
149 	 */
150 	@safe
151 	ushort convertEndianTo(size_t bit, T)(in T value)
152 	if (bit == 16 || bit == 32 || bit == 64)
153 	{
154 		static if (bit == 16)
155 			alias U = ushort;
156 		else static if (bit == 32)
157 			alias U = uint;
158 		else
159 			alias U = ulong;
160 		return cast(U)value;
161 	}
162 
163 	unittest
164 	{
165 		assert(convertEndianTo!16(0x0123)       == 0x0123);
166 		assert(convertEndianTo!32(0x01234567)   == 0x01234567);
167 		assert(convertEndianTo!64(0x0123456789) == 0x0123456789);
168 	}
169 
170 	/*
171 	 * Takes 8bit from $(D_PARAM value)
172 	 *
173 	 * Params:
174 	 *  value = the content to take.
175 	 *
176 	 * Returns:
177 	 *  the 8bit value corresponding $(D_PARAM bit) width.
178 	 */
179 	ubyte take8from(size_t bit = 8, T)(T value)
180 	if (bit == 8 || bit == 16 || bit == 32 || bit == 64)
181 	{
182 		return (cast(ubyte*)&value)[(bit >> 3) - 1];
183 	}
184 }
185 
186 unittest
187 {
188 	foreach (Int; AliasSeq!(ubyte, ushort, uint, ulong)) {
189 		assert(take8from!8 (cast(Int)0x01)               == 0x01);
190 		assert(take8from!16(cast(Int)0x0123)             == 0x23);
191 		assert(take8from!32(cast(Int)0x01234567)         == 0x67);
192 		assert(take8from!64(cast(Int)0x0123456789abcdef) == 0xef);
193 	}
194 }
195 
196 enum isInputBuffer(R, E) = __traits(compiles, (R r, size_t i) { E e = r[i++]; });
197 enum isOutputBuffer(R, E) =
198 	__traits(compiles, (R r, E e) { r.reserve(1); r ~= e; r = R(&r[0], 1, 1); }) || __traits(compiles,
199 	(R r, E e) { size_t sz = r.length; r.length = sz + E.sizeof; *cast(Unqual!E*)&r[sz] = e; })
200 	|| __traits(compiles, (R r, E e) { *cast(Unqual!E*)&r[0] = e; r += E.sizeof; });
201 
202 unittest
203 {
204 	static assert(!isInputBuffer!(void[], ubyte));
205 	static assert(isInputBuffer!(ubyte[9], ubyte));
206 	static assert(isInputBuffer!(ubyte[], ubyte));
207 	static assert(isInputBuffer!(ubyte*, ubyte));
208 	static assert(isInputBuffer!(Array!ubyte, ubyte));
209 	static assert(!isInputBuffer!(ubyte, ubyte));
210 	static assert(!isInputBuffer!(int[], ubyte));
211 	static assert(isOutputBuffer!(void[], ubyte));
212 	static assert(!isOutputBuffer!(ubyte[9], ubyte));
213 	static assert(isOutputBuffer!(ubyte[], ubyte));
214 	static assert(isOutputBuffer!(ubyte*, ubyte));
215 	static assert(isOutputBuffer!(Array!ubyte, ubyte));
216 	static assert(isOutputBuffer!(int[], ubyte));
217 }
218 
219 version(betterC){}else {
220 	pure:
221 	/**
222 	 * $(D MessagePackException) is a root Exception for MessagePack related operation.
223 	 */
224 	class MessagePackException : Exception
225 	{
226 		this(string msg) { super(msg); }
227 	}
228 	/**
229 	 * $(D UnpackException) is thrown on deserialization failure
230 	 */
231 	class UnpackException : MessagePackException
232 	{
233 		this(string msg) { super(msg); }
234 	}
235 }
236 
237 nothrow @nogc pure:
238 
239 /**
240  * MessagePack type-information format
241  *
242  * See_Also:
243  *  $(LINK2 http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec, MessagePack Specificaton)
244  */
245 enum Format : ubyte
246 {
247 	NONE,
248 
249 	// unsinged integer
250 	UINT8  = 0xcc,  // ubyte
251 	UINT16 = 0xcd,  // ushort
252 	UINT32 = 0xce,  // uint
253 	UINT64 = 0xcf,  // ulong
254 
255 	// signed integer
256 	INT8  = 0xd0,   // byte
257 	INT16 = 0xd1,   // short
258 	INT32 = 0xd2,   // int
259 	INT64 = 0xd3,   // long
260 
261 	// floating point
262 	FLOAT  = 0xca,  // float
263 	DOUBLE = 0xcb,  // double
264 
265 	// raw byte
266 	RAW   = 0xa0,
267 	RAW16 = 0xda,
268 	RAW32 = 0xdb,
269 
270 	// bin type
271 	BIN8  = 0xc4,
272 	BIN16 = 0xc5,
273 	BIN32 = 0xc6,
274 
275 	// ext type
276 	EXT   = 0xd4,  // fixext 1/2/4/8/16
277 	EXT8  = 0xc7,
278 	EXT16 = 0xc8,
279 	EXT32 = 0xc9,
280 
281 	// str type
282 	STR8  = 0xd9,
283 	//STR16 = 0xda,
284 	//STR32 = 0xdb,
285 
286 	// array
287 	ARRAY   = 0x90,
288 	ARRAY16 = 0xdc,
289 	ARRAY32 = 0xdd,
290 
291 	// map
292 	MAP   = 0x80,
293 	MAP16 = 0xde,
294 	MAP32 = 0xdf,
295 
296 	// other
297 	NIL   = 0xc0,   // null
298 	TRUE  = 0xc3,
299 	FALSE = 0xc2,
300 
301 	// real (This format is D only!)
302 	REAL = 0xd4
303 }
304 
305 /*
306  * Calculates the format size of container length.
307  */
308 size_t calculateSize(bool rawType = false)(in size_t length)
309 {
310 	enum S = rawType ? 32 : 16;
311 	return length < S ? 0 : length <= ushort.max ? ushort.sizeof : uint.sizeof;
312 }
313 
314 /// Adaptive Output Buffer
315 struct AOutputBuf(Stream, T = ubyte) if(isOutputBuffer!(Stream, T))
316 {
317 	@property ref Stream buf() { return *arr; }
318 
319 	@property ref const(Stream) buf() const { return *arr; }
320 
321 	Stream* arr;
322 	this(ref Stream array) { arr = &array; }
323 
324 	ref const(Stream) opOpAssign(string op : "~")(inout(T[]) rhs) {
325 		buf.reserve(buf.length + rhs.length);
326 		foreach(ref elem; rhs)
327 			this ~= elem;
328 		return buf;
329 	}
330 
331 	ref const(Stream) opOpAssign(string op : "~", U)(U rhs) if(!isDynamicArray!U) {
332 		static if (isPointer!Stream)
333 			size_t sz = 0;
334 		else
335 			size_t sz = buf.length;
336 		static if (__traits(compiles, buf.length = buf.length + 1))
337 			buf.length = sz + U.sizeof;
338 		else {
339 			// for Dvector!T
340 			buf.reserve(sz + U.sizeof);
341 			buf = Stream(&buf[0], sz + U.sizeof, buf.capacity);
342 		}
343 		*cast(Unqual!U*)&buf[sz] = rhs;
344 		static if (isPointer!Stream) buf += U.sizeof;
345 		return buf;
346 	}
347 
348 	// for Array!T
349 	static if (!__traits(compiles, buf[0 .. 2] == [0, 1])) @nogc {
350 		T[] opSlice()
351 		{
352 			return (&buf[0])[0..buf.length];
353 		}
354 
355 		T[] opSlice(size_t i, size_t j)
356 		{
357 			return (&buf[0])[i..j];
358 		}
359 	}
360 
361 	alias buf this;
362 }
363 
364 version(unittest)
365 {
366 	import
367 		std.container.array,
368 		core.stdc.string;
369 
370 	package:
371 	template DefinePacker()
372 	{
373 		auto arr = Array!ubyte();
374 		auto packer = Packer!(Array!ubyte)(arr);
375 	}
376 	template TestUnpacker()
377 	{
378 		auto unpacker = Unpacker!()(packer.buf[]);
379 		static if (is(typeof(test))) {
380 			auto result = unpacker.unpack!(typeof(test));
381 			auto testfunc = {
382 				import std.conv : text;
383 				assert(result == test, text(test, "\nExpected: ", result));
384 				return 0;
385 			}();
386 		}
387 	}
388 }