3838#include < godot_cpp/templates/safe_refcount.hpp>
3939
4040#include < cstring>
41+ #include < initializer_list>
4142#include < new>
4243#include < type_traits>
4344
@@ -167,13 +168,25 @@ class CowData {
167168 return *out;
168169 }
169170
170- 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 ();
171174 void _ref (const CowData *p_from);
172175 void _ref (const CowData &p_from);
173176 USize _copy_on_write ();
177+ Error _realloc (Size p_alloc_size);
174178
175179public:
176180 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+ }
177190
178191 _FORCE_INLINE_ T *ptrw () {
179192 _copy_on_write ();
@@ -222,19 +235,22 @@ class CowData {
222235 T *p = ptrw ();
223236 Size len = size ();
224237 for (Size i = p_index; i < len - 1 ; i++) {
225- p[i] = p[i + 1 ];
238+ p[i] = std::move ( p[i + 1 ]) ;
226239 }
227240
228241 resize (len - 1 );
229242 }
230243
231244 Error insert (Size p_pos, const T &p_val) {
232- ERR_FAIL_INDEX_V (p_pos, size () + 1 , ERR_INVALID_PARAMETER);
233- resize (size () + 1 );
234- for (Size i = (size () - 1 ); i > p_pos; i--) {
235- 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 ]);
236252 }
237- set ( p_pos, p_val) ;
253+ p[ p_pos] = p_val;
238254
239255 return OK;
240256 }
@@ -244,35 +260,47 @@ class CowData {
244260 Size count (const T &p_val) const ;
245261
246262 _FORCE_INLINE_ CowData () {}
247- _FORCE_INLINE_ ~CowData ();
248- _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+ }
249270};
250271
251272template <typename T>
252- void CowData<T>::_unref(void *p_data ) {
253- if (!p_data ) {
273+ void CowData<T>::_unref() {
274+ if (!_ptr ) {
254275 return ;
255276 }
256277
257278 SafeNumeric<USize> *refc = _get_refcount ();
258-
259279 if (refc->decrement () > 0 ) {
260- return ; // still in use
280+ // Data is still in use elsewhere.
281+ _ptr = nullptr ;
282+ return ;
261283 }
262- // 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 ;
263295
264296 if constexpr (!std::is_trivially_destructible_v<T>) {
265- USize *count = _get_size ();
266- T *data = (T *)(count + 1 );
267-
268- for (USize i = 0 ; i < *count; ++i) {
269- // call destructors
270- data[i].~T ();
297+ for (USize i = 0 ; i < current_size; ++i) {
298+ prev_ptr[i].~T ();
271299 }
272300 }
273301
274302 // free mem
275- Memory::free_static ((( uint8_t *)p_data) - DATA_OFFSET, false );
303+ Memory::free_static ((uint8_t *)prev_ptr - DATA_OFFSET, false );
276304}
277305
278306template <typename T>
@@ -307,7 +335,7 @@ typename CowData<T>::USize CowData<T>::_copy_on_write() {
307335 }
308336 }
309337
310- _unref (_ptr );
338+ _unref ();
311339 _ptr = _data_ptr;
312340
313341 rc = 1 ;
@@ -327,14 +355,13 @@ Error CowData<T>::resize(Size p_size) {
327355 }
328356
329357 if (p_size == 0 ) {
330- // wants to clean up
331- _unref (_ptr);
332- _ptr = nullptr ;
358+ // Wants to clean up.
359+ _unref (); // Resets _ptr to nullptr.
333360 return OK;
334361 }
335362
336363 // possibly changing size, copy on write
337- USize rc = _copy_on_write ();
364+ _copy_on_write ();
338365
339366 USize current_alloc_size = _get_alloc_size (current_size);
340367 USize alloc_size;
@@ -355,16 +382,12 @@ Error CowData<T>::resize(Size p_size) {
355382 *(_size_ptr) = 0 ; // size, currently none
356383
357384 _ptr = _data_ptr;
358- } else {
359- uint8_t *mem_new = (uint8_t *)Memory::realloc_static (((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false );
360- ERR_FAIL_NULL_V (mem_new, ERR_OUT_OF_MEMORY);
361-
362- SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr (mem_new);
363- T *_data_ptr = _get_data_ptr (mem_new);
364385
365- new (_refc_ptr) SafeNumeric<USize>(rc); // refcount
366-
367- _ptr = _data_ptr;
386+ } else {
387+ const Error error = _realloc (alloc_size);
388+ if (error) {
389+ return error;
390+ }
368391 }
369392 }
370393
@@ -390,15 +413,10 @@ Error CowData<T>::resize(Size p_size) {
390413 }
391414
392415 if (alloc_size != current_alloc_size) {
393- uint8_t *mem_new = (uint8_t *)Memory::realloc_static (((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false );
394- ERR_FAIL_NULL_V (mem_new, ERR_OUT_OF_MEMORY);
395-
396- SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr (mem_new);
397- T *_data_ptr = _get_data_ptr (mem_new);
398-
399- new (_refc_ptr) SafeNumeric<USize>(rc); // refcount
400-
401- _ptr = _data_ptr;
416+ const Error error = _realloc (alloc_size);
417+ if (error) {
418+ return error;
419+ }
402420 }
403421
404422 *_get_size () = p_size;
@@ -407,6 +425,21 @@ Error CowData<T>::resize(Size p_size) {
407425 return OK;
408426}
409427
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+
410443template <typename T>
411444typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
412445 Size ret = -1 ;
@@ -466,11 +499,10 @@ void CowData<T>::_ref(const CowData &p_from) {
466499 return ; // self assign, do nothing.
467500 }
468501
469- _unref (_ptr);
470- _ptr = nullptr ;
502+ _unref (); // Resets _ptr to nullptr.
471503
472504 if (!p_from._ptr ) {
473- return ; // nothing to do
505+ return ; // nothing to do
474506 }
475507
476508 if (p_from._get_refcount ()->conditional_increment () > 0 ) { // could reference
@@ -479,8 +511,16 @@ void CowData<T>::_ref(const CowData &p_from) {
479511}
480512
481513template <typename T>
482- CowData<T>::~CowData () {
483- _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+ }
484524}
485525
486526#if defined(__GNUC__) && !defined(__clang__)
0 commit comments