msgbuf: Add pldm_msgbuf_span_string_utf16()

Add pldm_msgbuf_span_string_utf16 API to return the start pointer of the
utf16 string in message buffer. The API also returns the UTF16 string
length in terms of bytes, including the NUL terminator.

```
__attribute__((always_inline)) static inline int
pldm_msgbuf_span_string_utf16(struct pldm_msgbuf *ctx, void **cursor,
			      size_t *length)
```

The `cursor` and `length` are optional. Input NULL to `cursor` and
`length` will cause the message buffer cursor points to remaining data.
The caller can ignore `length` option by input NULL if they don't care
about the size of utf16 string.

Change-Id: I1fc2865a21d9925e49416531b85212b3b07dc37a
Signed-off-by: Thu Nguyen <thu@os.amperecomputing.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/tests/msgbuf.cpp b/tests/msgbuf.cpp
index b19ee8e..f2c9aca 100644
--- a/tests/msgbuf.cpp
+++ b/tests/msgbuf.cpp
@@ -1,4 +1,5 @@
 #include <endian.h>
+#include <libpldm/utils.h>
 
 #include <cfloat>
 
@@ -1033,6 +1034,224 @@
     EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), -EOVERFLOW);
 }
 
+static size_t str16len(char16_t* startptr)
+{
+    char16_t* endptr = startptr;
+    while (*endptr)
+    {
+        endptr++;
+    }
+    return endptr - startptr;
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_good)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[] __attribute__((aligned(alignof(char16_t)))) = {
+        0x11, 0x22, 0x11, 0x68, 0x22, 0x65, 0x33, 0x6c,
+        0x44, 0x6c, 0x55, 0x6f, 0x00, 0x00, 0x34, 0x12};
+    const char expectData[] = {0x11, 0x68, 0x22, 0x65, 0x33, 0x6c,
+                               0x44, 0x6c, 0x55, 0x6f, 0x00, 0x00};
+    uint16_t testVal;
+    uint16_t testVal1;
+    void* retBuff = NULL;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal), 0);
+    EXPECT_EQ(0x2211, testVal);
+
+    ASSERT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff, NULL),
+              0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal1), 0);
+    EXPECT_EQ(0x1234, testVal1);
+
+    ASSERT_EQ(0, (uintptr_t)retBuff & (alignof(char16_t) - 1));
+    EXPECT_EQ(6, str16len((char16_t*)retBuff) + 1);
+    EXPECT_EQ(0, memcmp(expectData, retBuff, sizeof(expectData)));
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_good2)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[24] = {0x11, 0x22, 0x11, 0x68, 0x22, 0x65, 0x33, 0x6c,
+                       0x44, 0x6c, 0x55, 0x6f, 0x00, 0x00, 0x34, 0x12,
+                       0x44, 0x6c, 0x55, 0x6f, 0x00, 0x00, 0x34, 0x12};
+    constexpr size_t required = 6;
+    const char16_t expectData[required] = {0x6811, 0x6522, 0x6c33,
+                                           0x6c44, 0x6f55, 0x0000};
+    const char16_t expectData1[3] = {0x6c44, 0x6f55, 0x0000};
+    uint16_t testVal;
+    uint16_t testVal1;
+    char* retBuff = NULL;
+    char* retBuff1 = NULL;
+    size_t length = 0;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal), 0);
+    EXPECT_EQ(0x2211, testVal);
+
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff, NULL),
+              0);
+
+    ASSERT_EQ(0, (uintptr_t)retBuff & (alignof(char16_t) - 1));
+    EXPECT_EQ(6, str16len((char16_t*)retBuff) + 1);
+    EXPECT_EQ(memcmp(expectData, retBuff,
+                     sizeof(char16_t) * (str16len((char16_t*)retBuff) + 1)),
+              0);
+
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal1), 0);
+    EXPECT_EQ(0x1234, testVal1);
+
+    EXPECT_EQ(
+        pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff1, &length),
+        0);
+
+    EXPECT_EQ(0, length % 2);
+    EXPECT_EQ(memcmp(expectData1, retBuff1, length), 0);
+
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal1), 0);
+    EXPECT_EQ(0x1234, testVal1);
+
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_allow_null_args)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[14] = {0x11, 0x22, 0x11, 0x68, 0x22, 0x65, 0x33,
+                       0x6c, 0x44, 0x6c, 0x55, 0x6f, 0x00, 0x00};
+    uint16_t testVal;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal), 0);
+    EXPECT_EQ(0x2211, testVal);
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, NULL, NULL), 0);
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_bad_no_terminator)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[14] = {0x11, 0x22, 0x11, 0x68, 0x22, 0x65, 0x33,
+                       0x6c, 0x44, 0x6c, 0x55, 0x6f, 0x66, 0x77};
+    uint16_t testVal;
+    char16_t* retBuff = NULL;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal), 0);
+    EXPECT_EQ(0x2211, testVal);
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff, NULL),
+              -EOVERFLOW);
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), -EOVERFLOW);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_bad_odd_size)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[] = {0x11, 0x22, 0x11, 0x68, 0x22, 0x65, 0x33,
+                     0x6c, 0x44, 0x6c, 0x55, 0x00, 0x00};
+    uint16_t testVal;
+    char16_t* retBuff = NULL;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &testVal), 0);
+    EXPECT_EQ(0x2211, testVal);
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff, NULL),
+              -EOVERFLOW);
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), -EOVERFLOW);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_mix)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+    uint8_t src[36] = {0x2,  0x65, 0x6e, 0x00, // Language Tag "en"
+                       0x00, 0x53, 0x00, 0x30, 0x00, 0x53, 0x00,
+                       0x58, 0x00, 0x00,                   // Entity Name "S0S"
+                       0x66, 0x6e, 0x00,                   // Language Tag "en"
+                       0x00, 0x53, 0x00, 0x31, 0x00, 0x00, // Entity Name "S1"
+                       0x67, 0x6e, 0x00,                   // Language Tag "en"
+                       0x00, 0x52, 0x00, 0x52, 0x00, 0x33, 0x00,
+                       0x00, // Entity Name "RR3"
+                       0x77, 0x88};
+    uint8_t name_count;
+    uint16_t test_val;
+    char* tag = NULL;
+    char* name = NULL;
+    char* tag1 = NULL;
+    char* name1 = NULL;
+    char* tag2 = NULL;
+    char* name2 = NULL;
+    const char expectTag0[3] = {0x65, 0x6e, 0x00};
+    const char expectTag1[3] = {0x66, 0x6e, 0x00};
+    const char expectTag2[3] = {0x67, 0x6e, 0x00};
+
+    const char16_t expectName0[5] = {0x5300, 0x3000, 0x5300, 0x5800, 0x0000};
+    const char16_t expectName1[3] = {0x5300, 0x3100, 0x0000};
+    const char16_t expectName2[4] = {0x5200, 0x5200, 0x3300, 0x0000};
+    size_t length = 0;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, sizeof(src)), 0);
+    EXPECT_EQ(pldm_msgbuf_extract_uint8(ctxExtract, &name_count), 0);
+    EXPECT_EQ(0x2, name_count);
+
+    EXPECT_EQ(pldm_msgbuf_span_string_ascii(ctxExtract, (void**)&tag, NULL), 0);
+    EXPECT_EQ(strncmp(expectTag0, tag, strlen(tag) + 1), 0);
+
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&name, NULL),
+              0);
+    ASSERT_EQ(0, (uintptr_t)name & (alignof(char16_t) - 1));
+    EXPECT_EQ(5, str16len((char16_t*)name) + 1);
+    EXPECT_EQ(memcmp(expectName0, name,
+                     sizeof(char16_t) * (str16len((char16_t*)name) + 1)),
+              0);
+
+    EXPECT_EQ(pldm_msgbuf_span_string_ascii(ctxExtract, (void**)&tag1, &length),
+              0);
+    EXPECT_EQ(strncmp(expectTag1, tag1, length), 0);
+    EXPECT_EQ(
+        pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&name1, &length), 0);
+    EXPECT_EQ(0, length % 2);
+    EXPECT_EQ(memcmp(expectName1, name1, length), 0);
+
+    EXPECT_EQ(pldm_msgbuf_span_string_ascii(ctxExtract, (void**)&tag2, NULL),
+              0);
+    EXPECT_EQ(strncmp(expectTag2, tag2, strlen(tag2) + 1), 0);
+    EXPECT_EQ(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&name2, NULL),
+              0);
+    ASSERT_EQ(0, (uintptr_t)name2 & (alignof(char16_t) - 1));
+    EXPECT_EQ(4, str16len((char16_t*)name2) + 1);
+    EXPECT_EQ(memcmp(expectName2, name2,
+                     sizeof(char16_t) * (str16len((char16_t*)name2) + 1)),
+              0);
+
+    EXPECT_EQ(pldm_msgbuf_extract_uint16(ctxExtract, &test_val), 0);
+    EXPECT_EQ(0x8877, test_val);
+
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), 0);
+}
+
+TEST(msgbuf, pldm_msgbuf_span_string_utf16_under)
+{
+    struct pldm_msgbuf _ctxExtract;
+    struct pldm_msgbuf* ctxExtract = &_ctxExtract;
+
+    uint8_t src[1] = {};
+    char* retBuff = NULL;
+
+    ASSERT_EQ(pldm_msgbuf_init_errno(ctxExtract, 0, src, 0), 0);
+    ctxExtract->remaining = INTMAX_MIN;
+    EXPECT_NE(pldm_msgbuf_span_string_utf16(ctxExtract, (void**)&retBuff, NULL),
+              0);
+    EXPECT_EQ(pldm_msgbuf_destroy(ctxExtract), -EOVERFLOW);
+}
+
 TEST(msgbuf, pldm_msgbuf_span_remaining_good)
 {
     struct pldm_msgbuf _ctx;