@@ -46,32 +46,48 @@ namespace godot {
4646// Otherwise, it grows exponentially (the default and what you want in most cases).
4747template <typename T, typename U = uint32_t , bool force_trivial = false , bool tight = false >
4848class LocalVector {
49+ static_assert (!force_trivial, " force_trivial is no longer supported. Use resize_uninitialized instead." );
50+
4951private:
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+
5476public:
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
0 commit comments