1 /**
2     Implementation of non-owning pointer `GlobalPtr`.
3 
4     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
5     Authors:   Adam Búš
6 */
7 module btl.autoptr.global_ptr;
8 
9 import btl.traits.common;
10 
11 import btl.internal.traits;
12 
13 import btl.autoptr.common;
14 
15 
16 /**
17     Check if type `T` is `isGlobalPtr`.
18 */
19 public template isGlobalPtr(T){
20     import std.traits : isInstanceOf;
21 
22     enum bool isGlobalPtr = isInstanceOf!(GlobalPtr, T);
23 }
24 
25 ///
26 unittest{
27 	static assert(!isGlobalPtr!long);
28 	static assert(!isGlobalPtr!(void*));
29 
30 	static assert(isGlobalPtr!(GlobalPtr!long));
31 }
32 
33 
34 
35 /**
36 	Implementation of non-owning pointer to global data (static, gc, ...).
37     
38     `scope` global ptr can be assigned/initialized with non-scoped raw pointer/reference or with other scope GlobalPtr.
39     
40 	Template parameters:
41 
42 		`_Type` type of managed object
43 */
44 public template GlobalPtr(_Type){
45     import std.traits : CopyTypeQualifiers, Unqual, isDynamicArray;
46 
47     enum bool referenceElementType = isClassOrInterface!_Type || isDynamicArray!_Type;
48 
49     struct GlobalPtr{
50         /**
51             Type of element managed by `RcPtr`.
52         */
53         public alias ElementType = _Type;
54 
55 
56         /**
57             Same as `ElementType*` or `ElementType` if is class/interface/slice.
58         */
59         public alias ElementReferenceType = ElementReferenceTypeImpl!ElementType;
60 
61 
62 
63         /**
64             Constructs a `GlobalPtr` without managed object. Same as `GlobalPtr.init`
65 
66             Examples:
67                 --------------------
68                 GlobalPtr!long x = null;
69 
70                 assert(x == null);
71                 assert(x == GlobalPtr!long.init);
72                 --------------------
73         */
74         public this(typeof(null) nil, Forward = Forward.init)scope pure nothrow @safe @nogc{
75         }
76 
77 
78 
79         /**
80             Constructs a `GlobalPtr` without managed object. Same as `GlobalPtr.init`
81 
82             Examples:
83                 --------------------
84                 {
85                     GlobalPtr!long x = new long(42);
86 
87                     assert(x != null);
88                     assert(x.get == 42);
89                 }
90 
91                 {
92                     static const long i = 42;
93                     GlobalPtr!(const long) x = &i;
94 
95                     assert(x != null);
96                     assert(x.get == 42);
97                 }
98                 --------------------
99         */
100         public this(ElementReferenceType elm, Forward = Forward.init)scope pure nothrow @safe @nogc{
101             this._element = elm;
102         }
103 
104         /// ditto
105         public this(const ElementReferenceType elm, Forward = Forward.init)scope const pure nothrow @safe @nogc{
106             this._element = elm;
107         }
108 
109         /// ditto
110         public this(immutable ElementReferenceType elm, Forward = Forward.init)scope immutable pure nothrow @safe @nogc{
111             this._element = elm;
112         }
113 
114         /// ditto
115         public this(shared ElementReferenceType elm, Forward = Forward.init)scope shared pure nothrow @safe @nogc{
116             this._element = elm;
117         }
118 
119         /// ditto
120         public this(const shared ElementReferenceType elm, Forward = Forward.init)scope const shared pure nothrow @safe @nogc{
121             this._element = elm;
122         }
123 
124 
125 
126         /**
127             Forward constructor (merge move and copy constructor).
128         */
129         public this(Rhs, this This)(scope auto ref Rhs rhs)@trusted pure nothrow @nogc
130         if(isGlobalPtr!Rhs
131             && isConstructable!(Rhs, This)
132             && !is(Rhs == shared)
133             && !isMoveCtor!(This, rhs)
134         ){
135             this._element = rhs._element;
136         }
137 
138         public this(Rhs, this This)(scope auto ref Rhs rhs, Forward fw)@trusted pure nothrow @nogc
139         if(isGlobalPtr!Rhs
140             && isConstructable!(Rhs, This)
141             && !is(Rhs == shared)
142         ){
143             this._element = rhs._element;
144         }
145 
146         //copy ctors:
147         static if(isCopyConstructable!(typeof(this), typeof(this)))
148             this(ref scope typeof(this) rhs)@safe{this(rhs, Forward.init);}
149         else
150             @disable this(ref scope typeof(this) rhs)@safe;
151 
152         static if(isCopyConstructable!(typeof(this), const typeof(this)))
153             this(ref scope typeof(this) rhs)const @safe{this(rhs, Forward.init);}
154         else
155             @disable this(ref scope typeof(this) rhs)const @safe;
156 
157         static if(isCopyConstructable!(typeof(this), immutable typeof(this)))
158             this(ref scope typeof(this) rhs)immutable @safe{this(rhs, Forward.init);}
159         else
160             @disable this(ref scope typeof(this) rhs)immutable @safe;
161 
162         static if(isCopyConstructable!(typeof(this), shared typeof(this)))
163             this(ref scope typeof(this) rhs)shared @safe{this(rhs, Forward.init);}
164         else
165             @disable this(ref scope typeof(this) rhs)shared @safe;
166 
167         static if(isCopyConstructable!(typeof(this), const shared typeof(this)))
168             this(ref scope typeof(this) rhs)const shared @safe{this(rhs, Forward.init);}
169         else
170             @disable this(ref scope typeof(this) rhs)const shared @safe;
171 
172 
173 
174         /**
175             Assign.
176 
177             Examples:
178                 --------------------
179                 {
180                     GlobalPtr!long x = new long(1);
181 
182                     x = null;
183                     assert(x == null);
184                 }
185 
186                 {
187                     GlobalPtr!(shared long) x = new shared(long)(1);
188 
189                     x = null;
190                     assert(x == null);
191                 }
192 
193                 {
194                     shared GlobalPtr!(long) x = new shared(long)(1);
195 
196                     x = null;
197                     assert(x == null);
198                 }
199                 --------------------
200         */
201         public void opAssign(typeof(null) nil)scope pure nothrow @trusted @nogc{
202             this._set_element(null);
203         }
204 
205         /// ditto
206         public void opAssign(typeof(null) nil)shared scope pure nothrow @trusted @nogc{
207             this._set_element(null);
208         }
209 
210         /// ditto
211         public void opAssign(ElementReferenceType elm)scope pure nothrow @trusted @nogc{
212             this._set_element(elm);
213         }
214 
215 
216 
217         /**
218             Shares ownership of the object managed by `rhs`.
219 
220             If `rhs` manages no object, `this` manages no object too.
221             If `rhs` is rvalue then move-assigns a `RcPtr` from `rhs`
222 
223             Examples:
224                 --------------------
225                 {
226                     GlobalPtr!long px1 = new long(1);
227                     GlobalPtr!long px2 = new long(2);
228 
229                     px1 = px2;
230                     assert(*px1 == 2);
231                 }
232 
233 
234                 {
235                     GlobalPtr!long px = new long(1);
236                     GlobalPtr!(const long) pcx = new long(2);
237 
238                     pcx = px;
239                     assert(*pcx == 1);
240                 }
241 
242 
243                 {
244                     const GlobalPtr!long cpx = new long(1);
245                     GlobalPtr!(const long) pcx = new long(2);
246 
247                     pcx = cpx;
248                     assert(*pcx == 1);
249                 }
250 
251                 {
252                     GlobalPtr!(immutable long) pix = new immutable(long)(123);
253                     GlobalPtr!(const long) pcx = new long(2);
254 
255                     pcx = pix;
256                     assert(*pcx == 123);
257                 }
258                 --------------------
259         */
260         public void opAssign(Rhs, this This)(scope auto ref Rhs desired)scope @trusted
261         if(isGlobalPtr!Rhs
262             && isAssignable!(Rhs, This)
263             && !is(Rhs == shared)
264         ){
265             ()@trusted{
266                 this._set_element(desired._element);
267             }();
268         }
269 
270 
271 
272         /**
273             Swap `this` with `rhs`
274 
275             Examples:
276                 --------------------
277                 {
278                     GlobalPtr!long a = new long(1);
279                     GlobalPtr!long b = new long(2);
280 
281                     a.proxySwap(b);
282                     assert(*a == 2);
283                     assert(*b == 1);
284 
285                     import std.algorithm : swap;
286                     swap(a, b);
287                     assert(*a == 1);
288                     assert(*b == 2);
289                 }
290                 --------------------
291         */
292         public void proxySwap(ref scope typeof(this) rhs)scope @trusted pure nothrow @nogc{
293             auto element = this._element;
294             this._set_element(rhs._element);
295             rhs._set_element(element);
296         }
297 
298 
299 
300         /**
301 
302         */
303         alias get this;
304 
305 
306 
307         /**
308             Operator *, same as method 'get'.
309 
310             Examples:
311                 --------------------
312                 GlobalPtr!long x = new long(123);
313                 assert(*x == 123);
314 
315                 (*x = 321);
316                 assert(*x == 321);
317 
318                 const y = x;
319                 assert(*y == 321);
320                 assert(*x == 321);
321 
322                 static assert(is(typeof(*y) == const long));
323                 --------------------
324         */
325         public template opUnary(string op : "*")
326         if(op == "*"){  //doc
327             alias opUnary = get;
328         }
329 
330 
331 
332         /**
333             Get reference to managed object of `ElementType` or value if `ElementType` is reference type (class or interface) or dynamic array.
334 
335             Examples:
336                 --------------------
337                 GlobalPtr!long x = new long(123);
338                 assert(x.get == 123);
339 
340                 x.get = 321;
341                 assert(x.get == 321);
342 
343                 const y = x;
344                 assert(y.get == 321);
345                 assert(x.get == 321);
346 
347                 static assert(is(typeof(y.get) == const long));
348                 --------------------
349         */
350         static if(referenceElementType){
351             public @property inout(ElementType) get()inout scope pure nothrow @trusted @nogc{
352                 return this._element;
353             }
354         }
355         else static if(is(Unqual!ElementType == void)){
356             /// ditto
357             public @property inout(void) get()inout scope pure nothrow @safe @nogc{
358             }
359         }
360         else{
361             /// ditto
362             public @property ref inout(ElementType) get()inout scope pure nothrow @trusted @nogc{
363                 return *cast(inout(ElementType)*)this._element;
364             }
365         }
366 
367 
368 
369         /**
370             Get pointer to managed object of `ElementType` or reference if `ElementType` is reference type (class or interface) or dynamic array.
371 
372             Examples:
373                 --------------------
374                 {
375                     GlobalPtr!long x = new long(123);
376                     assert(*x.element == 123);
377 
378                     x.get = 321;
379                     assert(*x.element == 321);
380 
381                     const y = x;
382                     assert(*y.element == 321);
383                     assert(*x.element == 321);
384 
385                     static assert(is(typeof(y.element) == const(long)*));
386                 }
387                 --------------------
388         */
389         public @property ElementReferenceTypeImpl!(GetElementType!This) element(this This)()return pure nothrow @trusted @nogc
390         if(!is(This == shared)){
391             return this._element;
392         }
393 
394 
395 
396         /**
397             Get pointer to managed object of `ElementType` or reference if `ElementType` is reference type (class or interface) or pointer to first dynamic array element.
398 
399             Examples:
400                 --------------------
401                 {
402                     GlobalPtr!long x = new long(123);
403                     assert(*x.ptr == 123);
404 
405                     x.get = 321;
406                     assert(*x.ptr == 321);
407 
408                     const y = x;
409                     assert(*y.ptr == 321);
410                     assert(*x.ptr == 321);
411 
412                     static assert(is(typeof(y.ptr) == const(long)*));
413                 }
414                 --------------------
415         */
416         public @property ElementPointerTypeImpl!(GetElementType!This) ptr(this This)()return pure nothrow @trusted @nogc
417         if(!is(This == shared)){
418             static if(isDynamicArray!ElementType)
419                 return this._element.ptr;
420             else
421                 return this._element;
422         }
423 
424 
425 
426         /**
427             Checks if `this` stores a non-null pointer, i.e. whether `this != null`.
428 
429             Examples:
430                 --------------------
431                 GlobalPtr!long x = new long(123);
432                 assert(cast(bool)x);    //explicit cast
433                 assert(x);              //implicit cast
434                 
435                 x = null;
436                 assert(!cast(bool)x);   //explicit cast
437                 assert(!x);             //implicit cast
438                 --------------------
439         */
440         public bool opCast(To : bool)()const scope pure nothrow @safe @nogc
441         if(is(To : bool)){ //docs
442             return (this != null);
443         }
444 
445 
446 
447         /**
448             Support for quelifier cast.
449         */
450         public ref To opCast(To, this This)()return scope pure nothrow @nogc
451         if(is(immutable To : immutable typeof(this))){
452             static if(is(This : To)){
453                 return *(()@trusted => cast(To*)&this )();
454             }
455             else{
456                 return *(()@system => cast(To*)&this )();
457             }
458         }
459 
460 
461         /**
462             Operator == and != .
463             Compare pointers.
464 
465             Examples:
466                 --------------------
467                 {
468                     GlobalPtr!long x = new long(0);
469                     assert(x != null);
470                     x = null;
471                     assert(x == null);
472                 }
473 
474                 {
475                     GlobalPtr!long x = new long(123);
476                     GlobalPtr!long y = new long(123);
477                     assert(x == x);
478                     assert(y == y);
479                     assert(x != y);
480                 }
481 
482                 {
483                     GlobalPtr!long x;
484                     GlobalPtr!(const long) y;
485                     assert(x == x);
486                     assert(y == y);
487                     assert(x == y);
488                 }
489 
490                 {
491                     GlobalPtr!long x = new long(123);
492                     GlobalPtr!long y = new long(123);
493                     assert(x == x.element);
494                     assert(y.element == y);
495                     assert(x != y.element);
496                 }
497                 --------------------
498         */
499         public bool opEquals(typeof(null) nil)const @safe scope pure nothrow @nogc{
500             static if(isDynamicArray!ElementType)
501                 return (this._element.length == 0);
502             else
503                 return (this._element is null);
504         }
505 
506         /// ditto
507         public bool opEquals(Rhs)(scope auto ref const Rhs rhs)const @safe scope pure nothrow @nogc
508         if(isGlobalPtr!Rhs && !is(Rhs == shared)){
509             return this.opEquals(rhs._element);
510         }
511 
512         /// ditto
513         public bool opEquals(Elm)(scope const Elm elm)const @safe scope pure nothrow @nogc
514         if(is(Elm : GetElementReferenceType!(typeof(this)))){
515             static if(isDynamicArray!ElementType){
516                 static assert(isDynamicArray!Elm);
517 
518                 if(this._element.length != elm.length)
519                     return false;
520 
521                 if(this._element.ptr is elm.ptr)
522                     return true;
523 
524                 return (this._element.length == 0);
525             }
526             else{
527                 return (this._element is elm);
528             }
529         }
530 
531 
532 
533         /**
534             Operators <, <=, >, >= for `RcPtr`.
535 
536             Compare address of payload.
537 
538             Examples:
539                 --------------------
540                 {
541                     const GlobalPtr!long a = new long(42);
542                     const GlobalPtr!long b = new long(123);
543                     const GlobalPtr!long n = GlobalPtr!long.init;
544 
545                     assert(a <= a);
546                     assert(a >= a);
547 
548                     assert((a < b) == !(a >= b));
549                     assert((a > b) == !(a <= b));
550 
551                     assert(a > n);
552                     assert(a > null);
553 
554                     assert(n < a);
555                     assert(null < a);
556                 }
557 
558                 {
559                     const GlobalPtr!long a = new long(42);
560                     const GlobalPtr!long b = new long(123);
561 
562                     assert(a <= a.element);
563                     assert(a.element >= a);
564 
565                     assert((a < b.element) == !(a.element >= b));
566                     assert((a > b.element) == !(a.element <= b));
567                 }
568                 --------------------
569         */
570         public sizediff_t opCmp(typeof(null) nil)const @trusted scope pure nothrow @nogc{
571             static if(isDynamicArray!ElementType){
572                 return this._element.length;
573             }
574             else{
575                 return (cast(const void*)this._element) - (cast(const void*)null);
576             }
577 
578         }
579 
580         /// ditto
581         public sizediff_t opCmp(Elm)(scope const Elm elm)const @trusted scope pure nothrow @nogc
582         if(is(Elm : GetElementReferenceType!(typeof(this)))){
583             static if(isDynamicArray!ElementType){
584                 const void* lhs = cast(const void*)(this._element.ptr + this._element.length);
585                 const void* rhs = cast(const void*)(elm.ptr + elm.length);
586 
587                 return lhs - rhs;
588             }
589             else{
590                 return (cast(const void*)this._element) - (cast(const void*)elm);
591             }
592         }
593 
594         /// ditto
595         public sizediff_t opCmp(Rhs)(scope auto ref const Rhs rhs)const @trusted scope pure nothrow @nogc
596         if(isGlobalPtr!Rhs && !is(Rhs == shared)){
597             return this.opCmp(rhs._element);
598         }
599 
600 
601 
602         private ElementReferenceType _element;
603 
604         private void _set_element(ElementReferenceType e)scope pure nothrow @system @nogc{
605             (*cast(Unqual!ElementReferenceType*)&this._element) = cast(Unqual!ElementReferenceType)e;
606         }
607 
608         private void _set_element(ElementReferenceType e)scope shared pure nothrow @system @nogc{
609             import core.atomic : atomicStore;
610             atomicStore(
611                 (*cast(Unqual!ElementReferenceType*)&this._element),
612                 cast(Unqual!ElementReferenceType)e
613             );
614         }
615     }
616 }
617 
618 /// 
619 nothrow @safe unittest{
620     scope GlobalPtr!long ptr;
621 
622     {
623         scope GlobalPtr!long x = new long(42);
624         ptr.opAssign(x);
625     }
626 
627     {
628         static long val = 42;
629         scope GlobalPtr!long x = &val;
630         ptr = x;
631     }
632 }
633 
634 
635 //local traits:
636 private{
637 
638     //Constructable:
639     alias isCopyConstructable = isConstructable;
640 
641     template isConstructable(From, To){
642         enum bool isConstructable = true
643             && is(GetElementReferenceType!From : GetElementReferenceType!To);
644     }
645 
646     //Assignable:
647     template isAssignable(From, To){
648         import std.traits : isMutable;
649         enum bool isAssignable = true
650             && isMutable!To
651             && isConstructable!(From, To);
652     }
653 }
654 
655 
656 version(unittest){
657     //this(null)
658     nothrow @nogc @safe unittest{
659         GlobalPtr!long x = null;
660 
661         assert(x == null);
662         assert(x == GlobalPtr!long.init);
663     }
664 
665     //this(elm)
666     nothrow @safe unittest{
667         {
668             GlobalPtr!long x = new long(42);
669 
670             assert(x != null);
671             assert(x.get == 42);
672         }
673 
674         {
675             static const long i = 42;
676             GlobalPtr!(const long) x = &i;
677 
678             assert(x != null);
679             assert(x.get == 42);
680         }
681     }
682 
683     //opAssign(null|elm)
684     nothrow @safe unittest{
685         {
686             GlobalPtr!long x = new long(1);
687 
688             x = null;
689             assert(x == null);
690         }
691 
692         {
693             GlobalPtr!(shared long) x = new shared(long)(1);
694 
695             x = null;
696             assert(x == null);
697         }
698 
699         {
700             shared GlobalPtr!(long) x = new shared(long)(1);
701 
702             x = null;
703             //assert(x == null);
704         }
705 
706     }
707 
708     //opAssign(this)
709     nothrow @safe unittest{
710         {
711             GlobalPtr!long px1 = new long(1);
712             GlobalPtr!long px2 = new long(2);
713 
714             px1 = px2;
715             assert(*px1 == 2);
716         }
717 
718 
719         {
720             GlobalPtr!long px = new long(1);
721             GlobalPtr!(const long) pcx = new long(2);
722 
723             pcx = px;
724             assert(*pcx == 1);
725         }
726 
727 
728         {
729             const GlobalPtr!long cpx = new long(1);
730             GlobalPtr!(const long) pcx = new long(2);
731 
732             pcx = cpx;
733             assert(*pcx == 1);
734         }
735 
736         {
737             GlobalPtr!(immutable long) pix = new immutable(long)(123);
738             GlobalPtr!(const long) pcx = new long(2);
739 
740             pcx = pix;
741             assert(*pcx == 123);
742         }
743     }
744 
745     //proxySwap
746     nothrow @safe unittest{
747         {
748             GlobalPtr!long a = new long(1);
749             GlobalPtr!long b = new long(2);
750 
751             a.proxySwap(b);
752             assert(*a == 2);
753             assert(*b == 1);
754 
755             import std.algorithm : swap;
756             swap(a, b);
757             assert(*a == 1);
758             assert(*b == 2);
759         }
760     }
761 
762     //opUnary!*
763     nothrow @safe unittest{
764         GlobalPtr!long x = new long(123);
765         assert(*x == 123);
766 
767         (*x = 321);
768         assert(*x == 321);
769 
770         const y = x;
771         assert(*y == 321);
772         assert(*x == 321);
773 
774         static assert(is(typeof(*y) == const long));
775     }
776 
777     //get
778     nothrow @safe unittest{
779         GlobalPtr!long x = new long(123);
780         assert(x.get == 123);
781 
782         x.get = 321;
783         assert(x.get == 321);
784 
785         const y = x;
786         assert(y.get == 321);
787         assert(x.get == 321);
788 
789         static assert(is(typeof(y.get) == const long));
790     }
791 
792     //element
793     nothrow @safe unittest{
794         {
795             GlobalPtr!long x = new long(123);
796             assert(*x.element == 123);
797 
798             x.get = 321;
799             assert(*x.element == 321);
800 
801             const y = x;
802             assert(*y.element == 321);
803             assert(*x.element == 321);
804 
805             static assert(is(typeof(y.element) == const(long)*));
806         }
807     }
808 
809     //ptr
810     nothrow @safe unittest{
811         {
812             GlobalPtr!long x = new long(123);
813             assert(*x.ptr == 123);
814 
815             x.get = 321;
816             assert(*x.ptr == 321);
817 
818             const y = x;
819             assert(*y.ptr == 321);
820             assert(*x.ptr == 321);
821 
822             static assert(is(typeof(y.ptr) == const(long)*));
823         }
824     }
825 
826     //opCast!bool
827     nothrow @safe unittest{
828         GlobalPtr!long x = new long(123);
829         assert(cast(bool)x);    //explicit cast
830         assert(x);              //implicit cast
831 
832         x = null;
833         assert(!cast(bool)x);   //explicit cast
834         assert(!x);             //implicit cast
835     }
836 
837     //opEquals
838     nothrow @safe unittest{
839         {
840             GlobalPtr!long x = new long(0);
841             assert(x != null);
842             x = null;
843             assert(x == null);
844         }
845 
846         {
847             GlobalPtr!long x = new long(123);
848             GlobalPtr!long y = new long(123);
849             assert(x == x);
850             assert(y == y);
851             assert(x != y);
852         }
853 
854         {
855             GlobalPtr!long x;
856             GlobalPtr!(const long) y;
857             assert(x == x);
858             assert(y == y);
859             assert(x == y);
860         }
861 
862         {
863             GlobalPtr!long x = new long(123);
864             GlobalPtr!long y = new long(123);
865             assert(x == x.element);
866             assert(y.element == y);
867             assert(x != y.element);
868         }
869     }
870 
871     //opCmp
872     nothrow @safe unittest{
873         {
874             const GlobalPtr!long a = new long(42);
875             const GlobalPtr!long b = new long(123);
876             const GlobalPtr!long n = GlobalPtr!long.init;
877 
878             assert(a <= a);
879             assert(a >= a);
880 
881             assert((a < b) == !(a >= b));
882             assert((a > b) == !(a <= b));
883 
884             assert(a > n);
885             assert(a > null);
886 
887             assert(n < a);
888             assert(null < a);
889         }
890 
891         {
892             const GlobalPtr!long a = new long(42);
893             const GlobalPtr!long b = new long(123);
894 
895             assert(a <= a.element);
896             assert(a.element >= a);
897 
898             assert((a < b.element) == !(a.element >= b));
899             assert((a > b.element) == !(a.element <= b));
900         }
901     }
902 
903 }