1 /*
2  * Copyright (c) 2017-2018 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: Copyright (c) 2017-2018 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/sel-hncom/sel/hncom/io.d, sel/hncom/io.d)
28  */
29 module sel.hncom.io;
30 
31 import std.bitmanip : nativeToBigEndian, bigEndianToNative;
32 import std.json : JSONValue, parseJSON, JSONException;
33 import std.socket : Address, InternetAddress, Internet6Address, UnknownAddress;
34 import std.traits : isArray, isDynamicArray, isAssociativeArray, KeyType, ValueType, isIntegral, isSigned, Unsigned;
35 import std.typecons : isTuple;
36 import std.uuid : UUID;
37 
38 mixin template IO(E...) {
39 
40 	import sel.hncom.io : IOImpl, encodeType;
41 
42 	mixin IOImpl!E;
43 
44 	ubyte[] encode() {
45 		ubyte[] buffer;
46 		encodeType(ID, buffer);
47 		encodeValues(buffer);
48 		return buffer;
49 	}
50 
51 	typeof(this) decode(ubyte[] buffer) {
52 		size_t index = 0;
53 		decodeValues(buffer, index);
54 		return this;
55 	}
56 
57 	static typeof(this) fromBuffer(ubyte[] buffer) {
58 		return typeof(this)().decode(buffer);
59 	}
60 
61 }
62 
63 mixin template IOImpl(E...) {
64 
65 	import sel.hncom.io : encodeType, decodeType;
66 
67 	private void encodeValues(ref ubyte[] buffer) {
68 		foreach(ref value ; E) {
69 			encodeType(value, buffer);
70 		}
71 	}
72 
73 	private void decodeValues(ubyte[] buffer, ref size_t index) {
74 		foreach(ref value ; E) {
75 			value = decodeType!(typeof(value))(buffer, index);
76 		}
77 	}
78 
79 }
80 
81 void encodeType(T)(T value, ref ubyte[] buffer) {
82 	static if(isArray!T) {
83 		static if(isDynamicArray!T) encodeLength(value.length, buffer);
84 		encodeArray(value, buffer);
85 	} else static if(isAssociativeArray!T) {
86 		encodeLength(value.length, buffer);
87 		foreach(key, v; value) {
88 			encodeType(key, buffer);
89 			encodeType(v, buffer);
90 		}
91 	} else static if(isTuple!T) {
92 		foreach(i, name; T.fieldNames) {
93 			encodeType!(T.Types[i])(mixin("value." ~ name), buffer);
94 		}
95 	} else static if(is(T == JSONValue)) {
96 		encodeType(value.toString(), buffer);
97 	} else static if(is(T == UUID)) {
98 		buffer ~= value.data;
99 	} else static if(is(T == Address)) {
100 		if(cast(InternetAddress)value) {
101 			auto v4 = cast(InternetAddress)value;
102 			buffer ~= ubyte(4);
103 			encodeAddress(v4.addr, buffer);
104 			encodeAddress(v4.port, buffer);
105 		} else if(cast(Internet6Address)value) {
106 			auto v6 = cast(Internet6Address)value;
107 			buffer ~= ubyte(6);
108 			buffer ~= v6.addr;
109 			encodeAddress(v6.port, buffer);
110 		} else {
111 			buffer ~= ubyte(0);
112 		}
113 	} else static if(T.sizeof == 1) {
114 		buffer ~= value;
115 	} else static if(isIntegral!T) {
116 		static if(isSigned!T) {
117 			assert(value >= -1);
118 			encodeType(cast(Unsigned!T)(value+1), buffer);
119 		} else {
120 			while(value > 0b0111_1111) {
121 				buffer ~= (value & 0b0111_1111) | 0b1000_0000;
122 				value >>>= 7;
123 			}
124 			buffer ~= value & 0b0111_1111;
125 		}
126 	} else {
127 		buffer ~= nativeToBigEndian(value);
128 	}
129 }
130 
131 void encodeLength(size_t _length, ref ubyte[] buffer) {
132 	static if(is(size_t == uint)) {
133 		alias length = _length;
134 	} else {
135 		uint length = cast(uint)_length;
136 	}
137 	encodeType(length, buffer);
138 }
139 
140 void encodeArray(T)(T array, ref ubyte[] buffer) if(isArray!T) {
141 	alias E = typeof(T.init[0]);
142 	static if(is(typeof(E.sizeof)) && E.sizeof == 1) {
143 		buffer ~= cast(ubyte[])array;
144 	} else {
145 		foreach(element ; array) {
146 			encodeType(element, buffer);
147 		}
148 	}
149 }
150 
151 void encodeAddress(T)(T value, ref ubyte[] buffer) if(isIntegral!T) {
152 	buffer ~= nativeToBigEndian(value);
153 }
154 
155 T decodeType(T)(ubyte[] buffer, ref size_t index) {
156 	static if(isDynamicArray!T) {
157 		return decodeArray!T(decodeLength(buffer, index), buffer, index);
158 	} else static if(isArray!T) {
159 		return decodeArray!T(T.init.length, buffer, index);
160 	} else static if(isAssociativeArray!T) {
161 		T ret;
162 		foreach(i ; 0..decodeLength(buffer, index)) {
163 			ret[decodeType!(KeyType!T)(buffer, index)] = decodeType!(ValueType!T)(buffer, index);
164 		}
165 		return ret;
166 	} else static if(isTuple!T) {
167 		T ret;
168 		foreach(i, name; T.fieldNames) {
169 			mixin("ret." ~ name) = decodeType!(T.Types[i])(buffer, index);
170 		}
171 		return ret;
172 	} else static if(is(T == JSONValue)) {
173 		try {
174 			return parseJSON(decodeType!string(buffer, index));
175 		} catch(JSONException) {
176 			return JSONValue.init;
177 		}
178 	} else static if(is(T == UUID)) {
179 		return UUID(decodeType!(ubyte[16])(buffer, index));
180 	} else static if(is(T == Address)) {
181 		switch(decodeType!ubyte(buffer, index)) {
182 			case 4: return new InternetAddress(decodeAddress!uint(buffer, index), decodeAddress!ushort(buffer, index));
183 			case 6: return new Internet6Address(decodeType!(ubyte[16])(buffer, index), decodeAddress!ushort(buffer, index));
184 			default: return new UnknownAddress();
185 		}
186 	} else static if(T.sizeof == 1) {
187 		return cast(T)buffer[index++];
188 	} else static if(isIntegral!T) {
189 		// short, int, long
190 		static if(isSigned!T) {
191 			return cast(T)(decodeType!(Unsigned!T)(buffer, index) - 1);
192 		} else {
193 			T ret;
194 			size_t shift = 0;
195 			ubyte next;
196 			do {
197 				next = buffer[index++];
198 				ret |= ((next & 0b0111_1111) << shift);
199 				shift += 7;
200 			} while(next & 0b1000_0000);
201 			return ret;
202 		}
203 	} else {
204 		// float, double
205 		ubyte[T.sizeof] data = buffer[index..index+=T.sizeof];
206 		return bigEndianToNative!T(data);
207 	}
208 }
209 
210 size_t decodeLength(ubyte[] buffer, ref size_t index) {
211 	return decodeType!uint(buffer, index);
212 }
213 
214 T decodeArray(T)(size_t length, ubyte[] buffer, ref size_t index) if(isArray!T) {
215 	alias E = typeof(T.init[0]);
216 	static if(is(typeof(E.sizeof)) && E.sizeof == 1) {
217 		T ret = cast(E[])buffer[index..index+=length];
218 		return ret;
219 	} else {
220 		static if(isDynamicArray!T) auto ret = new E[length];
221 		else T ret;
222 		foreach(ref element ; ret) {
223 			element = decodeType!E(buffer, index);
224 		}
225 		return ret;
226 	}
227 }
228 
229 T decodeAddress(T)(ubyte[] buffer, ref size_t index) if(isIntegral!T) {
230 	ubyte[T.sizeof] data = buffer[index..index+=T.sizeof];
231 	return bigEndianToNative!T(data);
232 }
233 
234 unittest {
235 
236 	import std.conv;
237 
238 	ubyte[] encode(T)(T value) {
239 		ubyte[] buffer;
240 		encodeType(value, buffer);
241 		return buffer;
242 	}
243 
244 	T decode(T)(ubyte[] buffer) {
245 		size_t index = 0;
246 		return decodeType!T(buffer, index);
247 	}
248 
249 	// numbers
250 
251 	assert(encode(true) == [1]);
252 	assert(encode(ubyte(3)) == [3]);
253 	assert(encode(ushort(5)) == [5]);
254 	assert(encode(130u) == [130, 1]);
255 	assert(encode(12) == [13]);
256 	assert(encode(-1L) == [0]);
257 	assert(encode(2f) == [64, 0, 0, 0]);
258 
259 	assert(decode!bool([0]) == false);
260 	assert(decode!byte([255]) == -1);
261 	assert(decode!short([0]) == -1);
262 	assert(decode!uint([132, 1]) == 132);
263 	assert(decode!float([64, 64, 0, 0]) == 3);
264 
265 	// uuid
266 
267 	import std.uuid : randomUUID;
268 	auto uuid = randomUUID();
269 	assert(encode(uuid) == uuid.data);
270 	assert(decode!UUID(uuid.data) == uuid);
271 
272 	// json
273 
274 	import std.json : JSON_TYPE;
275 	assert(encode(JSONValue((JSONValue[string]).init)) == [2, '{', '}']);
276 	assert(decode!JSONValue([2, '{', '}']).type == JSON_TYPE.OBJECT);
277 	assert(decode!JSONValue([8, '[', '1', ',', '2', ',', ' ', '3', ']']) == JSONValue([1, 2, 3]));
278 	assert(decode!JSONValue([3, '{', '{', '}']).isNull);
279 
280 	// addresses
281 
282 	assert(encode(cast(Address)new InternetAddress("127.0.0.1", 0)) == [4, 127, 0, 0, 1, 0, 0]);
283 	assert(encode(cast(Address)new Internet6Address("::1", 80)) == [6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 80]);
284 	assert(encode(Address.init) == [0]);
285 
286 	assert(cast(UnknownAddress)decode!Address([0]));
287 	assert(decode!Address([4, 192, 168, 1, 1, 0, 80]) == new InternetAddress("192.168.1.1", 80));
288 	assert(decode!Address([6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).toString() == "[::]:0");
289 
290 	// dynamic arrays
291 
292 	assert(encode([1, 2, 3]) == [3, 2, 3, 4]);
293 	assert(encode(cast(ubyte[])[1, 2, 3]) == [3, 1, 2, 3]);
294 	assert(encode(["this", "is", "a", "string"]) == [4, 4, 't', 'h', 'i', 's', 2, 'i', 's', 1, 'a', 6, 's', 't', 'r', 'i', 'n', 'g']);
295 
296 	assert(decode!(bool[])([2, 0, 1]) == [false, true]);
297 	assert(decode!(uint[])([3, 0, 1, 2]) == [0, 1, 2]);
298 
299 	// static arrays
300 
301 	int[4] int_;
302 	string[2] string_;
303 	string_[1] = "$";
304 
305 	assert(encode(int_) == [1, 1, 1, 1]);
306 	assert(encode(string_) == [0, 1, '$']);
307 
308 	// associative arrays
309 
310 	assert(encode([0: 12u]) == [1, 1, 12]);
311 	assert(decode!(string[bool])([2, 1, 4, 't', 'r', 'u', 'e', 0, 5, 'f', 'a', 'l', 's', 'e']) == [true: "true", false: "false"]);
312 
313 	// tuples
314 
315 	import std.typecons : Tuple;
316 
317 	alias Test = Tuple!(Address, "address", int, "number");
318 
319 	assert(encode(Test.init) == [0, 1]);
320 	assert(decode!Test([4, 0, 0, 0, 0, 0, 80, 0]) == Test(new InternetAddress("0.0.0.0", 80), -1));
321 
322 }