From b41e64bbcd82e2dfc3d706ffd4678386fbc0166d Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Mon, 15 Dec 2025 10:23:30 +0100 Subject: [PATCH 1/3] Add null_safe_string_view(const char*, std::size_t) [why] There are use cases for constructing a string_view with a specified size that is still protected against null pointers. This is different from safe_string_view(const char*, std::size_t) which terminates the string at a null byte. Signed-off-by: Lars Froehlich --- include/gul17/string_util.h | 24 ++++++++++++++++++++++++ src/string_util.cc | 10 ++++++++++ tests/test_string_util.cc | 14 ++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/include/gul17/string_util.h b/include/gul17/string_util.h index b938f3c..b626b8a 100644 --- a/include/gul17/string_util.h +++ b/include/gul17/string_util.h @@ -265,6 +265,30 @@ std::string null_safe_string(const char* char_ptr); GUL_EXPORT std::string_view null_safe_string_view(const char* char_ptr); +/** + * Safely construct a string_view from a char pointer and a length. + * + * If the pointer is null, an empty string_view is constructed. Otherwise, the function + * constructs a std::string_view with the specified length from it. Zero bytes in the + * input range do not terminate the string_view. + * + * \code + * auto a = null_safe_string_view(nullptr, 10); // a == ""sv + * auto b = null_safe_string_view("ABC", 4); // b == "ABC\0"sv + * auto c = null_safe_string_view("AB\0CD", 4); // c == "AB\0C"sv + * \endcode + * + * \param char_ptr Pointer to a string with at least \c length accessible bytes, or a + * null pointer + * \param length Length of the generated string_view (unless the pointer is null) + * + * \see safe_string_view(), null_safe_string() + * + * \since version UNRELEASED + */ +GUL_EXPORT +std::string_view null_safe_string_view(const char* char_ptr, std::size_t length); + /** * Repeat a string N times. * \code diff --git a/src/string_util.cc b/src/string_util.cc index 7685a01..5454c90 100644 --- a/src/string_util.cc +++ b/src/string_util.cc @@ -50,6 +50,16 @@ std::string_view null_safe_string_view(const char* char_ptr) return result; } +std::string_view null_safe_string_view(const char* char_ptr, std::size_t length) +{ + std::string_view result; + + if (char_ptr) + result = std::string_view(char_ptr, length); + + return result; +} + std::string repeat(std::string_view str, std::size_t n) { std::string result; diff --git a/tests/test_string_util.cc b/tests/test_string_util.cc index db0f25a..d2a6783 100644 --- a/tests/test_string_util.cc +++ b/tests/test_string_util.cc @@ -134,6 +134,20 @@ TEST_CASE("null_safe_string_view(const char*)", "[string_util]") REQUIRE(null_safe_string_view("hi\0there") == "hi"sv); } +TEST_CASE("null_safe_string_view(const char*, std::size_t)", "[string_util]") +{ + using gul17::null_safe_string_view; + + REQUIRE(null_safe_string_view(nullptr, 0ull) == std::string_view{}); + REQUIRE(null_safe_string_view(nullptr, 10ull) == std::string_view{}); + REQUIRE(null_safe_string_view("", 0ull) == ""sv); + REQUIRE(null_safe_string_view("", 1ull) == "\0"sv); + REQUIRE(null_safe_string_view("hello", 5ull) == "hello"sv); + REQUIRE(null_safe_string_view("hello", 2ull) == "he"sv); + REQUIRE(null_safe_string_view("hi\0there", 2ull) == "hi"sv); + REQUIRE(null_safe_string_view("hi\0there", 4ull) == "hi\0t"sv); +} + TEST_CASE("repeat()", "[string_util]") { REQUIRE(repeat("du", 3) == "dududu"); From dbe1c313febf438dae441cc53438a73312ecbc8a Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Wed, 17 Dec 2025 16:20:00 +0100 Subject: [PATCH 2/3] Improve documentation of safe_string*(), null_safe_string*() functions [why] It is a jungle. The safe_string*() functions respect C-string style null termination, the null_safe_string*() functions only do that if they do not have a length parameter. Proposed-by: Fini Jastrow Signed-off-by: Lars Froehlich --- include/gul17/string_util.h | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/include/gul17/string_util.h b/include/gul17/string_util.h index b626b8a..43f7743 100644 --- a/include/gul17/string_util.h +++ b/include/gul17/string_util.h @@ -230,14 +230,14 @@ hex_string(const Container& container, std::string_view separator = "") * to find a zero-terminated C string and constructs a std::string from it. * * \code - * auto a = safe_string(nullptr); // a == ""s - * auto b = safe_string("ABC"); // b == "ABC"s - * auto c = safe_string("AB\0CD"); // c == "AB"s + * auto a = null_safe_string(nullptr); // a == ""s + * auto b = null_safe_string("ABC"); // b == "ABC"s + * auto c = null_safe_string("AB\0CD"); // c == "AB"s * \endcode * * \param char_ptr Pointer to a null-terminated string or a null pointer * - * \see safe_string(), null_safe_string_view() + * \see null_safe_string_view(), safe_string(), safe_string_view() * * \since version 25.7.0 */ @@ -245,20 +245,20 @@ GUL_EXPORT std::string null_safe_string(const char* char_ptr); /** - * Safely construct a string_view from a char pointer. + * Safely construct a string_view from a C string or a null pointer. * - * If the pointer is null, an empty string_view is constructed. Otherwise, the function + * If the pointer is null, an empty string_view is constructed. Otherwise, the function * assumes to find a zero-terminated C string and constructs a std::string_view from it. * * \code - * auto a = safe_string_view(nullptr); // a == ""sv - * auto b = safe_string_view("ABC"); // b == "ABC"sv - * auto c = safe_string_view("AB\0CD"); // c == "AB"sv + * auto a = null_safe_string_view(nullptr); // a == ""sv + * auto b = null_safe_string_view("ABC"); // b == "ABC"sv + * auto c = null_safe_string_view("AB\0CD"); // c == "AB"sv * \endcode * * \param char_ptr Pointer to a null-terminated string or a null pointer * - * \see safe_string_view(), null_safe_string() + * \see null_safe_string(), safe_string(), safe_string_view() * * \since version 25.7.0 */ @@ -266,7 +266,8 @@ GUL_EXPORT std::string_view null_safe_string_view(const char* char_ptr); /** - * Safely construct a string_view from a char pointer and a length. + * Safely construct a string_view from a char pointer and a length; intermediate null + * bytes do not terminate the string. * * If the pointer is null, an empty string_view is constructed. Otherwise, the function * constructs a std::string_view with the specified length from it. Zero bytes in the @@ -282,7 +283,7 @@ std::string_view null_safe_string_view(const char* char_ptr); * null pointer * \param length Length of the generated string_view (unless the pointer is null) * - * \see safe_string_view(), null_safe_string() + * \see null_safe_string(), safe_string(), safe_string_view() * * \since version UNRELEASED */ @@ -306,7 +307,8 @@ GUL_EXPORT std::string repeat(std::string_view str, std::size_t n); /** - * Safely construct a std::string from a char pointer and a length. + * Safely construct a std::string from a char pointer and a length, respecting null + * termination in the style of a C string. * * If the pointer is null, an empty string is constructed. If there are no zero bytes in * the input range, a string of length \c length is constructed. Otherwise, the input @@ -324,7 +326,7 @@ std::string repeat(std::string_view str, std::size_t n); * \c length accessible bytes, or a null pointer * \param length Maximum length of the generated string * - * \see null_safe_string(), safe_string_view() + * \see null_safe_string(), null_safe_string_view(), safe_string_view() * * \since GUL version 2.6 */ @@ -332,7 +334,8 @@ GUL_EXPORT std::string safe_string(const char* char_ptr, std::size_t length); /** - * Safely construct a string_view from a char pointer and a length. + * Safely construct a string_view from a char pointer and a length, respecting null + * termination in the style of a C string. * * If the pointer is null, an empty string_view is constructed. If there are no zero bytes * in the input range, a string_view of length \c length is constructed. Otherwise, the @@ -350,7 +353,7 @@ std::string safe_string(const char* char_ptr, std::size_t length); * \c length accessible bytes, or a null pointer * \param length Maximum length of the generated string_view * - * \see null_safe_string_view(), safe_string() + * \see null_safe_string(), null_safe_string_view(), safe_string() * * \since GUL version 25.4.0 */ From b7e28beecb5ce78b0fddc40f4bfd99df5e625323 Mon Sep 17 00:00:00 2001 From: Lars Froehlich Date: Mon, 15 Dec 2025 10:29:28 +0100 Subject: [PATCH 3/3] Update changelog with latest null_safe_string_view() changes Signed-off-by: Lars Froehlich --- data/doxygen.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/data/doxygen.h b/data/doxygen.h index 8a1e016..1741c99 100644 --- a/data/doxygen.h +++ b/data/doxygen.h @@ -146,6 +146,11 @@ namespace gul17 { * * \section changelog_gul17 GUL17 Versions * + * \subsection UNRELEASED To be released + * + * - Add gul17::null_safe_string_view(const char*, std::size_t), which returns an empty + * string_view if given a null pointer. + * * \subsection V25_7_0 Version 25.7.0 * * - Add gul17::null_safe_string(const char*) and