common: Improve printBuffer() & added unit tests

This patchset is improved version of the existing code for several
reasons:

1.The code is more concise and easier to read. It uses modern C++
features such as `std::ranges::for_each` and `std::format`, which
express the intent of the code more clearly compared to the nested
if-else statements and manual formatting.

2.The code now has fewer lines of code and avoids the need for creating
and managing an `std::ostringstream` object manually, which simplifies
the function and reduces the risk of errors.

3.This patch uses type-safe range-based for loop and `uint8_t` type
directly, whereas the existing code uses `int` for iterating over the
buffer elements, which may lead to unintentional type conversions or
loss of precision.

4.While the performance impact may not be significant for this patch,
using `std::ranges::for_each` with lambdas might allow for more
optimization opportunities by the compiler compared to the traditional
loop used in existing code.

Overall, this patchset leverages modern C++ features to achieve the same
functionality in a more concise, readable, and type-safe manner, making
it preferable.

Change-Id: I7be547ade053638cb4ca459ee795195f6f0883bf
Signed-off-by: Manojkiran Eda <manojkiran.eda@gmail.com>
diff --git a/common/test/pldm_utils_test.cpp b/common/test/pldm_utils_test.cpp
index 93cf335..295d364 100644
--- a/common/test/pldm_utils_test.cpp
+++ b/common/test/pldm_utils_test.cpp
@@ -29,6 +29,38 @@
     EXPECT_EQ(model, std::string("1234 - 00Z"));
 }
 
+TEST(printBuffer, testprintBufferGoodPath)
+{
+    std::vector<uint8_t> buffer = {10, 12, 14, 25, 233};
+    std::ostringstream localString;
+    auto coutBuffer = std::cout.rdbuf();
+    std::cout.rdbuf(localString.rdbuf());
+    printBuffer(false, buffer);
+    std::cout.rdbuf(coutBuffer);
+    EXPECT_EQ(localString.str(), "Rx: 0a 0c 0e 19 e9 \n");
+    localString.str("");
+    localString.clear();
+    std::cerr << localString.str() << std::endl;
+    buffer = {12, 0, 200, 12, 255};
+    std::cout.rdbuf(localString.rdbuf());
+    printBuffer(true, buffer);
+    std::cout.rdbuf(coutBuffer);
+    EXPECT_EQ(localString.str(), "Tx: 0c 00 c8 0c ff \n");
+}
+
+TEST(printBuffer, testprintBufferBadPath)
+{
+    std::vector<uint8_t> buffer = {};
+    std::ostringstream localString;
+    auto coutBuffer = std::cout.rdbuf();
+    std::cout.rdbuf(localString.rdbuf());
+    printBuffer(false, buffer);
+    EXPECT_EQ(localString.str(), "");
+    printBuffer(true, buffer);
+    std::cout.rdbuf(coutBuffer);
+    EXPECT_EQ(localString.str(), "");
+}
+
 TEST(decodeDate, testGooduintToDate)
 {
     uint64_t data = 20191212115959;
diff --git a/common/utils.cpp b/common/utils.cpp
index 3a19e8b..6193817 100644
--- a/common/utils.cpp
+++ b/common/utils.cpp
@@ -720,24 +720,18 @@
 
 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer)
 {
-    if (!buffer.empty())
+    if (buffer.empty())
     {
-        if (isTx)
-        {
-            std::cout << "Tx: ";
-        }
-        else
-        {
-            std::cout << "Rx: ";
-        }
-        std::ostringstream tempStream;
-        for (int byte : buffer)
-        {
-            tempStream << std::setfill('0') << std::setw(2) << std::hex << byte
-                       << " ";
-        }
-        std::cout << tempStream.str() << std::endl;
+        return;
     }
+
+    std::cout << (isTx ? "Tx: " : "Rx: ");
+
+    std::ranges::for_each(buffer, [](uint8_t byte) {
+        std::cout << std::format("{:02x} ", byte);
+    });
+
+    std::cout << std::endl;
 }
 
 std::string toString(const struct variable_field& var)