1 /** 2 * Основной модуль сериализатора 3 * 4 * Copyright: (c) 2015-2017, Milofon Project. 5 * License: Subject to the terms of the BSD license, as written in the included LICENSE.txt file. 6 * Author: <m.galanin@milofon.org> Maksim Galanin 7 * Date: 2018-01-28 8 */ 9 10 module dango.service.serializer.core; 11 12 13 public 14 { 15 import proped : Properties; 16 } 17 18 19 private 20 { 21 import std.algorithm.comparison : max; 22 import std.traits : isSigned, isUnsigned, isBoolean, 23 isNumeric, isFloatingPoint, isArray, ForeachType, 24 isStaticArray; 25 import std.format : fmt = format; 26 import std.array : appender; 27 import std.exception : enforceEx; 28 import std.conv : to; 29 30 import vibe.data.serialization : 31 vSerialize = serialize, 32 vDeserialize = deserialize; 33 } 34 35 36 /** 37 * Основной класс сериализатор 38 */ 39 abstract class Serializer 40 { 41 /** 42 * Сериализация объекта языка в массив байт 43 * Params: 44 * object = Объект для преобразования 45 * Return: массив байт 46 */ 47 ubyte[] serializeObject(T)(T object) 48 { 49 return serialize(marshalObject!T(object)); 50 } 51 52 /** 53 * Десериализация массива байт в объект языка 54 * Params: 55 * bytes = Массив байт 56 * Return: T 57 */ 58 T deserializeObject(T)(ubyte[] bytes) 59 { 60 return unmarshalObject!T(deserialize(bytes)); 61 } 62 63 /** 64 * Десериализация массива байт в UniNode 65 * Params: 66 * bytes = Массив байт 67 * Return: UniNode 68 */ 69 UniNode deserialize(ubyte[] bytes); 70 71 /** 72 * Сериализация UniNode в массив байт 73 * Params: 74 * node = Данные в UniNode 75 * Return: массив байт 76 */ 77 ubyte[] serialize(UniNode node); 78 79 /** 80 * Инициализация сериализатора при помощи 81 * объекта настроек 82 * Params: 83 * config = Объект настроек 84 */ 85 void initialize(Properties config); 86 } 87 88 89 /** 90 * Преобразует объекты языка в UniNode 91 * Params: 92 * object = Объект для преобразования 93 * Return: UniNode 94 */ 95 UniNode marshalObject(T)(T object) 96 { 97 return vSerialize!(UniNodeSerializer, T)(object); 98 } 99 100 101 /** 102 * Преобразует UniNode в объекты языка 103 * Params: 104 * object = UniNode 105 * Return: T 106 */ 107 T unmarshalObject(T)(UniNode node) 108 { 109 return vDeserialize!(UniNodeSerializer, T, UniNode)(node); 110 } 111 112 113 /** 114 * Проверка на целочисленное знаковое число 115 */ 116 template isSignedNumeric(T) 117 { 118 enum isSignedNumeric = isNumeric!T && isSigned!T && !isFloatingPoint!T; 119 } 120 121 122 /** 123 * Проверка на целочисленное без знвковое число 124 */ 125 template isUnsignedNumeric(T) 126 { 127 enum isUnsignedNumeric = isNumeric!T && isUnsigned!T && !isFloatingPoint!T; 128 } 129 130 131 /** 132 * Проверка на соотвествие бинарным данным 133 */ 134 template isRawData(T) 135 { 136 enum isRawData = isArray!T && is(ForeachType!T == ubyte); 137 } 138 139 140 /** 141 * Универсальная структура для хранения данных 142 */ 143 struct UniNode 144 { 145 enum Type 146 { 147 nil, 148 boolean, 149 unsigned, 150 signed, 151 floating, 152 text, 153 raw, 154 array, 155 object 156 } 157 158 159 union Via 160 { 161 bool boolean; 162 ulong uinteger; 163 long integer; 164 real floating; 165 ubyte[] raw; 166 UniNode[] array; 167 UniNode[string] map; 168 } 169 170 171 Via via; 172 Type type; 173 174 175 this(Type type) 176 { 177 this.type = type; 178 } 179 180 181 this(typeof(null)) 182 { 183 this(Type.nil); 184 } 185 186 187 unittest 188 { 189 auto node = UniNode(); 190 assert (node.type == Type.nil); 191 } 192 193 194 this(bool value) 195 { 196 this(Type.boolean); 197 via.boolean = value; 198 } 199 200 201 unittest 202 { 203 auto node = UniNode(false); 204 assert (node.type == Type.boolean); 205 assert (node.get!bool == false); 206 } 207 208 209 this(T)(T value) if (isSignedNumeric!T) 210 { 211 this(Type.signed); 212 via.uinteger = value; 213 } 214 215 216 unittest 217 { 218 import std.meta : AliasSeq; 219 foreach (TT; AliasSeq!(byte, short, int, long)) 220 { 221 TT v = -11; 222 auto node = UniNode(v); 223 assert (node.type == Type.signed); 224 assert (is (typeof(node.get!TT) == TT)); 225 assert (node.get!TT == -11); 226 } 227 } 228 229 230 this(T)(T value) if (isUnsignedNumeric!T) 231 { 232 this(Type.unsigned); 233 via.uinteger = value; 234 } 235 236 237 unittest 238 { 239 import std.meta : AliasSeq; 240 foreach (TT; AliasSeq!(ubyte, ushort, uint, ulong)) 241 { 242 TT v = 11; 243 auto node = UniNode(v); 244 assert (node.type == Type.unsigned); 245 assert (is (typeof(node.get!TT) == TT)); 246 assert (node.get!TT == 11); 247 } 248 } 249 250 251 this(T)(T value) if (isFloatingPoint!T) 252 { 253 this(Type.floating); 254 via.floating = value; 255 } 256 257 258 unittest 259 { 260 import std.meta : AliasSeq; 261 foreach (TT; AliasSeq!(float, double)) 262 { 263 TT v = 11.11; 264 auto node = UniNode(v); 265 assert (node.type == Type.floating); 266 assert (is (typeof(node.get!TT) == TT)); 267 assert (node.get!TT == cast(TT)11.11); 268 } 269 } 270 271 272 this(T)(T value) if (isRawData!T) 273 { 274 this(Type.raw); 275 static if (isStaticArray!T) 276 via.raw = value.dup; 277 else 278 via.raw = value; 279 } 280 281 282 unittest 283 { 284 ubyte[] dynArr = [1, 2, 3]; 285 auto node = UniNode(dynArr); 286 assert (node.type == Type.raw); 287 assert (is(typeof(node.get!(ubyte[])) == ubyte[])); 288 assert (node.get!(ubyte[]) == [1, 2, 3]); 289 290 ubyte[3] stArr = [1, 2, 3]; 291 node = UniNode(stArr); 292 assert (node.type == Type.raw); 293 assert (is(typeof(node.get!(ubyte[3])) == ubyte[3])); 294 assert (node.get!(ubyte[3]) == [1, 2, 3]); 295 } 296 297 298 this(string value) 299 { 300 this(Type.text); 301 via.raw = cast(ubyte[])value; 302 } 303 304 305 unittest 306 { 307 string str = "hello"; 308 auto node = UniNode(str); 309 assert(node.type == Type.text); 310 assert (is(typeof(node.get!(string)) == string)); 311 assert (node.get!(string) == "hello"); 312 } 313 314 315 this(UniNode[] value) 316 { 317 this(Type.array); 318 via.array = value; 319 } 320 321 322 this(UniNode[string] value) 323 { 324 this(Type.object); 325 via.map = value; 326 } 327 328 329 static UniNode emptyObject() @property 330 { 331 return UniNode(cast(UniNode[string])null); 332 } 333 334 335 static UniNode emptyArray() @property 336 { 337 return UniNode(cast(UniNode[])null); 338 } 339 340 341 static Type typeId(T)() @property 342 { 343 static if( is(T == typeof(null)) ) return Type.nil; 344 else static if( is(T == bool) ) return Type.boolean; 345 else static if( isFloatingPoint!T ) return Type.floating; 346 else static if( isSignedNumeric!T ) return Type.signed; 347 else static if( isUnsignedNumeric!T ) return Type.unsigned; 348 else static if( isRawData!T ) return Type.raw; 349 else static if( is(T == string) ) return Type.text; 350 else static if( is(T == UniNode[]) ) return Type.array; 351 else static if( is(T == UniNode[string]) ) return Type.object; 352 else static assert(false, "Unsupported UniNode type '"~T.stringof 353 ~"'. Only bool, long, ulong, double, string, ubyte[], UniNode[] and UniNode[string] are allowed."); 354 } 355 356 357 inout(T) get(T)() @property inout @trusted 358 { 359 static if (is(T == string) || isRawData!T) 360 checkType!(T, ubyte[], typeof(null))(); 361 else static if(isNumeric!T && (isSigned!T || isUnsigned!T) && !isFloatingPoint!T) 362 checkType!(long, ulong)(); 363 else 364 checkType!T(); 365 366 static if (is(T == bool)) return via.boolean; 367 else static if (is(T == double)) return via.floating; 368 else static if (is(T == float)) return cast(T)via.floating; 369 else static if (isSigned!T) return cast(T)via.integer; 370 else static if (isUnsigned!T) return cast(T)via.uinteger; 371 else static if (is(T == string)) 372 { 373 if (type == Type.nil) 374 return ""; 375 else 376 return cast(T)via.raw; 377 } 378 else static if (isRawData!T) 379 { 380 if (type == Type.nil) 381 { 382 T ret; 383 return cast(inout(T))ret; 384 } 385 static if (isStaticArray!T) 386 return cast(inout(T))via.raw[0..T.length]; 387 else 388 return cast(inout(T))via.raw; 389 } 390 else static if (is(T == UniNode[])) return via.array; 391 else static if (is(T == UniNode[string])) return via.map; 392 } 393 394 395 ref inout(UniNode) opIndex(size_t idx) inout 396 { 397 checkType!(UniNode[])(); 398 return via.array[idx]; 399 } 400 401 402 ref UniNode opIndex(string key) 403 { 404 checkType!(UniNode[string])(); 405 if (auto pv = key in via.map) 406 return *pv; 407 408 via.map[key] = UniNode(); 409 return via.map[key]; 410 } 411 412 413 void appendArrayElement(UniNode element) 414 { 415 enforceUniNode(type == Type.array, "'appendArrayElement' only allowed for array types, not " 416 ~.to!string(type)~"."); 417 via.array ~= element; 418 } 419 420 421 string toString() 422 { 423 auto buff = appender!string; 424 425 void fun(ref UniNode node) 426 { 427 switch (node.type) 428 { 429 case Type.nil: 430 buff.put("nil"); 431 break; 432 case Type.boolean: 433 buff.put("bool("~node.get!bool.to!string~")"); 434 break; 435 case Type.unsigned: 436 buff.put("unsigned("~node.get!ulong.to!string~")"); 437 break; 438 case Type.signed: 439 buff.put("signed("~node.get!long.to!string~")"); 440 break; 441 case Type.floating: 442 buff.put("floating("~node.get!double.to!string~")"); 443 break; 444 case Type.text: 445 buff.put("text("~node.get!string.to!string~")"); 446 break; 447 case Type.raw: 448 buff.put("raw("~node.get!(ubyte[]).to!string~")"); 449 break; 450 case Type.object: 451 { 452 buff.put("{"); 453 size_t len = node.via.map.length; 454 size_t count; 455 foreach (k, v; node.via.map) 456 { 457 count++; 458 buff.put(k ~ ":"); 459 fun(v); 460 if (count < len) 461 buff.put(", "); 462 } 463 buff.put("}"); 464 break; 465 } 466 case Type.array: 467 { 468 buff.put("["); 469 size_t len = node.via.array.length; 470 foreach (i, v; node.via.array) 471 { 472 fun(v); 473 if (i < len) 474 buff.put(", "); 475 } 476 buff.put("]"); 477 break; 478 } 479 default: 480 buff.put("undefined"); 481 break; 482 } 483 } 484 485 fun(this); 486 return buff.data; 487 } 488 489 490 private : 491 492 void checkType(TYPES...)(string op = null) const 493 { 494 bool matched = false; 495 foreach (T; TYPES) 496 { 497 if (type == typeId!T) 498 matched = true; 499 } 500 501 if (matched) 502 return; 503 504 string expected; 505 static if (TYPES.length == 1) 506 expected = typeId!(TYPES[0]).to!string; 507 else 508 { 509 foreach (T; TYPES) 510 { 511 if (expected.length > 0) 512 expected ~= ", "; 513 expected ~= typeId!T.to!string; 514 } 515 } 516 517 string name = "UniNode of type " ~ type.to!string; 518 if (!op.length) 519 throw new UniNodeException("Got %s, expected %s.".fmt(name, expected)); 520 else 521 throw new UniNodeException("Got %s, expected %s for %s.".fmt(name, expected, op)); 522 } 523 } 524 525 526 527 struct UniNodeSerializer 528 { 529 enum isSupportedValueType(T) = is(T == typeof(null)) 530 || isFloatingPoint!T 531 || isBoolean!T 532 || isRawData!T 533 || (isNumeric!T && (isSigned!T || isUnsigned!T)) 534 || is(T == string) 535 || is(T == UniNode); 536 537 private 538 { 539 UniNode _current; 540 UniNode[] _stack; 541 } 542 543 544 @disable this(this); 545 546 547 this(UniNode data) @safe 548 { 549 _current = data; 550 } 551 552 553 // serialization 554 UniNode getSerializedResult() 555 { 556 return _current; 557 } 558 559 560 void beginWriteDictionary(TypeTraits)() 561 { 562 _stack ~= UniNode.emptyObject(); 563 } 564 565 566 void endWriteDictionary(TypeTraits)() 567 { 568 _current = _stack[$-1]; 569 _stack.length--; 570 } 571 572 573 void beginWriteDictionaryEntry(ElementTypeTraits)(string name) {} 574 575 576 void endWriteDictionaryEntry(ElementTypeTraits)(string name) 577 { 578 _stack[$-1][name] = _current; 579 } 580 581 582 void beginWriteArray(TypeTraits)(size_t length) 583 { 584 _stack ~= UniNode.emptyArray(); 585 } 586 587 588 void endWriteArray(TypeTraits)() 589 { 590 _current = _stack[$-1]; 591 _stack.length--; 592 } 593 594 595 void beginWriteArrayEntry(ElementTypeTraits)(size_t index) {} 596 597 598 void endWriteArrayEntry(ElementTypeTraits)(size_t index) 599 { 600 _stack[$-1].appendArrayElement(_current); 601 } 602 603 604 void writeValue(TypeTraits, T)(T value) if (!is(T == UniNode)) 605 { 606 _current = UniNode(value); 607 } 608 609 610 // deserialization 611 void readDictionary(TypeTraits)(scope void delegate(string) entry_callback) 612 { 613 enforceUniNode(_current.type == UniNode.Type.object, "Expected UniNode object"); 614 auto old = _current; 615 foreach (string key, value; _current.get!(UniNode[string])) 616 { 617 _current = value; 618 entry_callback(key); 619 } 620 _current = old; 621 } 622 623 624 void beginReadDictionaryEntry(ElementTypeTraits)(string) {} 625 626 627 void endReadDictionaryEntry(ElementTypeTraits)(string) {} 628 629 630 void readArray(TypeTraits)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback) 631 { 632 enforceUniNode(_current.type == UniNode.Type.array, "Expected UniNode array"); 633 auto old = _current; 634 UniNode[] arr = old.get!(UniNode[]); 635 size_callback(arr.length); 636 foreach (ent; arr) 637 { 638 _current = ent; 639 entry_callback(); 640 } 641 _current = old; 642 } 643 644 645 void beginReadArrayEntry(ElementTypeTraits)(size_t index) {} 646 647 648 void endReadArrayEntry(ElementTypeTraits)(size_t index) {} 649 650 651 T readValue(TypeTraits, T)() @safe 652 { 653 static if (is(T == UniNode)) 654 return _current; 655 else static if (is(T == float) || is(T == double)) 656 { 657 switch (_current.type) 658 { 659 default: 660 return cast(T)_current.get!long; 661 case UniNode.Type.nil: 662 goto case; 663 case UniNode.Type.floating: 664 return cast(T)_current.get!double; 665 } 666 } 667 else 668 return _current.get!T(); 669 } 670 671 672 bool tryReadNull(TypeTraits)() 673 { 674 return _current.type == UniNode.Type.nil; 675 } 676 } 677 678 679 unittest 680 { 681 struct FD 682 { 683 int a; 684 ubyte[3] vector; 685 } 686 687 FD fd = FD(1, [1, 2, 3]); 688 auto data = marshalObject(fd); 689 assert(data.type == UniNode.Type.object); 690 } 691 692 693 class UniNodeException : Exception 694 { 695 this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 696 { 697 super(msg, file, line, next); 698 } 699 } 700 701 702 alias enforceUniNode = enforceEx!UniNodeException;