Revamped code for VPD parser

The commit removes all the pre-existing code from the branch
and pushes the revamped code.

Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.

Note: This code supports all the existing functionalities as it is.

Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/src/ddimm_parser.cpp b/vpd-manager/src/ddimm_parser.cpp
new file mode 100644
index 0000000..0da4ddc
--- /dev/null
+++ b/vpd-manager/src/ddimm_parser.cpp
@@ -0,0 +1,393 @@
+#include "ddimm_parser.hpp"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <cmath>
+#include <cstdint>
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace vpd
+{
+
+static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
+static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
+static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
+static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
+static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
+
+static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
+static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
+
+bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
+                                     uint8_t i_minValue, uint8_t i_maxValue)
+{
+    bool l_isValid = true;
+    uint8_t l_ByteValue = i_ByteValue >> i_shift;
+    if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue))
+    {
+        logging::logMessage(
+            "Non valid Value encountered value[" + std::to_string(l_ByteValue) +
+            "] range [" + std::to_string(i_minValue) + ".." +
+            std::to_string(i_maxValue) + "] found ");
+        return false;
+    }
+    return l_isValid;
+}
+
+uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue)
+{
+    uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+    if (i_ByteValue < constants::VALUE_5)
+    {
+        l_densityPerDie = i_ByteValue * constants::VALUE_4;
+    }
+    else
+    {
+        switch (i_ByteValue)
+        {
+            case constants::VALUE_5:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
+                break;
+
+            case constants::VALUE_6:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
+                break;
+
+            case constants::VALUE_7:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
+                break;
+
+            case constants::VALUE_8:
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
+                break;
+
+            default:
+                logging::logMessage(
+                    "default value encountered for density per die");
+                l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
+                break;
+        }
+    }
+    return l_densityPerDie;
+}
+
+uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue)
+{
+    uint8_t l_DiePerPackage = constants::VALUE_0;
+    if (i_ByteValue < constants::VALUE_2)
+    {
+        l_DiePerPackage = i_ByteValue + constants::VALUE_1;
+    }
+    else
+    {
+        l_DiePerPackage =
+            pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1));
+    }
+    return l_DiePerPackage;
+}
+
+size_t DdimmVpdParser::getDdr5BasedDdimmSize(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+
+    do
+    {
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_01,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_3) ||
+            !checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_345,
+                             constants::SHIFT_BITS_3, constants::VALUE_1,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for channels per DIMM. DDIMM Byte "
+                "235 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+            break;
+        }
+        uint8_t l_channelsPerPhy =
+            (((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BITS_01)
+                  ? constants::VALUE_1
+                  : constants::VALUE_0) +
+             ((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BITS_345)
+                  ? constants::VALUE_1
+                  : constants::VALUE_0));
+
+        uint8_t l_channelsPerDdimm =
+            (((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BIT_6) >>
+              constants::VALUE_6) +
+             ((i_iterator[constants::SPD_BYTE_235] &
+               constants::MASK_BYTE_BIT_7) >>
+              constants::VALUE_7)) *
+            l_channelsPerPhy;
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
+                                 constants::MASK_BYTE_BITS_012,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for bus width per channel. DDIMM "
+                "Byte 235 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
+            break;
+        }
+        uint8_t l_busWidthPerChannel =
+            (i_iterator[constants::SPD_BYTE_235] &
+             constants::MASK_BYTE_BITS_012)
+                ? PRIMARY_BUS_WIDTH_32_BITS
+                : PRIMARY_BUS_WIDTH_UNUSED;
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+                                 constants::MASK_BYTE_BITS_567,
+                             constants::SHIFT_BITS_5, constants::VALUE_0,
+                             constants::VALUE_5))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for die per package. DDIMM Byte 4 "
+                "value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+            break;
+        }
+        uint8_t l_diePerPackage = getDdr5DiePerPackage(
+            (i_iterator[constants::SPD_BYTE_4] &
+             constants::MASK_BYTE_BITS_567) >>
+            constants::VALUE_5);
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
+                                 constants::MASK_BYTE_BITS_01234,
+                             constants::SHIFT_BITS_0, constants::VALUE_1,
+                             constants::VALUE_8))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for SDRAM Density per Die. DDIMM "
+                "Byte 4 value [" +
+                std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
+            break;
+        }
+        uint8_t l_densityPerDie = getDdr5DensityPerDie(
+            i_iterator[constants::SPD_BYTE_4] &
+            constants::MASK_BYTE_BITS_01234);
+
+        uint8_t l_ranksPerChannel = 0;
+
+        if (((i_iterator[constants::SPD_BYTE_234] &
+              constants::MASK_BYTE_BIT_7) >>
+             constants::VALUE_7))
+        {
+            l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] &
+                                  constants::MASK_BYTE_BITS_345) >>
+                                 constants::VALUE_3) +
+                                constants::VALUE_1;
+        }
+        else if (((i_iterator[constants::SPD_BYTE_235] &
+                   constants::MASK_BYTE_BIT_6) >>
+                  constants::VALUE_6))
+        {
+            l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] &
+                                 constants::MASK_BYTE_BITS_012) +
+                                constants::VALUE_1;
+        }
+
+        if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] &
+                                 constants::MASK_BYTE_BITS_567,
+                             constants::SHIFT_BITS_5, constants::VALUE_0,
+                             constants::VALUE_3))
+        {
+            logging::logMessage(
+                "Capacity calculation failed for dram width DDIMM Byte 6 value "
+                "[" +
+                std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]");
+            break;
+        }
+        uint8_t l_dramWidth =
+            constants::VALUE_4 *
+            (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] &
+                                     constants::MASK_BYTE_BITS_567) >>
+                                    constants::VALUE_5));
+
+        // DDIMM size is calculated in GB
+        l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
+                      l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
+                     (8 * l_dramWidth);
+
+    } while (false);
+
+    return constants::CONVERT_GB_TO_KB * l_dimmSize;
+}
+
+size_t DdimmVpdParser::getDdr4BasedDdimmSize(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+    try
+    {
+        uint8_t l_tmpValue = 0;
+
+        // Calculate SDRAM capacity
+        l_tmpValue = i_iterator[constants::SPD_BYTE_4] &
+                     constants::JEDEC_SDRAM_CAP_MASK;
+
+        /* Make sure the bits are not Reserved */
+        if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so "
+                "dimm size.\n ");
+        }
+
+        uint16_t l_sdramCapacity = 1;
+        l_sdramCapacity = (l_sdramCapacity << l_tmpValue) *
+                          constants::JEDEC_SDRAMCAP_MULTIPLIER;
+
+        /* Calculate Primary bus width */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_13] &
+                     constants::JEDEC_PRI_BUS_WIDTH_MASK;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 13. Can't calculate primary bus width "
+                "and so dimm size.");
+        }
+
+        uint8_t l_primaryBusWid = 1;
+        l_primaryBusWid = (l_primaryBusWid << l_tmpValue) *
+                          constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
+
+        /* Calculate SDRAM width */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+                     constants::JEDEC_SDRAM_WIDTH_MASK;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 12. Can't calculate SDRAM width and so "
+                "dimm size.");
+        }
+
+        uint8_t l_sdramWidth = 1;
+        l_sdramWidth = (l_sdramWidth << l_tmpValue) *
+                       constants::JEDEC_SDRAM_WIDTH_MULTIPLIER;
+
+        /* Calculate Number of ranks */
+        l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
+                     constants::JEDEC_NUM_RANKS_MASK;
+        l_tmpValue >>= constants::JEDEC_RESERVED_BITS;
+
+        if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
+        {
+            throw std::runtime_error(
+                "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found.");
+        }
+
+        uint8_t l_logicalRanksPerDimm = l_tmpValue + 1;
+
+        // Determine is single load stack (3DS) or not
+        l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+                     constants::JEDEC_SIGNAL_LOADING_MASK;
+
+        if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK)
+        {
+            // Fetch die count
+            l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
+                         constants::JEDEC_DIE_COUNT_MASK;
+            l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT;
+
+            uint8_t l_dieCount = l_tmpValue + 1;
+            l_logicalRanksPerDimm *= l_dieCount;
+        }
+
+        l_dimmSize =
+            (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
+            (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm;
+
+        // Converting dimm size from MB to KB
+        l_dimmSize *= constants::CONVERT_MB_TO_KB;
+    }
+    catch (const std::exception& l_ex)
+    {
+        // TODO:: Need an error log here
+        logging::logMessage("DDR4 DDIMM calculation is failed, reason: " +
+                            std::string(l_ex.what()));
+    }
+    return l_dimmSize;
+}
+
+size_t
+    DdimmVpdParser::getDdimmSize(types::BinaryVector::const_iterator i_iterator)
+{
+    size_t l_dimmSize = 0;
+    if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5)
+    {
+        l_dimmSize = getDdr5BasedDdimmSize(i_iterator);
+    }
+    else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4)
+    {
+        l_dimmSize = getDdr4BasedDdimmSize(i_iterator);
+    }
+    else
+    {
+        logging::logMessage(
+            "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" +
+            std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]");
+    }
+    return l_dimmSize;
+}
+
+void DdimmVpdParser::readKeywords(
+    types::BinaryVector::const_iterator i_iterator)
+{
+    // collect DDIMM size value
+    auto l_dimmSize = getDdimmSize(i_iterator);
+    if (!l_dimmSize)
+    {
+        throw(DataException("Error: Calculated dimm size is 0."));
+    }
+
+    m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize);
+    // point the i_iterator to DIMM data and skip "11S"
+    advance(i_iterator, constants::DDIMM_11S_BARCODE_START +
+                            constants::DDIMM_11S_FORMAT_LEN);
+    types::BinaryVector l_partNumber(i_iterator,
+                                     i_iterator + constants::PART_NUM_LEN);
+
+    advance(i_iterator, constants::PART_NUM_LEN);
+    types::BinaryVector l_serialNumber(i_iterator,
+                                       i_iterator + constants::SERIAL_NUM_LEN);
+
+    advance(i_iterator, constants::SERIAL_NUM_LEN);
+    types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN);
+
+    m_parsedVpdMap.emplace("FN", l_partNumber);
+    m_parsedVpdMap.emplace("PN", move(l_partNumber));
+    m_parsedVpdMap.emplace("SN", move(l_serialNumber));
+    m_parsedVpdMap.emplace("CC", move(l_ccin));
+}
+
+types::VPDMapVariant DdimmVpdParser::parse()
+{
+    try
+    {
+        // Read the data and return the map
+        auto l_iterator = m_vpdVector.cbegin();
+        readKeywords(l_iterator);
+        return m_parsedVpdMap;
+    }
+    catch (const std::exception& exp)
+    {
+        logging::logMessage(exp.what());
+        throw exp;
+    }
+}
+
+} // namespace vpd