mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 05:31:01 +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
	
	 Yufeng Ying
						Yufeng Ying