1 module xserial.serial; 2 3 import std.system : Endian, endian; 4 import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs; 5 6 import xbuffer.buffer : canSwapEndianness, Buffer, BufferOverflowException; 7 import xbuffer.varint : isVar; 8 9 import xserial.attribute; 10 11 /** 12 * Serializes some data. 13 */ 14 ubyte[] serialize(Endian endianness, L, T)(T value, Buffer buffer) { 15 serializeImpl!(endianness, L, T)(buffer, value); 16 return buffer.data!ubyte; 17 } 18 19 /// ditto 20 ubyte[] serialize(Endian endianness, L, T)(T value) { 21 return serialize!(endianness, L, T)(value, new Buffer(64)); 22 } 23 24 /// ditto 25 ubyte[] serialize(Endian endianness, T)(T value, Buffer buffer) { 26 return serialize!(endianness, size_t)(value, buffer); 27 } 28 29 /// ditto 30 ubyte[] serialize(Endian endianness, T)(T value) { 31 return serialize!(endianness, size_t)(value); 32 } 33 34 /// ditto 35 ubyte[] serialize(T)(T value, Buffer buffer) { 36 return serialize!(endian, size_t, T)(value, buffer); 37 } 38 39 /// ditto 40 ubyte[] serialize(T)(T value) { 41 return serialize!(endian, size_t, T)(value); 42 } 43 44 /** 45 * Deserializes some data. 46 */ 47 T deserialize(T, Endian endianness, L)(Buffer buffer) { 48 return deserializeImpl!(endianness, L, T)(buffer); 49 } 50 51 /// ditto 52 T deserialize(T, Endian endianness, L)(in ubyte[] buffer) { 53 return deserialize!(T, endianness, L)(new Buffer(buffer)); 54 } 55 56 /// ditto 57 T deserialize(T, Endian endianness)(Buffer buffer) { 58 return deserialize!(T, endianness, size_t)(buffer); 59 } 60 61 /// ditto 62 T deserialize(T, Endian endianness)(in ubyte[] buffer) { 63 return deserialize!(T, endianness, size_t)(buffer); 64 } 65 66 /// ditto 67 T deserialize(T)(Buffer buffer) { 68 return deserialize!(T, endian, size_t)(buffer); 69 } 70 71 /// ditto 72 T deserialize(T)(in ubyte[] buffer) { 73 return deserialize!(T, endian, size_t)(buffer); 74 } 75 76 // ----------- 77 // common data 78 // ----------- 79 80 enum EndianType { 81 82 bigEndian = cast(int)Endian.bigEndian, 83 littleEndian = cast(int)Endian.littleEndian, 84 var, 85 86 } 87 88 template Members(T, alias Only) { 89 90 import std.typetuple : TypeTuple; 91 92 mixin({ 93 94 string ret = "alias Members = TypeTuple!("; 95 foreach(member ; __traits(derivedMembers, T)) { 96 static if(is(typeof(mixin("T." ~ member)))) { 97 mixin("alias M = typeof(T." ~ member ~ ");"); 98 static if( 99 isType!M && 100 !isCallable!M && 101 !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members 102 !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) && // properties 103 !hasUDA!(__traits(getMember, T, member), Exclude) && 104 !hasUDA!(__traits(getMember, T, member), Only) 105 ) { 106 ret ~= `"` ~ member ~ `",`; 107 108 } 109 } 110 } 111 return ret ~ ");"; 112 113 }()); 114 115 } 116 117 // ------------- 118 // serialization 119 // ------------- 120 121 void serializeImpl(Endian endianness, L, T)(Buffer buffer, T value) { 122 static if(isVar!L) serializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer, value); 123 else serializeImpl!(cast(EndianType)endianness, L, cast(EndianType)endianness, L, cast(EndianType)endianness, T)(buffer, value); 124 } 125 126 void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer, T value) { 127 static if(isArray!T) { 128 static if(isDynamicArray!T) serializeLength!(cle, CL)(buffer, value.length); 129 serializeArray!(endianness, OL, ole)(buffer, value); 130 } else static if(isAssociativeArray!T) { 131 serializeLength!(cle, CL)(buffer, value.length); 132 serializeAssociativeArray!(endianness, OL, ole)(buffer, value); 133 } else static if(is(T == class) || is(T == struct)) { 134 serializeMembers!(endianness, OL, ole)(buffer, value); 135 } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { 136 serializeNumber!endianness(buffer, value); 137 } else { 138 static assert(0, "Cannot serialize " ~ T.stringof); 139 } 140 } 141 142 void serializeNumber(EndianType endianness, T)(Buffer buffer, T value) { 143 static if(endianness == EndianType.var) { 144 static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); 145 buffer.writeVar!T(value); 146 } else static if(endianness == EndianType.bigEndian) { 147 buffer.write!(Endian.bigEndian, T)(value); 148 } else static if(endianness == EndianType.littleEndian) { 149 buffer.write!(Endian.littleEndian, T)(value); 150 } 151 } 152 153 void serializeLength(EndianType endianness, L)(Buffer buffer, size_t length) { 154 static if(L.sizeof < size_t.sizeof) serializeNumber!(endianness, L)(buffer, cast(L)length); 155 else serializeNumber!(endianness, L)(buffer, length); 156 } 157 158 void serializeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isArray!T) { 159 static if(canSwapEndianness!(ForeachType!T) && !is(ForeachType!T == struct) && !is(ForeachType!T == class) && endianness != EndianType.var) { 160 buffer.write!(cast(Endian)endianness)(array); 161 } else { 162 foreach(value ; array) { 163 serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value); 164 } 165 } 166 } 167 168 void serializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isAssociativeArray!T) { 169 foreach(key, value; array) { 170 serializeImpl!(endianness, OL, ole, OL, ole)(buffer, key); 171 serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value); 172 } 173 } 174 175 void serializeMembers(EndianType endianness, L, EndianType le, T)(Buffer __buffer, T __container) { 176 foreach(member ; Members!(T, DecodeOnly)) { 177 178 mixin("alias M = typeof(__container." ~ member ~ ");"); 179 180 static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) { 181 static if(is(uda : Custom!C, C)) { 182 enum __custom = true; 183 uda.C.serialize(mixin("__container." ~ member), __buffer); 184 } 185 } 186 187 static if(!is(typeof(__custom))) mixin({ 188 189 static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) { 190 import std.conv : to; 191 auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0]; 192 immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); 193 } else { 194 immutable e = "L, le, L, le"; 195 } 196 197 static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M)(__buffer, __container." ~ member ~ ");"; 198 else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; 199 else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; 200 else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; 201 else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; 202 203 static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; 204 else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; 205 206 }()); 207 208 } 209 } 210 211 // --------------- 212 // deserialization 213 // --------------- 214 215 T deserializeImpl(Endian endianness, L, T)(Buffer buffer) { 216 static if(isVar!L) return deserializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer); 217 else return deserializeImpl!(cast(EndianType)endianness, L, cast(EndianType)endianness, L, cast(EndianType)endianness, T)(buffer); 218 } 219 220 T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer) { 221 static if(isStaticArray!T) { 222 return deserializeStaticArray!(endianness, OL, ole, T)(buffer); 223 } else static if(isDynamicArray!T) { 224 return deserializeDynamicArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer)); 225 } else static if(isAssociativeArray!T) { 226 return deserializeAssociativeArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer)); 227 } else static if(is(T == class)) { 228 T ret = new T(); 229 deserializeMembers!(endianness, OL, ole)(buffer, ret); 230 return ret; 231 } else static if(is(T == struct)) { 232 T ret; 233 deserializeMembers!(endianness, OL, ole)(buffer, &ret); 234 return ret; 235 } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { 236 return deserializeNumber!(endianness, T)(buffer); 237 } else { 238 static assert(0, "Cannot deserialize " ~ T.stringof); 239 } 240 } 241 242 T deserializeNumber(EndianType endianness, T)(Buffer buffer) { 243 static if(endianness == EndianType.var) { 244 static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); 245 return buffer.readVar!T(); 246 } else static if(endianness == EndianType.bigEndian) { 247 return buffer.read!(Endian.bigEndian, T)(); 248 } else static if(endianness == EndianType.littleEndian) { 249 return buffer.read!(Endian.littleEndian, T)(); 250 } 251 } 252 253 size_t deserializeLength(EndianType endianness, L)(Buffer buffer) { 254 static if(L.sizeof > size_t.sizeof) return cast(size_t)deserializeNumber!(endianness, L)(buffer); 255 else return deserializeNumber!(endianness, L)(buffer); 256 } 257 258 T deserializeStaticArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isStaticArray!T) { 259 T ret; 260 foreach(ref value ; ret) { 261 value = deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); 262 } 263 return ret; 264 } 265 266 T deserializeDynamicArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isDynamicArray!T) { 267 T ret; 268 foreach(i ; 0..length) { 269 ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); 270 } 271 return ret; 272 } 273 274 T deserializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isAssociativeArray!T) { 275 T ret; 276 foreach(i ; 0..length) { 277 ret[deserializeImpl!(endianness, OL, ole, OL, ole, KeyType!T)(buffer)] = deserializeImpl!(endianness, OL, ole, OL, ole, ValueType!T)(buffer); 278 } 279 return ret; 280 } 281 282 T deserializeNoLengthArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isDynamicArray!T) { 283 T ret; 284 try { 285 while(true) ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); 286 } catch(BufferOverflowException) {} 287 return ret; 288 } 289 290 void deserializeMembers(EndianType endianness, L, EndianType le, C)(Buffer __buffer, C __container) { 291 static if(isPointer!C) alias T = typeof(*__container); 292 else alias T = C; 293 foreach(member ; Members!(T, EncodeOnly)) { 294 295 mixin("alias M = typeof(__container." ~ member ~ ");"); 296 297 static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) { 298 static if(is(uda : Custom!C, C)) { 299 enum __custom = true; 300 mixin("__container." ~ member) = uda.C.deserialize(__buffer); 301 } 302 } 303 304 static if(!is(typeof(__custom))) mixin({ 305 306 static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) { 307 import std.conv : to; 308 auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0]; 309 immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); 310 } else { 311 immutable e = "L, le, L, le"; 312 } 313 314 static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M)(__buffer);"; 315 else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer);"; 316 else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer);"; 317 else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer);"; 318 else immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M)(__buffer);"; 319 320 static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; 321 else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; 322 323 }()); 324 325 } 326 } 327 328 // --------- 329 // unittests 330 // --------- 331 332 @("numbers") unittest { 333 334 // bools and numbers 335 336 assert(true.serialize() == [1]); 337 assert(5.serialize!(Endian.bigEndian)() == [0, 0, 0, 5]); 338 339 assert(deserialize!(int, Endian.bigEndian)([0, 0, 0, 5]) == 5); 340 341 version(LittleEndian) assert(12.serialize() == [12, 0, 0, 0]); 342 version(BigEndian) assert(12.serialize() == [0, 0, 0, 12]); 343 344 } 345 346 @("arrays") unittest { 347 348 assert([1, 2, 3].serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); 349 assert([1, 2, 3].serialize().deserialize!(int[])() == [1, 2, 3]); 350 351 ushort[2] test1 = [1, 2]; 352 assert(test1.serialize!(Endian.bigEndian)() == [0, 1, 0, 2]); 353 test1 = deserialize!(ushort[2], Endian.littleEndian)([2, 0, 1, 0]); 354 assert(test1 == [2, 1]); 355 356 } 357 358 @("associative arrays") unittest { 359 360 // associative arrays 361 362 int[ushort] test; 363 test[1] = 112; 364 assert(test.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 1, 0, 1, 0, 0, 0, 112]); 365 366 test = deserialize!(int[ushort], Endian.bigEndian, ubyte)([1, 0, 0, 0, 0, 0, 55]); 367 assert(test == [ushort(0): 55]); 368 369 } 370 371 @("structs and classes") unittest { 372 373 struct Test { 374 375 byte a, b, c; 376 377 } 378 379 Test test = Test(1, 3, 55); 380 assert(test.serialize() == [1, 3, 55]); 381 382 assert(deserialize!Test([1, 3, 55]) == test); 383 384 } 385 386 @("attributes") unittest { 387 388 struct Test1 { 389 390 @BigEndian int a; 391 392 @EncodeOnly @LittleEndian ushort b; 393 394 @Condition("a==1") @Var uint c; 395 396 @DecodeOnly @Var uint d; 397 398 @Exclude ubyte e; 399 400 } 401 402 Test1 test1 = Test1(1, 2, 3, 4, 5); 403 assert(test1.serialize() == [0, 0, 0, 1, 2, 0, 3]); 404 assert(deserialize!Test1([0, 0, 0, 1, 4, 12]) == Test1(1, 0, 4, 12)); 405 406 test1.a = 0; 407 assert(test1.serialize() == [0, 0, 0, 0, 2, 0]); 408 assert(deserialize!Test1([0, 0, 0, 0, 0, 0, 0, 0]) == Test1(0, 0, 0, 0)); 409 410 struct Test2 { 411 412 ubyte[] a; 413 414 @Length!ushort ushort[] b; 415 416 @NoLength uint[] c; 417 418 } 419 420 Test2 test2 = Test2([1, 2], [3, 4], [5, 6]); 421 assert(test2.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6]); 422 assert(deserialize!(Test2, Endian.bigEndian, uint)([0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 1]) == test2); 423 424 struct Test3 { 425 426 @EndianLength!ushort(Endian.littleEndian) @LittleEndian ushort[] a; 427 428 @NoLength ushort[] b; 429 430 } 431 432 Test3 test3 = Test3([1, 2], [3, 4]); 433 assert(test3.serialize!(Endian.bigEndian)() == [2, 0, 1, 0, 2, 0, 0, 3, 0, 4]); 434 435 struct Test4 { 436 437 ubyte a; 438 439 @LittleEndian uint b; 440 441 } 442 443 struct Test5 { 444 445 @Length!ubyte Test4[] a; 446 447 @NoLength Test4[] b; 448 449 } 450 451 Test5 test5 = Test5([Test4(1, 2)], [Test4(1, 2), Test4(3, 4)]); 452 assert(test5.serialize() == [1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]); 453 assert(deserialize!Test5([1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]) == test5); 454 455 } 456 457 @("using buffer") unittest { 458 459 Buffer buffer = new Buffer(64); 460 461 serialize(ubyte(55), buffer); 462 assert(buffer.data.length == 1); 463 assert(buffer.data!ubyte == [55]); 464 465 assert(deserialize!ubyte(buffer) == 55); 466 assert(buffer.data.length == 0); 467 468 }