1 module automem;
2 
3 
4 version(unittest) {
5     import unit_threaded;
6     import test_allocator;
7 }
8 
9 
10 private void checkAllocator(T)() {
11     import std.experimental.allocator: make, dispose;
12     import std.traits: hasMember;
13 
14     static if(hasMember!(T, "instance"))
15         alias allocator = T.instance;
16     else
17         T allocator;
18 
19     int* i = allocator.make!int;
20     allocator.dispose(&i);
21     void[] bytes = allocator.allocate(size_t.init);
22     allocator.deallocate(bytes);
23 }
24 enum isAllocator(T) = is(typeof(checkAllocator!T));
25 
26 @("isAllocator")
27 @safe @nogc pure unittest {
28     import std.experimental.allocator.mallocator: Mallocator;
29     static assert(isAllocator!Mallocator);
30     static assert(isAllocator!TestAllocator);
31     static assert(!isAllocator!Struct);
32 }
33 
34 struct Unique(Type, Allocator) if(isAllocator!Allocator) {
35     import std.traits: hasMember;
36     import std.typecons: Proxy;
37 
38     enum hasInstance = hasMember!(Allocator, "instance");
39 
40     static if(is(Type == class))
41         alias Pointer = Type;
42     else
43         alias Pointer = Type*;
44 
45     static if(hasInstance)
46         /**
47            The allocator is a singleton, so no need to pass it in to the
48            constructor
49          */
50         this(Args...)(auto ref Args args) {
51             makeObject(args);
52         }
53     else
54         /**
55            Non-singleton allocator, must be passed in
56          */
57         this(Args...)(Allocator allocator, auto ref Args args) {
58             _allocator = allocator;
59             makeObject(args);
60         }
61 
62     this(T)(Unique!(T, Allocator) other) if(is(T: Type)) {
63         moveFrom(other);
64     }
65 
66     @disable this(this);
67 
68     ~this() {
69         deleteObject;
70     }
71 
72     /**
73        Gets the owned pointer. Use with caution.
74      */
75     inout(Pointer) get() inout @system {
76         return _object;
77     }
78 
79     /**
80        Releases ownership and transfers it to the returned
81        Unique object.
82      */
83     Unique unique() {
84         import std.algorithm: move;
85         Unique u;
86         move(this, u);
87         assert(_object is null);
88         return u;
89     }
90 
91     /**
92        "Truthiness" cast
93      */
94     bool opCast(T)() const if(is(T == bool)) {
95         return _object !is null;
96     }
97 
98     void opAssign(T)(Unique!(T, Allocator) other) if(is(T: Type)) {
99         deleteObject;
100         moveFrom(other);
101     }
102 
103     mixin Proxy!_object;
104 
105 private:
106 
107     Pointer _object;
108 
109     static if(hasInstance)
110         alias _allocator = Allocator.instance;
111     else
112         Allocator _allocator;
113 
114     void makeObject(Args...)(auto ref Args args) {
115         import std.experimental.allocator: make;
116         version(LDC)
117             _object = () @trusted { return _allocator.make!Type(args); }();
118         else
119             _object = _allocator.make!Type(args);
120     }
121 
122     void deleteObject() @safe {
123         import std.experimental.allocator: dispose;
124         if(_object !is null) () @trusted { _allocator.dispose(_object); }();
125     }
126 
127     void moveFrom(T)(ref Unique!(T, Allocator) other) if(is(T: Type)) {
128         _object = other._object;
129         other._object = null;
130 
131         static if(!hasInstance) {
132             import std.algorithm: move;
133             move(other._allocator, _allocator);
134         }
135     }
136 }
137 
138 
139 @("Unique with struct and test allocator")
140 @system unittest {
141 
142     auto allocator = TestAllocator();
143     {
144         const foo = Unique!(Struct, TestAllocator*)(&allocator, 5);
145         foo.twice.shouldEqual(10);
146         allocator.numAllocations.shouldEqual(1);
147         Struct.numStructs.shouldEqual(1);
148     }
149 
150     Struct.numStructs.shouldEqual(0);
151 }
152 
153 @("Unique with class and test allocator")
154 @system unittest {
155 
156     auto allocator = TestAllocator();
157     {
158         const foo = Unique!(Class, TestAllocator*)(&allocator, 5);
159         foo.twice.shouldEqual(10);
160         allocator.numAllocations.shouldEqual(1);
161         Class.numClasses.shouldEqual(1);
162     }
163 
164     Class.numClasses.shouldEqual(0);
165 }
166 
167 
168 @("Unique with struct and mallocator")
169 @system unittest {
170 
171     import std.experimental.allocator.mallocator: Mallocator;
172     {
173         const foo = Unique!(Struct, Mallocator)(5);
174         foo.twice.shouldEqual(10);
175         Struct.numStructs.shouldEqual(1);
176     }
177 
178     Struct.numStructs.shouldEqual(0);
179 }
180 
181 
182 @("Unique default constructor")
183 @system unittest {
184     auto allocator = TestAllocator();
185 
186     auto ptr = Unique!(Struct, TestAllocator*)();
187     (cast(bool)ptr).shouldBeFalse;
188     ptr.get.shouldBeNull;
189 
190     ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
191     ptr.get.shouldNotBeNull;
192     ptr.get.twice.shouldEqual(10);
193     (cast(bool)ptr).shouldBeTrue;
194 }
195 
196 @("Unique .init")
197 @system unittest {
198     auto allocator = TestAllocator();
199 
200     Unique!(Struct, TestAllocator*) ptr;
201     (cast(bool)ptr).shouldBeFalse;
202     ptr.get.shouldBeNull;
203 
204     ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
205     ptr.get.shouldNotBeNull;
206     ptr.get.twice.shouldEqual(10);
207     (cast(bool)ptr).shouldBeTrue;
208 }
209 
210 @("Unique move")
211 @system unittest {
212     import std.algorithm: move;
213 
214     auto allocator = TestAllocator();
215     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
216     Unique!(Struct, TestAllocator*) newPtr;
217     move(oldPtr, newPtr);
218     oldPtr.shouldBeNull;
219     newPtr.twice.shouldEqual(10);
220     Struct.numStructs.shouldEqual(1);
221 }
222 
223 @("Unique copy")
224 @system unittest {
225     auto allocator = TestAllocator();
226     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
227     Unique!(Struct, TestAllocator*) newPtr;
228     // non-copyable
229     static assert(!__traits(compiles, newPtr = oldPtr));
230 }
231 
232 @("Unique construct base class")
233 @system unittest {
234     auto allocator = TestAllocator();
235     {
236         Unique!(Object, TestAllocator*) bar = Unique!(Class, TestAllocator*)(&allocator, 5);
237         Class.numClasses.shouldEqual(1);
238     }
239 
240     Class.numClasses.shouldEqual(0);
241 }
242 
243 @("Unique assign base class")
244 @system unittest {
245     auto allocator = TestAllocator();
246     {
247         Unique!(Object, TestAllocator*) bar;
248         bar = Unique!(Class, TestAllocator*)(&allocator, 5);
249         Class.numClasses.shouldEqual(1);
250     }
251 
252     Class.numClasses.shouldEqual(0);
253 }
254 
255 @("Return Unique from function")
256 @system unittest {
257     auto allocator = TestAllocator();
258 
259     auto produce(int i) {
260         return Unique!(Struct, TestAllocator*)(&allocator, i);
261     }
262 
263     auto ptr = produce(4);
264     ptr.twice.shouldEqual(8);
265 }
266 
267 @("Unique unique")
268 @system unittest {
269     auto allocator = TestAllocator();
270     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
271     auto newPtr = oldPtr.unique;
272     newPtr.twice.shouldEqual(10);
273     oldPtr.shouldBeNull;
274 }
275 
276 @("Unique @nogc")
277 @system @nogc unittest {
278 
279     import std.experimental.allocator.mallocator: Mallocator;
280 
281     {
282         const ptr = Unique!(NoGcStruct, Mallocator)(5);
283         // shouldEqual isn't @nogc
284         assert(ptr.i == 5);
285         assert(NoGcStruct.numStructs == 1);
286     }
287 
288     assert(NoGcStruct.numStructs == 0);
289 }
290 
291 @("Unique @nogc @safe")
292 @safe @nogc unittest {
293 
294     auto allocator = SafeAllocator();
295 
296     {
297         const ptr = Unique!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6);
298         // shouldEqual isn't @nogc
299         assert(ptr.i == 6);
300         assert(NoGcStruct.numStructs == 1);
301     }
302 
303     assert(NoGcStruct.numStructs == 0);
304 }
305 
306 @("Unique deref")
307 @system unittest {
308     {
309         auto allocator = TestAllocator();
310         auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
311         *ptr = Struct(13);
312         ptr.twice.shouldEqual(26);
313         Struct.numStructs.shouldEqual(1);
314     }
315     Struct.numStructs.shouldEqual(0);
316 }
317 
318 @("Unique move from populated other unique")
319 @system unittest {
320 
321     import std.algorithm: move;
322 
323     {
324         auto allocator = TestAllocator();
325 
326         auto ptr1 = Unique!(Struct, TestAllocator*)(&allocator, 5);
327         Struct.numStructs.shouldEqual(1);
328 
329         {
330             auto ptr2 = Unique!(Struct, TestAllocator*)(&allocator, 10);
331             Struct.numStructs.shouldEqual(2);
332             move(ptr2, ptr1);
333             Struct.numStructs.shouldEqual(1);
334             ptr2.shouldBeNull;
335             ptr1.twice.shouldEqual(20);
336         }
337 
338     }
339 
340     Struct.numStructs.shouldEqual(0);
341 }
342 
343 @("Unique assign to rvalue")
344 @system unittest {
345 
346     import std.algorithm: move;
347 
348     {
349         auto allocator = TestAllocator();
350 
351         auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
352         ptr = Unique!(Struct, TestAllocator*)(&allocator, 7);
353 
354         Struct.numStructs.shouldEqual(1);
355         ptr.twice.shouldEqual(14);
356     }
357 
358     Struct.numStructs.shouldEqual(0);
359 }
360 
361 
362 
363 struct RefCounted(Type, Allocator) if(isAllocator!Allocator) {
364     import std.traits: hasMember;
365     import std.typecons: Proxy;
366 
367     enum hasInstance = hasMember!(Allocator, "instance");
368 
369     static if(is(Type == class))
370         alias Pointer = Type;
371     else
372         alias Pointer = Type*;
373 
374     static if(hasInstance)
375         /**
376            The allocator is a singleton, so no need to pass it in to the
377            constructor
378         */
379         this(Args...)(auto ref Args args) {
380             makeObject(args);
381         }
382     else
383         /**
384            Non-singleton allocator, must be passed in
385         */
386         this(Args...)(Allocator allocator, auto ref Args args) {
387             _allocator = allocator;
388             makeObject(args);
389         }
390 
391     this(this) {
392         assert(_impl !is null);
393         inc;
394     }
395 
396     ~this() {
397         release;
398     }
399 
400     /**
401        Assign to an lvalue RefCounted
402     */
403     void opAssign(ref RefCounted other) {
404 
405         if(_impl !is null) {
406             release;
407         }
408         static if(!hasInstance)
409             _allocator = other._allocator;
410 
411         _impl = other._impl;
412         inc;
413     }
414 
415     /**
416        Assign to an rvalue RefCounted
417      */
418     void opAssign(RefCounted other) {
419         import std.algorithm: swap;
420         swap(_impl, other._impl);
421         static if(!hasInstance)
422             swap(_allocator, other._allocator);
423     }
424 
425     /**
426        Dereference the smart pointer and yield a reference
427        to the contained type.
428      */
429     ref inout(Type) opUnary(string s)() inout if(s == "*") {
430         return _impl._object;
431     }
432 
433     alias _impl this;
434 
435 private:
436 
437     static struct Impl {
438         Type _object;
439 
440         static if(is(Type == shared))
441             shared size_t _count;
442         else
443             size_t _count;
444 
445         alias _object this;
446     }
447 
448     static if(hasInstance)
449         alias _allocator = Allocator.instance;
450     else
451         Allocator _allocator;
452 
453     Impl* _impl;
454 
455     void makeObject(Args...)(auto ref Args args) {
456         import std.conv: emplace;
457 
458         allocateImpl;
459         emplace(&_impl._object, args);
460     }
461 
462     void allocateImpl() {
463         import std.experimental.allocator: make;
464         import std.traits: hasIndirections;
465 
466         _impl = cast(Impl*)_allocator.allocate(Impl.sizeof);
467         _impl._count= 1;
468 
469         static if (hasIndirections!Type) {
470             import core.memory: GC;
471             GC.addRange(&_impl._object, Type.sizeof);
472         }
473     }
474 
475     void release() {
476         if(_impl is null) return;
477         assert(_impl._count > 0);
478 
479         dec;
480 
481         if(_impl._count == 0) {
482             destroy(_impl._object);
483             auto mem = cast(void*)_impl;
484             _allocator.deallocate(mem[0 .. Impl.sizeof]);
485         }
486     }
487 
488     void inc() {
489         static if(is(Type == shared)) {
490             import core.atomic: atomicOp;
491             _impl._count.atomicOp!"+="(1);
492         } else
493             ++_impl._count;
494 
495     }
496 
497     void dec() {
498         static if(is(Type == shared)) {
499             import core.atomic: atomicOp;
500             _impl._count.atomicOp!"-="(1);
501         } else
502             --_impl._count;
503     }
504 
505 }
506 
507 @("RefCounted struct test allocator no copies")
508 @system unittest {
509     auto allocator = TestAllocator();
510     {
511         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
512         Struct.numStructs.shouldEqual(1);
513     }
514     Struct.numStructs.shouldEqual(0);
515 }
516 
517 @("RefCounted struct test allocator one lvalue assignment")
518 @system unittest {
519     auto allocator = TestAllocator();
520     {
521         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
522         Struct.numStructs.shouldEqual(1);
523         RefCounted!(Struct, TestAllocator*) ptr2;
524         ptr2 = ptr1;
525         Struct.numStructs.shouldEqual(1);
526     }
527     Struct.numStructs.shouldEqual(0);
528 }
529 
530 @("RefCounted struct test allocator one rvalue assignment test allocator")
531 @system unittest {
532     auto allocator = TestAllocator();
533     {
534         RefCounted!(Struct, TestAllocator*) ptr;
535         ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
536         Struct.numStructs.shouldEqual(1);
537     }
538     Struct.numStructs.shouldEqual(0);
539 }
540 
541 @("RefCounted struct test allocator one rvalue assignment mallocator")
542 @system unittest {
543     import std.experimental.allocator.mallocator: Mallocator;
544     {
545         RefCounted!(Struct, Mallocator) ptr;
546         ptr = RefCounted!(Struct, Mallocator)(5);
547         Struct.numStructs.shouldEqual(1);
548     }
549     Struct.numStructs.shouldEqual(0);
550 }
551 
552 
553 @("RefCounted struct test allocator one lvalue copy constructor")
554 @system unittest {
555     auto allocator = TestAllocator();
556     {
557         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
558         Struct.numStructs.shouldEqual(1);
559         auto ptr2 = ptr1;
560         Struct.numStructs.shouldEqual(1);
561 
562         ptr1.i.shouldEqual(5);
563         ptr2.i.shouldEqual(5);
564     }
565     Struct.numStructs.shouldEqual(0);
566 }
567 
568 @("RefCounted struct test allocator one rvalue copy constructor")
569 @system unittest {
570     auto allocator = TestAllocator();
571     {
572         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
573         Struct.numStructs.shouldEqual(1);
574     }
575     Struct.numStructs.shouldEqual(0);
576 }
577 
578 @("RefCounted many copies made")
579 @system unittest {
580     auto allocator = TestAllocator();
581 
582     // helper function for intrusive testing, in case the implementation
583     // ever changes
584     size_t refCount(T)(ref T ptr) {
585         return ptr._impl._count;
586     }
587 
588     {
589         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
590         Struct.numStructs.shouldEqual(1);
591 
592         auto ptr2 = ptr1;
593         Struct.numStructs.shouldEqual(1);
594 
595         {
596             auto ptr3 = ptr2;
597             Struct.numStructs.shouldEqual(1);
598 
599             refCount(ptr1).shouldEqual(3);
600             refCount(ptr2).shouldEqual(3);
601             refCount(ptr3).shouldEqual(3);
602         }
603 
604         Struct.numStructs.shouldEqual(1);
605         refCount(ptr1).shouldEqual(2);
606         refCount(ptr2).shouldEqual(2);
607 
608         auto produce() {
609             return RefCounted!(Struct, TestAllocator*)(&allocator, 3);
610         }
611 
612         ptr1 = produce;
613         Struct.numStructs.shouldEqual(2);
614         refCount(ptr1).shouldEqual(1);
615         refCount(ptr2).shouldEqual(1);
616 
617         ptr1.twice.shouldEqual(6);
618         ptr2.twice.shouldEqual(10);
619     }
620 
621     Struct.numStructs.shouldEqual(0);
622 }
623 
624 
625 @("RefCounted deref")
626 @system unittest {
627     auto allocator = TestAllocator();
628     auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5);
629 
630     (*rc1).shouldEqual(5);
631     auto rc2 = rc1;
632     *rc2 = 42;
633     (*rc1).shouldEqual(42);
634 }
635 
636 @("RefCounted swap")
637 @system unittest {
638     import std.algorithm: swap;
639     RefCounted!(int, TestAllocator*) rc1, rc2;
640     swap(rc1, rc2);
641 }
642 
643 @("phobos bug 6606")
644 @system unittest {
645 
646     union U {
647        size_t i;
648        void* p;
649     }
650 
651     struct S {
652        U u;
653     }
654 
655     alias SRC = RefCounted!(S, TestAllocator*);
656 }
657 
658 @("phobos bug 6436")
659 @system unittest
660 {
661     static struct S {
662         this(ref int val, string file = __FILE__, size_t line = __LINE__) {
663             val.shouldEqual(3, file, line);
664             ++val;
665         }
666     }
667 
668     auto allocator = TestAllocator();
669     int val = 3;
670     auto s = RefCounted!(S, TestAllocator*)(&allocator, val);
671     val.shouldEqual(4);
672 }
673 
674 @("RefCounted assign from T")
675 @system unittest {
676     import std.experimental.allocator.mallocator: Mallocator;
677 
678     {
679         auto a = RefCounted!(Struct, Mallocator)(3);
680         Struct.numStructs.shouldEqual(1);
681 
682         *a = Struct(5);
683         Struct.numStructs.shouldEqual(1);
684         (*a).shouldEqual(Struct(5));
685 
686         RefCounted!(Struct, Mallocator) b;
687         b = a;
688         (*b).shouldEqual(Struct(5));
689         Struct.numStructs.shouldEqual(1);
690     }
691 
692     Struct.numStructs.shouldEqual(0);
693 }
694 
695 version(LDC) {}
696 else {
697     @("RefCounted SharedStruct")
698         @system unittest {
699         auto allocator = TestAllocator();
700         {
701             auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5);
702             SharedStruct.numStructs.shouldEqual(1);
703         }
704         SharedStruct.numStructs.shouldEqual(0);
705     }
706 }
707 
708 
709 version(unittest) {
710 
711     void _writelnUt(T...)(T args) {
712         try {
713             () @trusted { writelnUt(args); }();
714         } catch(Exception ex) {
715             assert(false);
716         }
717     }
718 
719     private struct Struct {
720         int i;
721         static int numStructs = 0;
722 
723         this(int i) @safe nothrow {
724             this.i = i;
725 
726             ++numStructs;
727             _writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs);
728         }
729 
730         this(this) @safe nothrow {
731             ++numStructs;
732             _writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs);
733         }
734 
735         ~this() @safe nothrow {
736             --numStructs;
737             _writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs);
738         }
739 
740         int twice() @safe pure const nothrow {
741             return i * 2;
742         }
743     }
744 
745     private struct SharedStruct {
746         int i;
747         static int numStructs = 0;
748 
749         this(int i) @safe nothrow shared {
750             this.i = i;
751 
752             ++numStructs;
753             try () @trusted {
754                     writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs);
755                 }();
756             catch(Exception ex) {}
757         }
758 
759         this(this) @safe nothrow shared {
760             ++numStructs;
761             try () @trusted {
762                     writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs);
763                 }();
764             catch(Exception ex) {}
765         }
766 
767         ~this() @safe nothrow shared {
768             --numStructs;
769             try () @trusted { writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); }();
770             catch(Exception ex) {}
771         }
772 
773         int twice() @safe pure const nothrow shared {
774             return i * 2;
775         }
776     }
777 
778     private class Class {
779         int i;
780         static int numClasses = 0;
781 
782         this(int i) @safe nothrow {
783             this.i = i;
784             ++numClasses;
785         }
786 
787         ~this() @safe nothrow {
788             --numClasses;
789         }
790 
791         int twice() @safe pure const nothrow {
792             return i * 2;
793         }
794     }
795 
796     private struct SafeAllocator {
797 
798         import std.experimental.allocator.mallocator: Mallocator;
799 
800         void[] allocate(size_t i) @trusted nothrow @nogc {
801             return Mallocator.instance.allocate(i);
802         }
803 
804         void deallocate(void[] bytes) @trusted nothrow @nogc {
805             Mallocator.instance.deallocate(bytes);
806         }
807     }
808 
809     static struct NoGcStruct {
810         int i;
811 
812         static int numStructs = 0;
813 
814         this(int i) @safe @nogc nothrow {
815             this.i = i;
816 
817             ++numStructs;
818         }
819 
820         this(this) @safe @nogc nothrow {
821             ++numStructs;
822         }
823 
824         ~this() @safe @nogc nothrow {
825             --numStructs;
826         }
827 
828     }
829 }