Skip to content

Commit af78526

Browse files
committed
Synchronize most shared template code with Godot 4.4
1 parent 98ea2f6 commit af78526

File tree

16 files changed

+724
-369
lines changed

16 files changed

+724
-369
lines changed

include/godot_cpp/templates/cowdata.hpp

Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
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

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

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

278306
template <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+
410443
template <typename T>
411444
typename 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

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

Comments
 (0)