base: Add size and buffer macros for struct pldm_msg

Give library users a supported way to allocate pldm_msg buffers on the
stack.

A demonstration conversion is done in tests/oem/ibm/host.cpp.

Change-Id: I71158bd6b062c6e6522dc4a4cdcb089a139cd841
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54e1195..529746d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,10 @@
 5. pdr: Add pldm_pdr_remove_fru_record_set_by_rsi()
 6. pldm_entity_association_tree_copy_root_check()
 
+7. base: Add size and buffer macros for struct pldm_msg
+
+   Together these macros reduce the need for use of reinterpret_cast<>() in C++.
+
 ### Changed
 
 1. dsp: bios_table: Null check for pldm_bios_table_iter_is_end()
diff --git a/include/libpldm/base.h b/include/libpldm/base.h
index 2f64849..0904803 100644
--- a/include/libpldm/base.h
+++ b/include/libpldm/base.h
@@ -9,6 +9,7 @@
 #include <libpldm/pldm_types.h>
 
 #include <asm/byteorder.h>
+#include <stdalign.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -173,6 +174,47 @@
 	uint8_t payload[1]; //!< &payload[0] is the beginning of the payload
 } __attribute__((packed));
 
+/** @brief Determine the underlying object size for a @struct pldm_msg
+ *
+ * @pre @p size must be a constant expression
+ *
+ * @note Providing an expression for @p size that is not an integer constant
+ *       expression will force a compilation failure.
+ *
+ * @param size The desired size of the @struct pldm_msg payload
+ */
+#define PLDM_MSG_SIZE(size)                                                    \
+	(sizeof(char[(__builtin_constant_p(size)) ? 1 : -1])) *                \
+		(sizeof(struct pldm_msg) -                                     \
+		 sizeof(((struct pldm_msg *)NULL)->payload) + (size))
+
+/** @brief Stack-allocate a buffer to hold a @struct pldm_msg
+ *
+ * Allocate an appropriately aligned array named @p name of type unsigned char
+ * with the necessary length to hold a payload of the requested size.
+ *
+ * @param name - The variable name used to define the buffer
+ * @param size - The desired payload length for the intended @struct pldm_msg
+ */
+#define PLDM_MSG_BUFFER(name, size)                                            \
+	alignas(struct pldm_msg) unsigned char(name)[PLDM_MSG_SIZE(size)]
+
+/** @brief Create a pointer to a stack-allocated @struct pldm_msg
+ *
+ * Define a pointer named @p name of type @struct pldm_msg to an object on the
+ * stack of appropriate alignment and length to hold a @struct pldm_msg with a
+ * payload of @p size.
+ *
+ * @param name - The variable name for pointer
+ * @param size - The desired payload length for the underlying @struct pldm_msg
+ *        buffer
+ */
+#ifdef __cplusplus
+#define PLDM_MSG_DEFINE_P(name, size)                                          \
+	PLDM_MSG_BUFFER(name##_buf, size);                                     \
+	auto *(name) = new (name##_buf) pldm_msg
+#endif
+
 /**
  * @brief Compare the headers from two PLDM messages to determine if the latter
  * is a message representing a response to the former, where the former must be
diff --git a/tests/oem/ibm/host.cpp b/tests/oem/ibm/host.cpp
index 86c9577..628c123 100644
--- a/tests/oem/ibm/host.cpp
+++ b/tests/oem/ibm/host.cpp
@@ -5,19 +5,18 @@
 #include <array>
 #include <cstdint>
 #include <cstring>
+#include <new>
 #include <vector>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-constexpr auto hdrSize = sizeof(pldm_msg_hdr);
-
 TEST(GetAlertStatus, testGoodEncodeRequest)
 {
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_REQ_BYTES> requestMsg{};
+    PLDM_MSG_DEFINE_P(request, PLDM_GET_ALERT_STATUS_REQ_BYTES);
 
     uint8_t versionId = 0x0;
 
-    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
     auto rc = encode_get_alert_status_req(0, versionId, request,
                                           PLDM_GET_ALERT_STATUS_REQ_BYTES);
     EXPECT_EQ(rc, PLDM_SUCCESS);
@@ -26,9 +25,7 @@
 
 TEST(GetAlertStatus, testBadEncodeRequest)
 {
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_REQ_BYTES> requestMsg{};
-
-    auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
+    PLDM_MSG_DEFINE_P(request, PLDM_GET_ALERT_STATUS_REQ_BYTES);
     auto rc = encode_get_alert_status_req(0, 0x0, request,
                                           PLDM_GET_ALERT_STATUS_REQ_BYTES + 1);
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
@@ -39,22 +36,19 @@
     uint8_t completionCode = PLDM_SUCCESS;
     uint32_t rack_entry = 0xff000030;
     uint32_t pri_cec_node = 0x00008030;
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_RESP_BYTES>
-        responseMsg{};
+
+    PLDM_MSG_DEFINE_P(response, PLDM_GET_ALERT_STATUS_RESP_BYTES);
+    auto* resp = new (response->payload) pldm_get_alert_status_resp;
+    resp->completion_code = completionCode;
+    resp->rack_entry = htole32(rack_entry);
+    resp->pri_cec_node = htole32(pri_cec_node);
 
     uint8_t retCompletionCode = 0;
     uint32_t retRack_entry = 0;
     uint32_t retPri_cec_node = 0;
 
-    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
-    struct pldm_get_alert_status_resp* resp =
-        reinterpret_cast<struct pldm_get_alert_status_resp*>(response->payload);
-    resp->completion_code = completionCode;
-    resp->rack_entry = htole32(rack_entry);
-    resp->pri_cec_node = htole32(pri_cec_node);
-
     auto rc = decode_get_alert_status_resp(
-        response, responseMsg.size() - hdrSize, &retCompletionCode,
+        response, PLDM_GET_ALERT_STATUS_RESP_BYTES, &retCompletionCode,
         &retRack_entry, &retPri_cec_node);
     EXPECT_EQ(rc, PLDM_SUCCESS);
     EXPECT_EQ(retCompletionCode, completionCode);
@@ -70,47 +64,38 @@
     uint8_t completionCode = PLDM_SUCCESS;
     uint32_t rack_entry = 0xff000030;
     uint32_t pri_cec_node = 0x00008030;
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_RESP_BYTES>
-        responseMsg{};
+
+    PLDM_MSG_DEFINE_P(response, PLDM_GET_ALERT_STATUS_RESP_BYTES);
+    auto* resp = new (response->payload) pldm_get_alert_status_resp;
+    resp->completion_code = completionCode;
+    resp->rack_entry = htole32(rack_entry);
+    resp->pri_cec_node = htole32(pri_cec_node);
 
     uint8_t retCompletionCode = 0;
     uint32_t retRack_entry = 0;
     uint32_t retPri_cec_node = 0;
 
-    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
-    struct pldm_get_alert_status_resp* resp =
-        reinterpret_cast<struct pldm_get_alert_status_resp*>(response->payload);
-    resp->completion_code = completionCode;
-    resp->rack_entry = htole32(rack_entry);
-    resp->pri_cec_node = htole32(pri_cec_node);
-
     rc = decode_get_alert_status_resp(
-        response, responseMsg.size() - hdrSize + 1, &retCompletionCode,
+        response, PLDM_GET_ALERT_STATUS_RESP_BYTES + 1, &retCompletionCode,
         &retRack_entry, &retPri_cec_node);
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }
 
 TEST(GetAlertStatus, testGoodEncodeResponse)
 {
-    uint8_t completionCode = 0;
     uint32_t rack_entry = 0xff000030;
     uint32_t pri_cec_node = 0x00008030;
 
-    std::vector<uint8_t> responseMsg(hdrSize +
-                                     PLDM_GET_ALERT_STATUS_RESP_BYTES);
-    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    PLDM_MSG_DEFINE_P(response, PLDM_GET_ALERT_STATUS_RESP_BYTES);
 
-    auto rc =
-        encode_get_alert_status_resp(0, PLDM_SUCCESS, rack_entry, pri_cec_node,
-                                     response, responseMsg.size() - hdrSize);
+    auto rc = encode_get_alert_status_resp(0, PLDM_SUCCESS, rack_entry,
+                                           pri_cec_node, response,
+                                           PLDM_GET_ALERT_STATUS_RESP_BYTES);
 
-    EXPECT_EQ(rc, PLDM_SUCCESS);
-    struct pldm_get_alert_status_resp* resp =
-        reinterpret_cast<struct pldm_get_alert_status_resp*>(response->payload);
-
-    EXPECT_EQ(completionCode, resp->completion_code);
-    EXPECT_EQ(rack_entry, le32toh(resp->rack_entry));
-    EXPECT_EQ(pri_cec_node, le32toh(resp->pri_cec_node));
+    ASSERT_EQ(rc, PLDM_SUCCESS);
+    EXPECT_THAT(response_buf, testing::ElementsAreArray(
+                                  {0x00, 0x3f, 0xf0, 0x00, 0x30, 0x00, 0x00,
+                                   0xff, 0x30, 0x80, 0x00, 0x00}));
 }
 
 TEST(GetAlertStatus, testBadEncodeResponse)
@@ -118,29 +103,24 @@
     uint32_t rack_entry = 0xff000030;
     uint32_t pri_cec_node = 0x00008030;
 
-    std::vector<uint8_t> responseMsg(hdrSize +
-                                     PLDM_GET_ALERT_STATUS_RESP_BYTES);
-    auto response = reinterpret_cast<pldm_msg*>(responseMsg.data());
+    PLDM_MSG_DEFINE_P(response, PLDM_GET_ALERT_STATUS_RESP_BYTES);
 
-    auto rc = encode_get_alert_status_resp(0, PLDM_SUCCESS, rack_entry,
-                                           pri_cec_node, response,
-                                           responseMsg.size() - hdrSize + 1);
+    auto rc = encode_get_alert_status_resp(
+        0, PLDM_SUCCESS, rack_entry, pri_cec_node, response,
+        PLDM_GET_ALERT_STATUS_RESP_BYTES + 1);
 
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 }
 
 TEST(GetAlertStatus, testGoodDecodeRequest)
 {
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_REQ_BYTES> requestMsg{};
-
     uint8_t versionId = 0x0;
     uint8_t retVersionId;
 
-    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
-
+    PLDM_MSG_DEFINE_P(req, PLDM_GET_ALERT_STATUS_REQ_BYTES);
     req->payload[0] = versionId;
 
-    auto rc = decode_get_alert_status_req(req, requestMsg.size() - hdrSize,
+    auto rc = decode_get_alert_status_req(req, PLDM_GET_ALERT_STATUS_REQ_BYTES,
                                           &retVersionId);
 
     EXPECT_EQ(rc, PLDM_SUCCESS);
@@ -149,17 +129,14 @@
 
 TEST(GetAlertStatus, testBadDecodeRequest)
 {
-    std::array<uint8_t, hdrSize + PLDM_GET_ALERT_STATUS_REQ_BYTES> requestMsg{};
-
     uint8_t versionId = 0x0;
     uint8_t retVersionId;
 
-    auto req = reinterpret_cast<pldm_msg*>(requestMsg.data());
-
+    PLDM_MSG_DEFINE_P(req, PLDM_GET_ALERT_STATUS_REQ_BYTES);
     req->payload[0] = versionId;
 
-    auto rc = decode_get_alert_status_req(req, requestMsg.size() - hdrSize + 1,
-                                          &retVersionId);
+    auto rc = decode_get_alert_status_req(
+        req, PLDM_GET_ALERT_STATUS_REQ_BYTES + 1, &retVersionId);
 
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);
 }