From ff86dce34263641b096ae9df8f3b696981283500 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Nov 2020 19:45:13 -0800 Subject: [PATCH 1/4] Add comparison operators for Str and String --- include/cxx.h | 14 +++++++++++++ src/cxx.cc | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/cxx.h b/include/cxx.h index e40876ddf..31b34fc78 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -59,6 +59,13 @@ class String final { const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + // Internal API only intended for the cxxbridge code generator. String(unsafe_bitcopy_t, const String &) noexcept; @@ -98,6 +105,13 @@ class Str final { const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + private: friend impl; // Not necessarily ABI compatible with &str. Codegen will translate to diff --git a/src/cxx.cc b/src/cxx.cc index c9e215169..ac10df181 100644 --- a/src/cxx.cc +++ b/src/cxx.cc @@ -1,4 +1,5 @@ #include "../include/cxx.h" +#include #include #include #include @@ -140,6 +141,30 @@ String::const_iterator String::cend() const noexcept { return this->data() + this->size(); } +bool String::operator==(const String &rhs) const noexcept { + return rust::Str(*this) == rust::Str(rhs); +} + +bool String::operator!=(const String &rhs) const noexcept { + return rust::Str(*this) != rust::Str(rhs); +} + +bool String::operator<(const String &rhs) const noexcept { + return rust::Str(*this) < rust::Str(rhs); +} + +bool String::operator<=(const String &rhs) const noexcept { + return rust::Str(*this) <= rust::Str(rhs); +} + +bool String::operator>(const String &rhs) const noexcept { + return rust::Str(*this) > rust::Str(rhs); +} + +bool String::operator>=(const String &rhs) const noexcept { + return rust::Str(*this) >= rust::Str(rhs); +} + String::String(unsafe_bitcopy_t, const String &bits) noexcept : repr(bits.repr) {} @@ -186,6 +211,39 @@ Str::const_iterator Str::cbegin() const noexcept { return this->ptr; } Str::const_iterator Str::cend() const noexcept { return this->ptr + this->len; } +bool Str::operator==(const Str &rhs) const noexcept { + return this->len == rhs.len && + std::equal(this->begin(), this->end(), rhs.begin()); +} + +bool Str::operator!=(const Str &rhs) const noexcept { return !(*this == rhs); } + +bool Str::operator<(const Str &rhs) const noexcept { + return std::lexicographical_compare(this->begin(), this->end(), rhs.begin(), + rhs.end()); +} + +bool Str::operator<=(const Str &rhs) const noexcept { + // std::mismatch(this->begin(), this->end(), rhs.begin(), rhs.end()), except + // without Undefined Behavior on C++11 if rhs is shorter than *this. + const_iterator liter = this->begin(), lend = this->end(), riter = rhs.begin(), + rend = rhs.end(); + while (liter != lend && riter != rend && *liter == *riter) { + ++liter, ++riter; + } + if (liter == lend) { + return true; // equal or *this is a prefix of rhs + } else if (riter == rend) { + return false; // rhs is a prefix of *this + } else { + return *liter <= *riter; + } +} + +bool Str::operator>(const Str &rhs) const noexcept { return rhs < *this; } + +bool Str::operator>=(const Str &rhs) const noexcept { return rhs <= *this; } + std::ostream &operator<<(std::ostream &os, const Str &s) { os.write(s.data(), s.size()); return os; From c0674ccbabb55c44d799a201f4e62ea0efd973e9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Nov 2020 22:37:43 -0800 Subject: [PATCH 2/4] Add string comparison tests --- tests/ffi/tests.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 340dc9c16..934e58e0d 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -6,6 +6,7 @@ #include #include #include +#include extern "C" void cxx_test_suite_set_correct() noexcept; extern "C" tests::R *cxx_test_suite_get_box() noexcept; @@ -656,6 +657,24 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(Shared{1} == Shared{1}); ASSERT(Shared{1} != Shared{2}); + rust::String first = "first", second = "second", sec = "sec"; + bool (rust::String::*cmp)(const rust::String &) const; + bool first_first, first_second, sec_second, second_sec; + for (auto test : (std::tuple[]) { + {&rust::String::operator==, true, false, false, false}, + {&rust::String::operator!=, false, true, true, true}, + {&rust::String::operator<, false, true, true, false}, + {&rust::String::operator<=, true, true, true, false}, + {&rust::String::operator>, false, false, false, true}, + {&rust::String::operator>=, true, false, false, true}, + }) { + std::tie(cmp, first_first, first_second, sec_second, second_sec) = test; + ASSERT((first.*cmp)(first) == first_first); + ASSERT((first.*cmp)(second) == first_second); + ASSERT((sec.*cmp)(second) == sec_second); + ASSERT((second.*cmp)(sec) == second_sec); + } + cxx_test_suite_set_correct(); return nullptr; } From b9dd936be9a8033ef3d05b9a2069d42bedfeb3e8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Nov 2020 22:41:49 -0800 Subject: [PATCH 3/4] Add string comparison operators to website --- book/src/binding/str.md | 7 +++++++ book/src/binding/string.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/book/src/binding/str.md b/book/src/binding/str.md index 93e975026..999727c06 100644 --- a/book/src/binding/str.md +++ b/book/src/binding/str.md @@ -37,6 +37,13 @@ public: const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; }; std::ostream &operator<<(std::ostream &, const Str &); diff --git a/book/src/binding/string.md b/book/src/binding/string.md index 47a2b964f..c4a1b6ad4 100644 --- a/book/src/binding/string.md +++ b/book/src/binding/string.md @@ -42,6 +42,13 @@ public: const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; }; std::ostream &operator<<(std::ostream &, const String &); From 2ecd4fd921b17c2b39747e02c521dece74ffbde6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Nov 2020 22:55:37 -0800 Subject: [PATCH 4/4] Change string comparison test to compile on MSVC Error from MSVC: > a parenthesized type followed by an initializer list is a non-standard > explicit type conversion syntax --- tests/ffi/tests.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 934e58e0d..244c6c011 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -660,7 +660,8 @@ extern "C" const char *cxx_run_test() noexcept { rust::String first = "first", second = "second", sec = "sec"; bool (rust::String::*cmp)(const rust::String &) const; bool first_first, first_second, sec_second, second_sec; - for (auto test : (std::tuple[]) { + for (auto test : { + std::tuple {&rust::String::operator==, true, false, false, false}, {&rust::String::operator!=, false, true, true, true}, {&rust::String::operator<, false, true, true, false},