3737#include < godot_cpp/templates/safe_refcount.hpp>
3838
3939#include < cstring>
40+ #include < initializer_list>
4041#include < new>
4142#include < type_traits>
43+ #include < utility>
4244
4345namespace godot {
4446
@@ -166,13 +168,25 @@ class CowData {
166168 return *out;
167169 }
168170
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 ();
170174 void _ref (const CowData *p_from);
171175 void _ref (const CowData &p_from);
172176 USize _copy_on_write ();
177+ Error _realloc (Size p_alloc_size);
173178
174179public:
175180 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+ }
176190
177191 _FORCE_INLINE_ T *ptrw () {
178192 _copy_on_write ();
@@ -221,19 +235,22 @@ class CowData {
221235 T *p = ptrw ();
222236 Size len = size ();
223237 for (Size i = p_index; i < len - 1 ; i++) {
224- p[i] = p[i + 1 ];
238+ p[i] = std::move ( p[i + 1 ]) ;
225239 }
226240
227241 resize (len - 1 );
228242 }
229243
230244 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 ]);
235252 }
236- set ( p_pos, p_val) ;
253+ p[ p_pos] = p_val;
237254
238255 return OK;
239256 }
@@ -243,35 +260,47 @@ class CowData {
243260 Size count (const T &p_val) const ;
244261
245262 _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+ }
248270};
249271
250272template <typename T>
251- void CowData<T>::_unref(void *p_data ) {
252- if (!p_data ) {
273+ void CowData<T>::_unref() {
274+ if (!_ptr ) {
253275 return ;
254276 }
255277
256278 SafeNumeric<USize> *refc = _get_refcount ();
257-
258279 if (refc->decrement () > 0 ) {
259- return ; // still in use
280+ // Data is still in use elsewhere.
281+ _ptr = nullptr ;
282+ return ;
260283 }
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 ;
262295
263296 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 ();
270299 }
271300 }
272301
273302 // free mem
274- Memory::free_static ((( uint8_t *)p_data) - DATA_OFFSET, false );
303+ Memory::free_static ((uint8_t *)prev_ptr - DATA_OFFSET, false );
275304}
276305
277306template <typename T>
@@ -306,7 +335,7 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() {
306335 }
307336 }
308337
309- _unref (_ptr );
338+ _unref ();
310339 _ptr = _data_ptr;
311340
312341 rc = 1 ;
@@ -326,14 +355,13 @@ Error CowData<T>::resize(Size p_size) {
326355 }
327356
328357 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.
332360 return OK;
333361 }
334362
335363 // possibly changing size, copy on write
336- USize rc = _copy_on_write ();
364+ _copy_on_write ();
337365
338366 USize current_alloc_size = _get_alloc_size (current_size);
339367 USize alloc_size;
@@ -354,16 +382,12 @@ Error CowData<T>::resize(Size p_size) {
354382 *(_size_ptr) = 0 ; // size, currently none
355383
356384 _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);
363385
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+ }
367391 }
368392 }
369393
@@ -389,15 +413,10 @@ Error CowData<T>::resize(Size p_size) {
389413 }
390414
391415 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+ }
401420 }
402421
403422 *_get_size () = p_size;
@@ -406,6 +425,21 @@ Error CowData<T>::resize(Size p_size) {
406425 return OK;
407426}
408427
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+
409443template <typename T>
410444typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
411445 Size ret = -1 ;
@@ -465,11 +499,10 @@ void CowData<T>::_ref(const CowData &p_from) {
465499 return ; // self assign, do nothing.
466500 }
467501
468- _unref (_ptr);
469- _ptr = nullptr ;
502+ _unref (); // Resets _ptr to nullptr.
470503
471504 if (!p_from._ptr ) {
472- return ; // nothing to do
505+ return ; // nothing to do
473506 }
474507
475508 if (p_from._get_refcount ()->conditional_increment () > 0 ) { // could reference
@@ -478,8 +511,16 @@ void CowData<T>::_ref(const CowData &p_from) {
478511}
479512
480513template <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+ }
483524}
484525
485526#if defined(__GNUC__) && !defined(__clang__)
0 commit comments