From e1bff79b5b5a69b0e7a33f6ff96b8a0ed7223eb8 Mon Sep 17 00:00:00 2001 From: Georg Fritzsche Date: Mon, 1 Sep 2014 19:26:16 +0200 Subject: [PATCH] Bug 1059179 - Add BinarySearchIf(). r=waldo --- mfbt/BinarySearch.h | 93 ++++++++++++++++++++++++++++----- mfbt/tests/TestBinarySearch.cpp | 41 +++++++++++++-- 2 files changed, 117 insertions(+), 17 deletions(-) diff --git a/mfbt/BinarySearch.h b/mfbt/BinarySearch.h index c8f593d6a92..676b82e416c 100644 --- a/mfbt/BinarySearch.h +++ b/mfbt/BinarySearch.h @@ -14,8 +14,9 @@ namespace mozilla { /* - * The algorithm searches the given container |aContainer| over the sorted - * index range [aBegin, aEnd) for an index |i| where |aContainer[i] == aTarget|. + * The BinarySearch() algorithm searches the given container |aContainer| over + * the sorted index range [aBegin, aEnd) for an index |i| where + * |aContainer[i] == aTarget|. * If such an index |i| is found, BinarySearch returns |true| and the index is * returned via the outparam |aMatchOrInsertionPoint|. If no index is found, * BinarySearch returns |false| and the outparam returns the first index in @@ -29,34 +30,61 @@ namespace mozilla { * if (BinarySearch(sortedInts, 0, sortedInts.length(), 13, &match)) { * printf("found 13 at %lu\n", match); * } + * + * The BinarySearchIf() version behaves similar, but takes |aComparator|, a + * functor to compare the values with, instead of a value to find. + * That functor should take one argument - the value to compare - and return an + * |int| with the comparison result: + * + * * 0, if the argument is equal to, + * * less than 0, if the argument is greater than, + * * greater than 0, if the argument is less than + * + * the value. + * + * Example: + * + * struct Comparator { + * int operator()(int val) const { + * if (mTarget < val) return -1; + * if (mValue > val) return 1; + * return 0; + * } + * Comparator(int target) : mTarget(target) {} + const int mTarget; + * }; + * + * Vector sortedInts = ... + * + * size_t match; + * if (BinarySearchIf(sortedInts, 0, sortedInts.length(), Comparator(13), &match)) { + * printf("found 13 at %lu\n", match); + * } + * */ -template +template bool -BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd, - T aTarget, size_t* aMatchOrInsertionPoint) +BinarySearchIf(const Container& aContainer, size_t aBegin, size_t aEnd, + const Comparator& aCompare, size_t* aMatchOrInsertionPoint) { MOZ_ASSERT(aBegin <= aEnd); size_t low = aBegin; size_t high = aEnd; - while (low != high) { + while (high != low) { size_t middle = low + (high - low) / 2; // Allow any intermediate type so long as it provides a suitable ordering // relation. - const auto& middleValue = aContainer[middle]; + const int result = aCompare(aContainer[middle]); - MOZ_ASSERT(aContainer[low] <= aContainer[middle]); - MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]); - MOZ_ASSERT(aContainer[low] <= aContainer[high - 1]); - - if (aTarget == middleValue) { + if (result == 0) { *aMatchOrInsertionPoint = middle; return true; } - if (aTarget < middleValue) { + if (result < 0) { high = middle; } else { low = middle + 1; @@ -67,6 +95,45 @@ BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd, return false; } +namespace detail { + +template +class BinarySearchDefaultComparator +{ +public: + BinarySearchDefaultComparator(const T& aTarget) + : mTarget(aTarget) + {} + + template + int operator()(const U& val) const { + if (mTarget == val) { + return 0; + } + + if (mTarget < val) { + return -1; + } + + return 1; + } + +private: + const T& mTarget; +}; + +} // namespace detail + +template +bool +BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd, + T aTarget, size_t* aMatchOrInsertionPoint) +{ + return BinarySearchIf(aContainer, aBegin, aEnd, + detail::BinarySearchDefaultComparator(aTarget), + aMatchOrInsertionPoint); +} + } // namespace mozilla #endif // mozilla_BinarySearch_h diff --git a/mfbt/tests/TestBinarySearch.cpp b/mfbt/tests/TestBinarySearch.cpp index 657d36287ec..334eb211051 100644 --- a/mfbt/tests/TestBinarySearch.cpp +++ b/mfbt/tests/TestBinarySearch.cpp @@ -8,8 +8,12 @@ #include "mozilla/BinarySearch.h" #include "mozilla/Vector.h" -using mozilla::Vector; +using mozilla::ArrayLength; using mozilla::BinarySearch; +using mozilla::BinarySearchIf; +using mozilla::Vector; + +#define A(a) MOZ_RELEASE_ASSERT(a) struct Person { @@ -25,8 +29,18 @@ struct GetAge int operator[](size_t index) const { return mV[index].mAge; } }; -int -main() +struct RangeFinder { + const int mLower, mUpper; + RangeFinder(int lower, int upper) : mLower(lower), mUpper(upper) {} + int operator()(int val) const { + if (val >= mUpper) return -1; + if (val < mLower) return 1; + return 0; + } +}; + +static void +TestBinarySearch() { size_t m; @@ -68,7 +82,6 @@ main() v3.append(Person(4, 13)); v3.append(Person(6, 360)); - #define A(a) MOZ_RELEASE_ASSERT(a) A(!BinarySearch(GetAge(v3), 0, v3.length(), 1, &m) && m == 0); A( BinarySearch(GetAge(v3), 0, v3.length(), 2, &m) && m == 0); A(!BinarySearch(GetAge(v3), 0, v3.length(), 3, &m) && m == 1); @@ -76,5 +89,25 @@ main() A(!BinarySearch(GetAge(v3), 0, v3.length(), 5, &m) && m == 2); A( BinarySearch(GetAge(v3), 0, v3.length(), 6, &m) && m == 2); A(!BinarySearch(GetAge(v3), 0, v3.length(), 7, &m) && m == 3); +} + +static void +TestBinarySearchIf() +{ + const int v1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const size_t len = ArrayLength(v1); + size_t m; + + A( BinarySearchIf(v1, 0, len, RangeFinder( 2, 3), &m) && m == 2); + A(!BinarySearchIf(v1, 0, len, RangeFinder(-5, -2), &m) && m == 0); + A( BinarySearchIf(v1, 0, len, RangeFinder( 3, 5), &m) && m >= 3 && m < 5); + A(!BinarySearchIf(v1, 0, len, RangeFinder(10, 12), &m) && m == 10); +} + +int +main() +{ + TestBinarySearch(); + TestBinarySearchIf(); return 0; }