1 /**
2  * Memory managment utilities.
3  */
4 module iz.memory;
5 
6 import
7     core.stdc..string, core.stdc.stdlib, core.exception;
8 import
9     std.traits, std.meta;
10 import
11     iz.types;
12 
13 /**
14  * Like malloc() but for @safe context.
15  */
16 Ptr getMem(size_t size) nothrow @trusted @nogc
17 in
18 {
19     //assert(size);
20 }
21 body
22 {
23     auto result = malloc(size);
24     if (!result)
25         throw construct!OutOfMemoryError;
26     return result;
27 }
28 
29 /**
30  * Like realloc() but for @safe context.
31  */
32 Ptr reallocMem(ref Ptr src, size_t newSize) nothrow @trusted @nogc
33 in
34 {
35     assert(newSize);
36 }
37 body
38 {
39     src = realloc(src, newSize);
40     if (!src)
41         throw construct!OutOfMemoryError;
42     return src;
43 }
44 
45 /**
46  * Like memmove() but for @safe context.
47  * dst and src can overlap.
48  *
49  * Params:
50  *      dst = The data source.
51  *      src = The data destination.
52  *      count = The count of byte to meove from src to dst.
53  * Returns:
54  *      the pointer to the destination, (same as dst).
55  */
56 Ptr moveMem(Ptr dst, const Ptr src, size_t count) nothrow @trusted @nogc
57 in
58 {
59     if (count)
60     {
61         assert(dst);
62         assert(src);
63     }
64 }
65 body
66 {
67     return memmove(dst, src, count);
68 }
69 
70 /**
71  * Like memmove() but for @safe context.
72  * dst and src can't overlap.
73  *
74  * Params:
75  *      dst = The data source.
76  *      src = The data destination.
77  *      count = The count of byte to meove from src to dst.
78  * Returns:
79  *      the pointer to the destination, (same as dst).
80  */
81 Ptr copyMem(Ptr dst, Ptr src, size_t count) nothrow @trusted @nogc
82 in
83 {
84     if (count)
85     {
86         assert(dst);
87         assert(src);
88         assert(dst + count <= src || dst >= src + count);
89     }
90 }
91 body
92 {
93     auto result = memcpy(dst, src, count);
94     if (!result)
95         throw construct!OutOfMemoryError;
96     return result;
97 }
98 
99 /**
100  * Frees a manually allocated pointer to a basic type.
101  * Like free() but for @safe context.
102  *
103  * Params:
104  *      src = The pointer to free.
105  */
106 void freeMem(T)(auto ref T src) nothrow @trusted @nogc
107 if (isPointer!T && isBasicType!(PointerTarget!T))
108 {
109     if (src)
110     {
111         free(cast(void*)src);
112         src = null;
113     }
114 }
115 
116 /**
117  * This enum must be used as an UDA to mark a variable of a type that looks
118  * like GC-managed but that is actually not GC-managed.
119  */
120 enum NoGc;
121 
122 /**
123  * When this enum is used as UDA on aggregate types whose instances are
124  * created with construct() a compile time message indicates if a GC range
125  * will be added for the members.
126  */
127 enum TellRangeAdded;
128 
129 /**
130  * When this enum is used as UDA on aggregate types whose instances are
131  * created with construct() they won't be initialized, i.e the
132  * static layout representing the initial value of the members is not copied.
133  *
134  * For example it can be used on a struct that has a $(D @disable this()) and
135  * when the others constructor are suposed to do the initialization job.
136  */
137 enum NoInit;
138 ///
139 unittest
140 {
141     @NoInit static struct Foo{int a = 42;}
142     Foo* foo = construct!Foo;
143     // initializer well skipped
144     assert(foo.a != 42);
145     destruct(foo);
146 }
147 
148 /**
149  * Indicates if an aggregate contains members that might be
150  * collected by the garbage collector. This is used in $(D construct)
151  * to determine if the content of a manually allocated aggregate must
152  * be declared to the GC.
153  */
154 template MustAddGcRange(T)
155 if (is(T==struct) || is(T==union) || is(T==class))
156 {
157     string check()
158     {
159         import std.meta: aliasSeqOf;
160         import std.range: iota;
161 
162         string managedMembers;
163 
164         enum addManaged = q{managedMembers ~= " " ~ T.tupleof[i].stringof;};
165 
166         // TODO: allow CPP classes detection when protection compliance removed.
167         static if (is(T == class) /*&& (!isCppClass!T)*/)
168         {
169             foreach(BT; BaseClassesTuple!T)
170             {
171                 string m = MustAddGcRange!BT;
172                 if (m.length)
173                     managedMembers ~= " " ~ m;
174             }
175         }
176         // TODO: use __trait(allMembers) when protection compliance removed.
177         // ".tupleof" doesn't include the static fields.
178         foreach(i; aliasSeqOf!(iota(0, T.tupleof.length)))
179         {
180             static if (!is(typeof(T.tupleof[i])== void))
181             {
182                 alias MT = typeof(T.tupleof[i]);
183                 static if (isArray!MT && !hasUDA!(T.tupleof[i], NoGc) && hasManagedDimension!MT)
184                     mixin(addManaged);
185                 else static if (isPointer!MT && !hasUDA!(T.tupleof[i], NoGc))
186                     mixin(addManaged);
187                 else static if (is(MT == class) && (!is(MT : T)) && !hasUDA!(T.tupleof[i], NoGc)
188                     && !(isTemplateInstance!T /*&& staticIndexOf!(MT,TemplateArgsOf!T) > 0*/))
189                 {
190                     // failure here when the class is a template and when one of the member
191                     // type is one of the template argument.
192                     //pragma(msg, T.stringof, " ", MT.stringof);
193                     static if (MustAddGcRange!MT)
194                         mixin(addManaged);
195                 }
196                 else static if (is(MT == struct) && !is(MT == T) && !hasUDA!(T.tupleof[i], NoGc))
197                 {
198                     static if (MustAddGcRange!MT)
199                         mixin(addManaged);
200                 }
201                 else static if (is(MT == union) && !is(MT == T) && !hasUDA!(T.tupleof[i], NoGc))
202                 {
203                     static if (MustAddGcRange!MT)
204                         mixin(addManaged);
205                 }
206             }
207         }
208         return managedMembers;
209     }
210 
211     static if (hasUDA!(T, NoGc))
212         static immutable MustAddGcRange = [];
213     else
214         static immutable MustAddGcRange = check();
215 
216     static if (hasUDA!(T, TellRangeAdded))
217     {
218         static if (MustAddGcRange.length)
219             pragma(msg, "a GC range will be added for any new " ~ T.stringof ~
220                 ", because of: " ~ MustAddGcRange);
221         else
222             pragma(msg, "a GC range wont be added for any new " ~ T.stringof);
223     }
224 
225 }
226 ///
227 unittest
228 {
229     // 'a' will be managed with expand/Shrink
230     class Foo{@NoGc int[] a; @NoGc void* b;}
231     static assert(!MustAddGcRange!Foo);
232     // 'a' will be managed with '.length' so druntime.
233     class Bar{int[] a; @NoGc void* b;}
234     // b's annotation is canceled by a type.
235     static assert(MustAddGcRange!Bar);
236     // Baz base is not @NoGc
237     class Baz: Bar{@NoGc void* c;}
238     static assert(MustAddGcRange!Baz);
239 }
240 
241 package template hasManagedDimension(T)
242 {
243     import std.range: ElementType;
244     static if (isDynamicArray!T)
245         enum hasManagedDimension = true;
246     else static if (isStaticArray!T)
247         enum hasManagedDimension = hasManagedDimension!(ElementType!T);
248     else
249         enum hasManagedDimension = false;
250 }
251 
252 unittest
253 {
254     alias A0 = int[];
255     static assert(hasManagedDimension!A0);
256     alias A1 = int[2][];
257     static assert(hasManagedDimension!A1);
258     alias A2 = int[][2];
259     static assert(hasManagedDimension!A2);
260     alias A3 = int[3][2];
261     static assert(!hasManagedDimension!A3);
262     alias A4 = int[][3][2];
263     static assert(hasManagedDimension!A4);
264 }
265 
266 unittest
267 {
268     class Foo{int[][2][4] a;}
269     static assert(MustAddGcRange!Foo);
270     class Bar{@NoGc int[][2][4] a;}
271     static assert(!MustAddGcRange!Bar);
272 }
273 
274 /**
275  * When mixed in a aggregate this template has for effect to disable the usage
276  * the $(D new) operator.
277  */
278 mixin template disableNew()
279 {
280     @disable new (size_t){return null;}
281 }
282 ///
283 unittest
284 {
285     // class requiring users to use allocators.
286     class NotUsableWithNew
287     {
288         mixin disableNew;
289     }
290 
291     // statically verify that `new` cannot be used.
292     static assert(!__traits(compiles, new NotUsableWithNew));
293 
294     // Ok with a custom allocator
295     auto a = construct!NotUsableWithNew();
296     destruct(a);
297 }
298 
299 /**
300  * When mixed in class this template has for effect to automatically call the
301  * nearest inherited destructor if no destructor is present, otherwise a call
302  * to $(D callInheritedDtor()) should be made in the last LOC of the destructor.
303  */
304 mixin template inheritedDtor()
305 {
306 
307     static assert(is(typeof(this) == class));
308 
309     private
310     {
311         import std.traits: BaseClassesTuple;
312 
313         alias __iz_B = BaseClassesTuple!(typeof(this));
314         enum hasDtor = __traits(hasMember, typeof(this), "__dtor");
315         static if (hasDtor && !__traits(isSame, __traits(parent, typeof(this).__dtor), typeof(this)))
316             enum inDtor = true;
317         else
318             enum inDtor = false;
319     }
320 
321     public void callInheritedDtor(classT = typeof(this))()
322     {
323         import std.meta: aliasSeqOf;
324         import std.range: iota;
325 
326         foreach(i; aliasSeqOf!(iota(0, __iz_B.length)))
327             static if (__traits(hasMember, __iz_B[i], "__xdtor"))
328             {
329                 mixin("this." ~ __iz_B[i].stringof ~ ".__xdtor;");
330                 break;
331             }
332     }
333 
334     static if (!hasDtor || inDtor)
335     public ~this() {callInheritedDtor();}
336 }
337 
338 /**
339  * Returns a new, GC-free, class instance.
340  *
341  * Params:
342  *      CT = A class type.
343  *      a = Variadic parameters passed to the constructor.
344  */
345 CT construct(CT, A...)(A a) @trusted
346 if (is(CT == class) && !isAbstractClass!CT)
347 {
348     auto size = typeid(CT).initializer.length;
349     auto memory = getMem(size);
350     static if (!hasUDA!(CT, NoInit))
351         memory[0 .. size] = typeid(CT).initializer[];
352     static if (__traits(hasMember, CT, "__ctor"))
353         (cast(CT) (memory)).__ctor(a);
354     static if (MustAddGcRange!CT)
355     {
356         import core.memory: GC;
357         GC.addRange(memory, size, typeid(CT));
358     }
359     return cast(CT) memory;
360 }
361 
362 /**
363  * Returns a new, GC-free, class instance.
364  *
365  * This overload is designed to create factories and like the default
366  * Object.factory method, it only calls, if possible, the default constructor.
367  * The factory() function implemented in this iz.memory is based on this
368  * construct() overload.
369  *
370  * Params:
371  *      tic = The TypeInfo_Class of the Object to create.
372  */
373 Object construct(TypeInfo_Class tic) @trusted
374 {
375     if (tic.m_flags & 64)
376         return null;
377     auto size = tic.initializer.length;
378     auto memory = getMem(size);
379     memory[0 .. size] = tic.initializer[];
380     Object result = cast(Object) memory;
381     if (tic.defaultConstructor)
382         tic.defaultConstructor(result);
383     import core.memory: GC;
384     GC.addRange(memory, size, tic);
385     return result;
386 }
387 
388 /**
389  * Returns a new, GC-free, pointer to a struct or to an union.
390  *
391  * Params:
392  *      ST = A struct or an union type.
393  *      a = Variadic parameters passed to the constructor.
394  */
395 ST* construct(ST, A...)(A a) @trusted
396 if(is(ST==struct) || is(ST==union))
397 {
398     auto size = ST.sizeof;
399     auto memory = getMem(size);
400     static if (!hasUDA!(ST, NoInit))
401     {
402         __gshared static ST init = ST.init;
403         void* atInit = &init;
404         memory[0..size] = atInit[0..size];
405     }
406     static if (A.length)
407     {
408         static if (__traits(hasMember, ST, "__ctor"))
409         {
410             (cast(ST*) (memory)).__ctor(a);
411         }
412         else static if (A.length <= (__traits(allMembers, ST)).length)
413         {
414             import std.range: iota;
415             foreach(i; aliasSeqOf!(iota(0, A.length)))
416             {
417                 __traits(getMember, cast(ST*) memory, __traits(allMembers, ST)[i]) = a[i];
418             }
419         }
420         else static assert(0, "too much argument to generate an automatic constructor");
421     }
422     static if (MustAddGcRange!ST)
423     {
424         import core.memory: GC;
425         GC.addRange(memory, size, typeid(ST));
426     }
427     return cast(ST*) memory;
428 }
429 
430 /**
431  * Destructs a struct or a union that's been previously constructed
432  * with $(D construct()).
433  *
434  * The function calls the destructor and, when passed as reference,
435  * set the the instance to null.
436  *
437  * Params:
438  *      T = A union or a struct type, likely to be infered.
439  *      instance = A $(D T) instance.
440  */
441 void destruct(T)(auto ref T* instance)
442 if (is(T == struct) || is(T==union))
443 {
444     if (!instance)
445         return;
446     static if (__traits(hasMember, T, "__xdtor"))
447         instance.__xdtor;
448     freeMem(cast(void*) instance);
449     instance = null;
450 }
451 
452 /**
453  * Destructs a struct or a union that's allocated within another
454  * aggregate that's not GC-managed.
455  *
456  * Params:
457  *      T = A union or a struct type, likely to be infered.
458  *      instance = A $(D T) instance.
459  */
460 void destruct(T)(ref T instance)
461 if (is(T == struct))
462 {
463     static if (__traits(hasMember, T, "__xdtor"))
464         instance.__xdtor;
465 }
466 
467 /**
468  * Destructs a class that's been previously constructed with $(D construct())
469  * and when the static type is known.
470  *
471  * The function calls the destructor and, when passed as reference,
472  * set the the instance to null.
473  * When the static type is not known, destruct must be called after a cast to
474  * Object.
475  *
476  * Params:
477  *      assumeNoDtor = When no __ctor is found this avoids to search one
478  *      in the base classes.
479  *      T = A class type (most derived), likely to be infered.
480  *      instance = A $(D T) instance.
481  */
482 void destruct(bool assumeNoDtor = false, T)(auto ref T instance)
483 if (is(T == class) && T.stringof != Object.stringof)
484 {
485     if (instance)
486     {
487         static if (__traits(hasMember, T, "__xdtor") || assumeNoDtor)
488         {
489             static if (__traits(hasMember, T, "__xdtor"))
490                 instance.__xdtor;
491             freeMem(cast(void*)instance);
492             instance = null;
493         }
494         else // dtor might be in an ancestor
495         {
496             destruct(cast(Object) instance);
497         }
498     }
499 }
500 
501 /**
502  * Destructs a class that's been previously constructed with $(D construct()) and
503  * when the static type is not known.
504  *
505  * This overload is only selected when the instance is casted as Object.
506  * It should be used when there no guarantee that the instance type is the most
507  * derived. This overload is never @nogc.
508  *
509  * Params:
510  *      instance = A class instance casted to Object.
511  */
512 void destruct(T)(auto ref T instance)
513 if (is(T == class) && T.stringof == Object.stringof)
514 {
515     if (instance)
516     {
517         TypeInfo_Class tic = cast(TypeInfo_Class) typeid(instance);
518         if (void* dtorPtr = tic.destructor)
519         {
520             void delegate() dtor;
521             dtor.funcptr = cast(void function()) dtorPtr;
522             dtor.ptr = cast(void*) instance;
523             dtor();
524         }
525         freeMem(cast(void*)instance);
526         instance = null;
527     }
528 }
529 
530 /**
531  * Destructs an interface implemented in an Object that's been previously
532  * constructed with $(D construct()).
533  *
534  * This overload is never @nogc.
535  *
536  * Params:
537  *      T = A class type, likely to be infered.
538  *      instance = A $(D T) instance.
539  */
540 void destruct(T)(auto ref T instance)
541 if (is(T == interface))
542 {
543     if (instance)
544     {
545         version(Windows)
546         {
547             import core.sys.windows.unknwn: IUnknown;
548             static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in "
549                 ~ __PRETTY_FUNCTION__);
550         }
551         static if (__traits(allMembers, T).length)
552         {
553             bool allCpp()
554             {
555                 bool result = true;
556                 foreach (member; __traits(allMembers, T))
557                     foreach (ov; __traits(getOverloads, T, member))
558                         static if (functionLinkage!ov != "C++")
559                 {
560                     result = false;
561                     break;
562                 }
563                 return result;
564             }
565             static assert(!allCpp, "C++ interfaces can't be destroyed in "
566                 ~ __PRETTY_FUNCTION__);
567         }
568         destruct(cast(Object) instance);
569         instance = null;
570     }
571 }
572 
573 /**
574  * Destructs on pointers simply forwards $(D freeMem).
575  *
576  * Params:
577  *      instance = A pointer, typed or not.
578  */
579 void destruct(T)(auto ref T* instance)
580 if (isBasicType!T)
581 {
582     if (instance)
583         freeMem(cast(void*) instance);
584     instance = null;
585 }
586 
587 /**
588  * Returns a pointer to a new, GC-free, basic variable.
589  * Any variable allocated using this function must be manually freed with freeMem.
590  *
591  * Params:
592  *      T = The type of the pointer to return.
593  *      preFill = Optional, boolean indicating if the result has to be initialized.
594  *      a = Optional, the value.
595  */
596 T* newPtr(T, bool preFill = false, A...)(A a)
597 if (isBasicType!T && A.length <= 1)
598 {
599     T* result = cast(T*) getMem(T.sizeof);
600     static if (A.length == 1)
601         *result = a[0];
602     else static if (preFill)
603         *result = T.init;
604     return result;
605 }
606 
607 /**
608  * Frees and invalidates a list of classes instances or struct pointers.
609  * $(D destruct()) is called for each item.
610  *
611  * Params:
612  *      objs = Variadic list of Object instances.
613  */
614 void destructEach(Objs...)(auto ref Objs objs)
615 if (Objs.length > 1)
616 {
617     foreach(ref obj; objs)
618         destruct(obj);
619 }
620 
621 /**
622  * Register a class type that can be created dynamically, using its name.
623  *
624  * Class registration should only be done in module constructors. This allow
625  * the registration to be thread safe since module constructors are executed
626  * in the main thread.
627  *
628  * Params:
629  *      T = A class.
630  *      name = The name used to register the class.
631  *      By default the $(D T.stringof) is used.
632  *      f = The class repository, a hashmap of TypeInfo_Class by string.
633  */
634 void registerFactoryClass(T, F)(ref F f, string name = "")
635 if (is(T == class) && !isAbstractClass!T)
636 {
637     if (!name.length)
638         name = T.stringof;
639     f[name] = typeid(T);
640 }
641 
642 /**
643  * Calls registerClass() for each template argument.
644  *
645  * Params:
646  *      A = A list of classes, from one up to nine.
647  *      f = The class repository, a hashmap of TypeInfo_Class by string.
648  */
649 void registerFactoryClasses(A1, A2 = void, A3 = void, A4 = void, A5 = void,
650     A6 = void, A7 = void, A8 = void, A9 = void, F)(ref F f)
651 {
652     void tryRegister(A)()
653     {
654         static if (is(A == class))
655             registerFactoryClass!(A, F)(f);
656         else static if (!is(A == void))
657             static assert(0, A.stringof ~ " is not a class");
658     }
659     tryRegister!A1;tryRegister!A2;tryRegister!A3;
660     tryRegister!A4;tryRegister!A5;tryRegister!A6;
661     tryRegister!A7;tryRegister!A8;tryRegister!A9;
662 }
663 
664 /**
665  * Constructs a class instance using a gc-free factory.
666  * The usage is similar to Object.factory() except that by default
667  * registered classes don't take the module in account.
668  *
669  * Params:
670  *      className = the class name, as registered in registerFactoryClass().
671  *      factory = The class repository, a hashmap of TypeInfo_Class by string.
672  * Throws:
673  *      An Exception if the class is not registered.
674  */
675 Object factory(F)(ref F f, string className)
676 {
677     TypeInfo_Class* tic = className in f;
678     if (!tic)
679         throw construct!Exception("factory exception, unregistered class");
680     return
681         construct(*tic);
682 }
683 
684 unittest
685 {
686     import core.memory: GC;
687 
688     interface I{}
689     class AI: I{}
690 
691     auto a = construct!Object;
692     a.destruct;
693     assert(!a);
694     a.destruct;
695     assert(!a);
696     a.destruct;
697 
698     auto b = construct!Object;
699     auto c = construct!Object;
700     destructEach(a,b,c);
701     assert(!a);
702     assert(!b);
703     assert(!c);
704     destructEach(a,b,c);
705     assert(!a);
706     assert(!b);
707     assert(!c);
708 
709     Object foo = construct!Object;
710     Object bar = new Object;
711     assert( GC.addrOf(cast(void*)foo) == null );
712     assert( GC.addrOf(cast(void*)bar) != null );
713     foo.destruct;
714     bar.destroy;
715 
716     struct Foo
717     {
718         this(size_t a,size_t b,size_t c)
719         {
720             this.a = a;
721             this.b = b;
722             this.c = c;
723         }
724         size_t a,b,c;
725     }
726 
727     Foo * foos = construct!Foo(1,2,3);
728     Foo * bars = new Foo(4,5,6);
729     assert(foos.a == 1);
730     assert(foos.b == 2);
731     assert(foos.c == 3);
732     assert( GC.addrOf(cast(void*)foos) == null );
733     assert( GC.addrOf(cast(void*)bars) != null );
734     foos.destruct;
735     bars.destroy;
736     assert(!foos);
737     foos.destruct;
738     assert(!foos);
739 
740     union Uni{bool b; ulong ul;}
741     Uni* uni0 = construct!Uni();
742     Uni* uni1 = new Uni();
743     assert( GC.addrOf(cast(void*)uni0) == null );
744     assert( GC.addrOf(cast(void*)uni1) != null );
745     uni0.destruct;
746     assert(!uni0);
747     uni0.destruct;
748     assert(!uni0);
749 
750     auto ai = construct!AI;
751     auto i = cast(I) ai;
752     destruct(i);
753     assert(i is null);
754 
755     abstract class Abst {}
756     Object ab = construct(typeid(Abst));
757     assert(ab is null);
758 }
759 
760 @nogc unittest
761 {
762     class Foo {@nogc this(int a){} @nogc~this(){}}
763     Foo foo = construct!Foo(0);
764     destruct(foo);
765     assert(foo is null);
766 
767     static struct Bar {}
768     Bar* bar = construct!Bar;
769     destruct(bar);
770     assert(bar is null);
771 
772     static struct Baz {int i; this(int,int) @nogc {}}
773     Baz* baz = construct!Baz(0,0);
774     destruct(baz);
775     assert(baz is null);
776 }
777 
778 unittest
779 {
780     struct Foo {int a; ulong b;}
781     Foo* f = construct!Foo(1,2);
782     assert(f.a == 1);
783     assert(f.b == 2);
784     destruct(f);
785 }
786 
787 unittest
788 {
789     import core.memory: GC;
790     import std.math: isNaN;
791 
792     auto f = newPtr!(float,true);
793     assert(isNaN(*f));
794     auto ui = newPtr!int;
795     auto i = newPtr!uint;
796     auto l = new ulong;
797     auto u = newPtr!uint(8);
798     assert(*u == 8u);
799 
800     assert(ui);
801     assert(i);
802     assert(f);
803 
804     assert(GC.addrOf(f) == null);
805     assert(GC.addrOf(i) == null);
806     assert(GC.addrOf(ui) == null);
807     assert(GC.addrOf(l) != null);
808     assert(GC.addrOf(u) == null);
809 
810     *i = 8u;
811     assert(*i == 8u);
812 
813     freeMem(ui);
814     freeMem(i);
815     freeMem(f);
816     freeMem(u);
817 
818     assert(ui == null);
819     assert(i == null);
820     assert(f == null);
821 
822     auto ptr = getMem(16);
823     assert(ptr);
824     assert(GC.addrOf(ptr) == null);
825     ptr.freeMem;
826     assert(!ptr);
827 }
828 
829 unittest
830 {
831     class A{string text; this(){text = "A";}}
832     class B{string text; this(){text = "B";}}
833     class C{int[] array; this(){array = [1,2,3];}}
834     enum TypeInfo_Class[3] tics = [typeid(A),typeid(B),typeid(C)];
835 
836     A a = cast(A) construct(tics[0]);
837     assert(a.text == "A");
838     B b = cast(B) construct(tics[1]);
839     assert(b.text == "B");
840     C c = cast(C) construct(tics[2]);
841     assert(c.array == [1,2,3]);
842 
843     destructEach(a, b, c);
844 }
845 
846 unittest
847 {
848     alias Factory = __gshared TypeInfo_Class[string];
849     Factory classRegistry;
850 
851     class A{string text; this(){text = "A";}}
852     class B{string text; this(){text = "B";}}
853     class C{int[] array; this(){array = [1,2,3];}}
854     registerFactoryClass!A(classRegistry);
855     classRegistry.registerFactoryClass!B;
856     classRegistry.registerFactoryClass!C;
857 
858     registerFactoryClasses!(A,B)(classRegistry);
859 
860     A a = cast(A) classRegistry.factory("A");
861     assert(a.text == "A");
862     B b = cast(B) classRegistry.factory("B");
863     assert(b.text == "B");
864     C c = cast(C) classRegistry.factory("C");
865     assert(c.array == [1,2,3]);
866 
867     version(checkleaks) {} else
868     {
869         import std.exception: assertThrown;
870         assertThrown(classRegistry.factory("D"));
871     }
872 
873     destructEach(a,b,c);
874 }
875 
876 @nogc unittest
877 {
878     void* src = getMem(32);
879     void* dst = getMem(32);
880     (cast (ubyte*)src)[0..32] = 8;
881     copyMem(dst, src, 32);
882     foreach(immutable i; 0 .. 32)
883         assert((cast (ubyte*)src)[i] == (cast (ubyte*)dst)[i]);
884     (cast (ubyte*)src)[0..32] = 7;
885     src = reallocMem(src, 32 + 16);
886     ubyte* ovl = (cast (ubyte*)src) + 16;
887     moveMem(cast(void*)ovl, cast(void*)src, 32);
888     assert((cast (ubyte*)ovl)[31] == 7);
889     freeMem(src);
890     freeMem(dst);
891 }
892 
893 @nogc unittest
894 {
895     @NoInit static struct Foo {uint a = 1; ~this() @nogc{}}
896     Foo* foo = construct!Foo;
897     assert(foo.a != 1);
898     @NoInit static class Bar {uint a = 1; ~this() @nogc{}}
899     Bar bar = construct!Bar;
900     assert(bar.a != 1);
901     destructEach(foo, bar);
902 }
903 
904 unittest
905 {
906     static int i;
907 
908     template impl()
909     {
910         ~this(){i += 1;}
911     }
912 
913     static class Base
914     {
915         mixin impl;
916         mixin inheritedDtor;
917         ~this(){i += 2;}
918     }
919 
920     static class Derived: Base
921     {
922         mixin inheritedDtor;
923         ~this(){i += 3; callInheritedDtor;}
924     }
925 
926     Base b = construct!Derived;
927     // without static type
928     destruct(cast(Object) b);
929     assert(i == 6);
930     i = 0;
931 
932     Derived d = construct!Derived;
933     // with static type
934     destruct(d);
935     assert(i == 6);
936 }
937