1 /**
2     Numem Base Classes.
3     
4     Copyright:
5         Copyright © 2000-2011, The D Language Foundation.
6         Copyright © 2023-2025, Kitsunebi Games
7         Copyright © 2023-2025, Inochi2D Project
8     
9     License:    $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10     Authors:    Luna Nielsen, Walter Bright, Sean Kelly
11 
12     See_Also:
13         $(D numem.core.memory.nu_resize)
14         $(D numem.core.memory.nu_dup)
15         $(D numem.core.memory.nu_idup)
16 */
17 module numem.object;
18 import numem.lifetime;
19 import numem.core.exception;
20 import numem.core.lifetime : nu_autorelease;
21 import numem.core.traits;
22 import numem.core.hooks;
23 
24 /**
25     Numem base-class which allows using basic class functions without a garbage
26     collector.
27 */
28 class NuObject {
29 @nogc:
30 private:
31 
32     TypeInfo_Class getClassInfo() {
33         return cast(TypeInfo_Class)typeid(this);
34     }
35 
36 public:
37 
38     /**
39         Gets the name of the class.
40 
41         Returns:
42             Name of class based on its compile-time generated typeid.
43     */
44     override
45     string toString() {
46         return getClassInfo().name;
47     }
48 
49     /**
50         Test whether $(D this) is equal to $(D other).
51 
52         Default implementation only checks whether they are stored at the same
53         memory address.
54     */
55     override
56     bool opEquals(const Object other) nothrow const {
57         return this is other;
58     }
59 
60     /**
61         Compare with another Object $(D other).
62         
63         Returns:
64             $(TABLE
65                 $(TR $(TD this < obj) $(TD < 0))
66                 $(TR $(TD this == obj) $(TD 0))
67                 $(TR $(TD this > obj) $(TD > 0))
68             )
69 
70         Notes:
71             If you are combining this with a compacting garbage collector,
72             it will prevent it from functioning properly.
73     */
74     override
75     int opCmp(const Object other) nothrow const {
76         return cast(int)cast(void*)this - cast(int)cast(void*)other;
77     }
78 
79     /**
80         Compute a hash for Object.
81     */
82     override
83     size_t toHash() @trusted nothrow {
84 
85         // Address of ourselves.
86         size_t addr = cast(size_t)cast(void*)this;
87 
88         // The bottom log2((void*).alignof) bits of the address will always
89         // be 0. Moreover it is likely that each Object is allocated with a
90         // separate call to malloc. The alignment of malloc differs from
91         // platform to platform, but rather than having special cases for
92         // each platform it is safe to use a shift of 4. To minimize
93         // collisions in the low bits it is more important for the shift to
94         // not be too small than for the shift to not be too big.
95         return addr ^ (addr >>> 4);
96     }
97 
98 }
99 
100 /**
101     A reference counted class.
102 
103     Reference counted classes in numem are manually reference counted,
104     this means that you are responsible for managing synchronising
105     $(D retain) and $(D release) calls.
106 
107     Threadsafety:
108         Threadsafety depends on whether the hookset used supports
109         atomic operations; see $(D numem.core.atomic.nu_atomic_supported).
110         If unsupported, retain and release will not be threadsafe on their own,
111         and should be wrapped in another synchronisation primitive.
112 
113     Memorysafety:
114         Once the reference count for a class reaches 0, it will be destructed
115         and freed automatically. All references to the class after refcount
116         reaches 0 will be invalid and should not be used.
117         $(D NuRefCounted.release) returns a value which can be used to determine whether
118         the destructor was invoked.
119 */
120 @nu_autoreleasewith!((ref obj) { obj.release(); })
121 class NuRefCounted : NuObject {
122 @nogc:
123 private:
124     uint refcount = 0;
125 
126 public:
127 
128     /**
129         The current reference count of the object.
130     */
131     final @property uint refs() => nu_atomic_load_32(refcount);
132 
133     /**
134         Base constructor, all subclasses *have* to invoke this constructor.
135         Otherwise the instance will be invalid on instantiation.
136     */
137     this() @safe {
138         refcount = 1;
139     }
140 
141     /**
142         Retains a reference to a valid object.
143 
144         Notes:
145             The object validity is determined by the refcount.
146             Uninitialized refcounted classes will be invalid.
147             As such, releasing an invalid object will not
148             invoke the destructor of said object.
149     */
150     final
151     NuRefCounted retain() @trusted nothrow {
152         if (isValid)
153             nu_atomic_add_32(refcount, 1);
154         
155         return this;
156     }
157 
158     /**
159         Releases a reference from a valid object.
160 
161         Notes:
162             The object validity is determined by the refcount.
163             Uninitialized refcounted classes will be invalid.
164             As such, releasing an invalid object will not
165             invoke the destructor of said object.
166 
167         Returns:
168             The class instance release was called on, $(D null) if
169             the class was freed.
170     */
171     final
172     NuRefCounted release() @trusted {
173         if (isValid) {
174             nu_atomic_sub_32(refcount, 1);
175 
176             // Handle destruction.
177             if (nu_atomic_load_32(refcount) == 0) {
178                 NuRefCounted self = this;
179                 nogc_delete(self);
180                 return null;
181             }
182         }
183 
184         return this;
185     }
186 
187     /**
188         Pushes this refcounted object to the topmost auto release pool.
189 
190         Returns:
191             The class instance release was called on.
192     */
193     final
194     NuRefCounted autorelease() @trusted {
195         nu_autorelease!NuRefCounted(this);
196         return this;
197     }
198 
199     /**
200         Returns whether this object is valid.
201 
202         Notes:
203             The object validity is determined by the refcount.
204             Uninitialized refcounted classes will be invalid.
205             As such, releasing an invalid object will not
206             invoke the destructor of said object.
207 
208         Returns:
209             Whether the object is valid (has a refcount higher than 0)
210     */
211     final
212     bool isValid() @trusted nothrow {
213         return nu_atomic_load_32(refcount) != 0;
214     }
215 }
216 
217 /**
218     Helper function which allows typed chaining of retain calls.
219 
220     Params:
221         elem = The element to perform the operation on
222     
223     Returns:
224         The element or $(D null) if it was freed as a result
225         of the operation.
226 */
227 T retained(T)(T elem) @nogc @trusted if (isRefcounted!T) {
228     import numem.core.memory : nu_retain;
229     return cast(T)elem.nu_retain();
230 }
231 
232 /// ditto
233 T released(T)(T elem) @nogc @trusted if (isRefcounted!T) {
234     import numem.core.memory : nu_release;
235     return cast(T)elem.nu_release();
236 }
237 
238 /// ditto
239 T autoreleased(T)(T elem) @nogc @trusted if (is(T : NuRefCounted)) {
240     return cast(T)elem.autorelease();
241 }