1 /**
2     Unique Pointers
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-2025, Inochi2D Project
7     
8     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9     Authors:   Luna Nielsen
10 */
11 module nulib.memory.unique_ptr;
12 import nulib.memory.weak_ptr;
13 import nulib.memory.internal;
14 import numem.core.traits;
15 import numem;
16 
17 public import numem.lifetime : nogc_move, move, moveTo;
18 
19 /**
20     A unique pointer.
21 
22     Unique pointers are a specialization of shared pointers, these pointers 
23     can not be copied, only moved. $(D numem.core.lifetime) contains functions
24     for achieving moves. 
25 
26     You may borrow weak references from $(D unique_ptr), these weak references
27     may become invalid at $(I any) point, so make sure to check the state of the 
28     object using $(D isValid).
29 
30     Example:
31         ---
32         static
33         class A { }
34 
35         auto p = unique_new!A();
36         auto b = move(p);
37 
38         assert(!p.isValid);
39         assert(b.isValid);
40         ---
41 
42 
43     Threadsafety:
44         The internal reference count kept by shared_ptr is $(B not) atomic.
45         As such you should take care with accessing the value and refcount
46         stored within.
47     
48     See_Also:
49         $(D nulib.memory.shared_ptr.shared_ptr)
50         $(D nulib.memory.weak_ptr.weak_ptr)
51 */
52 struct unique_ptr(T) {
53 @nogc:
54 private:
55     __refcount_t!(T)* ptr = null;
56 
57 public:
58     alias value this;
59 
60     /**
61         The value stored within the smart pointer.
62     */
63     @property ref T value() @trusted nothrow {
64         return ptr.get();
65     }
66 
67     /**
68         Whether the smart pointer value is still valid.
69     */
70     @property bool isValid() @trusted nothrow {
71         return ptr && ptr.strongrefs > 0 && ptr.ptr !is null;
72     }
73 
74     /// Destructor.
75     ~this() @trusted { 
76         if (ptr) {
77             ptr.release!true; 
78             ptr = null;
79         }
80     }
81 
82     /**
83         Constructs a shared_ptr with the given memory address.
84     */
85     this()(Ref!T ptr) @system if(!is(Ref!T == typeof(this))) {
86         this.ptr = __refcount_t!(T).createNew(ptr);
87     }
88 
89     /**
90         Disable copying.
91     */
92     @disable this()(ref inout(typeof(this)) src);
93     @disable this(this);
94 
95     /**
96         Borrows a weak reference from the shared pointer.
97 
98         Returns:
99             A new weak pointer, pointing to the same storage as
100             the shared pointer.
101     */
102     weak_ptr!T borrow() @trusted {
103         return weak_ptr!T(ptr);
104     }
105 
106     /**
107         Post-move operator.
108     */
109     void opPostMove(ref typeof(this) other) {
110         other.ptr = null;
111     }
112 }
113 
114 /**
115     Creates a new unique pointer.
116 
117     Params:
118         args =   The arguments to pass to $(D T)'s constructor.
119     
120     Returns:
121         A unique pointer pointing to the newly allocated object.
122 */
123 unique_ptr!T unique_new(T, Args...)(Args args) @trusted {
124     auto ptr = unique_ptr!T(nogc_new!T(args));
125 
126     // NOTE: This extra retain is needed since we can't
127     //
128     ptr.ptr.retain!true;
129     return ptr; 
130 }
131 
132 @("unique_ptr")
133 unittest {
134     static
135     class A { }
136 
137     auto p = unique_new!A();
138     auto b = move(p);
139 
140     assert(!p.isValid);
141     assert(b.isValid);
142 
143     // p.ptr should be null.
144     assert(p.ptr is null);
145 
146     // Move to nowhere, destroying it.
147     move(b);
148     assert(!b.isValid);
149 }