diff --git a/include/bx/inline/sort.inl b/include/bx/inline/sort.inl index 9996cdc..af26d57 100644 --- a/include/bx/inline/sort.inl +++ b/include/bx/inline/sort.inl @@ -218,6 +218,21 @@ namespace bx return isSorted(_data, _num, _stride, _fn); } + template + requires (isSame()(declVal(), declVal() ) ), bool>() ) + inline constexpr bool isSorted(const Ty* _data, uint32_t _num, LessFn _less) + { + for (uint32_t ii = 1; ii < _num; ++ii) + { + if (_less(_data[ii], _data[ii-1]) ) + { + return false; + } + } + + return true; + } + template inline int32_t binarySearch(const Ty& _key, const Ty* _data, uint32_t _num, const ComparisonFn _fn) { diff --git a/include/bx/sort.h b/include/bx/sort.h index 8e3bf0e..93f503e 100644 --- a/include/bx/sort.h +++ b/include/bx/sort.h @@ -203,6 +203,21 @@ namespace bx template bool isSorted(const Ty* _data, uint32_t _num, const ComparisonFn _fn = compareAscending); + /// Performs check if array is sorted. Usable in constant expressions, e.g. to `static_assert` + /// that a lookup table is in key order. + /// + /// @param _data Pointer to array data. + /// @param _num Number of elements. + /// @param _less Comparison function returning true if `_lhs` sorts before `_rhs`. With `<=` + /// instead of `<` the check is for strictly ascending elements, no two equal. + /// + /// @returns Returns `true` if no element sorts before the one ahead of it, otherwise returns + /// `false`. + /// + template + requires (isSame()(declVal(), declVal() ) ), bool>() ) + constexpr bool isSorted(const Ty* _data, uint32_t _num, LessFn _less); + /// Returns an index to the first element greater or equal than the `_key` value. /// /// @param _key Pointer to the key to search for. diff --git a/tests/sort_test.cpp b/tests/sort_test.cpp index be63b11..b03908b 100644 --- a/tests/sort_test.cpp +++ b/tests/sort_test.cpp @@ -49,6 +49,63 @@ TEST_CASE("quickSort", "[sort]") REQUIRE(bx::isSorted(byte, BX_COUNTOF(byte) ) ); } +namespace +{ + struct KeyedRow + { + uint32_t key; + uint32_t value; + }; + + constexpr KeyedRow s_dense[] = + { + { 0, 10 }, + { 1, 11 }, + { 2, 12 }, + { 3, 13 }, + }; + + constexpr KeyedRow s_repeated[] = + { + { 0, 10 }, + { 1, 11 }, + { 1, 12 }, + { 3, 13 }, + }; + + constexpr bool keyLess(const KeyedRow& _lhs, const KeyedRow& _rhs) + { + return _lhs.key < _rhs.key; + } + + constexpr bool keyLessEqual(const KeyedRow& _lhs, const KeyedRow& _rhs) + { + return _lhs.key <= _rhs.key; + } + +} // namespace + +TEST_CASE("isSorted with a less function", "[sort]") +{ + STATIC_REQUIRE( bx::isSorted(s_dense, BX_COUNTOF(s_dense), keyLess) ); + STATIC_REQUIRE( bx::isSorted(s_repeated, BX_COUNTOF(s_repeated), keyLess) ); + STATIC_REQUIRE( bx::isSorted(s_dense, BX_COUNTOF(s_dense), keyLessEqual) ); + STATIC_REQUIRE(!bx::isSorted(s_repeated, BX_COUNTOF(s_repeated), keyLessEqual) ); + STATIC_REQUIRE( bx::isSorted(s_dense, 0, keyLess) ); + STATIC_REQUIRE( bx::isSorted(s_dense, 1, keyLess) ); + + int32_t data[] = { 3, 1, 2 }; + const uint32_t num = BX_COUNTOF(data); + + REQUIRE(!bx::isSorted(data, num, [](const int32_t& _lhs, const int32_t& _rhs) { return _lhs < _rhs; }) ); + + bx::quickSort(data, num); + + REQUIRE( bx::isSorted(data, num, [](const int32_t& _lhs, const int32_t& _rhs) { return _lhs < _rhs; }) ); + REQUIRE( bx::isSorted(data, num, bx::compareAscending) ); + REQUIRE(!bx::isSorted(data, num, bx::compareDescending) ); +} + TEST_CASE("binarySearch", "[sort]") { const char* str[] =