From 1dda8cde867ec8834281c7d90e568a0189c39ce3 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Wed, 12 Jul 2023 13:56:52 +0200 Subject: [PATCH] string: Revise searching POSIX C strchr and strstr implementations return pointers and pointer arithmetic is error prone. Switch to std C++ index search offsets. Signed-off-by: Simon Rozman --- include/stdex/string.hpp | 101 +++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 26 deletions(-) diff --git a/include/stdex/string.hpp b/include/stdex/string.hpp index db67e8b6a..0176ca92a 100644 --- a/include/stdex/string.hpp +++ b/include/stdex/string.hpp @@ -173,49 +173,98 @@ namespace stdex return i; } + constexpr auto npos{ static_cast(-1) }; + /// /// Find a code unit in a string. /// /// \param[in] str String - /// \param[in] chr Code unit to search for /// \param[in] count Code unit count limit + /// \param[in] chr Code unit to search for /// - /// \return Pointer to the first occurence of chr code unit or nullptr if not found. + /// \return Offset to the first occurence of chr code unit or stdex::npos if not found. /// template - inline const T* strnchr( + inline size_t strnchr( _In_reads_or_z_opt_(count) const T* str, - _In_ T chr, - _In_ size_t count) + _In_ size_t count, + _In_ T chr) { assert(str || !count); for (size_t i = 0; i < count && str[i]; ++i) - if (str[i] == chr) return str + i; - return nullptr; + if (str[i] == chr) return i; + return npos; + } + + /// + /// Find a code unit in a string. + /// + /// \param[in] str String + /// \param[in] count Code unit count limit + /// \param[in] chr Code unit to search for + /// + /// \return Offset to the last occurence of chr code unit or stdex::npos if not found. + /// + template + inline size_t strrnchr( + _In_reads_or_z_opt_(count) const T* str, + _In_ size_t count, + _In_ T chr) + { + assert(str || !count); + size_t z = npos; + for (size_t i = 0; i < count && str[i]; ++i) + if (str[i] == chr) z = i; + return z; } /// /// Find a code unit in a string case-insensitive /// /// \param[in] str String - /// \param[in] chr Code unit to search for /// \param[in] count Code unit count limit + /// \param[in] chr Code unit to search for /// - /// \return Pointer to the first occurence of chr code unit or nullptr if not found. + /// \return Offset to the first occurence of chr code unit or stdex::npos if not found. /// template - inline const T* strnichr( + inline size_t strnichr( _In_reads_or_z_opt_(count) const T* str, - _In_ T chr, _In_ size_t count, + _In_ T chr, _In_ const std::locale& locale) { assert(str || !count); const auto& ctype = std::use_facet>(locale); chr = ctype.tolower(chr); for (size_t i = 0; i < count && str[i]; ++i) - if (ctype.tolower(str[i]) == chr) return str + i; - return nullptr; + if (ctype.tolower(str[i]) == chr) return i; + return npos; + } + + /// + /// Find a code unit in a string case-insensitive + /// + /// \param[in] str String + /// \param[in] count Code unit count limit + /// \param[in] chr Code unit to search for + /// + /// \return Offset to the last occurence of chr code unit or stdex::npos if not found. + /// + template + inline size_t strrnichr( + _In_reads_or_z_opt_(count) const T* str, + _In_ size_t count, + _In_ T chr, + _In_ const std::locale& locale) + { + assert(str || !count); + const auto& ctype = std::use_facet>(locale); + chr = ctype.tolower(chr); + size_t z = npos; + for (size_t i = 0; i < count && str[i]; ++i) + if (ctype.tolower(str[i]) == chr) z = i; + return z; } /// @@ -279,25 +328,25 @@ namespace stdex /// Binary search for a substring /// /// \param[in] str String to search in - /// \param[in] sample Substring to search for /// \param[in] count String code unit count limit + /// \param[in] sample Substring to search for /// - /// \return Pointer inside str where sample string is found; nullptr if not found + /// \return Offset inside str where sample string is found; stdex::npos if not found /// template - const T1* strnstr( + inline size_t strnstr( _In_reads_or_z_opt_(count) const T1* str, - _In_z_ const T2* sample, - _In_ size_t count) + _In_ size_t count, + _In_z_ const T2* sample) { assert(str || !count); assert(sample); for (size_t offset = 0;; ++offset) { for (size_t i = offset, j = 0;; ++i, ++j) { if (!sample[j]) - return str + offset; + return offset; if (i >= count || !str[i]) - return nullptr; + return npos; if (str[i] != sample[j]) break; } @@ -308,16 +357,16 @@ namespace stdex /// Binary search for a substring case-insensitive /// /// \param[in] str String to search in - /// \param[in] sample Substring to search for /// \param[in] count String code unit count limit + /// \param[in] sample Substring to search for /// - /// \return Pointer inside str where sample string is found; nullptr if not found + /// \return Offset inside str where sample string is found; stdex::npos if not found /// template - inline const T1* strnistr( + inline size_t strnistr( _In_reads_or_z_opt_(count) const T1* str, - _In_z_ const T2* sample, _In_ size_t count, + _In_z_ const T2* sample, _In_ const std::locale& locale) { assert(str || !count); @@ -327,9 +376,9 @@ namespace stdex for (size_t offset = 0;; ++offset) { for (size_t i = offset, j = 0;; ++i, ++j) { if (!sample[j]) - return str + offset; + return offset; if (i >= count || !str[i]) - return nullptr; + return npos; if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j])) break; }