Skip to content

Commit 3bf400f

Browse files
YYF233333Ivorforce
andcommitted
Move bisect to Span and deduplicate code.
Co-authored-by: Lukas Tenbrink <[email protected]>
1 parent 09fcbb8 commit 3bf400f

File tree

9 files changed

+114
-109
lines changed

9 files changed

+114
-109
lines changed

core/templates/span.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ class Span {
8686
constexpr int64_t rfind(const T &p_val, uint64_t p_from) const;
8787
_FORCE_INLINE_ constexpr int64_t rfind(const T &p_val) const { return rfind(p_val, size() - 1); }
8888
constexpr uint64_t count(const T &p_val) const;
89+
/// Find the index of the given value using binary search.
90+
/// Note: Assumes that elements in the span are sorted. Otherwise, use find() instead.
91+
template <typename Comparator = Comparator<T>>
92+
constexpr uint64_t bisect(const T &p_value, bool p_before, Comparator compare = Comparator()) const;
8993
};
9094

9195
template <typename T>
@@ -119,6 +123,33 @@ constexpr uint64_t Span<T>::count(const T &p_val) const {
119123
return amount;
120124
}
121125

126+
template <typename T>
127+
template <typename Comparator>
128+
constexpr uint64_t Span<T>::bisect(const T &p_value, bool p_before, Comparator compare) const {
129+
uint64_t lo = 0;
130+
uint64_t hi = size();
131+
if (p_before) {
132+
while (lo < hi) {
133+
const uint64_t mid = (lo + hi) / 2;
134+
if (compare(ptr()[mid], p_value)) {
135+
lo = mid + 1;
136+
} else {
137+
hi = mid;
138+
}
139+
}
140+
} else {
141+
while (lo < hi) {
142+
const uint64_t mid = (lo + hi) / 2;
143+
if (compare(p_value, ptr()[mid])) {
144+
hi = mid;
145+
} else {
146+
lo = mid + 1;
147+
}
148+
}
149+
}
150+
return lo;
151+
}
152+
122153
// Zero-constructing Span initializes _ptr and _len to 0 (and thus empty).
123154
template <typename T>
124155
struct is_zero_constructible<Span<T>> : std::true_type {};

core/templates/vector.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040

4141
#include "core/error/error_macros.h"
4242
#include "core/templates/cowdata.h"
43-
#include "core/templates/search_array.h"
4443
#include "core/templates/sort_array.h"
4544

4645
#include <initializer_list>
@@ -152,8 +151,7 @@ class Vector {
152151

153152
template <typename Comparator, typename Value, typename... Args>
154153
Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
155-
SearchArray<T, Comparator> search{ args... };
156-
return search.bisect(ptrw(), size(), p_value, p_before);
154+
return span().bisect(p_value, p_before, Comparator{ args... });
157155
}
158156

159157
Vector<T> duplicate() {

core/templates/vset.h

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,65 +37,31 @@ template <typename T>
3737
class VSet {
3838
Vector<T> _data;
3939

40+
protected:
4041
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
4142
r_exact = false;
4243
if (_data.is_empty()) {
4344
return 0;
4445
}
4546

46-
int low = 0;
47-
int high = _data.size() - 1;
48-
const T *a = &_data[0];
49-
int middle = 0;
47+
int64_t pos = _data.span().bisect(p_val, true);
5048

51-
#ifdef DEBUG_ENABLED
52-
if (low > high) {
53-
ERR_PRINT("low > high, this may be a bug");
49+
if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) {
50+
r_exact = true;
5451
}
55-
#endif
56-
57-
while (low <= high) {
58-
middle = (low + high) / 2;
59-
60-
if (p_val < a[middle]) {
61-
high = middle - 1; //search low end of array
62-
} else if (a[middle] < p_val) {
63-
low = middle + 1; //search high end of array
64-
} else {
65-
r_exact = true;
66-
return middle;
67-
}
68-
}
69-
70-
//return the position where this would be inserted
71-
if (a[middle] < p_val) {
72-
middle++;
73-
}
74-
return middle;
52+
return pos;
7553
}
7654

7755
_FORCE_INLINE_ int _find_exact(const T &p_val) const {
7856
if (_data.is_empty()) {
7957
return -1;
8058
}
8159

82-
int low = 0;
83-
int high = _data.size() - 1;
84-
int middle;
85-
const T *a = &_data[0];
86-
87-
while (low <= high) {
88-
middle = (low + high) / 2;
89-
90-
if (p_val < a[middle]) {
91-
high = middle - 1; //search low end of array
92-
} else if (a[middle] < p_val) {
93-
low = middle + 1; //search high end of array
94-
} else {
95-
return middle;
96-
}
97-
}
60+
int64_t pos = _data.span().bisect(p_val, true);
9861

62+
if (pos < _data.size() && !(p_val < _data[pos]) && !(_data[pos] < p_val)) {
63+
return pos;
64+
}
9965
return -1;
10066
}
10167

core/variant/array.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
#include "core/math/math_funcs.h"
3535
#include "core/object/script_language.h"
3636
#include "core/templates/hashfuncs.h"
37-
#include "core/templates/search_array.h"
3837
#include "core/templates/vector.h"
3938
#include "core/variant/callable.h"
4039
#include "core/variant/dictionary.h"
@@ -737,8 +736,7 @@ void Array::shuffle() {
737736
int Array::bsearch(const Variant &p_value, bool p_before) const {
738737
Variant value = p_value;
739738
ERR_FAIL_COND_V(!_p->typed.validate(value, "binary search"), -1);
740-
SearchArray<Variant, _ArrayVariantSort> avs;
741-
return avs.bisect(_p->array.ptr(), _p->array.size(), value, p_before);
739+
return _p->array.span().bisect<_ArrayVariantSort>(value, p_before);
742740
}
743741

744742
int Array::bsearch_custom(const Variant &p_value, const Callable &p_callable, bool p_before) const {

drivers/metal/metal_objects.mm

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,14 +1571,13 @@
15711571
}
15721572
}
15731573

1574-
SearchArray<__unsafe_unretained id<MTLResource>> search;
15751574
ResourceUsageMap usage_to_resources;
15761575
for (KeyValue<id<MTLResource>, StageResourceUsage> const &keyval : bound_resources) {
15771576
ResourceVector *resources = usage_to_resources.getptr(keyval.value);
15781577
if (resources == nullptr) {
15791578
resources = &usage_to_resources.insert(keyval.value, ResourceVector())->value;
15801579
}
1581-
int64_t pos = search.bisect(resources->ptr(), resources->size(), keyval.key, true);
1580+
int64_t pos = resources->span().bisect(keyval.key, true);
15821581
if (pos == resources->size() || (*resources)[pos] != keyval.key) {
15831582
resources->insert(pos, keyval.key);
15841583
}

editor/doc_tools.cpp

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,8 @@ static void merge_methods(Vector<DocData::MethodDoc> &p_to, const Vector<DocData
178178
DocData::MethodDoc *to_ptrw = p_to.ptrw();
179179
int64_t to_size = p_to.size();
180180

181-
SearchArray<DocData::MethodDoc, MethodCompare> search_array;
182-
183181
for (const DocData::MethodDoc &from : p_from) {
184-
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
182+
int64_t found = p_to.span().bisect<MethodCompare>(from, true);
185183

186184
if (found >= to_size) {
187185
continue;
@@ -206,10 +204,8 @@ static void merge_constants(Vector<DocData::ConstantDoc> &p_to, const Vector<Doc
206204
const DocData::ConstantDoc *from_ptr = p_from.ptr();
207205
int64_t from_size = p_from.size();
208206

209-
SearchArray<DocData::ConstantDoc> search_array;
210-
211207
for (DocData::ConstantDoc &to : p_to) {
212-
int64_t found = search_array.bisect(from_ptr, from_size, to, true);
208+
int64_t found = p_from.span().bisect(to, true);
213209

214210
if (found >= from_size) {
215211
continue;
@@ -234,10 +230,8 @@ static void merge_properties(Vector<DocData::PropertyDoc> &p_to, const Vector<Do
234230
DocData::PropertyDoc *to_ptrw = p_to.ptrw();
235231
int64_t to_size = p_to.size();
236232

237-
SearchArray<DocData::PropertyDoc> search_array;
238-
239233
for (const DocData::PropertyDoc &from : p_from) {
240-
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
234+
int64_t found = p_to.span().bisect(from, true);
241235

242236
if (found >= to_size) {
243237
continue;
@@ -262,10 +256,8 @@ static void merge_theme_properties(Vector<DocData::ThemeItemDoc> &p_to, const Ve
262256
DocData::ThemeItemDoc *to_ptrw = p_to.ptrw();
263257
int64_t to_size = p_to.size();
264258

265-
SearchArray<DocData::ThemeItemDoc> search_array;
266-
267259
for (const DocData::ThemeItemDoc &from : p_from) {
268-
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
260+
int64_t found = p_to.span().bisect(from, true);
269261

270262
if (found >= to_size) {
271263
continue;
@@ -290,10 +282,8 @@ static void merge_operators(Vector<DocData::MethodDoc> &p_to, const Vector<DocDa
290282
DocData::MethodDoc *to_ptrw = p_to.ptrw();
291283
int64_t to_size = p_to.size();
292284

293-
SearchArray<DocData::MethodDoc, OperatorCompare> search_array;
294-
295285
for (const DocData::MethodDoc &from : p_from) {
296-
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
286+
int64_t found = p_to.span().bisect(from, true);
297287

298288
if (found >= to_size) {
299289
continue;

scene/gui/item_list.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,20 +1414,7 @@ void ItemList::_notification(int p_what) {
14141414
const Rect2 clip(-base_ofs, size);
14151415

14161416
// Do a binary search to find the first separator that is below clip_position.y.
1417-
int first_visible_separator = 0;
1418-
{
1419-
int lo = 0;
1420-
int hi = separators.size();
1421-
while (lo < hi) {
1422-
const int mid = (lo + hi) / 2;
1423-
if (separators[mid] < clip.position.y) {
1424-
lo = mid + 1;
1425-
} else {
1426-
hi = mid;
1427-
}
1428-
}
1429-
first_visible_separator = lo;
1430-
}
1417+
int64_t first_visible_separator = separators.span().bisect(clip.position.y, true);
14311418

14321419
// If not in thumbnails mode, draw visible separators.
14331420
if (icon_mode != ICON_MODE_TOP) {
Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**************************************************************************/
2-
/* search_array.h */
2+
/* test_vset.h */
33
/**************************************************************************/
44
/* This file is part of: */
55
/* GODOT ENGINE */
@@ -30,35 +30,70 @@
3030

3131
#pragma once
3232

33-
#include "core/typedefs.h"
33+
#include "core/templates/vset.h"
3434

35-
template <typename T, typename Comparator = Comparator<T>>
36-
class SearchArray {
35+
#include "tests/test_macros.h"
36+
37+
namespace TestVSet {
38+
39+
template <typename T>
40+
class TestClass : public VSet<T> {
3741
public:
38-
Comparator compare;
39-
40-
inline int64_t bisect(const T *p_array, int64_t p_len, const T &p_value, bool p_before) const {
41-
int64_t lo = 0;
42-
int64_t hi = p_len;
43-
if (p_before) {
44-
while (lo < hi) {
45-
const int64_t mid = (lo + hi) / 2;
46-
if (compare(p_array[mid], p_value)) {
47-
lo = mid + 1;
48-
} else {
49-
hi = mid;
50-
}
51-
}
52-
} else {
53-
while (lo < hi) {
54-
const int64_t mid = (lo + hi) / 2;
55-
if (compare(p_value, p_array[mid])) {
56-
hi = mid;
57-
} else {
58-
lo = mid + 1;
59-
}
60-
}
61-
}
62-
return lo;
42+
int _find(const T &p_val, bool &r_exact) const {
43+
return VSet<T>::_find(p_val, r_exact);
6344
}
6445
};
46+
47+
TEST_CASE("[VSet] _find and _find_exact correctness.") {
48+
TestClass<int> set;
49+
50+
// insert some values
51+
set.insert(10);
52+
set.insert(20);
53+
set.insert(30);
54+
set.insert(40);
55+
set.insert(50);
56+
57+
// data should be sorted
58+
CHECK(set.size() == 5);
59+
CHECK(set[0] == 10);
60+
CHECK(set[1] == 20);
61+
CHECK(set[2] == 30);
62+
CHECK(set[3] == 40);
63+
CHECK(set[4] == 50);
64+
65+
// _find_exact return exact position for existing elements
66+
CHECK(set.find(10) == 0);
67+
CHECK(set.find(30) == 2);
68+
CHECK(set.find(50) == 4);
69+
70+
// _find_exact return -1 for non-existing elements
71+
CHECK(set.find(15) == -1);
72+
CHECK(set.find(0) == -1);
73+
CHECK(set.find(60) == -1);
74+
75+
// test _find
76+
bool exact;
77+
78+
// existing elements
79+
CHECK(set._find(10, exact) == 0);
80+
CHECK(exact == true);
81+
82+
CHECK(set._find(30, exact) == 2);
83+
CHECK(exact == true);
84+
85+
// non-existing elements
86+
CHECK(set._find(25, exact) == 2);
87+
CHECK(exact == false);
88+
89+
CHECK(set._find(35, exact) == 3);
90+
CHECK(exact == false);
91+
92+
CHECK(set._find(5, exact) == 0);
93+
CHECK(exact == false);
94+
95+
CHECK(set._find(60, exact) == 5);
96+
CHECK(exact == false);
97+
}
98+
99+
} // namespace TestVSet

tests/test_main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
#include "tests/core/templates/test_rid.h"
107107
#include "tests/core/templates/test_span.h"
108108
#include "tests/core/templates/test_vector.h"
109+
#include "tests/core/templates/test_vset.h"
109110
#include "tests/core/test_crypto.h"
110111
#include "tests/core/test_hashing_context.h"
111112
#include "tests/core/test_time.h"

0 commit comments

Comments
 (0)