Add support for appending std::string_view

std::string_view is used more in the project now that c++17 is
available.  It should be allowed as a base type in serialization of dbus
interfaces.

To avoid an extra string copy, this function makes use of
sd_bus_message_append_string_iovec, which allows appending in string
pieces that might not be null terminated.  This function is piped
through the test framework, and interfaces to match.

Change-Id: Iee3e2cea9759fa9759cec98ab30c12c822aa3b73
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/include/sdbusplus/message/append.hpp b/include/sdbusplus/message/append.hpp
index d198994..ddd71ce 100644
--- a/include/sdbusplus/message/append.hpp
+++ b/include/sdbusplus/message/append.hpp
@@ -8,6 +8,8 @@
 #include <sdbusplus/utility/tuple_to_array.hpp>
 #include <sdbusplus/utility/type_traits.hpp>
 
+#include <bit>
+#include <string_view>
 #include <tuple>
 #include <type_traits>
 #include <variant>
@@ -213,6 +215,18 @@
     }
 };
 
+/** @brief Specialization of append_single for std::string_views. */
+template <>
+struct append_single<std::string_view>
+{
+    template <typename T>
+    static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& s)
+    {
+        iovec iov{std::bit_cast<void*>(s.data()), s.size()};
+        intf->sd_bus_message_append_string_iovec(m, &iov, 1);
+    }
+};
+
 /** @brief Specialization of append_single for details::string_wrapper. */
 template <>
 struct append_single<details::string_wrapper>
diff --git a/include/sdbusplus/message/types.hpp b/include/sdbusplus/message/types.hpp
index faaf23a..e4be6a5 100644
--- a/include/sdbusplus/message/types.hpp
+++ b/include/sdbusplus/message/types.hpp
@@ -220,6 +220,9 @@
 struct type_id<std::string> : tuple_type_id<SD_BUS_TYPE_STRING>
 {};
 template <>
+struct type_id<std::string_view> : tuple_type_id<SD_BUS_TYPE_STRING>
+{};
+template <>
 struct type_id<object_path> : tuple_type_id<SD_BUS_TYPE_OBJECT_PATH>
 {};
 template <>
diff --git a/include/sdbusplus/sdbus.hpp b/include/sdbusplus/sdbus.hpp
index 37b7635..7f0a569 100644
--- a/include/sdbusplus/sdbus.hpp
+++ b/include/sdbusplus/sdbus.hpp
@@ -73,6 +73,10 @@
     virtual int sd_bus_message_append_basic(sd_bus_message* message, char type,
                                             const void* value) = 0;
 
+    virtual int sd_bus_message_append_string_iovec(sd_bus_message* message,
+                                                   const struct iovec* iov,
+                                                   int iovcnt) = 0;
+
     virtual int sd_bus_message_at_end(sd_bus_message* m, int complete) = 0;
 
     virtual int sd_bus_message_close_container(sd_bus_message* m) = 0;
@@ -315,6 +319,13 @@
         return ::sd_bus_message_append_basic(message, type, value);
     }
 
+    int sd_bus_message_append_string_iovec(sd_bus_message* message,
+                                           const struct iovec* iov,
+                                           int iovcnt) override
+    {
+        return ::sd_bus_message_append_string_iovec(message, iov, iovcnt);
+    }
+
     int sd_bus_message_at_end(sd_bus_message* m, int complete) override
     {
         return ::sd_bus_message_at_end(m, complete);
diff --git a/include/sdbusplus/test/sdbus_mock.hpp b/include/sdbusplus/test/sdbus_mock.hpp
index c61fe1c..01dc819 100644
--- a/include/sdbusplus/test/sdbus_mock.hpp
+++ b/include/sdbusplus/test/sdbus_mock.hpp
@@ -62,6 +62,9 @@
 
     MOCK_METHOD(int, sd_bus_message_append_basic,
                 (sd_bus_message*, char, const void*), (override));
+    MOCK_METHOD(int, sd_bus_message_append_string_iovec,
+                (sd_bus_message*, const struct iovec* iov, int iovcnt),
+                (override));
     MOCK_METHOD(int, sd_bus_message_at_end, (sd_bus_message*, int), (override));
     MOCK_METHOD(int, sd_bus_message_close_container, (sd_bus_message*),
                 (override));
diff --git a/test/message/append.cpp b/test/message/append.cpp
index 59e83b4..b0ef54e 100644
--- a/test/message/append.cpp
+++ b/test/message/append.cpp
@@ -27,6 +27,12 @@
 using testing::SafeMatcherCast;
 using testing::StrEq;
 
+MATCHER_P(iovec_equal, match_string, "")
+{
+    const char* start = std::bit_cast<char*>(arg->iov_base);
+    return std::string(start, arg->iov_len) == match_string;
+}
+
 class AppendTest : public testing::Test
 {
   protected:
@@ -64,6 +70,13 @@
                                   SafeMatcherCast<const char*>(StrEq(str)))))
             .WillOnce(Return(0));
     }
+    void expect_basic_string_iovec(const char* str, size_t size)
+    {
+        std::string tmp = {str, size};
+        EXPECT_CALL(mock, sd_bus_message_append_string_iovec(
+                              nullptr, iovec_equal(tmp), 1))
+            .WillOnce(Return(0));
+    }
 
     void expect_open_container(char type, const char* contents)
     {
@@ -187,6 +200,20 @@
     new_message().append(std::move(s));
 }
 
+TEST_F(AppendTest, LValueStringView)
+{
+    std::string_view s{"asdf"};
+    expect_basic_string_iovec(s.data(), s.size());
+    new_message().append(s);
+}
+
+TEST_F(AppendTest, RValueStringView)
+{
+    std::string_view s{"asdf"};
+    expect_basic_string_iovec(s.data(), s.size());
+    new_message().append(std::string_view{"asdf"});
+}
+
 TEST_F(AppendTest, ObjectPath)
 {
     sdbusplus::message::object_path o{"/asdf"};