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 }