comparison orange/serialization/Serializer.d @ 26:78e5fef4bbf2 experimental

Third step in refactoring the API. Stating to add unit tests.
author Jacob Carlborg <doob@me.com>
date Tue, 19 Oct 2010 10:22:10 +0200
parents b51e953f79eb
children fc315d786f24
comparison
equal deleted inserted replaced
25:b51e953f79eb 26:78e5fef4bbf2
13 { 13 {
14 import std.conv; 14 import std.conv;
15 alias ConvError ConversionException; 15 alias ConvError ConversionException;
16 } 16 }
17 17
18 import orange.core._;
18 import orange.serialization._; 19 import orange.serialization._;
19 import orange.serialization.archives._; 20 import orange.serialization.archives._;
20 import orange.util._; 21 import orange.util._;
21 22
22 private 23 private
29 deserializing 30 deserializing
30 } 31 }
31 32
32 alias Mode.serializing serializing; 33 alias Mode.serializing serializing;
33 alias Mode.deserializing deserializing; 34 alias Mode.deserializing deserializing;
35
36 private char toUpper (char c)
37 {
38 if (c >= 'a' && c <= 'z')
39 return c - 32;
40
41 return c;
42 }
34 } 43 }
35 44
36 class Serializer 45 class Serializer
37 { 46 {
38 alias void delegate (ArchiveException exception, IArchive.IDataType data) ErrorCallback; 47 alias void delegate (ArchiveException exception, string[] data) ErrorCallback;
39 alias IArchive.IDataType DataType; 48 alias UntypedData Data;
49 alias size_t Id;
40 50
41 private 51 private
42 { 52 {
43 ErrorCallback errorCallback_; 53 ErrorCallback errorCallback_;
44 IArchive archive; 54 IArchive archive;
45 55
46 size_t keyCounter; 56 size_t keyCounter;
47 size_t idCounter; 57 Id idCounter;
48 58
49 RegisterBase[string] serializers; 59 RegisterBase[string] serializers;
50 RegisterBase[string] deserializers; 60 RegisterBase[string] deserializers;
61
62 Id[void*] serializedReferences;
63 void*[Id] deserializedReferences;
64
65 Array[Id] serializedArrays;
66 void[][Id] deserializedSlices;
51 67
52 bool hasBegunSerializing; 68 bool hasBegunSerializing;
53 bool hasBegunDeserializing; 69 bool hasBegunDeserializing;
54 70
55 void delegate (ArchiveException exception, DataType data) throwOnErrorCallback; 71 void delegate (ArchiveException exception, string[] data) throwOnErrorCallback;
56 void delegate (ArchiveException exception, DataType data) doNothingOnErrorCallback; 72 void delegate (ArchiveException exception, string[] data) doNothingOnErrorCallback;
57 } 73 }
58 74
59 this (IArchive archive) 75 this (IArchive archive)
60 { 76 {
61 this.archive = archive; 77 this.archive = archive;
62 78
63 throwOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { throw exception; }; 79 throwOnErrorCallback = (ArchiveException exception, string[] data) { throw exception; };
64 doNothingOnErrorCallback = (ArchiveException exception, IArchive.IDataType data) { /* do nothing */ }; 80 doNothingOnErrorCallback = (ArchiveException exception, string[] data) { /* do nothing */ };
65 81
66 setThrowOnErrorCallback(); 82 setThrowOnErrorCallback();
67 } 83 }
68 84
69 ErrorCallback errorCallback () 85 ErrorCallback errorCallback ()
84 void setDoNothingOnErrorCallback () 100 void setDoNothingOnErrorCallback ()
85 { 101 {
86 errorCallback = doNothingOnErrorCallback; 102 errorCallback = doNothingOnErrorCallback;
87 } 103 }
88 104
89 DataType serialize (T) (T value, string key = null) 105 void reset ()
106 {
107 resetCounters();
108
109 serializers = null;
110 deserializers = null;
111
112 serializedReferences = null;
113 deserializedReferences = null;
114
115 serializedArrays = null;
116 deserializedSlices = null;
117
118 hasBegunSerializing = false;
119 hasBegunDeserializing = false;
120
121 archive.reset;
122 }
123
124 Data serialize (T) (T value, string key = null)
90 { 125 {
91 if (!hasBegunSerializing) 126 if (!hasBegunSerializing)
92 hasBegunSerializing = true; 127 hasBegunSerializing = true;
93 128
94 serializeInternal(value, key); 129 serializeInternal(value, key);
95 archive.postProcess; 130 postProcess;
96 131
97 return archive.data; 132 return archive.untypedData;
98 } 133 }
99 134
100 private void serializeInternal (T) (T value, string key = null) 135 private void serializeInternal (T) (T value, string key = null)
101 { 136 {
102 if (!key) 137 if (!key)
103 key = nextKey; 138 key = nextKey;
104 139
105 archive.beginArchiving(); 140 archive.beginArchiving();
106 141
107 static if (isTypeDef!(T)) 142 static if ( is(T == typedef) )
108 serializeTypeDef(value, key); 143 serializeTypedef(value, key);
109 144
110 static if (isObject!(T)) 145 else static if (isObject!(T))
111 serializeObject(value, key); 146 serializeObject(value, key);
112 147
113 else static if (isStruct!(T)) 148 else static if (isStruct!(T))
114 serializeStruct(value, key); 149 serializeStruct(value, key);
115 150
144 } 179 }
145 } 180 }
146 181
147 private void serializeObject (T) (T value, string key) 182 private void serializeObject (T) (T value, string key)
148 { 183 {
149 auto runtimeType = value.classinfo.name; 184 if (!value)
150 185 return archive.archiveNull(T.stringof, key);
151 triggerEvents(serializing, value, { 186
152 archive.archiveObject(runtimeType, T.stringof, key, nextId, { 187 auto reference = getSerializedReference(value);
188
189 if (reference != Id.max)
190 return archive.archiveReference(key, reference);
191
192 auto runtimeType = value.classinfo.name;
193
194 Id id = nextId;
195 addSerializedReference(value, id);
196
197 triggerEvents(serializing, value, {
198 archive.archiveObject(runtimeType, T.stringof, key, id, {
153 if (runtimeType in serializers) 199 if (runtimeType in serializers)
154 { 200 {
155 auto wrapper = getSerializerWrapper!(T)(runtimeType); 201 auto wrapper = getSerializerWrapper!(T)(runtimeType);
156 wrapper(value, this, key); 202 wrapper(value, this, key);
157 } 203 }
161 207
162 else 208 else
163 { 209 {
164 if (isBaseClass(value)) 210 if (isBaseClass(value))
165 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__); 211 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__);
166 212
167 objectStructSerializeHelper(value); 213 objectStructSerializeHelper(value);
168 } 214 }
169 }); 215 });
170 }); 216 });
171 } 217 }
172 218
173 private void serializeStruct (T) (T value, DataType key) 219 private void serializeStruct (T) (T value, string key)
174 { 220 {
175 auto type = T.stringof; 221 string type = T.stringof;
176 222
177 triggerEvents(serializing, value, { 223 triggerEvents(serializing, value, {
178 archive.archive(type, key, nextId, { 224 archive.archiveStruct(type, key, nextId, {
179 if (type in serializers) 225 if (type in serializers)
180 { 226 {
181 auto wrapper = getSerializerWrapper!(T)(type); 227 auto wrapper = getSerializerWrapper!(T)(type);
182 wrapper(value, this, key); 228 wrapper(value, this, key);
183 } 229 }
194 }); 240 });
195 } 241 }
196 242
197 private void serializeString (T) (T value, string key) 243 private void serializeString (T) (T value, string key)
198 { 244 {
245 auto id = nextId;
246 auto array = Array(value.ptr, value.length, ElementTypeOfArray!(T).sizeof);
247
248 archive.archive(value, key, id);
249 addSerializedArray(array, id);
250 }
251
252 private void serializeArray (T) (T value, string key)
253 {
254 auto array = Array(value.ptr, value.length, ElementTypeOfArray!(T).sizeof);
255 auto id = nextId;
256
257 archive.archiveArray(array, arrayToString!(T), key, id, {
258 foreach (i, e ; value)
259 serializeInternal(e, toData(i));
260 });
261
262 addSerializedArray(array, id);
263 }
264
265 private void serializeAssociativeArray (T) (T value, string key)
266 {
267 string keyType = KeyTypeOfAssociativeArray!(T).stringof;
268 string valueType = ValueTypeOfAssociativeArray!(T).stringof;
269
270 archive.archiveAssociativeArray(keyType, valueType, value.length, key, nextId, {
271 size_t i;
272
273 foreach(k, v ; value)
274 {
275 archive.archiveAssociativeArrayKey(toData(i), {
276 serializeInternal(k, toData(i));
277 });
278
279 archive.archiveAssociativeArrayValue(toData(i), {
280 serializeInternal(v, toData(i));
281 });
282
283 i++;
284 }
285 });
286 }
287
288 private void serializePointer (T) (T value, string key)
289 {
290 if (!value)
291 return archive.archiveNull(T.stringof, key);
292
293 auto reference = getSerializedReference(value);
294
295 if (reference != Id.max)
296 return archive.archiveReference(key, reference);
297
298 Id id = nextId;
299
300 addSerializedReference(value, id);
301
302 archive.archivePointer(key, id, {
303 if (key in serializers)
304 {
305 auto wrapper = getSerializerWrapper!(T)(key);
306 wrapper(value, this, key);
307 }
308
309 else static if (isSerializable!(T, Serializer))
310 value.toData(this, key);
311
312 else
313 {
314 static if (isVoid!(BaseTypeOfPointer!(T)))
315 throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be serialized on its own, either implement orange.serialization.Serializable.isSerializable or register a serializer.`), __FILE__, __LINE__);
316
317 else
318 serializeInternal(*value, nextKey);
319 }
320 });
321 }
322
323 private void serializeEnum (T) (T value, string key)
324 {
325 alias BaseTypeOfEnum!(T) EnumBaseType;
326 auto val = cast(EnumBaseType) value;
327 string type = T.stringof;
328
329 archive.archiveEnum(val, type, key, nextId);
330 }
331
332 private void serializePrimitive (T) (T value, string key)
333 {
199 archive.archive(value, key, nextId); 334 archive.archive(value, key, nextId);
200 } 335 }
201 336
202 private void serializeArray (T) (T value, string key) 337 private void serializeTypedef (T) (T value, string key)
203 { 338 {
204 auto array = Array(value.ptr, value.length, BaseTypeOfArray!(T).sizeof); 339 archive.archiveTypedef(T.stringof, key, nextId, {
205 340 serializeInternal!(BaseTypeOfTypedef!(T))(value, nextKey);
206 archive.archiveArray(array, arrayToString!(T), key, nextId, {
207 foreach (i, e ; value)
208 serializeInternal(e, toDataType(i));
209 }); 341 });
210 } 342 }
211 343
212 private void serializePrimitive (T) (T value, string key) 344 T deserialize (T) (Data data, string key = null)
213 { 345 {
214 archive.archive(value, key, nextId); 346 if (hasBegunSerializing && !hasBegunDeserializing)
347 resetCounters();
348
349 if (!hasBegunDeserializing)
350 hasBegunDeserializing = true;
351
352 if (!key)
353 key = nextKey;
354
355 archive.beginUnarchiving(data);
356 return deserializeInternal!(T)(key);
357 }
358
359 private T deserializeInternal (T) (string key)
360 {
361 static if (isTypedef!(T))
362 return deserializeTypedef!(T)(key);
363
364 else static if (isObject!(T))
365 return deserializeObject!(T)(key);
366
367 else static if (isStruct!(T))
368 return deserializeStruct!(T)(key);
369
370 else static if (isString!(T))
371 return deserializeString!(T)(key);
372
373 else static if (isArray!(T))
374 return deserializeArray!(T)(key);
375
376 else static if (isAssociativeArray!(T))
377 return deserializeAssociativeArray!(T)(key);
378
379 else static if (isPrimitive!(T))
380 return deserializePrimitive!(T)(key);
381
382 else static if (isPointer!(T))
383 {
384 static if (isFunctionPointer!(T))
385 goto error;
386
387 return deserializePointer!(T)(key);
388 }
389
390 else static if (isEnum!(T))
391 return deserializeEnum!(T)(key);
392
393 else
394 {
395 error:
396 throw new SerializationException(format!(`The type "`, T, `" cannot be deserialized.`), __FILE__, __LINE__);
397 }
398 }
399
400 private T deserializeObject (T) (string key)
401 {
402 auto id = deserializeReference(key);
403
404 if (auto reference = getDeserializedReference!(T)(id))
405 return *reference;
406
407 T value;
408 Object val = value;
409
410 archive.unarchiveObject(key, id, val, {
411 triggerEvents(deserializing, value, {
412 value = cast(T) val;
413 auto runtimeType = value.classinfo.name;
414
415 if (runtimeType in deserializers)
416 {
417 auto wrapper = getDeserializerWrapper!(T)(runtimeType);
418 wrapper(value, this, key);
419 }
420
421 else static if (isSerializable!(T, Serializer))
422 value.fromData(this, key);
423
424 else
425 {
426 if (isBaseClass(value))
427 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__);
428
429 objectStructDeserializeHelper(value);
430 }
431 });
432 });
433
434 addDeserializedReference(value, id);
435
436 return value;
437 }
438
439 private T deserializeStruct (T) (string key)
440 {
441 T value;
442
443 archive.unarchiveStruct(key, {
444 triggerEvents(deserializing, value, {
445 auto type = toData(T.stringof);
446
447 if (type in deserializers)
448 {
449 auto wrapper = getDeserializerWrapper!(T)(type);
450 wrapper(value, this, key);
451 }
452
453 else
454 {
455 static if (isSerializable!(T, Serializer))
456 value.fromData(this, key);
457
458 else
459 objectStructDeserializeHelper(value);
460 }
461 });
462 });
463
464 return value;
465 }
466
467 private T deserializeString (T) (string key)
468 {
469 auto slice = deserializeSlice(key);
470
471 if (auto tmp = getDeserializedSlice!(T)(slice))
472 return *tmp;
473
474 T value;
475
476 if (slice.id != size_t.max)
477 {
478 static if (is(T == string))
479 value = archive.unarchiveString(slice.id).toSlice(slice);
480
481 else static if (is(T == wstring))
482 value = archive.unarchiveWstring(slice.id).toSlice(slice);
483
484 else static if (is(T == dstring))
485 value = archive.unarchiveDstring(slice.id).toSlice(slice);
486 }
487
488 else
489 {
490 static if (is(T == string))
491 value = archive.unarchiveString(key, slice.id);
492
493 else static if (is(T == wstring))
494 value = archive.unarchiveWstring(key, slice.id);
495
496 else static if (is(T == dstring))
497 value = archive.unarchiveDstring(key, slice.id);
498 }
499
500 addDeserializedSlice(value, slice.id);
501
502 return value;
503 }
504
505 private T deserializeArray (T) (string key)
506 {
507 auto slice = deserializeSlice(key);
508
509 if (auto tmp = getDeserializedSlice!(T)(slice))
510 return *tmp;
511
512 T value;
513
514 auto dg = (size_t length) {
515 value.length = length;
516
517 foreach (i, ref e ; value)
518 e = deserializeInternal!(typeof(e))(toData(i));
519 };
520
521 if (slice.id != size_t.max)
522 {
523 archive.unarchiveArray(slice.id, dg);
524 addDeserializedSlice(value, slice.id);
525
526 return value.toSlice(slice);
527 }
528
529 else
530 {
531 slice.id = archive.unarchiveArray(key, dg);
532
533 if (auto a = slice.id in deserializedSlices)
534 return cast(T) *a;
535
536 addDeserializedSlice(value, slice.id);
537
538 return value;
539 }
540 }
541
542 private T deserializeAssociativeArray (T) (string key)
543 {
544 T value;
545
546 alias KeyTypeOfAssociativeArray!(T) Key;
547 alias ValueTypeOfAssociativeArray!(T) Value;
548
549 archive.unarchiveAssociativeArray(key, (size_t length) {
550 for (size_t i = 0; i < length; i++)
551 {
552 Key aaKey;
553 Value aaValue;
554 auto k = toData(i);
555
556 archive.unarchiveAssociativeArrayKey(k, {
557 aaKey = deserializeInternal!(Key)(k);
558 });
559
560 archive.unarchiveAssociativeArrayValue(k, {
561 aaValue = deserializeInternal!(Value)(k);
562 });
563
564 value[aaKey] = aaValue;
565 }
566 });
567
568 return value;
569 }
570
571 /* auto id = deserializeReference(key);
572
573 if (auto reference = getDeserializedReference!(T)(id))
574 return *reference;
575
576 T value;
577 Object val = value;
578
579 archive.unarchiveObject(key, id, val, {
580 triggerEvents(deserializing, value, {
581 value = cast(T) val;
582 auto runtimeType = value.classinfo.name;
583
584 if (runtimeType in deserializers)
585 {
586 auto wrapper = getDeserializerWrapper!(T)(runtimeType);
587 wrapper(value, this, key);
588 }
589
590 else static if (isSerializable!(T, Serializer))
591 value.fromData(this, key);
592
593 else
594 {
595 if (isBaseClass(value))
596 throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__);
597
598 objectStructDeserializeHelper(value);
599 }
600 });
601 });
602
603 addDeserializedReference(value, id);
604
605 return value;
606 */
607
608 private T deserializePointer (T) (string key)
609 {
610 auto id = deserializeReference(key);
611
612 if (auto reference = getDeserializedReference!(T)(id))
613 return *reference;
614
615 T value = new BaseTypeOfPointer!(T);
616
617 id = archive.unarchivePointer(key, {
618 if (key in deserializers)
619 {
620 auto wrapper = getDeserializerWrapper!(T)(key);
621 wrapper(value, this, key);
622 }
623
624 else static if (isSerializable!(T, Serializer))
625 value.fromData(this, key);
626
627 else
628 {
629 static if (isVoid!(BaseTypeOfPointer!(T)))
630 throw new SerializationException(`The value with the key "` ~ to!(string)(key) ~ `"` ~ format!(` of the type "`, T, `" cannot be deserialized on its own, either implement orange.serialization.Serializable.isSerializable or register a deserializer.`), __FILE__, __LINE__);
631
632 else
633 *value = deserializeInternal!(BaseTypeOfPointer!(T))(nextKey);
634 }
635 });
636
637 addDeserializedReference(value, id);
638
639 return value;
640 }
641
642 private T deserializeEnum (T) (string key)
643 {
644 alias BaseTypeOfEnum!(T) Enum;
645
646 const functionName = toUpper(Enum.stringof[0]) ~ Enum.stringof[1 .. $];
647 mixin("return cast(T) archive.unarchiveEnum" ~ functionName ~ "(key);");
648 }
649
650 private T deserializePrimitive (T) (string key)
651 {
652 const functionName = toUpper(T.stringof[0]) ~ T.stringof[1 .. $];
653 mixin("return archive.unarchive" ~ functionName ~ "(key);");
654 }
655
656 private T deserializeTypedef (T) (string key)
657 {
658 T value;
659
660 archive.unarchiveTypedef!(T)(key, {
661 value = cast(T) deserializeInternal!(BaseTypeOfTypedef!(T))(nextKey);
662 });
663
664 return value;
665 }
666
667 private Id deserializeReference (string key)
668 {
669 return archive.unarchiveReference(key);
670 }
671
672 private Slice deserializeSlice (string key)
673 {
674 return archive.unarchiveSlice(key);
215 } 675 }
216 676
217 private void objectStructSerializeHelper (T) (ref T value) 677 private void objectStructSerializeHelper (T) (ref T value)
218 { 678 {
219 static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`)); 679 static assert(isStruct!(T) || isObject!(T), format!(`The given value of the type "`, T, `" is not a valid type, the only valid types for this method are objects and structs.`));
225 685
226 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) 686 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field))
227 { 687 {
228 alias typeof(T.tupleof[i]) Type; 688 alias typeof(T.tupleof[i]) Type;
229 Type v = value.tupleof[i]; 689 Type v = value.tupleof[i];
230 serializeInternal(v, toDataType(field)); 690 serializeInternal(v, toData(field));
231 } 691 }
232 } 692 }
233 693
234 static if (isObject!(T) && !is(T == Object)) 694 static if (isObject!(T) && !is(T == Object))
235 serializeBaseTypes(value); 695 serializeBaseTypes(value);
245 const field = nameOfFieldAt!(T, i); 705 const field = nameOfFieldAt!(T, i);
246 706
247 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field)) 707 static if (!internalFields.ctfeContains(field) && !nonSerializedFields.ctfeContains(field))
248 { 708 {
249 alias TypeOfField!(T, field) Type; 709 alias TypeOfField!(T, field) Type;
250 auto fieldValue = deserializeInternal!(Type)(toDataType(field)); 710 auto fieldValue = deserializeInternal!(Type)(toData(field));
251 value.tupleof[i] = fieldValue; 711 value.tupleof[i] = fieldValue;
252 } 712 }
253 } 713 }
254 714
255 static if (isObject!(T) && !is(T == Object)) 715 static if (isObject!(T) && !is(T == Object))
257 } 717 }
258 718
259 private void serializeBaseTypes (T : Object) (T value) 719 private void serializeBaseTypes (T : Object) (T value)
260 { 720 {
261 alias BaseTypeTupleOf!(T)[0] Base; 721 alias BaseTypeTupleOf!(T)[0] Base;
262 722
263 static if (!is(Base == Object)) 723 static if (!is(Base == Object))
264 { 724 {
265 archive.archiveBaseClass(Base.stringof, nextKey, nextId); 725 archive.archiveBaseClass(Base.stringof, nextKey, nextId);
266 Base base = value; 726 Base base = value;
267 objectStructSerializeHelper(base); 727 objectStructSerializeHelper(base);
276 { 736 {
277 archive.unarchiveBaseClass!(Base)(nextKey); 737 archive.unarchiveBaseClass!(Base)(nextKey);
278 Base base = value; 738 Base base = value;
279 objectStructDeserializeHelper(base); 739 objectStructDeserializeHelper(base);
280 } 740 }
741 }
742
743 private void addSerializedReference (T) (T value, Id id)
744 {
745 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`));
746
747 serializedReferences[cast(void*) value] = id;
748 }
749
750 private void addDeserializedReference (T) (T value, Id id)
751 {
752 static assert(isReference!(T), format!(`The given type "`, T, `" is not a reference type, i.e. object or pointer.`));
753
754 deserializedReferences[id] = cast(void*) value;
755 }
756
757 private void addDeserializedSlice (T) (T value, Id id)
758 {
759 static assert(isArray!(T) || isString!(T), format!(`The given type "`, T, `" is not a slice type, i.e. array or string.`));
760
761 deserializedSlices[id] = value;
762 }
763
764 private Id getSerializedReference (T) (T value)
765 {
766 if (auto tmp = cast(void*) value in serializedReferences)
767 return *tmp;
768
769 return Id.max;
770 }
771
772 private T* getDeserializedReference (T) (Id id)
773 {
774 if (auto reference = id in deserializedReferences)
775 return cast(T*) reference;
776
777 return null;
778 }
779
780 private T* getDeserializedSlice (T) (Slice slice)
781 {
782 if (auto array = slice.id in deserializedSlices)
783 return &(cast(T) *array)[slice.offset .. slice.offset + slice.length]; // dereference the array, cast it to the right type,
784 // slice it and then return a pointer to the result }
785 return null;
786 }
787
788 private T* getDeserializedArray (T) (Id id)
789 {
790 if (auto array = id in deserializedSlices)
791 return cast(T*) array;
792 }
793
794 private T[] toSlice (T) (T[] array, Slice slice)
795 {
796 return array[slice.offset .. slice.offset + slice.length];
281 } 797 }
282 798
283 private SerializeRegisterWrapper!(T, Serializer) getSerializerWrapper (T) (string type) 799 private SerializeRegisterWrapper!(T, Serializer) getSerializerWrapper (T) (string type)
284 { 800 {
285 auto wrapper = cast(SerializeRegisterWrapper!(T, Serializer)) serializers[type]; 801 auto wrapper = cast(SerializeRegisterWrapper!(T, Serializer)) serializers[type];
318 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func) 834 private DeserializeRegisterWrapper!(T, Serializer) toDeserializeRegisterWrapper (T) (void function (ref T, Serializer, string) func)
319 { 835 {
320 return new DeserializeRegisterWrapper!(T, Serializer)(func); 836 return new DeserializeRegisterWrapper!(T, Serializer)(func);
321 } 837 }
322 838
839 private void addSerializedArray (Array array, Id id)
840 {
841 serializedArrays[id] = array;
842 }
843
844 private void postProcessArrays ()
845 {
846 bool foundSlice = true;
847
848 foreach (sliceKey, slice ; serializedArrays)
849 {
850 foreach (arrayKey, array ; serializedArrays)
851 {
852 if (slice.isSliceOf(array) && slice != array)
853 {
854 auto s = Slice(slice.length, (slice.ptr - array.ptr) / slice.elementSize);
855 archive.archiveSlice(s, sliceKey, arrayKey);
856 foundSlice = true;
857 break;
858 }
859
860 else
861 foundSlice = false;
862 }
863
864 if (!foundSlice)
865 archive.postProcessArray(sliceKey);
866 }
867 }
868
869 private void postProcess ()
870 {
871 postProcessArrays();
872 }
873
323 private template arrayToString (T) 874 private template arrayToString (T)
324 { 875 {
325 const arrayToString = BaseTypeOfArray!(T).stringof; 876 const arrayToString = ElementTypeOfArray!(T).stringof;
326 } 877 }
327 878
328 private bool isBaseClass (T) (T value) 879 private bool isBaseClass (T) (T value)
329 { 880 {
330 auto name = value.classinfo.name; 881 auto name = value.classinfo.name;
331 auto index = name.lastIndexOf('.'); 882 auto index = name.lastIndexOf('.');
332 883
333 return T.stringof != name[index + 1 .. $]; 884 return T.stringof != name[index + 1 .. $];
334 } 885 }
335 886
336 private size_t nextId () 887 private Id nextId ()
337 { 888 {
338 return idCounter++; 889 return idCounter++;
339 } 890 }
340 891
341 private string nextKey () 892 private string nextKey ()
342 { 893 {
343 return toDataType(keyCounter++); 894 return toData(keyCounter++);
344 } 895 }
345 896
346 private void resetCounters () 897 private void resetCounters ()
347 { 898 {
348 keyCounter = 0; 899 keyCounter = 0;
349 idCounter = 0; 900 idCounter = 0;
350 } 901 }
351 902
352 private string toDataType (T) (T value) 903 private string toData (T) (T value)
353 { 904 {
354 return to!(string)(value); 905 return to!(string)(value);
355 } 906 }
356 907
357 private void triggerEvent (string name, T) (T value) 908 private void triggerEvent (string name, T) (T value)
376 if (mode == serializing) 927 if (mode == serializing)
377 triggerEvent!(onSerializingField)(value); 928 triggerEvent!(onSerializingField)(value);
378 929
379 else 930 else
380 triggerEvent!(onDeserializingField)(value); 931 triggerEvent!(onDeserializingField)(value);
381 932
382 dg(); 933 dg();
383 934
384 if (mode == serializing) 935 if (mode == serializing)
385 triggerEvent!(onSerializedField)(value); 936 triggerEvent!(onSerializedField)(value);
386 937
387 else 938 else
388 triggerEvent!(onDeserializedField)(value); 939 triggerEvent!(onDeserializedField)(value);
403 } 954 }
404 955
405 return annotations; 956 return annotations;
406 } 957 }
407 } 958 }
959
960
961
962 debug (OrangeUnitTest)
963 {
964 import orange.serialization.archives.XMLArchive;
965
966 enum Foo { a, b, c }
967 typedef int Int;
968
969 class A {}
970 struct B {}
971 class C { string str; }
972 class D { int[] arr; }
973 class E { int[int] aa; }
974 class F { int value; int* ptr; }
975 class G { Foo foo; }
976
977 class H
978 {
979 bool bool_;
980 byte byte_;
981 //cdouble cdouble_; // currently not suppported by to!()
982 //cent cent_; // currently not implemented but a reserved keyword
983 //cfloat cfloat_; // currently not suppported by to!()
984 char char_;
985 //creal creal_; // currently not suppported by to!()
986 dchar dchar_;
987 double double_;
988 float float_;
989 //idouble idouble_; // currently not suppported by to!()
990 //ifloat ifloat_; // currently not suppported by to!()
991 int int_;
992 //ireal ireal_; // currently not suppported by to!()
993 long long_;
994 real real_;
995 short short_;
996 ubyte ubyte_;
997 //ucent ucent_; // currently not implemented but a reserved keyword
998 uint uint_;
999 ulong ulong_;
1000 ushort ushort_;
1001 wchar wchar_;
1002
1003 equals_t opEquals (Object other)
1004 {
1005 if (auto o = cast(H) other)
1006 {
1007 return bool_ == o.bool_ &&
1008 byte_ == o.byte_ &&
1009 //cdouble_ == o.cdouble_ && // currently not suppported by to!()
1010 //cent_ == o.cent_ && // currently not implemented but a reserved keyword
1011 //cfloat_ == o.cfloat_ && // currently not suppported by to!()
1012 char_ == o.char_ &&
1013 //creal_ == o.creal_ && // currently not suppported by to!()
1014 dchar_ == o.dchar_ &&
1015 double_ == o.double_ &&
1016 float_ == o.float_ &&
1017 //idouble_ == o.idouble_ && // currently not suppported by to!()
1018 //ifloat_ == o.ifloat_ && // currently not suppported by to!()
1019 int_ == o.int_ &&
1020 //ireal_ == o.ireal_ && // currently not suppported by to!()
1021 long_ == o.long_ &&
1022 real_ == o.real_ &&
1023 short_ == o.short_ &&
1024 ubyte_ == o.ubyte_ &&
1025 //ucent_ == o.ucent_ && // currently not implemented but a reserved keyword
1026 uint_ == o.uint_ &&
1027 ulong_ == o.ulong_ &&
1028 ushort_ == o.ushort_ &&
1029 wchar_ == o.wchar_;
1030 }
1031
1032 return false;
1033 }
1034 }
1035
1036 class I
1037 {
1038 Int a;
1039 }
1040
1041 void main ()
1042 {
1043 auto archive = new XMLArchive!(char);
1044 auto serializer = new Serializer(archive);
1045 string data;
1046
1047 void serializeObject ()
1048 {
1049 serializer.reset;
1050 data = `<?xml version="1.0" encoding="UTF-8"?>
1051 <archive type="org.dsource.orange.xml" version="1.0.0">
1052 <data>
1053 <struct type="B" key="0"/>
1054 </data>
1055 </archive>`;
1056
1057 serializer.serialize(B());
1058 assert(archive.data == data);
1059 }
1060
1061 void serializeStruct ()
1062 {
1063 serializer.reset;
1064 data = `<?xml version="1.0" encoding="UTF-8"?>
1065 <archive type="org.dsource.orange.xml" version="1.0.0">
1066 <data>
1067 <struct type="B" key="0"/>
1068 </data>
1069 </archive>`;
1070
1071 serializer.serialize(B());
1072 assert(archive.data == data);
1073 }
1074
1075 // Struct
1076
1077
1078
1079 // String
1080
1081 serializer.reset;
1082 data = `<?xml version="1.0" encoding="UTF-8"?>
1083 <archive type="org.dsource.orange.xml" version="1.0.0">
1084 <data>
1085 <object runtimeType="orange.serialization.Serializer.C" type="C" key="0" id="0">
1086 <string type="char" length="3" key="str" id="1">foo</string>
1087 </object>
1088 </data>
1089 </archive>`;
1090
1091 auto c = new C;
1092 c.str = "foo";
1093 serializer.serialize(c);
1094 assert(archive.data == data);
1095
1096 // Deserializing
1097
1098 auto cDeserialized = serializer.deserialize!(C)(data);
1099 assert(c.str == cDeserialized.str);
1100
1101 // Array
1102
1103 serializer.reset;
1104 data = `<?xml version="1.0" encoding="UTF-8"?>
1105 <archive type="org.dsource.orange.xml" version="1.0.0">
1106 <data>
1107 <object runtimeType="orange.serialization.Serializer.D" type="D" key="0" id="0">
1108 <array type="int" length="6" key="arr" id="1">
1109 <int key="0">27</int>
1110 <int key="1">382</int>
1111 <int key="2">283</int>
1112 <int key="3">3820</int>
1113 <int key="4">32</int>
1114 <int key="5">832</int>
1115 </array>
1116 </object>
1117 </data>
1118 </archive>`;
1119
1120 auto d = new D;
1121 d.arr = [27, 382, 283, 3820, 32, 832];
1122 serializer.serialize(d);
1123 assert(archive.data == data);
1124
1125 // Deserializing
1126
1127 auto dDeserialized = serializer.deserialize!(D)(data);
1128 assert(d.arr == dDeserialized.arr);
1129
1130 // Associative Array
1131
1132 serializer.reset();
1133 data = `<?xml version="1.0" encoding="UTF-8"?>
1134 <archive type="org.dsource.orange.xml" version="1.0.0">
1135 <data>
1136 <object runtimeType="orange.serialization.Serializer.E" type="E" key="0" id="0">
1137 <associativeArray keyType="int" valueType="int" length="4" key="aa">
1138 <key key="0">
1139 <int key="0">1</int>
1140 </key>
1141 <value key="0">
1142 <int key="0">2</int>
1143 </value>
1144 <key key="1">
1145 <int key="1">3</int>
1146 </key>
1147 <value key="1">
1148 <int key="1">4</int>
1149 </value>
1150 <key key="2">
1151 <int key="2">6</int>
1152 </key>
1153 <value key="2">
1154 <int key="2">7</int>
1155 </value>
1156 <key key="3">
1157 <int key="3">39</int>
1158 </key>
1159 <value key="3">
1160 <int key="3">472</int>
1161 </value>
1162 </associativeArray>
1163 </object>
1164 </data>
1165 </archive>`;
1166
1167 auto e = new E;
1168 e.aa = [3 : 4, 1 : 2, 39 : 472, 6 : 7];
1169 serializer.serialize(e);
1170 assert(archive.data == data);
1171
1172 // Deserializing
1173
1174 auto eDeserialized = serializer.deserialize!(E)(data);
1175 //assert(e.aa == eDeserialized.aa); // cannot compare associative array
1176
1177 // Pointer
1178
1179 serializer.reset();
1180 data = `<?xml version="1.0" encoding="UTF-8"?>
1181 <archive type="org.dsource.orange.xml" version="1.0.0">
1182 <data>
1183 <object runtimeType="orange.serialization.Serializer.F" type="F" key="0" id="0">
1184 <pointer key="ptr" id="2">
1185 <int key="1">9</int>
1186 </pointer>
1187 <int key="value">9</int>
1188 </object>
1189 </data>
1190 </archive>`;
1191
1192 auto f = new F;
1193 f.value = 9;
1194 f.ptr = &f.value;
1195 serializer.serialize(f);
1196 //assert(archive.data == data); // this is not a reliable comparison, the order of int and pointer is not reliable
1197
1198 // Deserializing
1199
1200 auto fDeserialized = serializer.deserialize!(F)(data);
1201 assert(*f.ptr == *fDeserialized.ptr);
1202
1203 // Enum
1204
1205 serializer.reset();
1206 data = `<?xml version="1.0" encoding="UTF-8"?>
1207 <archive type="org.dsource.orange.xml" version="1.0.0">
1208 <data>
1209 <object runtimeType="orange.serialization.Serializer.G" type="G" key="0" id="0">
1210 <enum type="Foo" baseType="int" key="foo">1</enum>
1211 </object>
1212 </data>
1213 </archive>`;
1214
1215 auto g = new G;
1216 g.foo = Foo.b;
1217 serializer.serialize(g);
1218 assert(archive.data == data);
1219
1220 // Deserializing
1221
1222 auto gDeserialized = serializer.deserialize!(G)(data);
1223 assert(g.foo == gDeserialized.foo);
1224
1225 // Primitives
1226
1227 serializer.reset;
1228 data = `<?xml version="1.0" encoding="UTF-8"?>
1229 <archive type="org.dsource.orange.xml" version="1.0.0">
1230 <data>
1231 <object runtimeType="orange.serialization.Serializer.H" type="H" key="0" id="0">
1232 <byte key="byte_">1</byte>
1233 <char key="char_">a</char>
1234 <dchar key="dchar_">b</dchar>
1235 <double key="double_">0</double>
1236 <float key="float_">0</float>
1237 <int key="int_">1</int>
1238 <long key="long_">1</long>
1239 <real key="real_">0</real>
1240 <short key="short_">1</short>
1241 <ubyte key="ubyte_">1</ubyte>
1242 <uint key="uint_">1</uint>
1243 <ulong key="ulong_">1</ulong>
1244 <ushort key="ushort_">1</ushort>
1245 <wchar key="wchar_">c</wchar>
1246 <bool key="bool_">true</bool>
1247 </object>
1248 </data>
1249 </archive>`;
1250
1251 auto h = new H;
1252
1253 h.bool_ = true;
1254 h.byte_ = 1;
1255 h.char_ = 'a';
1256 //h.cdouble_ = 0.0 + 0.0 * 1.0i; // currently not suppported by to!()
1257 //h.cfloat_ = 0.0f + 0.0f * 1.0i; // currently not suppported by to!()
1258 //h.creal_ = 0.0 + 0.0 * 1.0i; // currently not suppported by to!()
1259 h.dchar_ = 'b';
1260 h.double_ = 0.0;
1261 h.float_ = 0.0f;
1262 //h.idouble_ = 0.0 * 1.0i; // currently not suppported by to!()
1263 //h.ifloat_ = 0.0f * 1.0i; // currently not suppported by to!()
1264 h.int_ = 1;
1265 //h.ireal_ = 0.0 * 1.0i; // currently not suppported by to!()
1266 h.long_ = 1L;
1267 h.real_ = 0.0;
1268 h.short_ = 1;
1269 h.ubyte_ = 1U;
1270 h.uint_ = 1U;
1271 h.ulong_ = 1LU;
1272 h.ushort_ = 1U;
1273 h.wchar_ = 'c';
1274
1275 serializer.serialize(h);
1276 //assert(archive.data == data); // this is not a reliable comparison
1277
1278 // Deserializing
1279
1280 auto hDeserialized = serializer.deserialize!(H)(data);
1281 assert(h == hDeserialized);
1282
1283 // Typedef
1284
1285 serializer.reset();
1286 data = `<?xml version="1.0" encoding="UTF-8"?>
1287 <archive type="org.dsource.orange.xml" version="1.0.0">
1288 <data>
1289 <object runtimeType="orange.serialization.Serializer.I" type="I" key="0" id="0">
1290 <typedef type="Int" key="a">
1291 <int key="1">1</int>
1292 </typedef>
1293 </object>
1294 </data>
1295 </archive>`;
1296
1297 auto i = new I;
1298 i.a = 1;
1299 serializer.serialize(i);
1300 assert(archive.data == data);
1301
1302 // Deserializing
1303
1304 auto iDeserialized = serializer.deserialize!(I)(data);
1305 assert(i.a == iDeserialized.a);
1306
1307 println("unit tests successful");
1308 }
1309 }