瀏覽代碼

Add `resize_initialized` and `resize_uninitialized` to `LocalVector`.

Lukas Tenbrink 3 月之前
父節點
當前提交
670ab7a383
共有 1 個文件被更改,包括 40 次插入18 次删除
  1. 40 18
      core/templates/local_vector.h

+ 40 - 18
core/templates/local_vector.h

@@ -40,6 +40,8 @@
 
 // If tight, it grows strictly as much as needed.
 // Otherwise, it grows exponentially (the default and what you want in most cases).
+// force_trivial is used to avoid T's default value on resize, for improved performance.
+// This requires T to be trivially destructible.
 template <typename T, typename U = uint32_t, bool force_trivial = false, bool tight = false>
 class LocalVector {
 private:
@@ -47,6 +49,30 @@ private:
 	U capacity = 0;
 	T *data = nullptr;
 
+	template <bool p_init>
+	void _resize(U p_size) {
+		if (p_size < count) {
+			if constexpr (!std::is_trivially_destructible_v<T>) {
+				for (U i = p_size; i < count; i++) {
+					data[i].~T();
+				}
+			}
+			count = p_size;
+		} else if (p_size > count) {
+			if (unlikely(p_size > capacity)) {
+				capacity = tight ? p_size : nearest_power_of_2_templated(p_size);
+				data = (T *)memrealloc(data, capacity * sizeof(T));
+				CRASH_COND_MSG(!data, "Out of memory");
+			}
+			if constexpr (p_init) {
+				memnew_arr_placement(data + count, p_size - count);
+			} else {
+				static_assert(std::is_trivially_destructible_v<T>, "T must be trivially destructible to resize uninitialized");
+			}
+			count = p_size;
+		}
+	}
+
 public:
 	_FORCE_INLINE_ T *ptr() { return data; }
 	_FORCE_INLINE_ const T *ptr() const { return data; }
@@ -154,26 +180,22 @@ public:
 		}
 	}
 
+	/// Resize the vector.
+	/// Elements are initialized (or not) depending on what the default C++ behavior for T is.
+	/// Note: If force_trivial is set, this will behave like resize_trivial instead.
 	void resize(U p_size) {
-		// We must statically assert this in a function because otherwise,
-		// `LocalVector` cannot be used with a forward-declared type.
-		static_assert(!force_trivial || std::is_trivially_destructible_v<T>, "T must be trivially destructible if force_trivial is set");
-
-		if (p_size < count) {
-			if constexpr (!std::is_trivially_destructible_v<T>) {
-				for (U i = p_size; i < count; i++) {
-					data[i].~T();
-				}
-			}
-			count = p_size;
-		} else if (p_size > count) {
-			reserve(p_size);
-			if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
-				memnew_arr_placement(data + count, p_size - count);
-			}
-			count = p_size;
-		}
+		// Don't init when trivially constructible, or force_trivial is set.
+		_resize<!force_trivial && !std::is_trivially_constructible_v<T>>(p_size);
 	}
+
+	/// Resize and set all values to 0 / false / nullptr.
+	/// This is only available for zero constructible types.
+	_FORCE_INLINE_ void resize_initialized(U p_size) { _resize<true>(p_size); }
+
+	/// Resize and set all values to 0 / false / nullptr.
+	/// This is only available for trivially destructible types (otherwise, trivial resize might be UB).
+	_FORCE_INLINE_ void resize_uninitialized(U p_size) { _resize<false>(p_size); }
+
 	_FORCE_INLINE_ const T &operator[](U p_index) const {
 		CRASH_BAD_UNSIGNED_INDEX(p_index, count);
 		return data[p_index];