Editor implementation update for VPD-Manager app.

This commit updates the implementaion logic of reading VPD file
for vpd keyword update using VPD-Manager app.

In order to improve the efficiency, full vpd file will be read
in a single call instead of multiple blocks using multiple call.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: Ibf4c6ba1cfb1b098a542be7ade7f0046e444abbe
diff --git a/const.hpp b/const.hpp
index 5169987..1fc24cc 100644
--- a/const.hpp
+++ b/const.hpp
@@ -19,6 +19,7 @@
 using ECCOffset = uint16_t;
 using ECCLength = uint16_t;
 using LE2ByteData = uint16_t;
+using DataOffset = uint16_t;
 
 static constexpr auto MAC_ADDRESS_LEN_BYTES = 6;
 static constexpr auto LAST_KW = "PF";
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index 3bad2f8..b116d2d 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -1,5 +1,6 @@
 #include "editor_impl.hpp"
 
+#include "parser.hpp"
 #include "utils.hpp"
 
 #include <fstream>
@@ -67,7 +68,7 @@
     throw std::runtime_error("Record not found");
 }
 
-void EditorImpl::updateData(Binary kwdData)
+void EditorImpl::updateData(const Binary& kwdData)
 {
     std::size_t lengthToUpdate = kwdData.size() <= thisRecord.kwdDataLength
                                      ? kwdData.size()
@@ -77,30 +78,42 @@
     auto end = iteratorToNewdata;
     std::advance(end, lengthToUpdate);
 
+    // update data in file buffer as it will be needed to update ECC
+    // avoiding extra stream operation here
+    auto iteratorToKWdData = vpdFile.begin();
+    std::advance(iteratorToKWdData, thisRecord.kwDataOffset);
+    std::copy(iteratorToNewdata, end, iteratorToKWdData);
+
+    // update data in EEPROM as well. As we will not write complete file back
+    vpdFileStream.seekg(thisRecord.kwDataOffset, std::ios::beg);
+    iteratorToNewdata = kwdData.cbegin();
     std::copy(iteratorToNewdata, end,
               std::ostreambuf_iterator<char>(vpdFileStream));
+
+    // get a hold to new data in case encoding is needed
+    thisRecord.kwdUpdatedData.resize(thisRecord.kwdDataLength);
+    auto itrToKWdData = vpdFile.cbegin();
+    std::advance(itrToKWdData, thisRecord.kwDataOffset);
+    auto kwdDataEnd = itrToKWdData;
+    std::advance(kwdDataEnd, thisRecord.kwdDataLength);
+    std::copy(itrToKWdData, kwdDataEnd, thisRecord.kwdUpdatedData.begin());
 }
 
 void EditorImpl::checkRecordForKwd()
 {
     RecordOffset recOffset = thisRecord.recOffset;
 
-    // Jump to record name
-    auto nameOffset = recOffset + sizeof(RecordId) + sizeof(RecordSize) +
-                      // Skip past the RT keyword, which contains
-                      // the record name.
-                      lengths::KW_NAME + sizeof(KwSize);
+    // Amount to skip for record ID, size, and the RT keyword
+    constexpr auto skipBeg = sizeof(RecordId) + sizeof(RecordSize) +
+                             lengths::KW_NAME + sizeof(KwSize);
 
-    vpdFileStream.seekg(nameOffset + lengths::RECORD_NAME, std::ios::beg);
+    auto iterator = vpdFile.cbegin();
+    std::advance(iterator, recOffset + skipBeg + lengths::RECORD_NAME);
 
-    (thisRecord.recData).resize(thisRecord.recSize);
-    vpdFileStream.read(reinterpret_cast<char*>((thisRecord.recData).data()),
-                       thisRecord.recSize);
-
-    auto iterator = (thisRecord.recData).cbegin();
-    auto end = (thisRecord.recData).cend();
-
+    auto end = iterator;
+    std::advance(end, thisRecord.recSize);
     std::size_t dataLength = 0;
+
     while (iterator < end)
     {
         // Note keyword name
@@ -130,13 +143,8 @@
 
         if (thisRecord.recKWd == kw)
         {
-            // We're done
-            std::size_t kwdOffset =
-                std::distance((thisRecord.recData).cbegin(), iterator);
-            vpdFileStream.seekp(nameOffset + lengths::RECORD_NAME + kwdOffset,
-                                std::ios::beg);
+            thisRecord.kwDataOffset = std::distance(vpdFile.cbegin(), iterator);
             thisRecord.kwdDataLength = dataLength;
-
             return;
         }
 
@@ -149,51 +157,47 @@
 
 void EditorImpl::updateRecordECC()
 {
-    vpdFileStream.seekp(thisRecord.recECCoffset, std::ios::beg);
+    auto itrToRecordData = vpdFile.cbegin();
+    std::advance(itrToRecordData, thisRecord.recOffset);
 
-    (thisRecord.recEccData).resize(thisRecord.recECCLength);
-    vpdFileStream.read(reinterpret_cast<char*>((thisRecord.recEccData).data()),
-                       thisRecord.recECCLength);
-
-    auto recPtr = (thisRecord.recData).cbegin();
-    auto recEccPtr = (thisRecord.recEccData).cbegin();
+    auto itrToRecordECC = vpdFile.cbegin();
+    std::advance(itrToRecordECC, thisRecord.recECCoffset);
 
     auto l_status = vpdecc_create_ecc(
-        const_cast<uint8_t*>(&recPtr[0]), thisRecord.recSize,
-        const_cast<uint8_t*>(&recEccPtr[0]), &thisRecord.recECCLength);
+        const_cast<uint8_t*>(&itrToRecordData[0]), thisRecord.recSize,
+        const_cast<uint8_t*>(&itrToRecordECC[0]), &thisRecord.recECCLength);
     if (l_status != VPD_ECC_OK)
     {
         throw std::runtime_error("Ecc update failed");
     }
 
-    auto end = (thisRecord.recEccData).cbegin();
+    auto end = itrToRecordECC;
     std::advance(end, thisRecord.recECCLength);
 
-    std::copy((thisRecord.recEccData).cbegin(), end,
+    vpdFileStream.seekp(thisRecord.recECCoffset, std::ios::beg);
+    std::copy(itrToRecordECC, end,
               std::ostreambuf_iterator<char>(vpdFileStream));
 }
 
 auto EditorImpl::getValue(offsets::Offsets offset)
 {
-    Byte data = 0;
-    vpdFileStream.seekg(offset, std::ios::beg)
-        .get(*(reinterpret_cast<char*>(&data)));
-    LE2ByteData lowByte = data;
-
-    vpdFileStream.seekg(offset + 1, std::ios::beg)
-        .get(*(reinterpret_cast<char*>(&data)));
-    LE2ByteData highByte = data;
+    auto itr = vpdFile.cbegin();
+    std::advance(itr, offset);
+    LE2ByteData lowByte = *itr;
+    LE2ByteData highByte = *(itr + 1);
     lowByte |= (highByte << 8);
 
     return lowByte;
 }
 
-void EditorImpl::checkECC(const Binary& tocRecData, const Binary& tocECCData,
+void EditorImpl::checkECC(Binary::const_iterator& itrToRecData,
+                          Binary::const_iterator& itrToECCData,
                           RecordLength recLength, ECCLength eccLength)
 {
     auto l_status =
-        vpdecc_check_data(const_cast<uint8_t*>(&tocRecData[0]), recLength,
-                          const_cast<uint8_t*>(&tocECCData[0]), eccLength);
+        vpdecc_check_data(const_cast<uint8_t*>(&itrToRecData[0]), recLength,
+                          const_cast<uint8_t*>(&itrToECCData[0]), eccLength);
+
     if (l_status != VPD_ECC_OK)
     {
         throw std::runtime_error("Ecc check failed for VTOC");
@@ -214,42 +218,36 @@
     // read TOC ecc length
     ECCLength tocECCLength = getValue(offsets::VTOC_ECC_LEN);
 
-    // read toc record data
-    Binary vtocRecord(tocLength);
-    vpdFileStream.seekg(tocOffset, std::ios::beg);
-    vpdFileStream.read(reinterpret_cast<char*>(vtocRecord.data()), tocLength);
+    auto itrToRecord = vpdFile.cbegin();
+    std::advance(itrToRecord, tocOffset);
 
-    // read toc ECC for ecc check
-    Binary vtocECC(tocECCLength);
-    vpdFileStream.seekg(tocECCOffset, std::ios::beg);
-    vpdFileStream.read(reinterpret_cast<char*>(vtocECC.data()), tocECCLength);
+    auto iteratorToECC = vpdFile.cbegin();
+    std::advance(iteratorToECC, tocECCOffset);
 
-    auto iterator = vtocRecord.cbegin();
+    // validate ecc for the record
+    checkECC(itrToRecord, iteratorToECC, tocLength, tocECCLength);
 
     // to get to the record name.
-    std::advance(iterator, sizeof(RecordId) + sizeof(RecordSize) +
-                               // Skip past the RT keyword, which contains
-                               // the record name.
-                               lengths::KW_NAME + sizeof(KwSize));
+    std::advance(itrToRecord, sizeof(RecordId) + sizeof(RecordSize) +
+                                  // Skip past the RT keyword, which contains
+                                  // the record name.
+                                  lengths::KW_NAME + sizeof(KwSize));
 
-    std::string recordName(iterator, iterator + lengths::RECORD_NAME);
+    std::string recordName(itrToRecord, itrToRecord + lengths::RECORD_NAME);
 
     if ("VTOC" != recordName)
     {
         throw std::runtime_error("VTOC record not found");
     }
 
-    // validate ecc for the record
-    checkECC(vtocRecord, vtocECC, tocLength, tocECCLength);
-
     // jump to length of PT kwd
-    std::advance(iterator, lengths::RECORD_NAME + lengths::KW_NAME);
+    std::advance(itrToRecord, lengths::RECORD_NAME + lengths::KW_NAME);
 
     // Note size of PT
-    Byte ptLen = *iterator;
-    std::advance(iterator, 1);
+    Byte ptLen = *itrToRecord;
+    std::advance(itrToRecord, 1);
 
-    checkPTForRecord(iterator, ptLen);
+    checkPTForRecord(itrToRecord, ptLen);
 }
 
 void EditorImpl::updateKeyword(const Binary& kwdData)
@@ -261,17 +259,35 @@
         throw std::runtime_error("unable to open vpd file to edit");
     }
 
-    // process VTOC for PTT rkwd
-    readVTOC();
+    Binary completeVPDFile((std::istreambuf_iterator<char>(vpdFileStream)),
+                           std::istreambuf_iterator<char>());
+    vpdFile = completeVPDFile;
 
-    // check record for keywrod
-    checkRecordForKwd();
+    auto iterator = vpdFile.cbegin();
+    std::advance(iterator, IPZ_DATA_START);
 
-    // update the data to the file
-    updateData(kwdData);
+    Byte vpdType = *iterator;
+    if (vpdType == KW_VAL_PAIR_START_TAG)
+    {
+        openpower::vpd::keyword::editor::processHeader(
+            std::move(completeVPDFile));
 
-    // update the ECC data for the record once data has been updated
-    updateRecordECC();
+        // process VTOC for PTT rkwd
+        readVTOC();
+
+        // check record for keywrod
+        checkRecordForKwd();
+
+        // update the data to the file
+        updateData(kwdData);
+
+        // update the ECC data for the record once data has been updated
+        updateRecordECC();
+
+        return;
+    }
+
+    throw std::runtime_error("Invalid VPD file type");
 }
 
 } // namespace editor
diff --git a/vpd-manager/editor_impl.hpp b/vpd-manager/editor_impl.hpp
index 997bf85..70596a4 100644
--- a/vpd-manager/editor_impl.hpp
+++ b/vpd-manager/editor_impl.hpp
@@ -70,18 +70,19 @@
     void readVTOC();
 
     /** @brief validate ecc data for the VTOC record
-     *  @param[in] tocRecData - VTOC record data
-     *  @param[in] tocECCData - VTOC ECC data
-     *  @param[in] recLength - Lenght of VTOC record
-     *  @param[in] eccLength - Length of ECC record
+     *  @param[in] iterator to VTOC record data
+     *  @param[in] iterator to VTOC ECC data
+     *  @param[in] Lenght of VTOC record
+     *  @param[in] Length of ECC record
      */
-    void checkECC(const Binary& tocRecData, const Binary& tocECCData,
+    void checkECC(Binary::const_iterator& itrToRecData,
+                  Binary::const_iterator& itrToECCData,
                   openpower::vpd::constants::RecordLength recLength,
                   openpower::vpd::constants::ECCLength eccLength);
 
     /** @brief reads value at the given offset
      *  @param[in] offset - offset value
-     *  @return[out] - value at that offset in bigendian
+     *  @return  value at that offset in bigendian
      */
     auto getValue(openpower::vpd::constants::offsets::Offsets offset);
 
@@ -98,7 +99,7 @@
     /** @brief update data for given keyword
      *  @param[in] kwdData- data to be updated
      */
-    void updateData(Binary kwdData);
+    void updateData(const Binary& kwdData);
 
     /** @brief update record ECC
      */
@@ -113,8 +114,7 @@
     // structure to hold info about record to edit
     struct RecInfo
     {
-        Binary recData;
-        Binary recEccData;
+        Binary kwdUpdatedData; // need access to it in case encoding is needed
         const std::string& recName;
         const std::string& recKWd;
         openpower::vpd::constants::RecordOffset recOffset;
@@ -122,15 +122,17 @@
         std::size_t recECCLength;
         std::size_t kwdDataLength;
         openpower::vpd::constants::RecordSize recSize;
-
+        openpower::vpd::constants::DataOffset kwDataOffset;
         // constructor
         RecInfo(const std::string& rec, const std::string& kwd) :
             recName(rec), recKWd(kwd), recOffset(0), recECCoffset(0),
-            recECCLength(0), kwdDataLength(0), recSize(0)
+            recECCLength(0), kwdDataLength(0), recSize(0), kwDataOffset(0)
         {
         }
     } thisRecord;
 
+    Binary vpdFile;
+
 }; // class EditorImpl
 
 } // namespace editor
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index 79ab5a5..766583d 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -84,36 +84,12 @@
         }
 
         inventory::Path vpdFilePath = frus.find(path)->second;
-        std::ifstream vpdStream(vpdFilePath, std::ios::binary);
-        if (!vpdStream)
-        {
-            throw std::runtime_error("file not found");
-        }
 
-        Byte data;
-        vpdStream.seekg(IPZ_DATA_START, std::ios::beg);
-        vpdStream.get(*(reinterpret_cast<char*>(&data)));
+        // instantiate editor class to update the data
+        EditorImpl edit(vpdFilePath, recordName, keyword);
+        edit.updateKeyword(value);
 
-        // implies it is IPZ VPD
-        if (data == KW_VAL_PAIR_START_TAG)
-        {
-            Binary vpdHeader(lengths::VHDR_RECORD_LENGTH +
-                             lengths::VHDR_ECC_LENGTH);
-            vpdStream.seekg(0);
-            vpdStream.read(reinterpret_cast<char*>(vpdHeader.data()),
-                           vpdHeader.capacity());
-
-            // check if header is valid
-            openpower::vpd::keyword::editor::processHeader(
-                std::move(vpdHeader));
-
-            // instantiate editor class to update the data
-            EditorImpl edit(vpdFilePath, recordName, keyword);
-            edit.updateKeyword(value);
-
-            return;
-        }
-        throw std::runtime_error("Invalid VPD file type");
+        return;
     }
     catch (const std::exception& e)
     {