msgbuf: Add pldm_msgbuf_copy_string_utf16()

Safely copy a NUL-terminated UTF16-{BE,LE} string between msgbuf
instances.

Change-Id: If96df9598f17ac771d75f0831be270c5e0139578
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/src/msgbuf.h b/src/msgbuf.h
index 0936bac..1d06a75 100644
--- a/src/msgbuf.h
+++ b/src/msgbuf.h
@@ -1294,6 +1294,21 @@
 	return pldm__msgbuf_insert_array_void(dst, ascii, len);
 }
 
+__attribute__((always_inline)) static inline int
+pldm_msgbuf_copy_string_utf16(struct pldm_msgbuf *dst, struct pldm_msgbuf *src)
+{
+	void *utf16 = NULL;
+	size_t len = 0;
+	int rc;
+
+	rc = pldm_msgbuf_span_string_utf16(src, &utf16, &len);
+	if (rc < 0) {
+		return rc;
+	}
+
+	return pldm__msgbuf_insert_array_void(dst, utf16, len);
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tests/msgbuf.cpp b/tests/msgbuf.cpp
index f2c9aca..c84acce 100644
--- a/tests/msgbuf.cpp
+++ b/tests/msgbuf.cpp
@@ -1434,3 +1434,77 @@
     ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
     ASSERT_EQ(pldm_msgbuf_destroy(src), -EOVERFLOW);
 }
+
+TEST(msgbuf, pldm_msgbuf_copy_utf16_exact)
+{
+    const char16_t msg[] = u"this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg)] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_utf16(dst, src), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+    EXPECT_EQ(0, memcmp(buf, msg, sizeof(msg)));
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_utf16_dst_exceeds_src)
+{
+    const char16_t msg[] = u"this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg) + 1] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_utf16(dst, src), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+    EXPECT_EQ(0, memcmp(buf, msg, sizeof(msg)));
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_utf16_src_exceeds_dst)
+{
+    const char16_t msg[] = u"this is a message";
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg) - 1] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_utf16(dst, src), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_copy_utf16_unterminated_src)
+{
+    const char16_t msg[] = {u'a'};
+
+    struct pldm_msgbuf _src;
+    struct pldm_msgbuf* src = &_src;
+    struct pldm_msgbuf _dst;
+    struct pldm_msgbuf* dst = &_dst;
+
+    char buf[sizeof(msg)] = {};
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(src, 0, msg, sizeof(msg)), 0);
+    ASSERT_EQ(pldm_msgbuf_init_errno(dst, 0, buf, sizeof(buf)), 0);
+    EXPECT_EQ(pldm_msgbuf_copy_string_utf16(dst, src), -EOVERFLOW);
+    ASSERT_EQ(pldm_msgbuf_destroy(dst), 0);
+    ASSERT_EQ(pldm_msgbuf_destroy(src), -EOVERFLOW);
+}