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