diff --git a/src/inc/operators.hpp b/src/inc/operators.hpp index c1f018b9a..a58c75180 100644 --- a/src/inc/operators.hpp +++ b/src/inc/operators.hpp @@ -37,23 +37,3 @@ constexpr bool operator!=(const SMALL_RECT& a, const SMALL_RECT& b) noexcept { return !(a == b); } - -constexpr bool operator==(const std::wstring& wstr, const std::wstring_view& wstrView) -{ - return (wstrView == std::wstring_view{ wstr.c_str(), wstr.size() }); -} - -constexpr bool operator==(const std::wstring_view& wstrView, const std::wstring& wstr) -{ - return (wstr == wstrView); -} - -constexpr bool operator!=(const std::wstring& wstr, const std::wstring_view& wstrView) -{ - return !(wstr == wstrView); -} - -constexpr bool operator!=(const std::wstring_view& wstrView, const std::wstring& wstr) -{ - return !(wstr == wstrView); -} diff --git a/src/inc/til/string.h b/src/inc/til/string.h index 13299e73e..0f7d38738 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #pragma once @@ -34,10 +34,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" template constexpr bool starts_with(const std::basic_string_view& str, const std::basic_string_view& prefix) noexcept { -#ifdef __cpp_lib_starts_ends_with -#error This code can be replaced in C++20, which natively supports .starts_with(). -#endif - return str.size() >= prefix.size() && Traits::compare(str.data(), prefix.data(), prefix.size()) == 0; + return str.size() >= prefix.size() && __builtin_memcmp(str.data(), prefix.data(), prefix.size() * sizeof(T)) == 0; } constexpr bool starts_with(const std::string_view& str, const std::string_view& prefix) noexcept @@ -52,15 +49,10 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // std::string_view::ends_with support for C++17. template - constexpr bool ends_with(const std::basic_string_view& str, const std::basic_string_view& prefix) noexcept + constexpr bool ends_with(const std::basic_string_view& str, const std::basic_string_view& suffix) noexcept { -#ifdef __cpp_lib_ends_ends_with -#error This code can be replaced in C++20, which natively supports .ends_with(). -#endif -#pragma warning(push) -#pragma warning(disable : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). - return str.size() >= prefix.size() && Traits::compare(str.data() + (str.size() - prefix.size()), prefix.data(), prefix.size()) == 0; -#pragma warning(pop) +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + return str.size() >= suffix.size() && __builtin_memcmp(str.data() + (str.size() - suffix.size()), suffix.data(), suffix.size() * sizeof(T)) == 0; } constexpr bool ends_with(const std::string_view& str, const std::string_view& prefix) noexcept @@ -168,6 +160,20 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return c; } + // Just like std::wstring_view::operator==(). + // + // At the time of writing wmemcmp() is not an intrinsic for MSVC, + // but the STL uses it to implement wide string comparisons. + // This produces 3x the assembly _per_ comparison and increases + // runtime by 2-3x for strings of medium length (16 characters) + // and 5x or more for long strings (128 characters or more). + // See: https://github.com/microsoft/STL/issues/2289 + template + bool equals(const std::basic_string_view& str1, const std::basic_string_view& str2) noexcept + { + return lhs.size() == rhs.size() && __builtin_memcmp(lhs.data(), rhs.data(), lhs.size() * sizeof(T)) == 0; + } + // Just like _memicmp, but without annoying locales. template bool equals_insensitive_ascii(const std::basic_string_view& str1, const std::basic_string_view& str2) noexcept @@ -205,6 +211,39 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" return equals_insensitive_ascii<>(str1, str2); } + template + constexpr bool starts_with_insensitive_ascii(const std::basic_string_view& str, const std::basic_string_view& prefix) noexcept + { + return str.size() >= prefix.size() && equals_insensitive_ascii<>({ str.data(), prefix.size() }, prefix); + } + + constexpr bool starts_with_insensitive_ascii(const std::string_view& str, const std::string_view& prefix) noexcept + { + return starts_with_insensitive_ascii<>(str, prefix); + } + + constexpr bool starts_with_insensitive_ascii(const std::wstring_view& str, const std::wstring_view& prefix) noexcept + { + return starts_with_insensitive_ascii<>(str, prefix); + } + + template + constexpr bool ends_with_insensitive_ascii(const std::basic_string_view& str, const std::basic_string_view& suffix) noexcept + { +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + return str.size() >= suffix.size() && equals_insensitive_ascii<>({ str.data() - suffix.size(), suffix.size() }, suffix); + } + + constexpr bool ends_with_insensitive_ascii(const std::string_view& str, const std::string_view& prefix) noexcept + { + return ends_with_insensitive_ascii<>(str, prefix); + } + + constexpr bool ends_with_insensitive_ascii(const std::wstring_view& str, const std::wstring_view& prefix) noexcept + { + return ends_with<>(str, prefix); + } + // Give the arguments ("foo bar baz", " "), this method will // * modify the first argument to "bar baz" // * return "foo"