37
37
#include < godot_cpp/templates/safe_refcount.hpp>
38
38
39
39
#include < cstring>
40
+ #include < initializer_list>
40
41
#include < new>
41
42
#include < type_traits>
43
+ #include < utility>
42
44
43
45
namespace godot {
44
46
@@ -166,13 +168,25 @@ class CowData {
166
168
return *out;
167
169
}
168
170
169
- void _unref (void *p_data);
171
+ // Decrements the reference count. Deallocates the backing buffer if needed.
172
+ // After this function, _ptr is guaranteed to be NULL.
173
+ void _unref ();
170
174
void _ref (const CowData *p_from);
171
175
void _ref (const CowData &p_from);
172
176
USize _copy_on_write ();
177
+ Error _realloc (Size p_alloc_size);
173
178
174
179
public:
175
180
void operator =(const CowData<T> &p_from) { _ref (p_from); }
181
+ void operator =(CowData<T> &&p_from) {
182
+ if (_ptr == p_from._ptr ) {
183
+ return ;
184
+ }
185
+
186
+ _unref ();
187
+ _ptr = p_from._ptr ;
188
+ p_from._ptr = nullptr ;
189
+ }
176
190
177
191
_FORCE_INLINE_ T *ptrw () {
178
192
_copy_on_write ();
@@ -221,19 +235,22 @@ class CowData {
221
235
T *p = ptrw ();
222
236
Size len = size ();
223
237
for (Size i = p_index; i < len - 1 ; i++) {
224
- p[i] = p[i + 1 ];
238
+ p[i] = std::move ( p[i + 1 ]) ;
225
239
}
226
240
227
241
resize (len - 1 );
228
242
}
229
243
230
244
Error insert (Size p_pos, const T &p_val) {
231
- ERR_FAIL_INDEX_V (p_pos, size () + 1 , ERR_INVALID_PARAMETER);
232
- resize (size () + 1 );
233
- for (Size i = (size () - 1 ); i > p_pos; i--) {
234
- set (i, get (i - 1 ));
245
+ Size new_size = size () + 1 ;
246
+ ERR_FAIL_INDEX_V (p_pos, new_size, ERR_INVALID_PARAMETER);
247
+ Error err = resize (new_size);
248
+ ERR_FAIL_COND_V (err, err);
249
+ T *p = ptrw ();
250
+ for (Size i = new_size - 1 ; i > p_pos; i--) {
251
+ p[i] = std::move (p[i - 1 ]);
235
252
}
236
- set ( p_pos, p_val) ;
253
+ p[ p_pos] = p_val;
237
254
238
255
return OK;
239
256
}
@@ -243,35 +260,47 @@ class CowData {
243
260
Size count (const T &p_val) const ;
244
261
245
262
_FORCE_INLINE_ CowData () {}
246
- _FORCE_INLINE_ ~CowData ();
247
- _FORCE_INLINE_ CowData (CowData<T> &p_from) { _ref (p_from); };
263
+ _FORCE_INLINE_ ~CowData () { _unref (); }
264
+ _FORCE_INLINE_ CowData (std::initializer_list<T> p_init);
265
+ _FORCE_INLINE_ CowData (const CowData<T> &p_from) { _ref (p_from); }
266
+ _FORCE_INLINE_ CowData (CowData<T> &&p_from) {
267
+ _ptr = p_from._ptr ;
268
+ p_from._ptr = nullptr ;
269
+ }
248
270
};
249
271
250
272
template <typename T>
251
- void CowData<T>::_unref(void *p_data ) {
252
- if (!p_data ) {
273
+ void CowData<T>::_unref() {
274
+ if (!_ptr ) {
253
275
return ;
254
276
}
255
277
256
278
SafeNumeric<USize> *refc = _get_refcount ();
257
-
258
279
if (refc->decrement () > 0 ) {
259
- return ; // still in use
280
+ // Data is still in use elsewhere.
281
+ _ptr = nullptr ;
282
+ return ;
260
283
}
261
- // clean up
284
+ // Clean up.
285
+ // First, invalidate our own reference.
286
+ // NOTE: It is required to do so immediately because it must not be observable outside of this
287
+ // function after refcount has already been reduced to 0.
288
+ // WARNING: It must be done before calling the destructors, because one of them may otherwise
289
+ // observe it through a reference to us. In this case, it may try to access the buffer,
290
+ // which is illegal after some of the elements in it have already been destructed, and
291
+ // may lead to a segmentation fault.
292
+ USize current_size = *_get_size ();
293
+ T *prev_ptr = _ptr;
294
+ _ptr = nullptr ;
262
295
263
296
if constexpr (!std::is_trivially_destructible_v<T>) {
264
- USize *count = _get_size ();
265
- T *data = (T *)(count + 1 );
266
-
267
- for (USize i = 0 ; i < *count; ++i) {
268
- // call destructors
269
- data[i].~T ();
297
+ for (USize i = 0 ; i < current_size; ++i) {
298
+ prev_ptr[i].~T ();
270
299
}
271
300
}
272
301
273
302
// free mem
274
- Memory::free_static ((( uint8_t *)p_data) - DATA_OFFSET, false );
303
+ Memory::free_static ((uint8_t *)prev_ptr - DATA_OFFSET, false );
275
304
}
276
305
277
306
template <typename T>
@@ -306,7 +335,7 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() {
306
335
}
307
336
}
308
337
309
- _unref (_ptr );
338
+ _unref ();
310
339
_ptr = _data_ptr;
311
340
312
341
rc = 1 ;
@@ -326,14 +355,13 @@ Error CowData<T>::resize(Size p_size) {
326
355
}
327
356
328
357
if (p_size == 0 ) {
329
- // wants to clean up
330
- _unref (_ptr);
331
- _ptr = nullptr ;
358
+ // Wants to clean up.
359
+ _unref (); // Resets _ptr to nullptr.
332
360
return OK;
333
361
}
334
362
335
363
// possibly changing size, copy on write
336
- USize rc = _copy_on_write ();
364
+ _copy_on_write ();
337
365
338
366
USize current_alloc_size = _get_alloc_size (current_size);
339
367
USize alloc_size;
@@ -354,16 +382,12 @@ Error CowData<T>::resize(Size p_size) {
354
382
*(_size_ptr) = 0 ; // size, currently none
355
383
356
384
_ptr = _data_ptr;
357
- } else {
358
- uint8_t *mem_new = (uint8_t *)Memory::realloc_static (((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false );
359
- ERR_FAIL_NULL_V (mem_new, ERR_OUT_OF_MEMORY);
360
-
361
- SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr (mem_new);
362
- T *_data_ptr = _get_data_ptr (mem_new);
363
385
364
- new (_refc_ptr) SafeNumeric<USize>(rc); // refcount
365
-
366
- _ptr = _data_ptr;
386
+ } else {
387
+ const Error error = _realloc (alloc_size);
388
+ if (error) {
389
+ return error;
390
+ }
367
391
}
368
392
}
369
393
@@ -389,15 +413,10 @@ Error CowData<T>::resize(Size p_size) {
389
413
}
390
414
391
415
if (alloc_size != current_alloc_size) {
392
- uint8_t *mem_new = (uint8_t *)Memory::realloc_static (((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false );
393
- ERR_FAIL_NULL_V (mem_new, ERR_OUT_OF_MEMORY);
394
-
395
- SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr (mem_new);
396
- T *_data_ptr = _get_data_ptr (mem_new);
397
-
398
- new (_refc_ptr) SafeNumeric<USize>(rc); // refcount
399
-
400
- _ptr = _data_ptr;
416
+ const Error error = _realloc (alloc_size);
417
+ if (error) {
418
+ return error;
419
+ }
401
420
}
402
421
403
422
*_get_size () = p_size;
@@ -406,6 +425,21 @@ Error CowData<T>::resize(Size p_size) {
406
425
return OK;
407
426
}
408
427
428
+ template <typename T>
429
+ Error CowData<T>::_realloc(Size p_alloc_size) {
430
+ uint8_t *mem_new = (uint8_t *)Memory::realloc_static (((uint8_t *)_ptr) - DATA_OFFSET, p_alloc_size + DATA_OFFSET, false );
431
+ ERR_FAIL_NULL_V (mem_new, ERR_OUT_OF_MEMORY);
432
+
433
+ SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr (mem_new);
434
+ T *_data_ptr = _get_data_ptr (mem_new);
435
+
436
+ // If we realloc, we're guaranteed to be the only reference.
437
+ new (_refc_ptr) SafeNumeric<USize>(1 );
438
+ _ptr = _data_ptr;
439
+
440
+ return OK;
441
+ }
442
+
409
443
template <typename T>
410
444
typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
411
445
Size ret = -1 ;
@@ -465,11 +499,10 @@ void CowData<T>::_ref(const CowData &p_from) {
465
499
return ; // self assign, do nothing.
466
500
}
467
501
468
- _unref (_ptr);
469
- _ptr = nullptr ;
502
+ _unref (); // Resets _ptr to nullptr.
470
503
471
504
if (!p_from._ptr ) {
472
- return ; // nothing to do
505
+ return ; // nothing to do
473
506
}
474
507
475
508
if (p_from._get_refcount ()->conditional_increment () > 0 ) { // could reference
@@ -478,8 +511,16 @@ void CowData<T>::_ref(const CowData &p_from) {
478
511
}
479
512
480
513
template <typename T>
481
- CowData<T>::~CowData () {
482
- _unref (_ptr);
514
+ CowData<T>::CowData(std::initializer_list<T> p_init) {
515
+ Error err = resize (p_init.size ());
516
+ if (err != OK) {
517
+ return ;
518
+ }
519
+
520
+ Size i = 0 ;
521
+ for (const T &element : p_init) {
522
+ set (i++, element);
523
+ }
483
524
}
484
525
485
526
#if defined(__GNUC__) && !defined(__clang__)
0 commit comments