mirror of
https://github.com/godotengine/godot.git
synced 2025-11-01 06:01:14 +00:00
Move bisect to Span and deduplicate code.
Co-authored-by: Lukas Tenbrink <lukas.tenbrink@gmail.com>
This commit is contained in:
parent
09fcbb8645
commit
3bf400ffae
9 changed files with 113 additions and 108 deletions
|
|
@ -86,6 +86,10 @@ public:
|
||||||
constexpr int64_t rfind(const T &p_val, uint64_t p_from) const;
|
constexpr int64_t rfind(const T &p_val, uint64_t p_from) const;
|
||||||
_FORCE_INLINE_ constexpr int64_t rfind(const T &p_val) const { return rfind(p_val, size() - 1); }
|
_FORCE_INLINE_ constexpr int64_t rfind(const T &p_val) const { return rfind(p_val, size() - 1); }
|
||||||
constexpr uint64_t count(const T &p_val) const;
|
constexpr uint64_t count(const T &p_val) const;
|
||||||
|
/// Find the index of the given value using binary search.
|
||||||
|
/// Note: Assumes that elements in the span are sorted. Otherwise, use find() instead.
|
||||||
|
template <typename Comparator = Comparator<T>>
|
||||||
|
constexpr uint64_t bisect(const T &p_value, bool p_before, Comparator compare = Comparator()) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
@ -119,6 +123,33 @@ constexpr uint64_t Span<T>::count(const T &p_val) const {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Comparator>
|
||||||
|
constexpr uint64_t Span<T>::bisect(const T &p_value, bool p_before, Comparator compare) const {
|
||||||
|
uint64_t lo = 0;
|
||||||
|
uint64_t hi = size();
|
||||||
|
if (p_before) {
|
||||||
|
while (lo < hi) {
|
||||||
|
const uint64_t mid = (lo + hi) / 2;
|
||||||
|
if (compare(ptr()[mid], p_value)) {
|
||||||
|
lo = mid + 1;
|
||||||
|
} else {
|
||||||
|
hi = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (lo < hi) {
|
||||||
|
const uint64_t mid = (lo + hi) / 2;
|
||||||
|
if (compare(p_value, ptr()[mid])) {
|
||||||
|
hi = mid;
|
||||||
|
} else {
|
||||||
|
lo = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
|
||||||
// Zero-constructing Span initializes _ptr and _len to 0 (and thus empty).
|
// Zero-constructing Span initializes _ptr and _len to 0 (and thus empty).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_zero_constructible<Span<T>> : std::true_type {};
|
struct is_zero_constructible<Span<T>> : std::true_type {};
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
|
|
||||||
#include "core/error/error_macros.h"
|
#include "core/error/error_macros.h"
|
||||||
#include "core/templates/cowdata.h"
|
#include "core/templates/cowdata.h"
|
||||||
#include "core/templates/search_array.h"
|
|
||||||
#include "core/templates/sort_array.h"
|
#include "core/templates/sort_array.h"
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
@ -152,8 +151,7 @@ public:
|
||||||
|
|
||||||
template <typename Comparator, typename Value, typename... Args>
|
template <typename Comparator, typename Value, typename... Args>
|
||||||
Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
|
Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
|
||||||
SearchArray<T, Comparator> search{ args... };
|
return span().bisect(p_value, p_before, Comparator{ args... });
|
||||||
return search.bisect(ptrw(), size(), p_value, p_before);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<T> duplicate() {
|
Vector<T> duplicate() {
|
||||||
|
|
|
||||||
|
|
@ -37,41 +37,19 @@ template <typename T>
|
||||||
class VSet {
|
class VSet {
|
||||||
Vector<T> _data;
|
Vector<T> _data;
|
||||||
|
|
||||||
|
protected:
|
||||||
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
|
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
|
||||||
r_exact = false;
|
r_exact = false;
|
||||||
if (_data.is_empty()) {
|
if (_data.is_empty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int low = 0;
|
int64_t pos = _data.span().bisect(p_val, true);
|
||||||
int high = _data.size() - 1;
|
|
||||||
const T *a = &_data[0];
|
|
||||||
int middle = 0;
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) {
|
||||||
if (low > high) {
|
r_exact = true;
|
||||||
ERR_PRINT("low > high, this may be a bug");
|
|
||||||
}
|
}
|
||||||
#endif
|
return pos;
|
||||||
|
|
||||||
while (low <= high) {
|
|
||||||
middle = (low + high) / 2;
|
|
||||||
|
|
||||||
if (p_val < a[middle]) {
|
|
||||||
high = middle - 1; //search low end of array
|
|
||||||
} else if (a[middle] < p_val) {
|
|
||||||
low = middle + 1; //search high end of array
|
|
||||||
} else {
|
|
||||||
r_exact = true;
|
|
||||||
return middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//return the position where this would be inserted
|
|
||||||
if (a[middle] < p_val) {
|
|
||||||
middle++;
|
|
||||||
}
|
|
||||||
return middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_FORCE_INLINE_ int _find_exact(const T &p_val) const {
|
_FORCE_INLINE_ int _find_exact(const T &p_val) const {
|
||||||
|
|
@ -79,23 +57,11 @@ class VSet {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int low = 0;
|
int64_t pos = _data.span().bisect(p_val, true);
|
||||||
int high = _data.size() - 1;
|
|
||||||
int middle;
|
|
||||||
const T *a = &_data[0];
|
|
||||||
|
|
||||||
while (low <= high) {
|
if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) {
|
||||||
middle = (low + high) / 2;
|
return pos;
|
||||||
|
|
||||||
if (p_val < a[middle]) {
|
|
||||||
high = middle - 1; //search low end of array
|
|
||||||
} else if (a[middle] < p_val) {
|
|
||||||
low = middle + 1; //search high end of array
|
|
||||||
} else {
|
|
||||||
return middle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@
|
||||||
#include "core/math/math_funcs.h"
|
#include "core/math/math_funcs.h"
|
||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "core/templates/hashfuncs.h"
|
#include "core/templates/hashfuncs.h"
|
||||||
#include "core/templates/search_array.h"
|
|
||||||
#include "core/templates/vector.h"
|
#include "core/templates/vector.h"
|
||||||
#include "core/variant/callable.h"
|
#include "core/variant/callable.h"
|
||||||
#include "core/variant/dictionary.h"
|
#include "core/variant/dictionary.h"
|
||||||
|
|
@ -737,8 +736,7 @@ void Array::shuffle() {
|
||||||
int Array::bsearch(const Variant &p_value, bool p_before) const {
|
int Array::bsearch(const Variant &p_value, bool p_before) const {
|
||||||
Variant value = p_value;
|
Variant value = p_value;
|
||||||
ERR_FAIL_COND_V(!_p->typed.validate(value, "binary search"), -1);
|
ERR_FAIL_COND_V(!_p->typed.validate(value, "binary search"), -1);
|
||||||
SearchArray<Variant, _ArrayVariantSort> avs;
|
return _p->array.span().bisect<_ArrayVariantSort>(value, p_before);
|
||||||
return avs.bisect(_p->array.ptr(), _p->array.size(), value, p_before);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Array::bsearch_custom(const Variant &p_value, const Callable &p_callable, bool p_before) const {
|
int Array::bsearch_custom(const Variant &p_value, const Callable &p_callable, bool p_before) const {
|
||||||
|
|
|
||||||
|
|
@ -1571,14 +1571,13 @@ BoundUniformSet &MDUniformSet::bound_uniform_set(MDShader *p_shader, id<MTLDevic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchArray<__unsafe_unretained id<MTLResource>> search;
|
|
||||||
ResourceUsageMap usage_to_resources;
|
ResourceUsageMap usage_to_resources;
|
||||||
for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
|
for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
|
||||||
ResourceVector *resources = usage_to_resources.getptr(keyval.value);
|
ResourceVector *resources = usage_to_resources.getptr(keyval.value);
|
||||||
if (resources == nullptr) {
|
if (resources == nullptr) {
|
||||||
resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
|
resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
|
||||||
}
|
}
|
||||||
int64_t pos = search.bisect(resources->ptr(), resources->size(), keyval.key, true);
|
int64_t pos = resources->span().bisect(keyval.key, true);
|
||||||
if (pos == resources->size() || (*resources)[pos] != keyval.key) {
|
if (pos == resources->size() || (*resources)[pos] != keyval.key) {
|
||||||
resources->insert(pos, keyval.key);
|
resources->insert(pos, keyval.key);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,10 +178,8 @@ static void merge_methods(Vector<DocData::MethodDoc> &p_to, const Vector<DocData
|
||||||
DocData::MethodDoc *to_ptrw = p_to.ptrw();
|
DocData::MethodDoc *to_ptrw = p_to.ptrw();
|
||||||
int64_t to_size = p_to.size();
|
int64_t to_size = p_to.size();
|
||||||
|
|
||||||
SearchArray<DocData::MethodDoc, MethodCompare> search_array;
|
|
||||||
|
|
||||||
for (const DocData::MethodDoc &from : p_from) {
|
for (const DocData::MethodDoc &from : p_from) {
|
||||||
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
|
int64_t found = p_to.span().bisect<MethodCompare>(from, true);
|
||||||
|
|
||||||
if (found >= to_size) {
|
if (found >= to_size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -206,10 +204,8 @@ static void merge_constants(Vector<DocData::ConstantDoc> &p_to, const Vector<Doc
|
||||||
const DocData::ConstantDoc *from_ptr = p_from.ptr();
|
const DocData::ConstantDoc *from_ptr = p_from.ptr();
|
||||||
int64_t from_size = p_from.size();
|
int64_t from_size = p_from.size();
|
||||||
|
|
||||||
SearchArray<DocData::ConstantDoc> search_array;
|
|
||||||
|
|
||||||
for (DocData::ConstantDoc &to : p_to) {
|
for (DocData::ConstantDoc &to : p_to) {
|
||||||
int64_t found = search_array.bisect(from_ptr, from_size, to, true);
|
int64_t found = p_from.span().bisect(to, true);
|
||||||
|
|
||||||
if (found >= from_size) {
|
if (found >= from_size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -234,10 +230,8 @@ static void merge_properties(Vector<DocData::PropertyDoc> &p_to, const Vector<Do
|
||||||
DocData::PropertyDoc *to_ptrw = p_to.ptrw();
|
DocData::PropertyDoc *to_ptrw = p_to.ptrw();
|
||||||
int64_t to_size = p_to.size();
|
int64_t to_size = p_to.size();
|
||||||
|
|
||||||
SearchArray<DocData::PropertyDoc> search_array;
|
|
||||||
|
|
||||||
for (const DocData::PropertyDoc &from : p_from) {
|
for (const DocData::PropertyDoc &from : p_from) {
|
||||||
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
|
int64_t found = p_to.span().bisect(from, true);
|
||||||
|
|
||||||
if (found >= to_size) {
|
if (found >= to_size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -262,10 +256,8 @@ static void merge_theme_properties(Vector<DocData::ThemeItemDoc> &p_to, const Ve
|
||||||
DocData::ThemeItemDoc *to_ptrw = p_to.ptrw();
|
DocData::ThemeItemDoc *to_ptrw = p_to.ptrw();
|
||||||
int64_t to_size = p_to.size();
|
int64_t to_size = p_to.size();
|
||||||
|
|
||||||
SearchArray<DocData::ThemeItemDoc> search_array;
|
|
||||||
|
|
||||||
for (const DocData::ThemeItemDoc &from : p_from) {
|
for (const DocData::ThemeItemDoc &from : p_from) {
|
||||||
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
|
int64_t found = p_to.span().bisect(from, true);
|
||||||
|
|
||||||
if (found >= to_size) {
|
if (found >= to_size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -290,10 +282,8 @@ static void merge_operators(Vector<DocData::MethodDoc> &p_to, const Vector<DocDa
|
||||||
DocData::MethodDoc *to_ptrw = p_to.ptrw();
|
DocData::MethodDoc *to_ptrw = p_to.ptrw();
|
||||||
int64_t to_size = p_to.size();
|
int64_t to_size = p_to.size();
|
||||||
|
|
||||||
SearchArray<DocData::MethodDoc, OperatorCompare> search_array;
|
|
||||||
|
|
||||||
for (const DocData::MethodDoc &from : p_from) {
|
for (const DocData::MethodDoc &from : p_from) {
|
||||||
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
|
int64_t found = p_to.span().bisect(from, true);
|
||||||
|
|
||||||
if (found >= to_size) {
|
if (found >= to_size) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -1414,20 +1414,7 @@ void ItemList::_notification(int p_what) {
|
||||||
const Rect2 clip(-base_ofs, size);
|
const Rect2 clip(-base_ofs, size);
|
||||||
|
|
||||||
// Do a binary search to find the first separator that is below clip_position.y.
|
// Do a binary search to find the first separator that is below clip_position.y.
|
||||||
int first_visible_separator = 0;
|
int64_t first_visible_separator = separators.span().bisect(clip.position.y, true);
|
||||||
{
|
|
||||||
int lo = 0;
|
|
||||||
int hi = separators.size();
|
|
||||||
while (lo < hi) {
|
|
||||||
const int mid = (lo + hi) / 2;
|
|
||||||
if (separators[mid] < clip.position.y) {
|
|
||||||
lo = mid + 1;
|
|
||||||
} else {
|
|
||||||
hi = mid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
first_visible_separator = lo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not in thumbnails mode, draw visible separators.
|
// If not in thumbnails mode, draw visible separators.
|
||||||
if (icon_mode != ICON_MODE_TOP) {
|
if (icon_mode != ICON_MODE_TOP) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
/* search_array.h */
|
/* test_vset.h */
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
/* This file is part of: */
|
/* This file is part of: */
|
||||||
/* GODOT ENGINE */
|
/* GODOT ENGINE */
|
||||||
|
|
@ -30,35 +30,70 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/typedefs.h"
|
#include "core/templates/vset.h"
|
||||||
|
|
||||||
template <typename T, typename Comparator = Comparator<T>>
|
#include "tests/test_macros.h"
|
||||||
class SearchArray {
|
|
||||||
|
namespace TestVSet {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class TestClass : public VSet<T> {
|
||||||
public:
|
public:
|
||||||
Comparator compare;
|
int _find(const T &p_val, bool &r_exact) const {
|
||||||
|
return VSet<T>::_find(p_val, r_exact);
|
||||||
inline int64_t bisect(const T *p_array, int64_t p_len, const T &p_value, bool p_before) const {
|
|
||||||
int64_t lo = 0;
|
|
||||||
int64_t hi = p_len;
|
|
||||||
if (p_before) {
|
|
||||||
while (lo < hi) {
|
|
||||||
const int64_t mid = (lo + hi) / 2;
|
|
||||||
if (compare(p_array[mid], p_value)) {
|
|
||||||
lo = mid + 1;
|
|
||||||
} else {
|
|
||||||
hi = mid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (lo < hi) {
|
|
||||||
const int64_t mid = (lo + hi) / 2;
|
|
||||||
if (compare(p_value, p_array[mid])) {
|
|
||||||
hi = mid;
|
|
||||||
} else {
|
|
||||||
lo = mid + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lo;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_CASE("[VSet] _find and _find_exact correctness.") {
|
||||||
|
TestClass<int> set;
|
||||||
|
|
||||||
|
// insert some values
|
||||||
|
set.insert(10);
|
||||||
|
set.insert(20);
|
||||||
|
set.insert(30);
|
||||||
|
set.insert(40);
|
||||||
|
set.insert(50);
|
||||||
|
|
||||||
|
// data should be sorted
|
||||||
|
CHECK(set.size() == 5);
|
||||||
|
CHECK(set[0] == 10);
|
||||||
|
CHECK(set[1] == 20);
|
||||||
|
CHECK(set[2] == 30);
|
||||||
|
CHECK(set[3] == 40);
|
||||||
|
CHECK(set[4] == 50);
|
||||||
|
|
||||||
|
// _find_exact return exact position for existing elements
|
||||||
|
CHECK(set.find(10) == 0);
|
||||||
|
CHECK(set.find(30) == 2);
|
||||||
|
CHECK(set.find(50) == 4);
|
||||||
|
|
||||||
|
// _find_exact return -1 for non-existing elements
|
||||||
|
CHECK(set.find(15) == -1);
|
||||||
|
CHECK(set.find(0) == -1);
|
||||||
|
CHECK(set.find(60) == -1);
|
||||||
|
|
||||||
|
// test _find
|
||||||
|
bool exact;
|
||||||
|
|
||||||
|
// existing elements
|
||||||
|
CHECK(set._find(10, exact) == 0);
|
||||||
|
CHECK(exact == true);
|
||||||
|
|
||||||
|
CHECK(set._find(30, exact) == 2);
|
||||||
|
CHECK(exact == true);
|
||||||
|
|
||||||
|
// non-existing elements
|
||||||
|
CHECK(set._find(25, exact) == 2);
|
||||||
|
CHECK(exact == false);
|
||||||
|
|
||||||
|
CHECK(set._find(35, exact) == 3);
|
||||||
|
CHECK(exact == false);
|
||||||
|
|
||||||
|
CHECK(set._find(5, exact) == 0);
|
||||||
|
CHECK(exact == false);
|
||||||
|
|
||||||
|
CHECK(set._find(60, exact) == 5);
|
||||||
|
CHECK(exact == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace TestVSet
|
||||||
|
|
@ -106,6 +106,7 @@
|
||||||
#include "tests/core/templates/test_rid.h"
|
#include "tests/core/templates/test_rid.h"
|
||||||
#include "tests/core/templates/test_span.h"
|
#include "tests/core/templates/test_span.h"
|
||||||
#include "tests/core/templates/test_vector.h"
|
#include "tests/core/templates/test_vector.h"
|
||||||
|
#include "tests/core/templates/test_vset.h"
|
||||||
#include "tests/core/test_crypto.h"
|
#include "tests/core/test_crypto.h"
|
||||||
#include "tests/core/test_hashing_context.h"
|
#include "tests/core/test_hashing_context.h"
|
||||||
#include "tests/core/test_time.h"
|
#include "tests/core/test_time.h"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue