Skip to content

Commit 64b9a43

Browse files
authored
Merge pull request #2016 from Ivorforce/localvector-10.x
Sync `LocalVector` with upstream Godot
2 parents 572ff3c + 52fcc9f commit 64b9a43

3 files changed

Lines changed: 95 additions & 76 deletions

File tree

include/godot_cpp/core/method_bind.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class MethodBind {
111111
}
112112

113113
void set_argument_names(const LocalVector<StringName> &p_names);
114-
LocalVector<StringName> get_argument_names() const;
114+
const LocalVector<StringName> &get_argument_names() const;
115115

116116
virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int p_argument) const = 0;
117117

include/godot_cpp/templates/local_vector.hpp

Lines changed: 93 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -46,32 +46,48 @@ namespace godot {
4646
// Otherwise, it grows exponentially (the default and what you want in most cases).
4747
template <typename T, typename U = uint32_t, bool force_trivial = false, bool tight = false>
4848
class LocalVector {
49+
static_assert(!force_trivial, "force_trivial is no longer supported. Use resize_uninitialized instead.");
50+
4951
private:
5052
U count = 0;
5153
U capacity = 0;
5254
T *data = nullptr;
5355

56+
template <bool p_init>
57+
void _resize(U p_size) {
58+
if (p_size < count) {
59+
if constexpr (!std::is_trivially_destructible_v<T>) {
60+
for (U i = p_size; i < count; i++) {
61+
data[i].~T();
62+
}
63+
}
64+
count = p_size;
65+
} else if (p_size > count) {
66+
reserve(p_size);
67+
if constexpr (p_init) {
68+
memnew_arr_placement(data + count, p_size - count);
69+
} else {
70+
static_assert(std::is_trivially_destructible_v<T>, "T must be trivially destructible to resize uninitialized");
71+
}
72+
count = p_size;
73+
}
74+
}
75+
5476
public:
55-
_FORCE_INLINE_ T *ptr() { return data; }
56-
_FORCE_INLINE_ const T *ptr() const { return data; }
77+
_FORCE_INLINE_ T *ptr() _LIFETIME_BOUND_ { return data; }
78+
_FORCE_INLINE_ const T *ptr() const _LIFETIME_BOUND_ { return data; }
5779
_FORCE_INLINE_ U size() const { return count; }
5880

59-
_FORCE_INLINE_ Span<T> span() const { return Span(data, count); }
60-
_FORCE_INLINE_ operator Span<T>() const { return span(); }
81+
_FORCE_INLINE_ Span<T> span() const _LIFETIME_BOUND_ { return Span(data, count); }
82+
_FORCE_INLINE_ operator Span<T>() const _LIFETIME_BOUND_ { return span(); }
6183

6284
// Must take a copy instead of a reference (see GH-31736).
6385
_FORCE_INLINE_ void push_back(T p_elem) {
6486
if (unlikely(count == capacity)) {
65-
capacity = tight ? (capacity + 1) : MAX((U)1, capacity << 1);
66-
data = (T *)memrealloc(data, capacity * sizeof(T));
67-
CRASH_COND_MSG(!data, "Out of memory");
87+
reserve(count + 1);
6888
}
6989

70-
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
71-
memnew_placement(&data[count++], T(p_elem));
72-
} else {
73-
data[count++] = std::move(p_elem);
74-
}
90+
memnew_placement(&data[count++], T(std::move(p_elem)));
7591
}
7692

7793
void remove_at(U p_index) {
@@ -80,9 +96,7 @@ class LocalVector {
8096
for (U i = p_index; i < count; i++) {
8197
data[i] = std::move(data[i + 1]);
8298
}
83-
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
84-
data[count].~T();
85-
}
99+
data[count].~T();
86100
}
87101

88102
/// Removes the item copying the last value into the position of the one to
@@ -93,9 +107,7 @@ class LocalVector {
93107
if (count > p_index) {
94108
data[p_index] = std::move(data[count]);
95109
}
96-
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
97-
data[count].~T();
98-
}
110+
data[count].~T();
99111
}
100112

101113
_FORCE_INLINE_ bool erase(const T &p_val) {
@@ -107,6 +119,15 @@ class LocalVector {
107119
return false;
108120
}
109121

122+
bool erase_unordered(const T &p_val) {
123+
int64_t idx = find(p_val);
124+
if (idx >= 0) {
125+
remove_at_unordered(idx);
126+
return true;
127+
}
128+
return false;
129+
}
130+
110131
U erase_multiple_unordered(const T &p_val) {
111132
U from = 0;
112133
U occurrences = 0;
@@ -123,11 +144,14 @@ class LocalVector {
123144
return occurrences;
124145
}
125146

126-
void invert() {
147+
void reverse() {
127148
for (U i = 0; i < count / 2; i++) {
128149
SWAP(data[i], data[count - i - 1]);
129150
}
130151
}
152+
#ifndef DISABLE_DEPRECATED
153+
[[deprecated("Use reverse() instead")]] void invert() { reverse(); }
154+
#endif
131155

132156
_FORCE_INLINE_ void clear() { resize(0); }
133157
_FORCE_INLINE_ void reset() {
@@ -140,42 +164,48 @@ class LocalVector {
140164
}
141165
_FORCE_INLINE_ bool is_empty() const { return count == 0; }
142166
_FORCE_INLINE_ U get_capacity() const { return capacity; }
143-
_FORCE_INLINE_ void reserve(U p_size) {
144-
p_size = tight ? p_size : Math::nearest_power_of_2_templated(p_size);
167+
void reserve(U p_size) {
145168
if (p_size > capacity) {
146-
capacity = p_size;
169+
if (tight) {
170+
capacity = p_size;
171+
} else {
172+
// Try 1.5x the current capacity.
173+
// This ratio was chosen because it is close to the ideal growth rate of the golden ratio.
174+
// See https://archive.ph/Z2R8w for details.
175+
capacity = MAX((U)2, capacity + ((1 + capacity) >> 1));
176+
// If 1.5x growth isn't enough, just use the needed size exactly.
177+
if (p_size > capacity) {
178+
capacity = p_size;
179+
}
180+
}
147181
data = (T *)memrealloc(data, capacity * sizeof(T));
148182
CRASH_COND_MSG(!data, "Out of memory");
183+
} else if (p_size < count) {
184+
WARN_VERBOSE("reserve() called with a capacity smaller than the current size. This is likely a mistake.");
149185
}
150186
}
151187

188+
/// Resize the vector.
189+
/// Elements are initialized (or not) depending on what the default C++ behavior for T is.
190+
/// Note: If force_trivial is set, this will behave like resize_uninitialized instead.
152191
void resize(U p_size) {
153-
if (p_size < count) {
154-
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
155-
for (U i = p_size; i < count; i++) {
156-
data[i].~T();
157-
}
158-
}
159-
count = p_size;
160-
} else if (p_size > count) {
161-
if (unlikely(p_size > capacity)) {
162-
capacity = tight ? p_size : Math::nearest_power_of_2_templated(p_size);
163-
data = (T *)memrealloc(data, capacity * sizeof(T));
164-
CRASH_COND_MSG(!data, "Out of memory");
165-
}
166-
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
167-
for (U i = count; i < p_size; i++) {
168-
memnew_placement(&data[i], T);
169-
}
170-
}
171-
count = p_size;
172-
}
192+
// Don't init when trivially constructible.
193+
_resize<!std::is_trivially_constructible_v<T>>(p_size);
173194
}
174-
_FORCE_INLINE_ const T &operator[](U p_index) const {
195+
196+
/// Resize and set new values to 0 / false / nullptr.
197+
_FORCE_INLINE_ void resize_initialized(U p_size) { _resize<true>(p_size); }
198+
199+
/// Resize and keep memory uninitialized.
200+
/// This means that any newly added elements have an unknown value, and are expected to be set after the `resize_uninitialized` call.
201+
/// This is only available for trivially destructible types (otherwise, trivial resize might be UB).
202+
_FORCE_INLINE_ void resize_uninitialized(U p_size) { _resize<false>(p_size); }
203+
204+
_FORCE_INLINE_ const T &operator[](U p_index) const _LIFETIME_BOUND_ {
175205
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
176206
return data[p_index];
177207
}
178-
_FORCE_INLINE_ T &operator[](U p_index) {
208+
_FORCE_INLINE_ T &operator[](U p_index) _LIFETIME_BOUND_ {
179209
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
180210
return data[p_index];
181211
}
@@ -230,17 +260,17 @@ class LocalVector {
230260
const T *elem_ptr = nullptr;
231261
};
232262

233-
_FORCE_INLINE_ Iterator begin() {
263+
_FORCE_INLINE_ Iterator begin() _LIFETIME_BOUND_ {
234264
return Iterator(data);
235265
}
236-
_FORCE_INLINE_ Iterator end() {
266+
_FORCE_INLINE_ Iterator end() _LIFETIME_BOUND_ {
237267
return Iterator(data + size());
238268
}
239269

240-
_FORCE_INLINE_ ConstIterator begin() const {
270+
_FORCE_INLINE_ ConstIterator begin() const _LIFETIME_BOUND_ {
241271
return ConstIterator(ptr());
242272
}
243-
_FORCE_INLINE_ ConstIterator end() const {
273+
_FORCE_INLINE_ ConstIterator end() const _LIFETIME_BOUND_ {
244274
return ConstIterator(ptr() + size());
245275
}
246276

@@ -257,13 +287,14 @@ class LocalVector {
257287
}
258288
}
259289

260-
int64_t find(const T &p_val, U p_from = 0) const {
261-
for (U i = p_from; i < count; i++) {
262-
if (data[i] == p_val) {
263-
return int64_t(i);
264-
}
290+
int64_t find(const T &p_val, int64_t p_from = 0) const {
291+
if (p_from < 0) {
292+
p_from = size() + p_from;
265293
}
266-
return -1;
294+
if (p_from < 0 || p_from >= size()) {
295+
return -1;
296+
}
297+
return span().find(p_val, p_from);
267298
}
268299

269300
bool has(const T &p_val) const {
@@ -282,7 +313,7 @@ class LocalVector {
282313
}
283314

284315
void sort() {
285-
sort_custom<_DefaultComparator<T>>();
316+
sort_custom<Comparator<T>>();
286317
}
287318

288319
void ordered_insert(T p_val) {
@@ -295,22 +326,6 @@ class LocalVector {
295326
insert(i, p_val);
296327
}
297328

298-
operator Vector<T>() const {
299-
Vector<T> ret;
300-
ret.resize(count);
301-
T *w = ret.ptrw();
302-
if (w) {
303-
if constexpr (std::is_trivially_copyable_v<T>) {
304-
memcpy(w, data, sizeof(T) * count);
305-
} else {
306-
for (U i = 0; i < count; i++) {
307-
w[i] = data[i];
308-
}
309-
}
310-
}
311-
return ret;
312-
}
313-
314329
Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
315330
Vector<uint8_t> ret;
316331
ret.resize(count * sizeof(T));
@@ -328,7 +343,7 @@ class LocalVector {
328343
push_back(element);
329344
}
330345
}
331-
_FORCE_INLINE_ LocalVector(const LocalVector &p_from) {
346+
_FORCE_INLINE_ explicit LocalVector(const LocalVector &p_from) {
332347
resize(p_from.size());
333348
for (U i = 0; i < p_from.count; i++) {
334349
data[i] = p_from.data[i];
@@ -384,7 +399,11 @@ class LocalVector {
384399
}
385400
};
386401

387-
template <typename T, typename U = uint32_t, bool force_trivial = false>
388-
using TightLocalVector = LocalVector<T, U, force_trivial, true>;
402+
template <typename T, typename U = uint32_t>
403+
using TightLocalVector = LocalVector<T, U, false, true>;
404+
405+
// Zero-constructing LocalVector initializes count, capacity and data to 0 and thus empty.
406+
template <typename T, typename U, bool force_trivial, bool tight>
407+
struct is_zero_constructible<LocalVector<T, U, force_trivial, tight>> : std::true_type {};
389408

390409
} // namespace godot

src/core/method_bind.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void MethodBind::set_argument_names(const LocalVector<StringName> &p_names) {
6060
argument_names = p_names;
6161
}
6262

63-
LocalVector<StringName> MethodBind::get_argument_names() const {
63+
const LocalVector<StringName> &MethodBind::get_argument_names() const {
6464
return argument_names;
6565
}
6666

0 commit comments

Comments
 (0)