Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/godot_cpp/core/method_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class MethodBind {
}

void set_argument_names(const LocalVector<StringName> &p_names);
LocalVector<StringName> get_argument_names() const;
const LocalVector<StringName> &get_argument_names() const;

virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int p_argument) const = 0;

Expand Down
167 changes: 93 additions & 74 deletions include/godot_cpp/templates/local_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,32 +46,48 @@ namespace godot {
// Otherwise, it grows exponentially (the default and what you want in most cases).
template <typename T, typename U = uint32_t, bool force_trivial = false, bool tight = false>
class LocalVector {
static_assert(!force_trivial, "force_trivial is no longer supported. Use resize_uninitialized instead.");

private:
U count = 0;
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) {
reserve(p_size);
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; }
_FORCE_INLINE_ T *ptr() _LIFETIME_BOUND_ { return data; }
_FORCE_INLINE_ const T *ptr() const _LIFETIME_BOUND_ { return data; }
_FORCE_INLINE_ U size() const { return count; }

_FORCE_INLINE_ Span<T> span() const { return Span(data, count); }
_FORCE_INLINE_ operator Span<T>() const { return span(); }
_FORCE_INLINE_ Span<T> span() const _LIFETIME_BOUND_ { return Span(data, count); }
_FORCE_INLINE_ operator Span<T>() const _LIFETIME_BOUND_ { return span(); }

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

if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
memnew_placement(&data[count++], T(p_elem));
} else {
data[count++] = std::move(p_elem);
}
memnew_placement(&data[count++], T(std::move(p_elem)));
}

void remove_at(U p_index) {
Expand All @@ -80,9 +96,7 @@ class LocalVector {
for (U i = p_index; i < count; i++) {
data[i] = std::move(data[i + 1]);
}
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
data[count].~T();
}

/// Removes the item copying the last value into the position of the one to
Expand All @@ -93,9 +107,7 @@ class LocalVector {
if (count > p_index) {
data[p_index] = std::move(data[count]);
}
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
data[count].~T();
}

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

bool erase_unordered(const T &p_val) {
int64_t idx = find(p_val);
if (idx >= 0) {
remove_at_unordered(idx);
return true;
}
return false;
}

U erase_multiple_unordered(const T &p_val) {
U from = 0;
U occurrences = 0;
Expand All @@ -123,11 +144,14 @@ class LocalVector {
return occurrences;
}

void invert() {
void reverse() {
for (U i = 0; i < count / 2; i++) {
SWAP(data[i], data[count - i - 1]);
}
}
#ifndef DISABLE_DEPRECATED
[[deprecated("Use reverse() instead")]] void invert() { reverse(); }
#endif

_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ void reset() {
Expand All @@ -140,42 +164,48 @@ class LocalVector {
}
_FORCE_INLINE_ bool is_empty() const { return count == 0; }
_FORCE_INLINE_ U get_capacity() const { return capacity; }
_FORCE_INLINE_ void reserve(U p_size) {
p_size = tight ? p_size : Math::nearest_power_of_2_templated(p_size);
void reserve(U p_size) {
if (p_size > capacity) {
capacity = p_size;
if (tight) {
capacity = p_size;
} else {
// Try 1.5x the current capacity.
// This ratio was chosen because it is close to the ideal growth rate of the golden ratio.
// See https://archive.ph/Z2R8w for details.
capacity = MAX((U)2, capacity + ((1 + capacity) >> 1));
// If 1.5x growth isn't enough, just use the needed size exactly.
if (p_size > capacity) {
capacity = p_size;
}
}
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
} else if (p_size < count) {
WARN_VERBOSE("reserve() called with a capacity smaller than the current size. This is likely a mistake.");
}
}

/// 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_uninitialized instead.
void resize(U p_size) {
if (p_size < count) {
if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
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 : Math::nearest_power_of_2_templated(p_size);
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
}
if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
for (U i = count; i < p_size; i++) {
memnew_placement(&data[i], T);
}
}
count = p_size;
}
// Don't init when trivially constructible.
_resize<!std::is_trivially_constructible_v<T>>(p_size);
}
_FORCE_INLINE_ const T &operator[](U p_index) const {

/// Resize and set new values to 0 / false / nullptr.
_FORCE_INLINE_ void resize_initialized(U p_size) { _resize<true>(p_size); }

/// Resize and keep memory uninitialized.
/// This means that any newly added elements have an unknown value, and are expected to be set after the `resize_uninitialized` call.
/// 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 _LIFETIME_BOUND_ {
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
return data[p_index];
}
_FORCE_INLINE_ T &operator[](U p_index) {
_FORCE_INLINE_ T &operator[](U p_index) _LIFETIME_BOUND_ {
CRASH_BAD_UNSIGNED_INDEX(p_index, count);
return data[p_index];
}
Expand Down Expand Up @@ -230,17 +260,17 @@ class LocalVector {
const T *elem_ptr = nullptr;
};

_FORCE_INLINE_ Iterator begin() {
_FORCE_INLINE_ Iterator begin() _LIFETIME_BOUND_ {
return Iterator(data);
}
_FORCE_INLINE_ Iterator end() {
_FORCE_INLINE_ Iterator end() _LIFETIME_BOUND_ {
return Iterator(data + size());
}

_FORCE_INLINE_ ConstIterator begin() const {
_FORCE_INLINE_ ConstIterator begin() const _LIFETIME_BOUND_ {
return ConstIterator(ptr());
}
_FORCE_INLINE_ ConstIterator end() const {
_FORCE_INLINE_ ConstIterator end() const _LIFETIME_BOUND_ {
return ConstIterator(ptr() + size());
}

Expand All @@ -257,13 +287,14 @@ class LocalVector {
}
}

int64_t find(const T &p_val, U p_from = 0) const {
for (U i = p_from; i < count; i++) {
if (data[i] == p_val) {
return int64_t(i);
}
int64_t find(const T &p_val, int64_t p_from = 0) const {
if (p_from < 0) {
p_from = size() + p_from;
}
return -1;
if (p_from < 0 || p_from >= size()) {
return -1;
}
return span().find(p_val, p_from);
}

bool has(const T &p_val) const {
Expand All @@ -282,7 +313,7 @@ class LocalVector {
}

void sort() {
sort_custom<_DefaultComparator<T>>();
sort_custom<Comparator<T>>();
}

void ordered_insert(T p_val) {
Expand All @@ -295,22 +326,6 @@ class LocalVector {
insert(i, p_val);
}

operator Vector<T>() const {
Vector<T> ret;
ret.resize(count);
T *w = ret.ptrw();
if (w) {
if constexpr (std::is_trivially_copyable_v<T>) {
memcpy(w, data, sizeof(T) * count);
} else {
for (U i = 0; i < count; i++) {
w[i] = data[i];
}
}
}
return ret;
}

Vector<uint8_t> to_byte_array() const { //useful to pass stuff to gpu or variant
Vector<uint8_t> ret;
ret.resize(count * sizeof(T));
Expand All @@ -328,7 +343,7 @@ class LocalVector {
push_back(element);
}
}
_FORCE_INLINE_ LocalVector(const LocalVector &p_from) {
_FORCE_INLINE_ explicit LocalVector(const LocalVector &p_from) {
resize(p_from.size());
for (U i = 0; i < p_from.count; i++) {
data[i] = p_from.data[i];
Expand Down Expand Up @@ -384,7 +399,11 @@ class LocalVector {
}
};

template <typename T, typename U = uint32_t, bool force_trivial = false>
using TightLocalVector = LocalVector<T, U, force_trivial, true>;
template <typename T, typename U = uint32_t>
using TightLocalVector = LocalVector<T, U, false, true>;

// Zero-constructing LocalVector initializes count, capacity and data to 0 and thus empty.
template <typename T, typename U, bool force_trivial, bool tight>
struct is_zero_constructible<LocalVector<T, U, force_trivial, tight>> : std::true_type {};

} // namespace godot
2 changes: 1 addition & 1 deletion src/core/method_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ void MethodBind::set_argument_names(const LocalVector<StringName> &p_names) {
argument_names = p_names;
}

LocalVector<StringName> MethodBind::get_argument_names() const {
const LocalVector<StringName> &MethodBind::get_argument_names() const {
return argument_names;
}

Expand Down
Loading