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 }