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/ipz_parser.cpp b/vpd-manager/src/ipz_parser.cpp
new file mode 100644
index 0000000..4300e12
--- /dev/null
+++ b/vpd-manager/src/ipz_parser.cpp
@@ -0,0 +1,842 @@
+#include "config.h"
+
+#include "ipz_parser.hpp"
+
+#include "vpdecc/vpdecc.h"
+
+#include "constants.hpp"
+#include "exceptions.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <typeindex>
+
+namespace vpd
+{
+
+// Offset of different entries in VPD data.
+enum Offset
+{
+    VHDR = 17,
+    VHDR_TOC_ENTRY = 29,
+    VTOC_PTR = 35,
+    VTOC_REC_LEN = 37,
+    VTOC_ECC_OFF = 39,
+    VTOC_ECC_LEN = 41,
+    VTOC_DATA = 13,
+    VHDR_ECC = 0,
+    VHDR_RECORD = 11
+};
+
+// Length of some specific entries w.r.t VPD data.
+enum Length
+{
+    RECORD_NAME = 4,
+    KW_NAME = 2,
+    RECORD_OFFSET = 2,
+    RECORD_MIN = 44,
+    RECORD_LENGTH = 2,
+    RECORD_ECC_OFFSET = 2,
+    VHDR_ECC_LENGTH = 11,
+    VHDR_RECORD_LENGTH = 44,
+    RECORD_TYPE = 2,
+    SKIP_A_RECORD_IN_PT = 14,
+    JUMP_TO_RECORD_NAME = 6
+}; // enum Length
+
+/**
+ * @brief API to read 2 bytes LE data.
+ *
+ * @param[in] iterator - iterator to VPD vector.
+ * @return read bytes.
+ */
+static uint16_t readUInt16LE(types::BinaryVector::const_iterator iterator)
+{
+    uint16_t lowByte = *iterator;
+    uint16_t highByte = *(iterator + 1);
+    lowByte |= (highByte << 8);
+    return lowByte;
+}
+
+bool IpzVpdParser::vhdrEccCheck()
+{
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_RECORD]),
+        Length::VHDR_RECORD_LENGTH,
+        const_cast<uint8_t*>(&vpdPtr[Offset::VHDR_ECC]),
+        Length::VHDR_ECC_LENGTH);
+    if (l_status == VPD_ECC_CORRECTABLE_DATA)
+    {
+        try
+        {
+            if (m_vpdFileStream.is_open())
+            {
+                m_vpdFileStream.seekp(m_vpdStartOffset + Offset::VHDR_RECORD,
+                                      std::ios::beg);
+                m_vpdFileStream.write(reinterpret_cast<const char*>(
+                                          &m_vpdVector[Offset::VHDR_RECORD]),
+                                      Length::VHDR_RECORD_LENGTH);
+            }
+            else
+            {
+                logging::logMessage("File not open");
+                return false;
+            }
+        }
+        catch (const std::fstream::failure& e)
+        {
+            logging::logMessage(
+                "Error while operating on file with exception: " +
+                std::string(e.what()));
+            return false;
+        }
+    }
+    else if (l_status != VPD_ECC_OK)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool IpzVpdParser::vtocEccCheck()
+{
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    std::advance(vpdPtr, Offset::VTOC_PTR);
+
+    // The offset to VTOC could be 1 or 2 bytes long
+    auto vtocOffset = readUInt16LE(vpdPtr);
+
+    // Get the VTOC Length
+    std::advance(vpdPtr, sizeof(types::RecordOffset));
+    auto vtocLength = readUInt16LE(vpdPtr);
+
+    // Get the ECC Offset
+    std::advance(vpdPtr, sizeof(types::RecordLength));
+    auto vtocECCOffset = readUInt16LE(vpdPtr);
+
+    // Get the ECC length
+    std::advance(vpdPtr, sizeof(types::ECCOffset));
+    auto vtocECCLength = readUInt16LE(vpdPtr);
+
+    // Reset pointer to start of the vpd,
+    // so that Offset will point to correct address
+    vpdPtr = m_vpdVector.cbegin();
+    auto l_status = vpdecc_check_data(
+        const_cast<uint8_t*>(&m_vpdVector[vtocOffset]), vtocLength,
+        const_cast<uint8_t*>(&m_vpdVector[vtocECCOffset]), vtocECCLength);
+    if (l_status == VPD_ECC_CORRECTABLE_DATA)
+    {
+        try
+        {
+            if (m_vpdFileStream.is_open())
+            {
+                m_vpdFileStream.seekp(m_vpdStartOffset + vtocOffset,
+                                      std::ios::beg);
+                m_vpdFileStream.write(
+                    reinterpret_cast<const char*>(&m_vpdVector[vtocOffset]),
+                    vtocLength);
+            }
+            else
+            {
+                logging::logMessage("File not open");
+                return false;
+            }
+        }
+        catch (const std::fstream::failure& e)
+        {
+            logging::logMessage(
+                "Error while operating on file with exception " +
+                std::string(e.what()));
+            return false;
+        }
+    }
+    else if (l_status != VPD_ECC_OK)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool IpzVpdParser::recordEccCheck(types::BinaryVector::const_iterator iterator)
+{
+    auto recordOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(types::RecordOffset));
+    auto recordLength = readUInt16LE(iterator);
+
+    if (recordOffset == 0 || recordLength == 0)
+    {
+        throw(DataException("Invalid record offset or length"));
+    }
+
+    std::advance(iterator, sizeof(types::RecordLength));
+    auto eccOffset = readUInt16LE(iterator);
+
+    std::advance(iterator, sizeof(types::ECCOffset));
+    auto eccLength = readUInt16LE(iterator);
+
+    if (eccLength == 0 || eccOffset == 0)
+    {
+        throw(EccException("Invalid ECC length or offset."));
+    }
+
+    auto vpdPtr = m_vpdVector.cbegin();
+
+    if (vpdecc_check_data(
+            const_cast<uint8_t*>(&vpdPtr[recordOffset]), recordLength,
+            const_cast<uint8_t*>(&vpdPtr[eccOffset]), eccLength) == VPD_ECC_OK)
+    {
+        return true;
+    }
+
+    return false;
+}
+
+void IpzVpdParser::checkHeader(types::BinaryVector::const_iterator itrToVPD)
+{
+    if (m_vpdVector.empty() || (Length::RECORD_MIN > m_vpdVector.size()))
+    {
+        throw(DataException("Malformed VPD"));
+    }
+
+    std::advance(itrToVPD, Offset::VHDR);
+    auto stop = std::next(itrToVPD, Length::RECORD_NAME);
+
+    std::string record(itrToVPD, stop);
+    if ("VHDR" != record)
+    {
+        throw(DataException("VHDR record not found"));
+    }
+
+    if (!vhdrEccCheck())
+    {
+        throw(EccException("ERROR: VHDR ECC check Failed"));
+    }
+}
+
+auto IpzVpdParser::readTOC(types::BinaryVector::const_iterator& itrToVPD)
+{
+    // The offset to VTOC could be 1 or 2 bytes long
+    uint16_t vtocOffset =
+        readUInt16LE((itrToVPD + Offset::VTOC_PTR)); // itrToVPD);
+
+    // Got the offset to VTOC, skip past record header and keyword header
+    // to get to the record name.
+    std::advance(itrToVPD, vtocOffset + sizeof(types::RecordId) +
+                               sizeof(types::RecordSize) +
+                               // Skip past the RT keyword, which contains
+                               // the record name.
+                               Length::KW_NAME + sizeof(types::KwSize));
+
+    std::string record(itrToVPD, std::next(itrToVPD, Length::RECORD_NAME));
+    if ("VTOC" != record)
+    {
+        throw(DataException("VTOC record not found"));
+    }
+
+    if (!vtocEccCheck())
+    {
+        throw(EccException("ERROR: VTOC ECC check Failed"));
+    }
+
+    // VTOC record name is good, now read through the TOC, stored in the PT
+    // PT keyword; vpdBuffer is now pointing at the first character of the
+    // name 'VTOC', jump to PT data.
+    // Skip past record name and KW name, 'PT'
+    std::advance(itrToVPD, Length::RECORD_NAME + Length::KW_NAME);
+
+    // Note size of PT
+    auto ptLen = *itrToVPD;
+
+    // Skip past PT size
+    std::advance(itrToVPD, sizeof(types::KwSize));
+
+    // length of PT keyword
+    return ptLen;
+}
+
+types::RecordOffsetList IpzVpdParser::readPT(
+    types::BinaryVector::const_iterator& itrToPT, auto ptLength)
+{
+    types::RecordOffsetList recordOffsets;
+
+    auto end = itrToPT;
+    std::advance(end, ptLength);
+
+    // Look at each entry in the PT keyword. In the entry,
+    // we care only about the record offset information.
+    while (itrToPT < end)
+    {
+        std::string recordName(itrToPT, itrToPT + Length::RECORD_NAME);
+        // Skip record name and record type
+        std::advance(itrToPT, Length::RECORD_NAME + sizeof(types::RecordType));
+
+        // Get record offset
+        recordOffsets.push_back(readUInt16LE(itrToPT));
+        try
+        {
+            // Verify the ECC for this Record
+            if (!recordEccCheck(itrToPT))
+            {
+                throw(EccException("ERROR: ECC check failed"));
+            }
+        }
+        catch (const EccException& ex)
+        {
+            logging::logMessage(ex.what());
+
+            /*TODO: uncomment when PEL code goes in */
+
+            /*std::string errMsg =
+                std::string{ex.what()} + " Record: " + recordName;
+
+            inventory::PelAdditionalData additionalData{};
+            additionalData.emplace("DESCRIPTION", errMsg);
+            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+            createPEL(additionalData, PelSeverity::WARNING,
+                      errIntfForEccCheckFail, nullptr);*/
+        }
+        catch (const DataException& ex)
+        {
+            logging::logMessage(ex.what());
+
+            /*TODO: uncomment when PEL code goes in */
+
+            /*std::string errMsg =
+                std::string{ex.what()} + " Record: " + recordName;
+
+            inventory::PelAdditionalData additionalData{};
+            additionalData.emplace("DESCRIPTION", errMsg);
+            additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
+            createPEL(additionalData, PelSeverity::WARNING,
+                      errIntfForInvalidVPD, nullptr);*/
+        }
+
+        // Jump record size, record length, ECC offset and ECC length
+        std::advance(itrToPT,
+                     sizeof(types::RecordOffset) + sizeof(types::RecordLength) +
+                         sizeof(types::ECCOffset) + sizeof(types::ECCLength));
+    }
+
+    return recordOffsets;
+}
+
+types::IPZVpdMap::mapped_type
+    IpzVpdParser::readKeywords(types::BinaryVector::const_iterator& itrToKwds)
+{
+    types::IPZVpdMap::mapped_type kwdValueMap{};
+    while (true)
+    {
+        // Note keyword name
+        std::string kwdName(itrToKwds, itrToKwds + Length::KW_NAME);
+        if (constants::LAST_KW == kwdName)
+        {
+            // We're done
+            break;
+        }
+        // Check if the Keyword is '#kw'
+        char kwNameStart = *itrToKwds;
+
+        // Jump past keyword name
+        std::advance(itrToKwds, Length::KW_NAME);
+
+        std::size_t kwdDataLength;
+        std::size_t lengthHighByte;
+
+        if (constants::POUND_KW == kwNameStart)
+        {
+            // Note keyword data length
+            kwdDataLength = *itrToKwds;
+            lengthHighByte = *(itrToKwds + 1);
+            kwdDataLength |= (lengthHighByte << 8);
+
+            // Jump past 2Byte keyword length
+            std::advance(itrToKwds, sizeof(types::PoundKwSize));
+        }
+        else
+        {
+            // Note keyword data length
+            kwdDataLength = *itrToKwds;
+
+            // Jump past keyword length
+            std::advance(itrToKwds, sizeof(types::KwSize));
+        }
+
+        // support all the Keywords
+        auto stop = std::next(itrToKwds, kwdDataLength);
+        std::string kwdata(itrToKwds, stop);
+        kwdValueMap.emplace(std::move(kwdName), std::move(kwdata));
+
+        // Jump past keyword data length
+        std::advance(itrToKwds, kwdDataLength);
+    }
+
+    return kwdValueMap;
+}
+
+void IpzVpdParser::processRecord(auto recordOffset)
+{
+    // Jump to record name
+    auto recordNameOffset =
+        recordOffset + sizeof(types::RecordId) + sizeof(types::RecordSize) +
+        // Skip past the RT keyword, which contains
+        // the record name.
+        Length::KW_NAME + sizeof(types::KwSize);
+
+    // Get record name
+    auto itrToVPDStart = m_vpdVector.cbegin();
+    std::advance(itrToVPDStart, recordNameOffset);
+
+    std::string recordName(itrToVPDStart, itrToVPDStart + Length::RECORD_NAME);
+
+    // proceed to find contained keywords and their values.
+    std::advance(itrToVPDStart, Length::RECORD_NAME);
+
+    // Reverse back to RT Kw, in ipz vpd, to Read RT KW & value
+    std::advance(itrToVPDStart, -(Length::KW_NAME + sizeof(types::KwSize) +
+                                  Length::RECORD_NAME));
+
+    // Add entry for this record (and contained keyword:value pairs)
+    // to the parsed vpd output.
+    m_parsedVPDMap.emplace(std::move(recordName),
+                           std::move(readKeywords(itrToVPDStart)));
+}
+
+types::VPDMapVariant IpzVpdParser::parse()
+{
+    try
+    {
+        auto itrToVPD = m_vpdVector.cbegin();
+
+        // Check vaidity of VHDR record
+        checkHeader(itrToVPD);
+
+        // Read the table of contents
+        auto ptLen = readTOC(itrToVPD);
+
+        // Read the table of contents record, to get offsets
+        // to other records.
+        auto recordOffsets = readPT(itrToVPD, ptLen);
+        for (const auto& offset : recordOffsets)
+        {
+            processRecord(offset);
+        }
+
+        return m_parsedVPDMap;
+    }
+    catch (const std::exception& e)
+    {
+        logging::logMessage(e.what());
+        throw e;
+    }
+}
+
+types::BinaryVector IpzVpdParser::getKeywordValueFromRecord(
+    const types::Record& i_recordName, const types::Keyword& i_keywordName,
+    const types::RecordOffset& i_recordDataOffset)
+{
+    auto l_iterator = m_vpdVector.cbegin();
+
+    // Go to the record name in the given record's offset
+    std::ranges::advance(l_iterator,
+                         i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+                         m_vpdVector.cend());
+
+    // Check if the record is present in the given record's offset
+    if (i_recordName !=
+        std::string(l_iterator,
+                    std::ranges::next(l_iterator, Length::RECORD_NAME,
+                                      m_vpdVector.cend())))
+    {
+        throw std::runtime_error(
+            "Given record is not present in the offset provided");
+    }
+
+    std::ranges::advance(l_iterator, Length::RECORD_NAME, m_vpdVector.cend());
+
+    std::string l_kwName = std::string(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+
+    // Iterate through the keywords until the last keyword PF is found.
+    while (l_kwName != constants::LAST_KW)
+    {
+        // First character required for #D keyword check
+        char l_kwNameStart = *l_iterator;
+
+        std::ranges::advance(l_iterator, Length::KW_NAME, m_vpdVector.cend());
+
+        // Get the keyword's data length
+        auto l_kwdDataLength = 0;
+
+        if (constants::POUND_KW == l_kwNameStart)
+        {
+            l_kwdDataLength = readUInt16LE(l_iterator);
+            std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+                                 m_vpdVector.cend());
+        }
+        else
+        {
+            l_kwdDataLength = *l_iterator;
+            std::ranges::advance(l_iterator, sizeof(types::KwSize),
+                                 m_vpdVector.cend());
+        }
+
+        if (l_kwName == i_keywordName)
+        {
+            // Return keyword's value to the caller
+            return types::BinaryVector(
+                l_iterator, std::ranges::next(l_iterator, l_kwdDataLength,
+                                              m_vpdVector.cend()));
+        }
+
+        // next keyword search
+        std::ranges::advance(l_iterator, l_kwdDataLength, m_vpdVector.cend());
+
+        // next keyword name
+        l_kwName = std::string(
+            l_iterator,
+            std::ranges::next(l_iterator, Length::KW_NAME, m_vpdVector.cend()));
+    }
+
+    // Keyword not found
+    throw std::runtime_error("Given keyword not found.");
+}
+
+types::RecordData IpzVpdParser::getRecordDetailsFromVTOC(
+    const types::Record& i_recordName, const types::RecordOffset& i_vtocOffset)
+{
+    // Get VTOC's PT keyword value.
+    const auto l_vtocPTKwValue =
+        getKeywordValueFromRecord("VTOC", "PT", i_vtocOffset);
+
+    // Parse through VTOC PT keyword value to find the record which we are
+    // interested in.
+    auto l_vtocPTItr = l_vtocPTKwValue.cbegin();
+
+    types::RecordData l_recordData;
+
+    while (l_vtocPTItr < l_vtocPTKwValue.cend())
+    {
+        if (i_recordName ==
+            std::string(l_vtocPTItr, l_vtocPTItr + Length::RECORD_NAME))
+        {
+            // Record found in VTOC PT keyword. Get offset
+            std::ranges::advance(l_vtocPTItr,
+                                 Length::RECORD_NAME + Length::RECORD_TYPE,
+                                 l_vtocPTKwValue.cend());
+            const auto l_recordOffset = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_OFFSET,
+                                 l_vtocPTKwValue.cend());
+            const auto l_recordLength = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_LENGTH,
+                                 l_vtocPTKwValue.cend());
+            const auto l_eccOffset = readUInt16LE(l_vtocPTItr);
+
+            std::ranges::advance(l_vtocPTItr, Length::RECORD_ECC_OFFSET,
+                                 l_vtocPTKwValue.cend());
+            const auto l_eccLength = readUInt16LE(l_vtocPTItr);
+
+            l_recordData = std::make_tuple(l_recordOffset, l_recordLength,
+                                           l_eccOffset, l_eccLength);
+            break;
+        }
+
+        std::ranges::advance(l_vtocPTItr, Length::SKIP_A_RECORD_IN_PT,
+                             l_vtocPTKwValue.cend());
+    }
+
+    return l_recordData;
+}
+
+types::DbusVariantType IpzVpdParser::readKeywordFromHardware(
+    const types::ReadVpdParams i_paramsToReadData)
+{
+    // Extract record and keyword from i_paramsToReadData
+    types::Record l_record;
+    types::Keyword l_keyword;
+
+    if (const types::IpzType* l_ipzData =
+            std::get_if<types::IpzType>(&i_paramsToReadData))
+    {
+        l_record = std::get<0>(*l_ipzData);
+        l_keyword = std::get<1>(*l_ipzData);
+    }
+    else
+    {
+        logging::logMessage(
+            "Input parameter type provided isn't compatible with the given VPD type.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Read keyword's value from vector
+    auto l_itrToVPD = m_vpdVector.cbegin();
+
+    if (l_record == "VHDR")
+    {
+// Disable providing a way to read keywords from VHDR for the time being.
+#if 0
+        std::ranges::advance(l_itrToVPD, Offset::VHDR_RECORD,
+                             m_vpdVector.cend());
+
+        return types::DbusVariantType{getKeywordValueFromRecord(
+            l_record, l_keyword, Offset::VHDR_RECORD)};
+#endif
+
+        logging::logMessage("Read cannot be performed on VHDR record.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Get VTOC offset
+    std::ranges::advance(l_itrToVPD, Offset::VTOC_PTR, m_vpdVector.cend());
+    auto l_vtocOffset = readUInt16LE(l_itrToVPD);
+
+    if (l_record == "VTOC")
+    {
+        // Disable providing a way to read keywords from VTOC for the time
+        // being.
+#if 0
+        return types::DbusVariantType{
+            getKeywordValueFromRecord(l_record, l_keyword, l_vtocOffset)};
+#endif
+
+        logging::logMessage("Read cannot be performed on VTOC record.");
+        throw types::DbusInvalidArgument();
+    }
+
+    // Get record offset from VTOC's PT keyword value.
+    auto l_recordData = getRecordDetailsFromVTOC(l_record, l_vtocOffset);
+    const auto l_recordOffset = std::get<0>(l_recordData);
+
+    if (l_recordOffset == 0)
+    {
+        throw std::runtime_error("Record not found in VTOC PT keyword.");
+    }
+
+    // Get the given keyword's value
+    return types::DbusVariantType{
+        getKeywordValueFromRecord(l_record, l_keyword, l_recordOffset)};
+}
+
+void IpzVpdParser::updateRecordECC(
+    const auto& i_recordDataOffset, const auto& i_recordDataLength,
+    const auto& i_recordECCOffset, size_t i_recordECCLength,
+    types::BinaryVector& io_vpdVector)
+{
+    auto l_recordDataBegin =
+        std::next(io_vpdVector.begin(), i_recordDataOffset);
+
+    auto l_recordECCBegin = std::next(io_vpdVector.begin(), i_recordECCOffset);
+
+    auto l_eccStatus = vpdecc_create_ecc(
+        const_cast<uint8_t*>(&l_recordDataBegin[0]), i_recordDataLength,
+        const_cast<uint8_t*>(&l_recordECCBegin[0]), &i_recordECCLength);
+
+    if (l_eccStatus != VPD_ECC_OK)
+    {
+        throw(EccException("ECC update failed with error " + l_eccStatus));
+    }
+
+    auto l_recordECCEnd = std::next(l_recordECCBegin, i_recordECCLength);
+
+    m_vpdFileStream.seekp(m_vpdStartOffset + i_recordECCOffset, std::ios::beg);
+
+    std::copy(l_recordECCBegin, l_recordECCEnd,
+              std::ostreambuf_iterator<char>(m_vpdFileStream));
+}
+
+int IpzVpdParser::setKeywordValueInRecord(
+    const types::Record& i_recordName, const types::Keyword& i_keywordName,
+    const types::BinaryVector& i_keywordData,
+    const types::RecordOffset& i_recordDataOffset,
+    types::BinaryVector& io_vpdVector)
+{
+    auto l_iterator = io_vpdVector.begin();
+
+    // Go to the record name in the given record's offset
+    std::ranges::advance(l_iterator,
+                         i_recordDataOffset + Length::JUMP_TO_RECORD_NAME,
+                         io_vpdVector.end());
+
+    const std::string l_recordFound(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::RECORD_NAME, io_vpdVector.end()));
+
+    // Check if the record is present in the given record's offset
+    if (i_recordName != l_recordFound)
+    {
+        throw(DataException("Given record found at the offset " +
+                            std::to_string(i_recordDataOffset) + " is : " +
+                            l_recordFound + " and not " + i_recordName));
+    }
+
+    std::ranges::advance(l_iterator, Length::RECORD_NAME, io_vpdVector.end());
+
+    std::string l_kwName = std::string(
+        l_iterator,
+        std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+
+    // Iterate through the keywords until the last keyword PF is found.
+    while (l_kwName != constants::LAST_KW)
+    {
+        // First character required for #D keyword check
+        char l_kwNameStart = *l_iterator;
+
+        std::ranges::advance(l_iterator, Length::KW_NAME, io_vpdVector.end());
+
+        // Find the keyword's data length
+        size_t l_kwdDataLength = 0;
+
+        if (constants::POUND_KW == l_kwNameStart)
+        {
+            l_kwdDataLength = readUInt16LE(l_iterator);
+            std::ranges::advance(l_iterator, sizeof(types::PoundKwSize),
+                                 io_vpdVector.end());
+        }
+        else
+        {
+            l_kwdDataLength = *l_iterator;
+            std::ranges::advance(l_iterator, sizeof(types::KwSize),
+                                 io_vpdVector.end());
+        }
+
+        if (l_kwName == i_keywordName)
+        {
+            // Before writing the keyword's value, get the maximum size that can
+            // be updated.
+            const auto l_lengthToUpdate =
+                i_keywordData.size() <= l_kwdDataLength
+                    ? i_keywordData.size()
+                    : l_kwdDataLength;
+
+            // Set the keyword's value on vector. This is required to update the
+            // record's ECC based on the new value set.
+            const auto i_keywordDataEnd = std::ranges::next(
+                i_keywordData.cbegin(), l_lengthToUpdate, i_keywordData.cend());
+
+            std::copy(i_keywordData.cbegin(), i_keywordDataEnd, l_iterator);
+
+            // Set the keyword's value on hardware
+            const auto l_kwdDataOffset =
+                std::distance(io_vpdVector.begin(), l_iterator);
+            m_vpdFileStream.seekp(m_vpdStartOffset + l_kwdDataOffset,
+                                  std::ios::beg);
+
+            std::copy(i_keywordData.cbegin(), i_keywordDataEnd,
+                      std::ostreambuf_iterator<char>(m_vpdFileStream));
+
+            // return no of bytes set
+            return l_lengthToUpdate;
+        }
+
+        // next keyword search
+        std::ranges::advance(l_iterator, l_kwdDataLength, io_vpdVector.end());
+
+        // next keyword name
+        l_kwName = std::string(
+            l_iterator,
+            std::ranges::next(l_iterator, Length::KW_NAME, io_vpdVector.end()));
+    }
+
+    // Keyword not found
+    throw(DataException(
+        "Keyword " + i_keywordName + " not found in record " + i_recordName));
+}
+
+int IpzVpdParser::writeKeywordOnHardware(
+    const types::WriteVpdParams i_paramsToWriteData)
+{
+    int l_sizeWritten = -1;
+
+    try
+    {
+        types::Record l_recordName;
+        types::Keyword l_keywordName;
+        types::BinaryVector l_keywordData;
+
+        // Extract record, keyword and value from i_paramsToWriteData
+        if (const types::IpzData* l_ipzData =
+                std::get_if<types::IpzData>(&i_paramsToWriteData))
+        {
+            l_recordName = std::get<0>(*l_ipzData);
+            l_keywordName = std::get<1>(*l_ipzData);
+            l_keywordData = std::get<2>(*l_ipzData);
+        }
+        else
+        {
+            logging::logMessage(
+                "Input parameter type provided isn't compatible with the given FRU's VPD type.");
+            throw types::DbusInvalidArgument();
+        }
+
+        if (l_recordName == "VHDR" || l_recordName == "VTOC")
+        {
+            logging::logMessage(
+                "Write operation not allowed on the given record : " +
+                l_recordName);
+            throw types::DbusNotAllowed();
+        }
+
+        if (l_keywordData.size() == 0)
+        {
+            logging::logMessage(
+                "Write operation not allowed as the given keyword's data length is 0.");
+            throw types::DbusInvalidArgument();
+        }
+
+        auto l_vpdBegin = m_vpdVector.begin();
+
+        // Get VTOC offset
+        std::ranges::advance(l_vpdBegin, Offset::VTOC_PTR, m_vpdVector.end());
+        auto l_vtocOffset = readUInt16LE(l_vpdBegin);
+
+        // Get the details of user given record from VTOC
+        const types::RecordData& l_inputRecordDetails =
+            getRecordDetailsFromVTOC(l_recordName, l_vtocOffset);
+
+        const auto& l_inputRecordOffset = std::get<0>(l_inputRecordDetails);
+
+        if (l_inputRecordOffset == 0)
+        {
+            throw(DataException("Record not found in VTOC PT keyword."));
+        }
+
+        // Create a local copy of m_vpdVector to perform keyword update and ecc
+        // update on filestream.
+        types::BinaryVector l_vpdVector = m_vpdVector;
+
+        // write keyword's value on hardware
+        l_sizeWritten =
+            setKeywordValueInRecord(l_recordName, l_keywordName, l_keywordData,
+                                    l_inputRecordOffset, l_vpdVector);
+
+        if (l_sizeWritten <= 0)
+        {
+            throw(DataException("Unable to set value on " + l_recordName + ":" +
+                                l_keywordName));
+        }
+
+        // Update the record's ECC
+        updateRecordECC(l_inputRecordOffset, std::get<1>(l_inputRecordDetails),
+                        std::get<2>(l_inputRecordDetails),
+                        std::get<3>(l_inputRecordDetails), l_vpdVector);
+
+        logging::logMessage(std::to_string(l_sizeWritten) +
+                            " bytes updated successfully on hardware for " +
+                            l_recordName + ":" + l_keywordName);
+    }
+    catch (const std::exception& l_exception)
+    {
+        throw;
+    }
+
+    return l_sizeWritten;
+}
+} // namespace vpd