From b01d7f2a4f1136f24f3a8618a7787f80f204715c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Tue, 21 Jul 2026 21:32:53 -0700 Subject: [PATCH] Added bx::isEqual for bx::StringView. --- include/bx/inline/string.inl | 41 ++++++++++++++++++++++++---- include/bx/string.h | 21 ++++++++++---- tests/string_test.cpp | 53 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 11 deletions(-) diff --git a/include/bx/inline/string.inl b/include/bx/inline/string.inl index 6c428f2e0..b34fe95a7 100644 --- a/include/bx/inline/string.inl +++ b/include/bx/inline/string.inl @@ -188,7 +188,7 @@ namespace bx return m_0terminated; } - inline constexpr bool operator==(const StringView& _lhs, const StringView& _rhs) + inline BX_CONSTEXPR_FUNC bool isEqual(const StringView& _lhs, const StringView& _rhs, bool _caseSensitive) { const int32_t len = _lhs.getLength(); @@ -214,14 +214,43 @@ namespace bx } } - for (int32_t ii = 0, num = len-1 - ; ii < num && *lhs == *rhs - ; ++ii, ++lhs, ++rhs - ) + if (_caseSensitive) { + for (int32_t ii = 0; ii < len; ++ii) + { + if (lhs[ii] != rhs[ii]) + { + return false; + } + } + + return true; + } + + for (int32_t ii = 0; ii < len; ++ii) + { + const char lch = lhs[ii]; + const char rch = rhs[ii]; + const char ll = 'A' <= lch && lch <= 'Z' ? char(lch + 0x20) : lch; + const char rl = 'A' <= rch && rch <= 'Z' ? char(rch + 0x20) : rch; + + if (ll != rl) + { + return false; + } } - return *lhs == *rhs; + return true; + } + + inline constexpr bool operator==(const StringView& _lhs, const StringView& _rhs) + { + return isEqual(_lhs, _rhs); + } + + inline constexpr bool operator!=(const StringView& _lhs, const StringView& _rhs) + { + return !(_lhs == _rhs); } inline constexpr bool overlap(const StringView& _a, const StringView& _b) diff --git a/include/bx/string.h b/include/bx/string.h index a4177e9b8..3bfc41b02 100644 --- a/include/bx/string.h +++ b/include/bx/string.h @@ -139,9 +139,20 @@ namespace bx bool m_0terminated; }; - /// Compare two string views. + /// Returns true if two string views are equal. + /// + /// @param[in] _lhs Left-hand side string. + /// @param[in] _rhs Right-hand side string. + /// @param[in] _caseSensitive Use case sensitive comparison if true. + /// + BX_CONSTEXPR_FUNC bool isEqual(const StringView& _lhs, const StringView& _rhs, bool _caseSensitive = true); + + /// Compare two string views for equality. constexpr bool operator==(const StringView& _lhs, const StringView& _rhs); + /// Compare two string views for inequality. + constexpr bool operator!=(const StringView& _lhs, const StringView& _rhs); + /// Returns true if two string views overlap. constexpr bool overlap(const StringView& _a, const StringView& _b); @@ -471,18 +482,18 @@ namespace bx /// int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, const StringView& _unit = "", char _prefix = ' '); - /// + /// int32_t formatHumanNumber(char* _out, uint32_t _count, double _value, uint8_t _numFrac, double _unitStep, const StringView& _unit, const StringView& _prefix, uint8_t _basePrefix = 0); - /// + /// template FixedStringT toHuman(uint64_t _value); - /// + /// template FixedStringT toHuman(uint64_t _value, Units::Enum _units, uint8_t _numFrac = 2); - /// + /// template FixedStringT toHuman(Ticks _value, uint8_t _numFrac = 4); diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 8b1355b2b..6da9aaf0c 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -120,6 +120,59 @@ TEST_CASE("strCmpI", "[string]") REQUIRE(0 < bx::strCmpI(abvgd, empty) ); } +TEST_CASE("isEqual", "[string]") +{ + // Case sensitive (default). + REQUIRE( bx::isEqual("test", "test") ); + REQUIRE(!bx::isEqual("test", "Test") ); + REQUIRE(!bx::isEqual("test", "testtest") ); + REQUIRE(!bx::isEqual("testtest", "test") ); + REQUIRE( bx::isEqual("", "") ); + REQUIRE(!bx::isEqual("", "test") ); + REQUIRE(!bx::isEqual("a", "A") ); + + // Case insensitive. + REQUIRE( bx::isEqual("test", "TEST", false) ); + REQUIRE( bx::isEqual("TeSt", "tEsT", false) ); + REQUIRE( bx::isEqual("a", "A", false) ); + REQUIRE(!bx::isEqual("test", "testest", false) ); + REQUIRE( bx::isEqual("", "", false) ); + + // Non-letters are unaffected by case folding. + REQUIRE( bx::isEqual("1389-[]", "1389-[]", false) ); + REQUIRE(!bx::isEqual("1389-[]", "1389-{}", false) ); + + // Mixed string types (all implicitly convert to StringView). + const bx::StringView sv("RGBA"); + const bx::StringLiteral sl("RGBA"); + REQUIRE( bx::isEqual(sv, sl) ); + REQUIRE( bx::isEqual(sv, "RGBA") ); + REQUIRE( bx::isEqual("rgba", sl, false) ); +} + +TEST_CASE("operator==/operator!=", "[string]") +{ + REQUIRE( bx::StringView("test") == bx::StringView("test") ); + REQUIRE( bx::StringView("test") == "test" ); + REQUIRE( "test" == bx::StringView("test") ); + REQUIRE(!(bx::StringView("test") == "Test") ); + + REQUIRE( bx::StringView("test") != "Test" ); + REQUIRE(!(bx::StringView("test") != "test") ); +} + +TEST_CASE("isEqual constexpr", "[string]") +{ + STATIC_REQUIRE( bx::isEqual("1389", "1389") ); + STATIC_REQUIRE(!bx::isEqual("1389", "1388") ); + STATIC_REQUIRE( bx::isEqual("abvgd", "ABVGD", false) ); + STATIC_REQUIRE(!bx::isEqual("abvgd", "ABVGD") ); + + STATIC_REQUIRE( bx::StringView("mac") == "mac" ); + STATIC_REQUIRE( bx::StringView("mac") != "pod" ); + STATIC_REQUIRE( bx::isEqual(bx::StringLiteral("pod"), bx::StringView("POD"), false) ); +} + TEST_CASE("strCmpV", "[string]") { REQUIRE(0 == bx::strCmpV("test", "test") );