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 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     inout(Pointer) get() @safe pure nothrow inout {
73         return _object;
74     }
75 
76     Unique unique() {
77         import std.algorithm: move;
78         Unique u;
79         move(this, u);
80         assert(_object is null);
81         return u;
82     }
83 
84     Pointer release() @safe pure nothrow {
85         auto ret = _object;
86         _object = null;
87         return ret;
88     }
89 
90     void reset(Pointer newObject) {
91         deleteObject;
92         _object = newObject;
93     }
94 
95     bool opCast(T)() @safe pure nothrow const if(is(T == bool)) {
96         return _object !is null;
97     }
98 
99     void opAssign(T)(Unique!(T, Allocator) other) if(is(T: Type)) {
100         deleteObject;
101         moveFrom(other);
102     }
103 
104     mixin Proxy!_object;
105 
106 private:
107 
108     Pointer _object;
109 
110     static if(hasInstance)
111         alias _allocator = Allocator.instance;
112     else
113         Allocator _allocator;
114 
115     void makeObject(Args...)(auto ref Args args) {
116         import std.experimental.allocator: make;
117         _object = _allocator.make!Type(args);
118     }
119 
120     void deleteObject() {
121         import std.experimental.allocator: dispose;
122         if(_object !is null) _allocator.dispose(_object);
123     }
124 
125     void moveFrom(T)(ref Unique!(T, Allocator) other) if(is(T: Type)) {
126         _object = other._object;
127         other._object = null;
128 
129         static if(!hasInstance) {
130             import std.algorithm: move;
131             move(other._allocator, _allocator);
132         }
133     }
134 }
135 
136 
137 @("Unique with struct and test allocator")
138 @system unittest {
139 
140     auto allocator = TestAllocator();
141     {
142         const foo = Unique!(Struct, TestAllocator*)(&allocator, 5);
143         foo.twice.shouldEqual(10);
144         allocator.numAllocations.shouldEqual(1);
145         Struct.numStructs.shouldEqual(1);
146     }
147 
148     Struct.numStructs.shouldEqual(0);
149 }
150 
151 @("Unique with class and test allocator")
152 @system unittest {
153 
154     auto allocator = TestAllocator();
155     {
156         const foo = Unique!(Class, TestAllocator*)(&allocator, 5);
157         foo.twice.shouldEqual(10);
158         allocator.numAllocations.shouldEqual(1);
159         Class.numClasses.shouldEqual(1);
160     }
161 
162     Class.numClasses.shouldEqual(0);
163 }
164 
165 
166 @("Unique with struct and mallocator")
167 @system unittest {
168 
169     import std.experimental.allocator.mallocator: Mallocator;
170     {
171         const foo = Unique!(Struct, Mallocator)(5);
172         foo.twice.shouldEqual(10);
173         Struct.numStructs.shouldEqual(1);
174     }
175 
176     Struct.numStructs.shouldEqual(0);
177 }
178 
179 
180 @("Unique default constructor")
181 @system unittest {
182     auto allocator = TestAllocator();
183 
184     auto ptr = Unique!(Struct, TestAllocator*)();
185     (cast(bool)ptr).shouldBeFalse;
186     ptr.get.shouldBeNull;
187 
188     ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
189     ptr.get.shouldNotBeNull;
190     ptr.get.twice.shouldEqual(10);
191     (cast(bool)ptr).shouldBeTrue;
192 }
193 
194 
195 @("Unique release")
196 @system unittest {
197     import std.experimental.allocator: dispose;
198 
199     auto allocator = TestAllocator();
200 
201     auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
202     auto obj = ptr.release;
203     obj.twice.shouldEqual(10);
204     allocator.dispose(obj);
205 }
206 
207 @("Unique reset")
208 @system unittest {
209     import std.experimental.allocator: make;
210 
211     auto allocator = TestAllocator();
212 
213     auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5);
214     ptr.reset(allocator.make!Struct(2));
215     ptr.twice.shouldEqual(4);
216 }
217 
218 @("Unique move")
219 @system unittest {
220     import std.algorithm: move;
221 
222     auto allocator = TestAllocator();
223     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
224     Unique!(Struct, TestAllocator*) newPtr;
225     move(oldPtr, newPtr);
226     oldPtr.shouldBeNull;
227     newPtr.twice.shouldEqual(10);
228 }
229 
230 @("Unique copy")
231 @system unittest {
232     import std.algorithm: move;
233 
234     auto allocator = TestAllocator();
235     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
236     Unique!(Struct, TestAllocator*) newPtr;
237     // non-copyable
238     static assert(!__traits(compiles, newPtr = oldPtr));
239 }
240 
241 @("Unique construct base class")
242 @system unittest {
243     auto allocator = TestAllocator();
244     {
245         Unique!(Object, TestAllocator*) bar = Unique!(Class, TestAllocator*)(&allocator, 5);
246         Class.numClasses.shouldEqual(1);
247     }
248 
249     Class.numClasses.shouldEqual(0);
250 }
251 
252 @("Unique assign base class")
253 @system unittest {
254     import std.algorithm: move;
255     auto allocator = TestAllocator();
256     {
257         Unique!(Object, TestAllocator*) bar;
258         bar = Unique!(Class, TestAllocator*)(&allocator, 5);
259         Class.numClasses.shouldEqual(1);
260     }
261 
262     Class.numClasses.shouldEqual(0);
263 }
264 
265 @("Return Unique from function")
266 @system unittest {
267     auto allocator = TestAllocator();
268 
269     auto produce(int i) {
270         return Unique!(Struct, TestAllocator*)(&allocator, i);
271     }
272 
273     auto ptr = produce(4);
274     ptr.twice.shouldEqual(8);
275 }
276 
277 @("Unique unique")
278 @system unittest {
279     auto allocator = TestAllocator();
280     auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5);
281     auto newPtr = oldPtr.unique;
282     newPtr.twice.shouldEqual(10);
283     oldPtr.shouldBeNull;
284 }
285 
286 
287 struct RefCounted(Type, Allocator) if(isAllocator!Allocator) {
288     import std.traits: hasMember;
289     import std.typecons: Proxy;
290 
291     enum hasInstance = hasMember!(Allocator, "instance");
292 
293     static if(is(Type == class))
294         alias Pointer = Type;
295     else
296         alias Pointer = Type*;
297 
298     static if(hasInstance)
299         /**
300            The allocator is a singleton, so no need to pass it in to the
301            constructor
302         */
303         this(Args...)(auto ref Args args) {
304             makeObject(args);
305         }
306     else
307         /**
308            Non-singleton allocator, must be passed in
309         */
310         this(Args...)(Allocator allocator, auto ref Args args) {
311             _allocator = allocator;
312             makeObject(args);
313         }
314 
315     this(this) @safe pure nothrow @nogc {
316         assert(_impl !is null);
317         ++_impl._count;
318     }
319 
320     ~this() {
321         release;
322     }
323 
324     void opAssign(ref RefCounted other) {
325 
326         if(_impl !is null) {
327             release;
328         }
329         static if(!hasInstance)
330             _allocator = other._allocator;
331 
332         _impl = other._impl;
333         ++_impl._count;
334     }
335 
336     void opAssign(RefCounted other) {
337         import std.algorithm: swap;
338         swap(_impl, other._impl);
339         static if(!hasInstance)
340             swap(_allocator, other._allocator);
341     }
342 
343     /**
344      If the allocator isn't a singleton, assigning to the raw type is unsafe.
345      If RefCounted was default-contructed then there is no allocator
346      */
347     static if(hasInstance) {
348         void opAssign(Type object) {
349             import std.algorithm: move;
350 
351             if(_impl is null) {
352                 allocateImpl;
353             }
354 
355             move(object, _impl._object);
356         }
357     }
358 
359     ref inout(Type) get() inout {
360         return _impl._object;
361     }
362 
363     alias _impl this;
364 
365 private:
366 
367     static struct Impl {
368         Type _object;
369         size_t _count;
370         alias _object this;
371     }
372 
373     static if(hasInstance)
374         alias _allocator = Allocator.instance;
375     else
376         Allocator _allocator;
377 
378     Impl* _impl;
379 
380     void makeObject(Args...)(auto ref Args args) {
381         import std.conv: emplace;
382 
383         allocateImpl;
384         emplace(&_impl._object, args);
385     }
386 
387     void allocateImpl() {
388         import std.experimental.allocator: make;
389         import std.traits: hasIndirections;
390         import core.memory : GC;
391 
392         _impl = cast(Impl*)_allocator.allocate(Impl.sizeof);
393         _impl._count= 1;
394 
395         static if (hasIndirections!Type)
396             GC.addRange(&_impl._object, Type.sizeof);
397     }
398 
399     void release() {
400         if(_impl is null) return;
401         assert(_impl._count > 0);
402 
403         --_impl._count;
404 
405         if(_impl._count == 0) {
406             destroy(_impl._object);
407             auto mem = cast(void*)_impl;
408             _allocator.deallocate(mem[0 .. Impl.sizeof]);
409         }
410     }
411 
412 }
413 
414 @("RefCounted struct test allocator no copies")
415 @system unittest {
416     auto allocator = TestAllocator();
417     {
418         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
419         Struct.numStructs.shouldEqual(1);
420     }
421     Struct.numStructs.shouldEqual(0);
422 }
423 
424 @("RefCounted struct test allocator one lvalue assignment")
425 @system unittest {
426     auto allocator = TestAllocator();
427     {
428         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
429         Struct.numStructs.shouldEqual(1);
430         RefCounted!(Struct, TestAllocator*) ptr2;
431         ptr2 = ptr1;
432         Struct.numStructs.shouldEqual(1);
433     }
434     Struct.numStructs.shouldEqual(0);
435 }
436 
437 @("RefCounted struct test allocator one rvalue assignment test allocator")
438 @system unittest {
439     auto allocator = TestAllocator();
440     {
441         RefCounted!(Struct, TestAllocator*) ptr;
442         ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
443         Struct.numStructs.shouldEqual(1);
444     }
445     Struct.numStructs.shouldEqual(0);
446 }
447 
448 @("RefCounted struct test allocator one rvalue assignment mallocator")
449 @system unittest {
450     import std.experimental.allocator.mallocator: Mallocator;
451     {
452         RefCounted!(Struct, Mallocator) ptr;
453         ptr = RefCounted!(Struct, Mallocator)(5);
454         Struct.numStructs.shouldEqual(1);
455     }
456     Struct.numStructs.shouldEqual(0);
457 }
458 
459 
460 @("RefCounted struct test allocator one lvalue copy constructor")
461 @system unittest {
462     auto allocator = TestAllocator();
463     {
464         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
465         Struct.numStructs.shouldEqual(1);
466         auto ptr2 = ptr1;
467         Struct.numStructs.shouldEqual(1);
468     }
469     Struct.numStructs.shouldEqual(0);
470 }
471 
472 @("RefCounted struct test allocator one rvalue copy constructor")
473 @system unittest {
474     auto allocator = TestAllocator();
475     {
476         auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
477         Struct.numStructs.shouldEqual(1);
478     }
479     Struct.numStructs.shouldEqual(0);
480 }
481 
482 @("RefCounted many copies made")
483 @system unittest {
484     auto allocator = TestAllocator();
485 
486     // helper function for intrusive testing, in case the implementation
487     // ever changes
488     size_t refCount(T)(ref T ptr) {
489         return ptr._impl._count;
490     }
491 
492     {
493         auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5);
494         Struct.numStructs.shouldEqual(1);
495 
496         auto ptr2 = ptr1;
497         Struct.numStructs.shouldEqual(1);
498 
499         {
500             auto ptr3 = ptr2;
501             Struct.numStructs.shouldEqual(1);
502 
503             refCount(ptr1).shouldEqual(3);
504             refCount(ptr2).shouldEqual(3);
505             refCount(ptr3).shouldEqual(3);
506         }
507 
508         Struct.numStructs.shouldEqual(1);
509         refCount(ptr1).shouldEqual(2);
510         refCount(ptr2).shouldEqual(2);
511 
512         auto produce() {
513             return RefCounted!(Struct, TestAllocator*)(&allocator, 3);
514         }
515 
516         ptr1 = produce;
517         Struct.numStructs.shouldEqual(2);
518         refCount(ptr1).shouldEqual(1);
519         refCount(ptr2).shouldEqual(1);
520 
521         ptr1.twice.shouldEqual(6);
522         ptr2.twice.shouldEqual(10);
523     }
524 
525     Struct.numStructs.shouldEqual(0);
526 }
527 
528 
529 // TODO: get this to compile
530 // @("RefCounted reference semantics")
531 // @system unittest {
532 //     auto allocator = TestAllocator();
533 //     auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5);
534 
535 //     rc1.shouldEqual(5);
536 //     auto rc2 = rc1;
537 //     rc2 = 42;
538 //     rc1.shouldEqual(42);
539 // }
540 
541 @("RefCounted swap")
542 @system unittest {
543     import std.algorithm: swap;
544     RefCounted!(int, TestAllocator*) rc1, rc2;
545     swap(rc1, rc2);
546 }
547 
548 @("phobos bug 6606")
549 @system unittest {
550 
551     union U {
552        size_t i;
553        void* p;
554     }
555 
556     struct S {
557        U u;
558     }
559 
560     alias SRC = RefCounted!(S, TestAllocator*);
561 }
562 
563 @("phobos bug 6436")
564 @system unittest
565 {
566     static struct S {
567         this(ref int val, string file = __FILE__, size_t line = __LINE__) {
568             val.shouldEqual(3, file, line);
569             ++val;
570         }
571     }
572 
573     auto allocator = TestAllocator();
574     int val = 3;
575     auto s = RefCounted!(S, TestAllocator*)(&allocator, val);
576     val.shouldEqual(4);
577 }
578 
579 @("RefCounted assign from T")
580 @system unittest {
581     import std.experimental.allocator.mallocator: Mallocator;
582 
583     {
584         auto a = RefCounted!(Struct, Mallocator)(3);
585         Struct.numStructs.shouldEqual(1);
586 
587         a = Struct(5);
588         ++Struct.numStructs; // compensate for move not calling the constructor
589         Struct.numStructs.shouldEqual(1);
590         // TODO - change this to not use get
591         a.get.shouldEqual(Struct(5));
592 
593         RefCounted!(Struct, Mallocator) b;
594         b = a;
595         // TODO - change this to not use get
596         b.get.shouldEqual(Struct(5));
597         Struct.numStructs.shouldEqual(1);
598     }
599 
600     Struct.numStructs.shouldEqual(0);
601 }
602 
603 version(unittest) {
604 
605     private struct Struct {
606         int i;
607         static int numStructs = 0;
608 
609         this(int i) @safe nothrow {
610             this.i = i;
611 
612             ++numStructs;
613             try () @trusted {
614                     writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs);
615                 }();
616             catch(Exception ex) {}
617         }
618 
619         this(this) @safe nothrow {
620             ++numStructs;
621             try () @trusted {
622                     writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs);
623                 }();
624             catch(Exception ex) {}
625         }
626 
627         ~this() @safe nothrow {
628             --numStructs;
629             try () @trusted { writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); }();
630             catch(Exception ex) {}
631         }
632 
633         int twice() @safe pure const nothrow {
634             return i * 2;
635         }
636     }
637 
638     private class Class {
639         int i;
640         static int numClasses = 0;
641 
642         this(int i) @safe nothrow {
643             this.i = i;
644             ++numClasses;
645         }
646 
647         ~this() @safe nothrow {
648             --numClasses;
649         }
650 
651         int twice() @safe pure const nothrow {
652             return i * 2;
653         }
654     }
655 }