Skip to content

Commit f08e781

Browse files
authored
Merge pull request #1718 from dsnopek/godot-sync-pre44-templates
Synchronize most shared template code with Godot 4.4
2 parents 2b4802d + 1edfca2 commit f08e781

16 files changed

+725
-369
lines changed

include/godot_cpp/templates/cowdata.hpp

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
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

4345
namespace 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

174179
public:
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

250272
template <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

277306
template <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+
409443
template <typename T>
410444
typename 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

480513
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+
}
483524
}
484525

485526
#if defined(__GNUC__) && !defined(__clang__)

0 commit comments

Comments
 (0)