1 /**
2     Numem Lifetime Handling.
3 
4     This module implements wrappers around $(D numem.core.lifetime) to provide
5     an easy way to instantiate various D types and slices of D types.
6     
7     Copyright:
8         Copyright © 2023-2025, Kitsunebi Games
9         Copyright © 2023-2025, Inochi2D Project
10     
11     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12     Authors:   Luna Nielsen
13 
14     See_Also:
15         $(D numem.core.memory.nu_resize)
16         $(D numem.core.memory.nu_dup)
17         $(D numem.core.memory.nu_idup)
18 */
19 module numem.lifetime;
20 import numem.core.exception;
21 import numem.core.traits;
22 import numem.core.lifetime;
23 import numem.core.hooks;
24 import numem.core.memory;
25 import numem.core.math;
26 import numem.core.cpp;
27 import numem.casting;
28 import numem.heap;
29 
30 // Utilities that are useful in the same regard from core.
31 public import numem.core.memory : nu_dup, nu_idup, nu_resize, nu_malloca, nu_freea, nu_terminate, nu_swap;
32 public import numem.core.lifetime : nu_destroywith, nu_autoreleasewith;
33 
34 @nogc:
35 
36 /**
37     Creates a new auto release pool spanning the current scope.
38 
39     Returns:
40         A stack allocated object with copying and moving disabled.
41 */
42 auto autoreleasepool_scope() {
43     static
44     struct nu_arpool_stackctx {
45     @nogc:
46     private:
47         void* ctx;
48     
49     public:
50         ~this() { nu_autoreleasepool_pop(ctx); }
51         this(void* ctx) { this.ctx = ctx; }
52 
53         @disable this(this);
54     }
55 
56     return nu_arpool_stackctx(nu_autoreleasepool_push());
57 }
58 
59 /**
60     Creates a scoped auto release pool.
61 
62     Params:
63         scope_ = The scope in which the auto release pool acts.
64 */
65 void autoreleasepool(void delegate() scope @nogc scope_) @trusted {
66     void* ctx = nu_autoreleasepool_push();
67     scope_();
68     nu_autoreleasepool_pop(ctx);
69 }
70 
71 /**
72     Constructs the given object.
73 
74     Attempting to construct a non-initialized $(D object) is undefined behaviour.
75 */
76 void nogc_construct(T, Args...)(ref T object, Args args) @trusted {
77     static if (isPointer!T)
78         emplace(*object, args);
79     else
80         emplace(object, args);
81 }
82 
83 /**
84     Allocates a new instance of $(D T) using the DLang allocation
85     strategy.
86 
87     See_Also:
88         $(D nogc_trynew), $(D cpp_new)
89 */
90 Ref!T nogc_new(T, Args...)(auto ref Args args) @trusted {
91     if (Ref!T newobject = cast(Ref!T)nu_malloc(AllocSize!T)) {
92         try {
93             nogc_construct(newobject, args);
94             return newobject;
95 
96         } catch(Exception ex) {
97             nu_free(cast(void*)newobject);
98             throw ex;
99         }
100     }
101     return null;
102 }
103 
104 /** 
105     Allocates a new instance of $(D T) using the DLang allocation
106     strategy on the specified heap.
107 
108     Params:
109         heap = The heap to allocate the instance on.
110         args = The arguments to pass to the type's constructor.
111 
112     See_Also:
113         $(D nogc_trynew), $(D cpp_new)
114 */
115 Ref!T nogc_new(T, Args...)(NuHeap heap, auto ref Args args) @trusted {
116     if (Ref!T newobject = cast(Ref!T)heap.alloc(AllocSize!T)) {
117         try {
118             nogc_construct(newobject, args);
119         } catch(Exception ex) {
120             nu_free(cast(void*)newobject);
121             throw ex;
122         }
123     }
124     return null;
125 }
126 
127 /**
128     Attempts to allocate a new instance of $(D T) using the DLang allocation
129     strategy.
130 
131     Params:
132         args = The arguments to pass to the type's constructor.
133     
134     Returns: 
135         A reference to the instantiated object or $(D null) if allocation
136         failed.
137 
138     See_Also:
139         $(D nogc_new), $(D cpp_new), $(D cpp_trynew)
140 */
141 Ref!T nogc_trynew(T, Args...)(auto ref Args args) @trusted nothrow {
142     if (Ref!T newobject = cast(Ref!T)nu_malloc(AllocSize!T)) {
143         try {
144             nogc_construct(newobject, args);
145             return newobject;
146 
147         } catch(Exception ex) {
148 
149             nu_free(cast(void*)newobject);
150             if (ex) 
151                 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex);
152             return null;
153         }
154     }
155     return null;
156 }
157 
158 /**
159     Attempts to allocate a new instance of $(D T) on the specified heap.
160 
161     Params:
162         heap = The heap to allocate the instance on.
163         args = The arguments to pass to the type's constructor.
164     
165     Returns: 
166         A reference to the instantiated object or $(D null) if allocation
167         failed.
168 */
169 Ref!T nogc_trynew(T, Args...)(NuHeap heap, auto ref Args args) @trusted nothrow {
170     if (Ref!T newobject = cast(Ref!T)heap.alloc(AllocSize!T)) {
171         try {
172             nogc_construct(newobject, args);
173             return newobject;
174 
175         } catch(Exception ex) {
176             
177             nu_free(cast(void*)newobject);
178             if (ex) 
179                 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex);
180             return null;
181         }
182     }
183     return null;
184 }
185 
186 /**
187     Finalizes $(D obj_) by calling its destructor (if any).
188 
189     Notes:
190         If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 
191         after finalizers have run; otherwise the object is reset to its original state.
192 
193     Params:
194         obj_ = Instance to destroy and deallocate.
195 */
196 void nogc_delete(T, bool doFree=true)(ref T obj_) @trusted {
197     static if (isHeapAllocated!T) {
198 
199         // Ensure type is not null.
200         if (reinterpret_cast!(void*)(obj_) !is null) {
201 
202             destruct!(T, !doFree)(obj_);
203 
204             // Free memory if need be.
205             static if (doFree)
206                 nu_free(cast(void*)obj_);
207 
208             obj_ = null;
209         }
210     } else {
211         destruct!(T, !doFree)(obj_);
212     }
213 }
214 
215 /**
216     Deallocates the specified instance of $(D T) from the specified heap.
217     Finalizes $(D obj_) by calling its destructor (if any).
218 
219     Notes:
220         If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 
221         after finalizers have run; otherwise the object is reset to its original state.
222 
223     Params:
224         heap = The heap to allocate the instance on.
225         obj_ = Instance to destroy and deallocate.
226 */
227 void nogc_delete(T, bool doFree=true)(NuHeap heap, ref T obj_) @trusted {
228     static if (isHeapAllocated!T) {
229         if (reinterpret_cast!(void*)(obj_) !is null) {
230 
231             destruct!(T, !doFree)(obj_);
232 
233             // Free memory if need be.
234             static if (doFree)
235                 heap.free(cast(void*)obj_);
236 
237             obj_ = null;
238         }
239     }
240 }
241 
242 /**
243     Finalizes the objects referenced by $(D objects) by calling the
244     destructors of its members (if any).
245 
246     Notes:
247         If $(D doFree) is $(D true), memory associated with each object
248         will additionally be freed after finalizers have run; otherwise the object 
249         is reset to its original state.
250 
251     Params:
252         objects = The objects to delete.
253 */
254 void nogc_delete(T, bool doFree=true)(T[] objects) @trusted {
255     foreach(i; 0..objects.length)
256         nogc_delete!(T, doFree)(objects[i]);
257 }
258 
259 /**
260     Attempts to finalize $(D obj_) by calling its destructor (if any).
261 
262     If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 
263     after finalizers have run; otherwise the object is reset to its original state.
264 
265     Params:
266         obj_ = Instance to destroy and deallocate.
267 
268     Returns:
269         Whether the operation succeeded.
270 */
271 bool nogc_trydelete(T, bool doFree=true)(ref T obj_) @trusted nothrow {
272     try {
273         nogc_delete!(T, doFree)(obj_);
274         return true;
275     
276     } catch (Exception ex) {
277         if (ex) 
278             assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex);
279 
280         return false;
281     }
282 }
283 
284 /**
285     Attempts to deallocate the specified instance of $(D T) from the specified heap.
286     Finalizes $(D obj_) by calling its destructor (if any).
287 
288     Notes:
289         If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 
290         after finalizers have run; otherwise the object is reset to its original state.
291 
292     Params:
293         heap = The heap to allocate the instance on.
294         obj_ = Instance to destroy and deallocate.
295 
296     Returns:
297         Whether the operation succeeded.
298 */
299 bool nogc_trydelete(T, bool doFree=true)(NuHeap heap, ref T obj_) @trusted nothrow {
300     static if (isHeapAllocated!T) {
301         try {
302 
303             nogc_delete!(T, doFree)(heap, obj_);
304             return true;
305         } catch (Exception ex) {
306 
307             if (ex)
308                 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex);
309             return false;
310         }
311     }
312 }
313 
314 /**
315     Attempts to finalize the objects referenced by $(D objects) by calling the
316     destructors of its members (if any).
317 
318     Notes:
319         If $(D doFree) is $(D true), memory associated with each object
320         will additionally be freed after finalizers have run; otherwise the object 
321         is reset to its original state.
322 
323     Params:
324         objects = The objects to try to delete.
325 
326     Returns:
327         Whether the operation succeeded.
328 */
329 bool nogc_trydelete(T, bool doFree=true)(T[] objects) @trusted {
330     size_t failed = 0;
331     foreach(i; 0..objects.length)
332         failed += nogc_trydelete!(T, doFree)(objects[i]);
333     
334     return failed != 0;
335 }
336 
337 /**
338     Allocates a new instance of $(D T) using the C++ allocation
339     strategy.
340 
341     Notes:
342         $(D doXCtor) is used to specify whether to also call any D mangled
343         constructors defined for the C++ type.
344 
345     See_Also:
346         @(D nogc_new), $(D nogc_trynew), $(D cpp_trynew), 
347 */
348 Ref!T cpp_new(T, bool doXCtor=true, Args...)(auto ref Args args) @trusted {
349     return _nu_cpp_new!(T, doXCtor, Args)(forward!args);
350 }
351 
352 /**
353     Attempts to allocate a new instance of $(D T) using the C++ allocation
354     strategy.
355 
356     Notes:
357         $(D doXCtor) is used to specify whether to also call any D mangled
358         constructors defined for the C++ type.
359 
360     See_Also:
361         @(D nogc_new), $(D nogc_trynew), $(D cpp_new)
362 */
363 Ref!T cpp_trynew(T, bool doXCtor=true, Args...)(auto ref Args args) @trusted nothrow {
364     return assumeNoThrowNoGC((Args args) => _nu_cpp_new!(T, doXCtor, Args)(forward!args), args);
365 }
366 
367 /**
368     Finalizes $(D obj_) by calling its destructor (if any).
369 
370     Notes:
371         $(D doXDtor) is used to specify whether to also call any D mangled
372         destructors defined for the C++ type.
373 
374     Params:
375         obj_ = Instance to destroy and deallocate.
376 
377     See_Also:
378         @(D nogc_delete), $(D nogc_trydelete), $(D cpp_trydelete)
379 */
380 void cpp_delete(T, bool doXDtor=true)(ref T obj_) @trusted if (isCPP!T)  {
381     _nu_cpp_delete!(T, doXDtor)(obj_);
382 }
383 
384 /**
385     Finalizes the objects referenced by $(D objects) by calling the
386     destructors of its members (if any).
387 
388     Notes:
389         $(D doXDtor) is used to specify whether to also call any D mangled
390         destructors defined for the C++ type.
391 
392     Params:
393         objects = The objects to delete.
394     
395     See_Also:
396         @(D nogc_delete), $(D nogc_trydelete), $(D cpp_trydelete)
397 */
398 void cpp_delete(T, bool doXDtor=true)(T[] objects) @trusted if (isCPP!T)  {
399     foreach(i; 0..objects.length)
400         _nu_cpp_delete!(T, doXDtor)(objects[i]);
401 }
402 
403 /**
404     Attempts to finalize the objects referenced by $(D objects) by calling the
405     destructors of its members (if any).
406 
407     Notes:
408         $(D doXDtor) is used to specify whether to also call any D mangled
409         destructors defined for the C++ type.
410 
411     Params:
412         objects = The objects to try to delete.
413 
414     Returns:
415         Whether the operation succeeded.
416     
417     See_Also:
418         @(D nogc_delete), $(D nogc_trydelete), $(D cpp_delete)
419 */
420 bool cpp_trydelete(T, bool doFree=true)(T[] objects) @trusted {
421     size_t failed = 0;
422     foreach(i; 0..objects.length)
423         failed += cpp_trydelete!(T, doFree)(objects[i]);
424     
425     return failed != 0;
426 }
427 
428 /**
429     Attempts to finalize $(D obj_) by calling its C++ destructor.
430 
431     Notes:
432         $(D doXDtor) is used to specify whether to also call any D mangled
433         destructors defined for the C++ type.
434 
435     Params:
436         obj_ = Instance to destroy and deallocate.
437 
438     Returns:
439         Whether the operation succeeded.
440 
441     See_Also:
442         @(D nogc_delete), $(D nogc_trydelete), $(D cpp_delete)
443 */
444 bool cpp_trydelete(T, bool doXDtor=true)(ref T obj_) @trusted nothrow if (isCPP!T)  {
445     try {
446 
447         _nu_cpp_delete!(T, doXDtor)(obj_);
448         return true;
449     } catch (Exception ex) {
450         if (ex)
451             assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex);
452         
453         return false;
454     }
455 }
456 
457 /**
458     Initializes the object at the memory in $(D dst), filling it out with
459     its default state.
460 
461     This variant will also initialize class instances on the stack which
462     can then be constructed with $(D nogc_construct). However you may
463     still need to $(D nogc_delete) these instances, with $(D doFree) set 
464     to false.
465 
466     Params:
467         dst = A memory range allocated, big enough to store $(D T)
468     
469     Returns:
470         A reference to the initialized element, or $(D T.init) if it failed.
471 */
472 T nogc_initialize(T)(void[] dst) @trusted {
473     if (dst.length < AllocSize!T)
474         return T.init;
475 
476     T tmp = cast(T)dst.ptr;
477     initializeAt(tmp);
478     return tmp;
479 }
480 
481 /**
482     Initializes the object at $(D element), filling it out with
483     its default state.
484 
485     Params:
486         element = A reference to the allocated memory to initialize.
487     
488     Returns:
489         A reference to the initialized element.
490 */
491 ref T nogc_initialize(T)(ref T element) @trusted {
492     initializeAtNoCtx(element);
493     return element;
494 }
495 
496 /**
497     Initializes the objects at $(D elements), filling them out with
498     their default state.
499 
500     Params:
501         elements = A slice of the elements to initialize
502     
503     Returns:
504         The slice with now initialized contents.
505 */
506 T[] nogc_initialize(T)(T[] elements) @trusted {
507     foreach(i; 0..elements.length)
508         initializeAtNoCtx(elements[i]);
509     
510     return elements;
511 }
512 
513 /**
514     Zero-fills an object
515 */
516 void nogc_zeroinit(T)(ref T element) @trusted nothrow pure {
517     nu_memset(&element, 0, element.sizeof);
518 }
519 
520 /**
521     Zero-fills an object
522 */
523 void nogc_zeroinit(T)(T[] elements) @trusted nothrow pure {
524     nu_memset(elements.ptr, 0, elements.length*T.sizeof);
525 }
526 
527 /**
528     Allocates a new class on the heap.
529     Immediately exits the application if out of memory.
530 */
531 void nogc_emplace(T, Args...)(auto ref T dest, Args args) @trusted {
532     emplace!(T, T, Args)(dest, args);
533 }
534 
535 /**
536     Moves elements in $(D src) to $(D dst) via destructive copy.
537     
538     If an element in $(D src) is not a valid object,
539     then accessing the moved element in $(D to) will be undefined behaviour.
540 
541     After the move operation, the original memory locations in $(D from) will be
542     reset to their base initialized state before any constructors are run.
543 */
544 void nogc_move(T)(T[] dst, T[] src) @trusted {
545     size_t toTx = nu_min(dst.length, src.length);
546 
547     foreach(i; 0..toTx)
548         __move(src[i], dst[i]);
549 }
550 
551 /**
552     Copies $(D src) to $(D dst) via a blit operation.
553 
554     Postblits and copy constructors will be called subsequently.
555 */
556 void nogc_copy(T)(T[] dst, T[] src) @trusted {
557     size_t toTx = nu_min(dst.length, src.length);
558 
559     foreach(i; 0..toTx)
560         __copy(src[i], dst[i]);
561 }
562 
563 /**
564     Moves $(D from) to $(D to) via a destructive copy.
565     
566     If $(D from) is not a valid object, then accessing $(D to) will be undefined
567     behaviour.
568 
569     After the move operation, the original memory location of $(D from) will be
570     reset to its base initialized state before any constructors are run.
571 
572     Params: 
573         from = The source of the move operation.
574         to = The destination of the move operation.
575 */
576 void moveTo(T)(ref T from, ref T to) @trusted {
577     __move(from, to);
578 }
579 
580 /**
581     Moves $(D from).
582     
583     Useful for moving stack allocated structs.
584 
585     Params: 
586         from = The source of the move operation.
587     
588     Returns: 
589         The moved value, $(D from) will be reset to its initial state.
590 */
591 T move(T)(scope ref return T from) @trusted {
592     return __move(from);
593 }
594 
595 /**
596     Copies $(D from) to $(D to) via a blit operation.
597 
598     Postblits and copy constructors will be called subsequently.
599 
600     Params: 
601         from = The source of the copy operation.
602         to = The destination of the copy operation.
603 */
604 void copyTo(T)(ref T from, ref T to) @trusted {
605     __copy(from, to);
606 }