libpldm: util: Convert version field to string

Convert version field to string, see DSP0240 5.5

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ife36c0e0ae916173fa73309b490cd380a43b3db6
diff --git a/libpldm/utils.c b/libpldm/utils.c
index 2edb65a..398a5da 100644
--- a/libpldm/utils.c
+++ b/libpldm/utils.c
@@ -1,5 +1,7 @@
 #include "utils.h"
 
+#include <stdio.h>
+
 /** CRC32 code derived from work by Gary S. Brown.
  *  http://web.mit.edu/freebsd/head/sys/libkern/crc32.c
  *
@@ -59,4 +61,50 @@
 	while (size--)
 		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
 	return crc ^ ~0U;
+}
+
+static int print_version_field(uint8_t bcd, char *buffer, size_t buffer_size)
+{
+	int v;
+	if (bcd == 0xff)
+		return 0;
+	if ((bcd & 0xf0) == 0xf0) {
+		v = bcd & 0x0f;
+		return snprintf(buffer, buffer_size, "%d", v);
+	}
+	v = ((bcd >> 4) * 10) + (bcd & 0x0f);
+	return snprintf(buffer, buffer_size, "%02d", v);
+}
+
+#define POINTER_MOVE(rc, buffer, buffer_size, original_size)                   \
+	do {                                                                   \
+		if (rc < 0)                                                    \
+			return rc;                                             \
+		if ((size_t)rc >= buffer_size)                                 \
+			return original_size - 1;                              \
+		buffer += rc;                                                  \
+		buffer_size -= rc;                                             \
+	} while (0)
+
+int ver2str(const ver32_t *version, char *buffer, size_t buffer_size)
+{
+	int rc;
+	size_t original_size = buffer_size;
+	rc = print_version_field(version->major, buffer, buffer_size);
+	POINTER_MOVE(rc, buffer, buffer_size, original_size);
+	rc = snprintf(buffer, buffer_size, ".");
+	POINTER_MOVE(rc, buffer, buffer_size, original_size);
+	rc = print_version_field(version->minor, buffer, buffer_size);
+	POINTER_MOVE(rc, buffer, buffer_size, original_size);
+	if (version->update != 0xff) {
+		rc = snprintf(buffer, buffer_size, ".");
+		POINTER_MOVE(rc, buffer, buffer_size, original_size);
+		rc = print_version_field(version->update, buffer, buffer_size);
+		POINTER_MOVE(rc, buffer, buffer_size, original_size);
+	}
+	if (version->alpha != 0) {
+		rc = snprintf(buffer, buffer_size, "%c", version->alpha);
+		POINTER_MOVE(rc, buffer, buffer_size, original_size);
+	}
+	return original_size - buffer_size;
 }
\ No newline at end of file
diff --git a/libpldm/utils.h b/libpldm/utils.h
index 7a347d4..2c2d365 100644
--- a/libpldm/utils.h
+++ b/libpldm/utils.h
@@ -5,6 +5,7 @@
 extern "C" {
 #endif
 
+#include "pldm_types.h"
 #include <stddef.h>
 #include <stdint.h>
 
@@ -16,8 +17,17 @@
  */
 uint32_t crc32(const void *data, size_t size);
 
+/** @brief Convert ver32_t to string
+ *  @param[in] version - Pointer to ver32_t
+ *  @param[out] buffer - Pointer to the buffer
+ *  @param[in] buffer_size - Size of the buffer
+ *  @return The number of characters(excluding the null byte) or negative if
+ * error is encountered
+ */
+int ver2str(const ver32_t *version, char *buffer, size_t buffer_size);
+
 #ifdef __cplusplus
 }
 #endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/test/libpldm_utils_test.cpp b/test/libpldm_utils_test.cpp
index 2429097..971416f 100644
--- a/test/libpldm_utils_test.cpp
+++ b/test/libpldm_utils_test.cpp
@@ -1,3 +1,4 @@
+#include <cstring>
 #include <vector>
 
 #include "libpldm/utils.h"
@@ -9,4 +10,42 @@
     const char* password = "123456789";
     auto checksum = crc32(password, 9);
     EXPECT_EQ(checksum, 0xcbf43926);
+}
+
+TEST(Ver2string, Ver2string)
+{
+    ver32_t version{0xf3, 0xf7, 0x10, 0x61};
+    const char* vstr = "3.7.10a";
+    char buffer[1024];
+    auto rc = ver2str(&version, buffer, sizeof(buffer));
+    EXPECT_EQ(rc, std::strlen(vstr));
+    EXPECT_STREQ(vstr, buffer);
+
+    version = {0x10, 0x01, 0xf7, 0x00};
+    vstr = "10.01.7";
+    rc = ver2str(&version, buffer, sizeof(buffer));
+    EXPECT_EQ(rc, std::strlen(vstr));
+    EXPECT_STREQ(vstr, buffer);
+
+    version = {0xf3, 0xf1, 0xff, 0x00};
+    vstr = "3.1";
+    rc = ver2str(&version, buffer, sizeof(buffer));
+    EXPECT_EQ(rc, std::strlen(vstr));
+    EXPECT_STREQ(vstr, buffer);
+
+    version = {0xf1, 0xf0, 0xff, 0x61};
+    vstr = "1.0a";
+    rc = ver2str(&version, buffer, sizeof(buffer));
+    EXPECT_EQ(rc, std::strlen(vstr));
+    EXPECT_STREQ(vstr, buffer);
+
+    rc = ver2str(&version, buffer, 3);
+    EXPECT_EQ(rc, 2);
+    EXPECT_STREQ("1.", buffer);
+
+    rc = ver2str(&version, buffer, 1);
+    EXPECT_EQ(rc, 0);
+
+    rc = ver2str(&version, buffer, 0);
+    EXPECT_EQ(rc, -1);
 }
\ No newline at end of file