zstring: Fix char array conversion
We don't want to assume the entire char array is used for strings,
otherwise perfectly valid buffers from C are unusable as they always
hold less than or equal to the buffer size of string.
Change-Id: I1354d63b1aa64995ec245edb876fdf6e69be554a
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/stdplus/zstring.hpp b/include/stdplus/zstring.hpp
index 5cf5f0a..2612b09 100644
--- a/include/stdplus/zstring.hpp
+++ b/include/stdplus/zstring.hpp
@@ -13,25 +13,40 @@
namespace detail
{
-template <typename CharT, std::size_t N>
-constexpr bool zstring_validate(const CharT (&str)[N]) noexcept
+template <typename CharT>
+constexpr std::ptrdiff_t zstring_find_term(const CharT* str, std::size_t min,
+ std::size_t max) noexcept
{
- for (size_t i = 0; i < N - 1; ++i)
+ for (size_t i = 0; i < min; ++i)
{
if (str[i] == '\0')
{
- return false;
+ return -1;
}
}
- return str[N - 1] == '\0';
+ for (size_t i = min; i < max; ++i)
+ {
+ if (str[i] == '\0')
+ {
+ return i;
+ }
+ }
+ return -1;
}
-template <typename CharT, typename Traits>
-inline constexpr bool
- zstring_validate(const std::basic_string<CharT, Traits>& str) noexcept
+#ifndef NDEBUG
+template <typename CharT>
+constexpr std::size_t zstring_validate(const CharT* str, std::size_t min,
+ std::size_t max)
{
- return str.find('\0') == str.npos;
+ auto ret = zstring_find_term(str, min, max);
+ if (ret < 0)
+ {
+ throw std::invalid_argument("stdplus::zstring");
+ }
+ return ret;
}
+#endif
template <typename T, typename CharT, typename Traits>
struct same_string : std::false_type
@@ -69,7 +84,7 @@
using size_type = std::size_t;
template <typename T, size_type N>
- constexpr basic_zstring(T (&str)[N])
+ inline constexpr basic_zstring(T (&str)[N])
#ifdef NDEBUG
noexcept
#endif
@@ -77,10 +92,7 @@
data_(str)
{
#ifndef NDEBUG
- if (!detail::zstring_validate(str))
- {
- throw std::invalid_argument("stdplus::zstring");
- }
+ detail::zstring_validate(str, 0, N);
#endif
}
template <typename T, std::enable_if_t<std::is_pointer_v<T>, bool> = true>
@@ -91,7 +103,7 @@
std::enable_if_t<detail::same_string<std::remove_cvref_t<T>,
decay_t, Traits>::value,
bool> = true>
- constexpr basic_zstring(T&& str)
+ inline constexpr basic_zstring(T&& str)
#ifdef NDEBUG
noexcept
#endif
@@ -99,10 +111,7 @@
data_(str.data())
{
#ifndef NDEBUG
- if (!detail::zstring_validate(str))
- {
- throw std::invalid_argument("stdplus::zstring");
- }
+ detail::zstring_validate(str.data(), str.size(), str.size() + 1);
#endif
}
@@ -195,12 +204,6 @@
{
return compare(rhs.data()) <=> 0;
}
- template <typename CharTO, size_type N>
- inline constexpr Traits::comparison_category
- operator<=>(const CharTO (&rhs)[N]) const noexcept
- {
- return compare(rhs, N - 1) <=> 0;
- }
template <typename T, std::enable_if_t<std::is_pointer_v<T>, bool> = true>
inline constexpr Traits::comparison_category
operator<=>(T rhs) const noexcept
diff --git a/include/stdplus/zstring_view.hpp b/include/stdplus/zstring_view.hpp
index f7b62dd..616790d 100644
--- a/include/stdplus/zstring_view.hpp
+++ b/include/stdplus/zstring_view.hpp
@@ -36,7 +36,7 @@
constexpr compile_zstring_view(const CharT (&str)[N]) noexcept
{
- valid = zstring_validate(str);
+ valid = zstring_find_term(str, N - 1, N) >= 0;
for (std::size_t i = 0; i < N - 1; ++i)
{
data[i] = str[i];
@@ -75,19 +75,15 @@
static constexpr size_type npos = string_view_base::npos;
template <typename T, size_type N>
- constexpr basic_zstring_view(T (&str)[N])
+ inline constexpr basic_zstring_view(T (&str)[N])
#ifdef NDEBUG
- noexcept
-#endif
+ noexcept :
+ sv(str)
+#else
:
- sv(str, N - 1)
- {
-#ifndef NDEBUG
- if (!detail::zstring_validate(str))
- {
- throw std::invalid_argument("stdplus::zstring_view");
- }
+ sv(str, detail::zstring_validate(str, 0, N))
#endif
+ {
}
template <typename T, std::enable_if_t<std::is_pointer_v<T>, bool> = true>
inline constexpr basic_zstring_view(T str) noexcept : sv(str)
@@ -96,19 +92,16 @@
template <typename T,
std::enable_if_t<detail::same_string<T, CharT, Traits>::value,
bool> = true>
- constexpr basic_zstring_view(const T& str)
+ inline constexpr basic_zstring_view(const T& str)
#ifdef NDEBUG
- noexcept
-#endif
+ noexcept :
+ sv(str)
+#else
:
- sv(str.data())
- {
-#ifndef NDEBUG
- if (!detail::zstring_validate(str))
- {
- throw std::invalid_argument("stdplus::zstring_view");
- }
+ sv(str.data(),
+ detail::zstring_validate(str.data(), str.size(), str.size() + 1))
#endif
+ {
}
template <
typename T,
@@ -288,22 +281,11 @@
return basic_zstring_view(unsafe(), sv.substr(pos));
}
- template <typename CharTO, size_type N>
- inline constexpr bool operator==(const CharTO (&rhs)[N]) const noexcept
- {
- return *this == std::basic_string_view<CharTO, Traits>(rhs, N - 1);
- }
constexpr bool
operator==(std::basic_string_view<CharT, Traits> rhs) const noexcept
{
return size() == rhs.size() && compare(rhs) == 0;
}
- template <typename CharTO, size_type N>
- inline constexpr Traits::comparison_category
- operator<=>(const CharTO (&rhs)[N]) const noexcept
- {
- return *this <=> std::basic_string_view<CharTO, Traits>(rhs, N - 1);
- }
constexpr Traits::comparison_category
operator<=>(std::basic_string_view<CharT, Traits> rhs) const noexcept
{
diff --git a/src/zstring.cpp b/src/zstring.cpp
index 660b81c..ccddc83 100644
--- a/src/zstring.cpp
+++ b/src/zstring.cpp
@@ -3,7 +3,17 @@
namespace stdplus
{
+#ifdef NDEBUG
+#define zstring_debug_instance(char_t)
+#else
+#define zstring_debug_instance(char_t) \
+ template std::size_t detail::zstring_validate<char_t>( \
+ const char_t* str, std::size_t min, std::size_t max)
+#endif
#define zstring_instance(char_t) \
+ template std::ptrdiff_t detail::zstring_find_term<char_t>( \
+ const char_t* str, std::size_t min, std::size_t max) noexcept; \
+ zstring_debug_instance(char_t); \
template class basic_zstring<char_t>; \
template class basic_zstring<const char_t>
zstring_instance(char);
@@ -12,5 +22,6 @@
zstring_instance(char32_t);
zstring_instance(wchar_t);
#undef zstring_instance
+#undef zstring_debug_instance
} // namespace stdplus
diff --git a/test/zstring.cpp b/test/zstring.cpp
index 600b493..69b3c9f 100644
--- a/test/zstring.cpp
+++ b/test/zstring.cpp
@@ -24,11 +24,7 @@
EXPECT_THROW((const_zstring(str)), std::invalid_argument);
EXPECT_THROW((const_zstring(cstr)), std::invalid_argument);
#endif
-#ifdef NDEBUG
EXPECT_EQ("b", const_zstring("b\0"));
-#else
- EXPECT_THROW(const_zstring("b\0"), std::invalid_argument);
-#endif
char as[] = "c";
EXPECT_EQ("c", zstring(as));
EXPECT_EQ("c", const_zstring(as));
@@ -51,14 +47,14 @@
auto zs = zstring(empty);
auto czs = const_zstring("");
- EXPECT_NE(zs, "\0");
- EXPECT_NE("\0", zs);
+ EXPECT_EQ(zs, "\0");
+ EXPECT_EQ("\0", zs);
EXPECT_NE(zs, "\0"sv);
EXPECT_NE("\0"sv, zs);
EXPECT_LT(zs, "\0"sv);
EXPECT_GT("\0"sv, zs);
- EXPECT_NE(czs, "\0");
- EXPECT_NE("\0", czs);
+ EXPECT_EQ(czs, "\0");
+ EXPECT_EQ("\0", czs);
EXPECT_NE(czs, "\0"sv);
EXPECT_NE("\0"sv, czs);
EXPECT_LT(czs, "\0"sv);
diff --git a/test/zstring_view.cpp b/test/zstring_view.cpp
index 95e9aa5..14a1e04 100644
--- a/test/zstring_view.cpp
+++ b/test/zstring_view.cpp
@@ -67,18 +67,15 @@
{
auto s = "hi\0"s;
#ifdef NDEBUG
- EXPECT_EQ("hi", zstring_view(s));
+ EXPECT_EQ("hi\0"sv, zstring_view(s));
#else
EXPECT_THROW((zstring_view(s)), std::invalid_argument);
#endif
char mut1[] = "aa\0";
+ EXPECT_EQ("aa", zstring_view(mut1));
+#ifndef NDEBUG
char mut2[] = {'a', 'a'};
-#ifdef NDEBUG
- EXPECT_EQ("aa\0", zstring_view(mut1));
- EXPECT_EQ("a", zstring_view(mut2));
-#else
- EXPECT_THROW((zstring_view(mut1)), std::invalid_argument);
EXPECT_THROW((zstring_view(mut2)), std::invalid_argument);
#endif
}
@@ -95,8 +92,8 @@
TEST(ZstringView, NoTypeCoercion)
{
- EXPECT_NE(""_zsv, "\0");
- EXPECT_NE("\0", ""_zsv);
+ EXPECT_EQ(""_zsv, "\0");
+ EXPECT_EQ("\0", ""_zsv);
EXPECT_NE(""_zsv, "\0"sv);
EXPECT_NE("\0"sv, ""_zsv);
EXPECT_LT(""_zsv, "\0"sv);