1 /*
2  * Copyright (c) 2017 SEL
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  * 
14  */
15 module sel.hncom.io;
16 
17 import std.bitmanip : nativeToBigEndian, nativeToLittleEndian, bigEndianToNative, littleEndianToNative;
18 import std.json : JSONValue, parseJSON, JSONException;
19 import std.socket : Address, InternetAddress, Internet6Address;
20 import std.traits : isArray, isDynamicArray, isAssociativeArray, KeyType, ValueType, isIntegral, isSigned, Unsigned;
21 import std.typecons : isTuple;
22 import std.uuid : UUID;
23 
24 mixin template IO(E...) {
25 
26 	import sel.hncom.io : IOImpl, encodeType;
27 
28 	mixin IOImpl!E;
29 
30 	ubyte[] encode() {
31 		ubyte[] buffer;
32 		encodeType(ID, buffer);
33 		encodeValues(buffer);
34 		return buffer;
35 	}
36 
37 	typeof(this) decode(ubyte[] buffer) {
38 		size_t index = 0;
39 		decodeValues(buffer, index);
40 		return this;
41 	}
42 
43 	static typeof(this) fromBuffer(ubyte[] buffer) {
44 		return typeof(this)().decode(buffer);
45 	}
46 
47 }
48 
49 mixin template IOImpl(E...) {
50 
51 	import sel.hncom.io : encodeType, decodeType;
52 
53 	private void encodeValues(ref ubyte[] buffer) {
54 		foreach(ref value ; E) {
55 			encodeType(value, buffer);
56 		}
57 	}
58 
59 	private void decodeValues(ubyte[] buffer, ref size_t index) {
60 		foreach(ref value ; E) {
61 			value = decodeType!(typeof(value))(buffer, index);
62 		}
63 	}
64 
65 }
66 
67 void encodeType(T)(T value, ref ubyte[] buffer) {
68 	static if(isArray!T) {
69 		static if(isDynamicArray!T) encodeLength(value.length, buffer);
70 		encodeArray(value, buffer);
71 	} else static if(isAssociativeArray!T) {
72 		encodeLength(value.length, buffer);
73 		foreach(key, v; value) {
74 			encodeType(key, buffer);
75 			encodeType(v, buffer);
76 		}
77 	} else static if(isTuple!T) {
78 		foreach(i, name; T.fieldNames) {
79 			encodeType!(T.Types[i])(mixin("value." ~ name), buffer);
80 		}
81 	} else static if(is(T == JSONValue)) {
82 		encodeType(value.toString(), buffer);
83 	} else static if(is(T == UUID)) {
84 		buffer ~= value.data;
85 	} else static if(is(T == Address)) {
86 		if(cast(InternetAddress)value) {
87 			auto v4 = cast(InternetAddress)value;
88 			buffer ~= ubyte(4);
89 			encodeAddress(v4.addr, buffer);
90 			encodeAddress(v4.port, buffer);
91 		} else if(cast(Internet6Address)value) {
92 			auto v6 = cast(Internet6Address)value;
93 			buffer ~= ubyte(16);
94 			buffer ~= v6.addr;
95 			encodeAddress(v6.port, buffer);
96 		} else {
97 			buffer ~= ubyte(0);
98 		}
99 	} else static if(T.sizeof == 1) {
100 		buffer ~= value;
101 	} else static if(isIntegral!T) {
102 		static if(isSigned!T) {
103 			assert(value >= -1);
104 			encodeType(cast(Unsigned!T)(value+1), buffer);
105 		} else {
106 			while(value > 0b0111_1111) {
107 				buffer ~= (value & 0b0111_1111) | 0b1000_0000;
108 				value >>>= 7;
109 			}
110 			buffer ~= value & 0b0111_1111;
111 		}
112 	} else {
113 		buffer ~= nativeToBigEndian(value);
114 	}
115 }
116 
117 void encodeLength(size_t _length, ref ubyte[] buffer) {
118 	static if(is(size_t == uint)) {
119 		alias length = _length;
120 	} else {
121 		uint length = cast(uint)_length;
122 	}
123 	encodeType(length, buffer);
124 }
125 
126 void encodeArray(T)(T array, ref ubyte[] buffer) if(isArray!T) {
127 	alias E = typeof(T.init[0]);
128 	static if(is(typeof(E.sizeof)) && E.sizeof == 1) {
129 		buffer ~= cast(ubyte[])array;
130 	} else {
131 		foreach(element ; array) {
132 			encodeType(element, buffer);
133 		}
134 	}
135 }
136 
137 void encodeAddress(T)(T value, ref ubyte[] buffer) if(isIntegral!T) {
138 	buffer ~= nativeToLittleEndian(value);
139 }
140 
141 T decodeType(T)(ubyte[] buffer, ref size_t index) {
142 	static if(isDynamicArray!T) {
143 		return decodeArray!T(decodeLength(buffer, index), buffer, index);
144 	} else static if(isArray!T) {
145 		return decodeArray!T(T.init.length, buffer, index);
146 	} else static if(isAssociativeArray!T) {
147 		T ret;
148 		foreach(i ; 0..decodeLength(buffer, index)) {
149 			ret[decodeType!(KeyType!T)(buffer, index)] = decodeType!(ValueType!T)(buffer, index);
150 		}
151 		return ret;
152 	} else static if(isTuple!T) {
153 		T ret;
154 		foreach(i, name; T.fieldNames) {
155 			mixin("ret." ~ name) = decodeType!(T.Types[i])(buffer, index);
156 		}
157 		return ret;
158 	} else static if(is(T == JSONValue)) {
159 		try {
160 			return parseJSON(decodeType!string(buffer, index));
161 		} catch(JSONException) {
162 			return JSONValue.init;
163 		}
164 	} else static if(is(T == UUID)) {
165 		return UUID(decodeType!(ubyte[16])(buffer, index));
166 	} else static if(is(T == Address)) {
167 		switch(decodeType!ubyte(buffer, index)) {
168 			case 4: return new InternetAddress(decodeAddress!uint(buffer, index), decodeAddress!ushort(buffer, index));
169 			case 16: return new Internet6Address(decodeType!(ubyte[16])(buffer, index), decodeAddress!ushort(buffer, index));
170 			default: return null;
171 		}
172 	} else static if(T.sizeof == 1) {
173 		return cast(T)buffer[index++];
174 	} else static if(isIntegral!T) {
175 		static if(isSigned!T) {
176 			return cast(T)(decodeType!(Unsigned!T)(buffer, index) - 1);
177 		} else {
178 			T ret;
179 			size_t shift = 0;
180 			ubyte next;
181 			do {
182 				next = buffer[index++];
183 				ret |= ((next & 0b0111_1111) << shift);
184 				shift += 7;
185 			} while(next & 0b1000_0000);
186 			return ret;
187 		}
188 	} else {
189 		ubyte[T.sizeof] data = buffer[index..index+=T.sizeof];
190 		return bigEndianToNative!T(data);
191 	}
192 }
193 
194 size_t decodeLength(ubyte[] buffer, ref size_t index) {
195 	return decodeType!uint(buffer, index);
196 }
197 
198 T decodeArray(T)(size_t length, ubyte[] buffer, ref size_t index) if(isArray!T) {
199 	alias E = typeof(T.init[0]);
200 	static if(is(typeof(E.sizeof)) && E.sizeof == 1) {
201 		T ret = cast(E[])buffer[index..index+=length];
202 		return ret;
203 	} else {
204 		static if(isDynamicArray!T) auto ret = new E[length];
205 		else T ret;
206 		foreach(ref element ; ret) {
207 			element = decodeType!E(buffer, index);
208 		}
209 		return ret;
210 	}
211 }
212 
213 T decodeAddress(T)(ubyte[] buffer, ref size_t index) if(isIntegral!T) {
214 	ubyte[T.sizeof] data = buffer[index..index+=T.sizeof];
215 	return littleEndianToNative!T(data);
216 }
217 
218 unittest {
219 
220 	import std.conv;
221 
222 	ubyte[] encode(T)(T value) {
223 		ubyte[] buffer;
224 		encodeType(value, buffer);
225 		return buffer;
226 	}
227 
228 	T decode(T)(ubyte[] buffer) {
229 		size_t index = 0;
230 		return decodeType!T(buffer, index);
231 	}
232 
233 	// numbers
234 
235 	assert(encode(true) == [1]);
236 	assert(encode(ubyte(3)) == [3]);
237 	assert(encode(ushort(5)) == [5]);
238 	assert(encode(130u) == [130, 1]);
239 	assert(encode(12) == [13]);
240 	assert(encode(-1L) == [0]);
241 	assert(encode(2f) == [64, 0, 0, 0]);
242 
243 	assert(decode!bool([0]) == false);
244 	assert(decode!byte([255]) == -1);
245 	assert(decode!short([0]) == -1);
246 	assert(decode!uint([132, 1]) == 132);
247 	assert(decode!float([64, 64, 0, 0]) == 3);
248 
249 	// uuid
250 
251 	import std.uuid : randomUUID;
252 	auto uuid = randomUUID();
253 	assert(encode(uuid) == uuid.data);
254 	assert(decode!UUID(uuid.data) == uuid);
255 
256 	// json
257 
258 	import std.json : JSON_TYPE;
259 	assert(encode(JSONValue((JSONValue[string]).init)) == [2, '{', '}']);
260 	assert(decode!JSONValue([2, '{', '}']).type == JSON_TYPE.OBJECT);
261 	assert(decode!JSONValue([8, '[', '1', ',', '2', ',', ' ', '3', ']']) == JSONValue([1, 2, 3]));
262 	assert(decode!JSONValue([3, '{', '{', '}']).isNull);
263 
264 	// addresses
265 
266 	assert(encode(cast(Address)new InternetAddress("127.0.0.1", 0)) == [4, 1, 0, 0, 127, 0, 0]);
267 	assert(encode(cast(Address)new Internet6Address("::1", 80)) == [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 80, 0]);
268 	assert(encode(Address.init) == [0]);
269 
270 	assert(decode!Address([0]) is null);
271 	assert(decode!Address([4, 1, 1, 168, 192, 80, 0]) == new InternetAddress("192.168.1.1", 80));
272 	assert(decode!Address([16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).toString() == "[::]:0");
273 
274 	// dynamic arrays
275 
276 	assert(encode([1, 2, 3]) == [3, 2, 3, 4]);
277 	assert(encode(cast(ubyte[])[1, 2, 3]) == [3, 1, 2, 3]);
278 	assert(encode(["this", "is", "a", "string"]) == [4, 4, 't', 'h', 'i', 's', 2, 'i', 's', 1, 'a', 6, 's', 't', 'r', 'i', 'n', 'g']);
279 
280 	assert(decode!(bool[])([2, 0, 1]) == [false, true]);
281 	assert(decode!(uint[])([3, 0, 1, 2]) == [0, 1, 2]);
282 	assert(decode!(Address[])([3, 0, 0, 0]) == [null, null, null]);
283 
284 	// static arrays
285 
286 	int[4] int_;
287 	string[2] string_;
288 	string_[1] = "$";
289 
290 	assert(encode(int_) == [1, 1, 1, 1]);
291 	assert(encode(string_) == [0, 1, '$']);
292 
293 	// associative arrays
294 
295 	assert(encode([0: 12u]) == [1, 1, 12]);
296 	assert(decode!(string[bool])([2, 1, 4, 't', 'r', 'u', 'e', 0, 5, 'f', 'a', 'l', 's', 'e']) == [true: "true", false: "false"]);
297 
298 	// tuples
299 
300 	import std.typecons : Tuple;
301 
302 	alias Test = Tuple!(Address, "address", int, "number");
303 
304 	assert(encode(Test.init) == [0, 1]);
305 	assert(decode!Test([4, 0, 0, 0, 0, 80, 0, 0]) == Test(new InternetAddress("0.0.0.0", 80), -1));
306 
307 }