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 <simon@rozman.si>
This commit is contained in:
Simon Rozman 2023-07-12 13:56:52 +02:00
parent a6c1c6c7ae
commit 1dda8cde86

View File

@ -173,49 +173,98 @@ namespace stdex
return i; return i;
} }
constexpr auto npos{ static_cast<size_t>(-1) };
/// ///
/// Find a code unit in a string. /// Find a code unit in a string.
/// ///
/// \param[in] str String /// \param[in] str String
/// \param[in] chr Code unit to search for
/// \param[in] count Code unit count limit /// \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 <class T> template <class T>
inline const T* strnchr( inline size_t strnchr(
_In_reads_or_z_opt_(count) const T* str, _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); assert(str || !count);
for (size_t i = 0; i < count && str[i]; ++i) for (size_t i = 0; i < count && str[i]; ++i)
if (str[i] == chr) return str + i; if (str[i] == chr) return i;
return nullptr; 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 <class T>
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 /// Find a code unit in a string case-insensitive
/// ///
/// \param[in] str String /// \param[in] str String
/// \param[in] chr Code unit to search for
/// \param[in] count Code unit count limit /// \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 <class T> template <class T>
inline const T* strnichr( inline size_t strnichr(
_In_reads_or_z_opt_(count) const T* str, _In_reads_or_z_opt_(count) const T* str,
_In_ T chr,
_In_ size_t count, _In_ size_t count,
_In_ T chr,
_In_ const std::locale& locale) _In_ const std::locale& locale)
{ {
assert(str || !count); assert(str || !count);
const auto& ctype = std::use_facet<std::ctype<T>>(locale); const auto& ctype = std::use_facet<std::ctype<T>>(locale);
chr = ctype.tolower(chr); chr = ctype.tolower(chr);
for (size_t i = 0; i < count && str[i]; ++i) for (size_t i = 0; i < count && str[i]; ++i)
if (ctype.tolower(str[i]) == chr) return str + i; if (ctype.tolower(str[i]) == chr) return i;
return nullptr; 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 <class T>
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<std::ctype<T>>(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 /// Binary search for a substring
/// ///
/// \param[in] str String to search in /// \param[in] str String to search in
/// \param[in] sample Substring to search for
/// \param[in] count String code unit count limit /// \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 <class T1, class T2> template <class T1, class T2>
const T1* strnstr( inline size_t strnstr(
_In_reads_or_z_opt_(count) const T1* str, _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(str || !count);
assert(sample); assert(sample);
for (size_t offset = 0;; ++offset) { for (size_t offset = 0;; ++offset) {
for (size_t i = offset, j = 0;; ++i, ++j) { for (size_t i = offset, j = 0;; ++i, ++j) {
if (!sample[j]) if (!sample[j])
return str + offset; return offset;
if (i >= count || !str[i]) if (i >= count || !str[i])
return nullptr; return npos;
if (str[i] != sample[j]) if (str[i] != sample[j])
break; break;
} }
@ -308,16 +357,16 @@ namespace stdex
/// Binary search for a substring case-insensitive /// Binary search for a substring case-insensitive
/// ///
/// \param[in] str String to search in /// \param[in] str String to search in
/// \param[in] sample Substring to search for
/// \param[in] count String code unit count limit /// \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 <class T1, class T2> template <class T1, class T2>
inline const T1* strnistr( inline size_t strnistr(
_In_reads_or_z_opt_(count) const T1* str, _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,
_In_ const std::locale& locale) _In_ const std::locale& locale)
{ {
assert(str || !count); assert(str || !count);
@ -327,9 +376,9 @@ namespace stdex
for (size_t offset = 0;; ++offset) { for (size_t offset = 0;; ++offset) {
for (size_t i = offset, j = 0;; ++i, ++j) { for (size_t i = offset, j = 0;; ++i, ++j) {
if (!sample[j]) if (!sample[j])
return str + offset; return offset;
if (i >= count || !str[i]) if (i >= count || !str[i])
return nullptr; return npos;
if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j])) if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
break; break;
} }