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/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