1 /****************************************************************************** 2 3 Struct data serialization and deserialization tools 4 5 Used for plugin-based serialization and stream serialization. For binary 6 struct serialization use `ocean.util.serialize.contiguous` package. 7 8 Copyright: 9 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 10 All rights reserved. 11 12 License: 13 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 14 Alternatively, this file may be distributed under the terms of the Tango 15 3-Clause BSD License (see LICENSE_BSD.txt for details). 16 17 *******************************************************************************/ 18 19 module ocean.io.serialize.StructSerializer; 20 21 22 import ocean.meta.types.Qualifiers; 23 24 import ocean.io.serialize.SimpleStreamSerializer; 25 26 import ocean.core.Exception; 27 28 import ocean.io.model.IConduit: IOStream, InputStream, OutputStream; 29 30 import ocean.meta.traits.Basic; 31 import ocean.meta.traits.Indirections : hasIndirections; 32 import ocean.meta.types.Arrays : ElementTypeOf, StripAllArrays; 33 import ocean.meta.types.Typedef : castToBase; 34 35 import ocean.meta.codegen.Identifier; 36 37 38 39 /******************************************************************************* 40 41 SerializerException 42 43 *******************************************************************************/ 44 45 class SerializerException : Exception 46 { 47 mixin ReusableExceptionImplementation!(); 48 alias .LengthMismatch LengthMismatch; 49 } 50 51 /******************************************************************************* 52 53 StructSerializer Exception 54 Reusable exception instance. 55 56 *******************************************************************************/ 57 58 class LengthMismatch : SerializerException 59 { 60 size_t bytes_expected, bytes_got; 61 62 this ( size_t bytes_expected, size_t bytes_got, 63 istring msg, istring file, typeof(__LINE__) line ) 64 { 65 this.set(msg, file, line); 66 this.bytes_expected = bytes_expected; 67 this.bytes_got = bytes_got; 68 } 69 } 70 71 /******************************************************************************* 72 73 Reusable exception instance. 74 75 *******************************************************************************/ 76 77 private SerializerException serializer_exception; 78 79 static this () 80 { 81 .serializer_exception = new SerializerException(); 82 } 83 84 /******************************************************************************* 85 86 Struct serializer 87 88 Params: 89 AllowUnions = if true, unions will be serialized as raw bytes, without 90 checking whether the union contains dynamic arrays. Otherwise unions 91 cause a compile-time error. 92 93 TODO: proper union support -- must recurse into unions looking for dynamic 94 arrays 95 96 *******************************************************************************/ 97 98 struct StructSerializer ( bool AllowUnions = false ) 99 { 100 static: 101 102 /*************************************************************************** 103 104 Dumps/serializes the content of s and its array members, writing 105 serialized data to output. 106 107 Params: 108 s = struct instance (pointer) 109 output = output stream to write serialized data to 110 111 Returns: 112 number of bytes written 113 114 ***************************************************************************/ 115 116 size_t dump ( S ) ( S* s, OutputStream output ) 117 { 118 return dump(s, (void[] data) {SimpleStreamSerializer.transmit(output, data);}); 119 } 120 121 /*************************************************************************** 122 123 Dumps/serializes the content of s and its array members. 124 125 send is called repeatedly; on each call, it must store or forward the 126 provided data. 127 128 Params: 129 s = struct instance (pointer) 130 receive = receiving callback delegate 131 132 Returns: 133 number of bytes written 134 135 ***************************************************************************/ 136 137 size_t dump ( S ) ( S* s, scope void delegate ( void[] data ) receive ) 138 { 139 enforceStructPtr!("dump")(s); 140 return transmit!(false)(s, receive); 141 } 142 143 /*************************************************************************** 144 145 Loads/deserializes the content of s and its array members, reading 146 serialized data from input. 147 148 Params: 149 s = struct instance (pointer) 150 input = input stream to read data from 151 152 Returns: 153 number of bytes read 154 155 ***************************************************************************/ 156 157 size_t load ( S ) ( S* s, InputStream input ) 158 { 159 return load(s, (void[] data) {SimpleStreamSerializer.transmit(input, data);}); 160 } 161 162 /*************************************************************************** 163 164 Loads/deserializes the content of s and its array members. 165 166 receive is called repeatedly; on each call, it must populate the 167 provided data buffer with data previously produced by dump(). Data which 168 was populated once should not be populated again. So the delegate must 169 behave like a stream receive function. 170 171 Params: 172 s = struct instance (pointer) 173 receive = receiving callback delegate 174 175 Returns: 176 number of bytes read 177 178 ***************************************************************************/ 179 180 size_t load ( S ) ( S* s, scope void delegate ( void[] data ) receive ) 181 { 182 enforceStructPtr!("load")(s); 183 return transmit!(true)(s, receive); 184 } 185 186 /*************************************************************************** 187 188 Dumps/serializes or loads/deserializes the content of s and its 189 members. 190 191 transmit_data is called repeatedly; on each call, 192 - if receive is false, it must it must store or forward the provided 193 data; 194 - if receive is true, it must populate the provided data buffer with 195 data previously produced by dump(). Data which was populated once 196 should not be populated again. So the delegate must behave like a 197 stream receive function. 198 199 Params: 200 s = struct instance (pointer) 201 transmit_data = sending/receiving callback delegate 202 203 Returns: 204 number of bytes read or written 205 206 ***************************************************************************/ 207 208 size_t transmit ( bool receive, S ) ( S* s, scope void delegate ( void[] data ) transmit_data ) 209 { 210 .serializer_exception.enforce(s !is null, 211 typeof (this).stringof ~ ".transmit (receive = " ~ 212 receive.stringof ~ "): source pointer of type '" ~ S.stringof ~ 213 "*' is null"); 214 215 S s_copy = *s; 216 217 S* s_copy_ptr = &s_copy; 218 219 static if (receive) 220 { 221 transmit_data((cast (void*) s)[0 .. S.sizeof]); 222 223 copyReferences(s_copy_ptr, s); 224 } 225 else 226 { 227 resetReferences(s_copy_ptr); 228 229 transmit_data((cast (void*) s_copy_ptr)[0 .. S.sizeof]); 230 } 231 232 return S.sizeof + transmitArrays!(receive)(s, transmit_data); 233 } 234 235 236 /*************************************************************************** 237 238 Dumps/serializes the content of s and its array members, using the given 239 serializer object. The serializer object needs the following methods: 240 241 void open ( D, cstring name ); 242 243 void close ( D, cstring name ); 244 245 void serialize ( T ) ( D, ref T item, cstring name ); 246 247 void openStruct ( D, cstring name ); 248 249 void closeStruct ( D, cstring name ); 250 251 void serializeArray ( T ) ( D, cstring name, T[] array ); 252 253 Optional: 254 255 void serializeStaticArray ( T ) ( D, cstring name, T[] array ); 256 257 If this methond doesn't exist, serializeArray will be used. 258 259 void openStructArray ( T ) ( D, cstring name, T[] array ); 260 261 void closeStructArray ( T ) ( D, cstring name, T[] array ); 262 263 Unfortunately, as some of these methods are templates, it's not 264 possible to make an interface for it. But the compiler will let you know 265 whether a given serializer object is suitable or not 266 267 See ocean.io.serialize.JsonStructSerializer for an example. 268 269 Params: 270 S = type of struct to serialize 271 Serializer = type of serializer object 272 D = tuple of data parameters passed to the serializer 273 s = struct instance (pointer) 274 serializer = object to do the serialization 275 data = parameters for serializer 276 277 ***************************************************************************/ 278 279 public void serialize ( S, Serializer, D ... ) ( S* s, Serializer serializer, ref D data ) 280 { 281 serializer.open(data, S.stringof); 282 serialize_(s, serializer, data); 283 serializer.close(data, S.stringof); 284 } 285 286 287 /*************************************************************************** 288 289 Loads/deserializes the content of s and its array members, using the 290 given deserializer object. The deserializer object needs the following 291 methods: 292 293 void open ( ref Char[] input, cstring name ); 294 295 void close ( ); 296 297 void deserialize ( T ) ( ref T output, cstring name ); 298 299 void deserializeStruct ( ref T output, Char[] name, void delegate ( ) deserialize_struct ); 300 301 void deserializeArray ( T ) ( ref T[] output, Char[] name ); 302 303 void deserializeStaticArray ( T ) ( T[] output, Char[] name ); 304 305 void deserializeStructArray ( T ) ( ref T[] output, Char[] name, void delegate ( ref T ) deserialize_element ); 306 307 Unfortunately, as some of these methods are templates, it's not 308 possible to make an interface for it. But the compiler will let you know 309 whether a given deserializer object is suitable or not 310 311 See ocean.io.serialize.JsonStructDeserializer for an example. 312 313 Params: 314 s = struct instance (pointer) 315 deserializer = object to do the deserialization 316 data = input buffer to read serialized data from 317 318 ***************************************************************************/ 319 320 public void deserialize ( S, Deserializer, D ) ( S* s, Deserializer deserializer, D[] data ) 321 { 322 deserializer.open(data, S.stringof); 323 deserialize_(s, deserializer, data); 324 deserializer.close(); 325 } 326 327 /************************************************************************** 328 329 Resets all references in s to null. 330 331 Params: 332 s = struct instance (pointer) 333 334 ***************************************************************************/ 335 336 S* resetReferences ( S ) ( S* s ) 337 { 338 foreach (i, ref field; s.tupleof) 339 { 340 alias typeof(field) T; 341 342 static if (is (T == struct)) 343 { 344 resetReferences(&field); // recursive call 345 } 346 else static if (isReferenceType!(T)) 347 { 348 field = null; 349 } 350 } 351 352 return s; 353 } 354 355 /*************************************************************************** 356 357 Copies all references from dst to src. 358 359 Params: 360 src = source struct instance (pointer) 361 dst = destination struct instance (pointer) 362 363 ***************************************************************************/ 364 365 S* copyReferences ( S ) ( S* src, S* dst ) 366 { 367 foreach (i, ref src_field; src.tupleof) 368 { 369 alias typeof(src_field) T; 370 371 T* dst_field = &dst.tupleof[i]; 372 373 static if (is (T == struct)) 374 { 375 copyReferences(&src_field, dst_field); // recursive call 376 } 377 else static if (isReferenceType!(T)) 378 { 379 *dst_field = src_field; 380 } 381 } 382 383 return dst; 384 } 385 386 /*************************************************************************** 387 388 Transmits (sends or receives) the serialized data of all array fields in 389 s. 390 391 Template parameter: 392 receive = true: receive array data, false: send array data 393 394 Params: 395 s = struct instance (pointer) 396 transmit = sending/receiving callback delegate 397 398 Returns: 399 passes through return value of transmit 400 401 FIXME: Does currently not scan static array fields for a struct type 402 containing dynamic arrays. Example: 403 404 --- 405 struct S1 406 { 407 int[] x; 408 } 409 410 struct S2 411 { 412 413 S1[7] y; // elements contain a dynamic array 414 } 415 --- 416 417 ***************************************************************************/ 418 419 size_t transmitArrays ( bool receive, S ) ( S* s, scope void delegate ( void[] array ) transmit ) 420 { 421 size_t bytes = 0; 422 423 foreach (i, ref field; s.tupleof) 424 { 425 alias typeof(field) T; 426 427 static if (is (T == struct)) 428 { 429 bytes += transmitArrays!(receive)(&field, transmit); // recursive call 430 } 431 else static if (is (T U == U[])) 432 { 433 mixin AssertSupportedArray!(T, U, S, i); 434 435 bytes += transmitArray!(receive)(field, transmit); 436 } 437 else mixin AssertSupportedType!(T, S, i); 438 } 439 440 return bytes; 441 } 442 443 /*************************************************************************** 444 445 Transmits (sends or receives) the serialized data of array. That is, 446 first transmit the array content byte length as size_t value, then the 447 array content raw data. 448 449 Template parameter: 450 receive = true: receive array data, false: send array data 451 452 Params: 453 array = array to send serialized data of (pointer) 454 transmit_dg = sending/receiving callback delegate 455 456 Returns: 457 passes through return value of send 458 459 TODO: array needs to be duped 460 461 ***************************************************************************/ 462 463 size_t transmitArray ( bool receive, T ) ( ref T[] array, scope void delegate ( void[] data ) transmit_dg ) 464 { 465 size_t len, 466 bytes = len.sizeof; 467 468 static if (!receive) 469 { 470 len = array.length; 471 } 472 473 transmit_dg((cast (void*) &len)[0 .. len.sizeof]); 474 475 static if (receive) 476 { 477 array.length = len; 478 } 479 480 static if (is (T == struct)) // recurse into substruct 481 { // if it contains dynamic 482 enum RecurseIntoStruct = hasIndirections!(typeof (T.tupleof));// arrays 483 } 484 else 485 { 486 enum RecurseIntoStruct = false; 487 } 488 489 static if (is (T U == U[])) // recurse into subarray 490 { 491 foreach (ref element; array) 492 { 493 bytes += transmitArray!(receive)(element, transmit_dg); 494 } 495 } 496 else static if (RecurseIntoStruct) 497 { 498 debug ( StructSerializer ) 499 pragma (msg, typeof (this).stringof ~ ".transmitArray: " 500 ~ "array elements of struct type '" ~ T.stringof ~ 501 ~ "' contain subarrays"); 502 503 foreach (ref element; array) 504 { 505 bytes += transmit!(receive)(&element, transmit_dg); 506 } 507 } 508 else 509 { 510 size_t n = len * T.sizeof; 511 512 transmit_dg((cast (void*) array.ptr)[0 .. n]); 513 514 bytes += n; 515 } 516 517 return bytes; 518 } 519 520 /************************************************************************** 521 522 Dumps/serializes the content of s and its array members, using the given 523 serializer object. See the description of the dump() method above for a 524 full description of how the serializer object should behave. 525 526 Params: 527 S = type of struct to serialize 528 Serializer = type of serializer object 529 D = tuple of data parameters passed to the serializer 530 s = struct instance (pointer) 531 serializer = object to do the serialization 532 data = parameters for serializer 533 534 ***************************************************************************/ 535 536 private void serialize_ ( S, Serializer, D ... ) ( S* s, Serializer serializer, ref D data ) 537 { 538 foreach (i, ref field; s.tupleof) 539 { 540 alias typeof(field) T; 541 enum field_name = identifier!(S.tupleof[i]); 542 543 // imitate D1 style formatting for D2 typedef struct 544 static if ( is(T == struct) && !isTypedef!(T) ) 545 { 546 serializer.openStruct(data, field_name); 547 serialize_(&field, serializer, data); // recursive call 548 serializer.closeStruct(data, field_name); 549 } 550 else static if ( is(T U : U[]) ) 551 { 552 // slice array (passing a static array as ref is not allowed) 553 U[] array = field; 554 alias StripAllArrays!(U) Element; 555 556 // imitate D1 style formatting for arrays of D2 typedef structs 557 static if ( is(Element == struct) && !isTypedef!(Element) ) 558 { 559 serializeStructArray(array, field_name, serializer, data); 560 } 561 else static if ( 562 isArrayType!(T) == ArrayKind.Static 563 && is(typeof(serializer.serializeStaticArray!(T))) ) 564 { 565 serializer.serializeStaticArray(data, field_name, array); 566 } 567 else 568 { 569 serializer.serializeArray(data, field_name, array); 570 } 571 } 572 else 573 { 574 mixin AssertSupportedType!(T, S, i); 575 576 static if ( isTypedef!(T) == TypedefKind.Keyword ) 577 { 578 auto lvalue = castToBase(field); 579 serializer.serialize(data, lvalue, field_name); 580 } 581 else static if ( is(T B == enum) ) 582 { 583 auto lvalue = cast(B)(field); 584 serializer.serialize(data, lvalue, field_name); 585 } 586 else 587 { 588 serializer.serialize(data, field, field_name); 589 } 590 } 591 } 592 } 593 594 /*************************************************************************** 595 596 Dumps/serializes array which is expected to be a one- or multi- 597 dimensional array of structs, using the given serializer object. See the 598 description of the dump() method above for a full description of how the 599 serializer object should behave. 600 601 Params: 602 T = array base type, should be a struct or a (possibly 603 multi-dimensional) array of structs 604 Serializer = type of serializer object 605 D = tuple of data parameters passed to the serializer 606 array = array to serialize 607 field_name = the name of the struct field that contains the array 608 serializer = object to do the serialization 609 data = parameters for serializer 610 611 ***************************************************************************/ 612 613 private void serializeStructArray ( T, Serializer, D ... ) ( T[] array, 614 cstring field_name, Serializer serializer, ref D data ) 615 { 616 serializer.openStructArray(data, field_name, array); 617 618 foreach ( ref element; array ) 619 { 620 static if ( is(T U : U[]) ) 621 { 622 serializeStructArray(element, field_name, serializer, data); 623 } 624 else 625 { 626 static assert(is(T == struct)); 627 serializer.openStruct(data, T.stringof); 628 serialize_(&element, serializer, data); 629 serializer.closeStruct(data, T.stringof); 630 } 631 } 632 633 serializer.closeStructArray(data, field_name, array); 634 } 635 636 /*************************************************************************** 637 638 Loads/deserializes the content of s and its array members, using the 639 given deserializer object. See the description of the load() method 640 above for a full description of how the deserializer object should 641 behave. 642 643 Params: 644 s = struct instance (pointer) 645 deserializer = object to do the deserialization 646 data = input buffer to read serialized data from 647 648 ***************************************************************************/ 649 650 private void deserialize_ ( S, Deserializer, D ) ( S* s, Deserializer deserializer, D[] data ) 651 { 652 foreach (i, T; typeof (S.tupleof)) 653 { 654 T* field = &s.tupleof[i]; 655 auto field_name = identifier!(S.tupleof[i]); 656 657 static if ( is(T == struct) ) 658 { 659 deserializer.openStruct(data, field_name); 660 deserialize_(field, serializer, data); // recursive call 661 deserializer.closeStruct(data, field_name); 662 } 663 else static if ( is(T U : U[]) ) 664 { 665 static if ( is(U == struct) ) 666 { 667 deserializer.openStructArray(data, field_name, array); 668 foreach ( element; array ) 669 { 670 deserializer.openStruct(data, U.stringof); 671 deserialize_(&element, serializer, data); // recursive call 672 deserializer.closeStruct(data, U.stringof); 673 } 674 deserializer.closeStructArray(data, field_name, array); 675 } 676 else 677 { 678 static if ( isStaticArrayType!(T) ) 679 { 680 deserializer.deserializeStaticArray(*field, field_name); 681 } 682 else 683 { 684 deserializer.deserializeArray(*field, field_name); 685 } 686 } 687 } 688 else 689 { 690 mixin AssertSupportedType!(T, S, i); 691 692 static if ( isTypedef!(T) == TypedefKind.Keyword ) 693 { 694 mixin(` 695 else static if ( is(T B == typedef) ) 696 { 697 deserializer.deserialize(cast(B)(*field), field_name); 698 } 699 `); 700 } 701 else static if ( is(T B == enum) ) 702 { 703 deserializer.deserialize(cast(B)(*field), field_name); 704 } 705 else 706 { 707 deserializer.deserialize(*field, field_name); 708 } 709 } 710 } 711 } 712 713 /*************************************************************************** 714 715 Asserts s != null; s is assumed to be the struct source or destination 716 pointer. In addition a warning message is printed at compile time if 717 S is a pointer to a pointer. 718 The s != null checking is done in assert() fashion; that is, it is not 719 done in release mode. 720 721 Params: 722 func = invoking function (for message generation) 723 s = pointer to a source or destination struct; shall not be null 724 725 Throws: 726 Exception if s is null 727 728 ***************************************************************************/ 729 730 private void enforceStructPtr ( istring func, S ) ( S* s ) 731 { 732 static if (is (S T == T*)) 733 { 734 pragma (msg, typeof (this).stringof ~ '.' ~ func ~ " - warning: " 735 ~ "passing struct pointer argument of type '" ~ (S*).stringof ~ 736 ~ "' (you probably want '" ~ (T*).stringof ~ "')"); 737 } 738 739 .serializer_exception.enforce(s, typeof (this).stringof ~ '.' ~ func ~ ": " 740 ~ "pointer of type '" ~ S.stringof ~ "*' is null"); 741 } 742 743 /*************************************************************************** 744 745 Asserts that T, which is the type of the i-th field of S, is a supported 746 field type for struct serialization; typedefs and unions are currently 747 not supported. 748 Warns if T is an associative array. 749 750 Template parameters: 751 T = type to check 752 S = struct type (for message generation) 753 i = struct field index (for message generation) 754 755 ***************************************************************************/ 756 757 template AssertSupportedType ( T, S, size_t i ) 758 { 759 static assert (AllowUnions || !is (T == union), 760 typeof (this).stringof ~ ": unions are not supported, sorry " 761 ~ "(affects " ~ FieldInfo!(T, S, i) ~ ") -- use AllowUnions " 762 ~ "template flag to enable shallow serialization of unions"); 763 764 static if (isArrayType!(T) == ArrayKind.Associative) 765 pragma (msg, typeof (this).stringof ~ 766 ~ " - Warning: content of associative array will be discarded " 767 ~ "(affects " ~ FieldInfo!(T, S, i) ~ ')'); 768 } 769 770 /*************************************************************************** 771 772 Asserts that T, which is an array of U and the type of the i-th field of 773 S, is a supported array field type for struct serialization; 774 multi-dimensional arrays and arrays of reference types or structs are 775 currently not supported. 776 777 Template parameter: 778 T = type to check 779 U = element type of array type T 780 S = struct type (for message generation) 781 i = struct field index (for message generation) 782 783 ***************************************************************************/ 784 785 template AssertSupportedArray ( T, U, S, size_t i ) 786 { 787 static if (is (U V == V[])) 788 { 789 static assert (!isReferenceType!(V), typeof (this).stringof 790 ~ ": arrays of reference types are not supported, " 791 ~ "sorry (affects " ~ FieldInfo!(T, S, i) ~ ')'); 792 } 793 else 794 { 795 static assert (!isReferenceType!(U), typeof (this).stringof 796 ~ ": arrays of reference types are not supported, " 797 ~ "sorry (affects " ~ FieldInfo!(T, S, i) ~ ')'); 798 } 799 } 800 801 /*************************************************************************** 802 803 Generates a struct field information string for messages 804 805 ***************************************************************************/ 806 807 template FieldInfo ( T, S, size_t i ) 808 { 809 enum FieldInfo = '\'' ~ S.tupleof[i].stringof ~ "' of type '" ~ T.stringof ~ '\''; 810 } 811 } 812 813 814 /******************************************************************************* 815 816 Test for plugin serializer 817 818 *******************************************************************************/ 819 820 version (unittest) 821 { 822 import ocean.core.Test; 823 824 struct TestSerializer 825 { 826 import ocean.text.convert.Formatter; 827 828 void open ( ref char[] dst, cstring name ) 829 { 830 dst ~= "{"; 831 } 832 833 void close ( ref char[] dst, cstring name ) 834 { 835 dst ~= "}"; 836 } 837 838 void serialize ( T ) ( ref char[] dst, ref T item, cstring name ) 839 { 840 sformat(dst, "{} {}={} ", T.stringof, name, item); 841 } 842 843 void openStruct ( ref char[] dst, cstring name ) 844 { 845 dst ~= name ~ "={"; 846 } 847 848 void closeStruct ( ref char[] dst, cstring name ) 849 { 850 dst ~= "} "; 851 } 852 853 void serializeArray ( T ) ( ref char[] dst, cstring name, T[] array ) 854 { 855 static if ( is(T == char) ) 856 { 857 sformat(dst, "{}[] {}=\"{}\" ", T.stringof, name, array); 858 } 859 else 860 { 861 sformat(dst, "{}[] {}={} ", T.stringof, name, array); 862 } 863 } 864 865 void serializeStaticArray ( T ) ( ref char[] dst, cstring name, T[] array ) 866 { 867 sformat(dst, "{}[{}] {}={} ", T.stringof, array.length, name, array); 868 } 869 870 void openStructArray ( T ) ( ref char[] dst, cstring name, T[] array ) 871 { 872 dst ~= name ~ "={"; 873 } 874 875 void closeStructArray ( T ) ( ref char[] dst, cstring name, T[] array ) 876 { 877 dst ~= "} "; 878 } 879 } 880 } 881 882 unittest 883 { 884 struct TestStruct 885 { 886 mstring name; 887 int[] numbers; 888 int x; 889 float y; 890 struct InnerStruct 891 { 892 int z; 893 } 894 895 int[4] static_array; 896 InnerStruct a_struct; 897 InnerStruct[] some_structs; 898 } 899 900 TestStruct s; 901 s.name = "hello".dup; 902 s.numbers = [12, 23]; 903 s.some_structs.length = 2; 904 905 TestSerializer ser; 906 char[] dst; 907 StructSerializer!().serialize(&s, ser, dst); 908 test!("==")(dst, "{char[] name=\"hello\" int[] numbers=[12, 23] int x=0 float y=nan int[4] static_array=[0, 0, 0, 0] a_struct={int z=0 } some_structs={InnerStruct={int z=0 } InnerStruct={int z=0 } } }"[]); 909 } 910 911 912 /******************************************************************************* 913 914 Unittests 915 916 *******************************************************************************/ 917 918 version (unittest) 919 { 920 921 922 import core.stdc.time; 923 import ocean.util.Convert : to; 924 import ocean.time.StopWatch; 925 import core.memory; 926 debug ( OceanPerformanceTest ) import ocean.io.Stdout : Stderr; 927 928 /*************************************************************************** 929 930 Provides a growing container. It will overwrite the oldest entries as soon 931 as the maxLength is reached. 932 933 ***************************************************************************/ 934 935 struct CircularBuffer_ (T) 936 { 937 /*********************************************************************** 938 939 growing array of elements 940 941 ***********************************************************************/ 942 943 T[] elements; 944 945 /*********************************************************************** 946 947 maximum allowed size of the array 948 949 ***********************************************************************/ 950 951 size_t maxLength = 50; 952 953 /*********************************************************************** 954 955 current write position 956 957 ***********************************************************************/ 958 959 size_t write = 0; 960 961 /*********************************************************************** 962 963 Pushes an element on the Cache. If maxLength isn't reached, resizes 964 the cache. If it is reached, overwrites the oldest element 965 966 Params: 967 element = The element to push into the cache 968 969 ***********************************************************************/ 970 971 void push (T element) 972 { 973 if (this.elements.length == this.write) 974 { 975 if (this.elements.length < this.maxLength) 976 { 977 this.elements.length = this.elements.length + 1; 978 } 979 else 980 { 981 this.write = 0; 982 } 983 } 984 985 static if (isArrayType!(T)) 986 { 987 this.elements[this.write].length = element.length; 988 this.elements[this.write][] = element[]; 989 } 990 else 991 { 992 this.elements[this.write] = element; 993 } 994 995 ++this.write; 996 } 997 998 /*********************************************************************** 999 1000 Returns the offset-newest element. Defaults to 0 (the newest) 1001 1002 Params: 1003 offset = the offset-newest element. The higher this number, the 1004 older the returned element. Defaults to zero. (the newest 1005 element) 1006 1007 ***********************************************************************/ 1008 1009 T* get (size_t offset=0) 1010 { 1011 if (offset < this.elements.length) 1012 { 1013 if (cast(int)(this.write - 1 - offset) < 0) 1014 { 1015 return &elements[$ - offset + this.write - 1]; 1016 } 1017 1018 return &elements[this.write - 1 - offset]; 1019 } 1020 1021 throw new Exception("Element does not exist"); 1022 } 1023 } 1024 1025 alias CircularBuffer_!(char[]) Urls; 1026 1027 1028 /*************************************************************************** 1029 1030 Retargeting profile 1031 1032 ***************************************************************************/ 1033 1034 struct RetargetingAction 1035 { 1036 hash_t id; 1037 hash_t adpan_id; 1038 time_t lastseen; 1039 ubyte action; 1040 1041 1042 static RetargetingAction opCall(hash_t id,hash_t adpan_id,time_t lastseen, 1043 ubyte action) 1044 { 1045 1046 RetargetingAction a = { id,adpan_id,lastseen,action }; 1047 1048 return a; 1049 } 1050 } 1051 1052 /*************************************************************************** 1053 1054 Retargeting list 1055 1056 ***************************************************************************/ 1057 1058 alias CircularBuffer_!(RetargetingAction) Retargeting; 1059 1060 struct MeToo(int deep) 1061 { 1062 uint a; 1063 char[] jo; 1064 int[2] staticArray; 1065 static if(deep > 0) 1066 MeToo!(deep-1) rec; 1067 1068 static if(deep > 0) 1069 static MeToo opCall(uint aa, char[] jo, int sta, int stb,MeToo!(deep-1) rec) 1070 { 1071 MeToo a = {aa,jo,[sta,stb],rec}; 1072 return a; 1073 } 1074 else 1075 static MeToo!(0) opCall(uint aa, char[] jo, int sta, int stb,) 1076 { 1077 MeToo!(0) a = {aa,jo,[sta,stb]}; 1078 return a; 1079 } 1080 } 1081 }