Interface & Factory implementation for VPD Parsers

This commit abstracts the implementation logic of different parser.
A parser factory is implemented to provide instance of required
parser based on the type of vpd file needed to be parsed.

The interface should be derived to implement any further parser logic
related to vpd.

Status: This does not add any new function, so basic testing of
VPD parsing, VPD tool and writes was performed.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: I3ce1a2d6b7e8d8984fd7800132e78ab8a9a21e56
diff --git a/vpd-parser/ipz_parser.cpp b/vpd-parser/ipz_parser.cpp
new file mode 100644
index 0000000..5af3441
--- /dev/null
+++ b/vpd-parser/ipz_parser.cpp
@@ -0,0 +1,37 @@
+#include "ipz_parser.hpp"
+
+#include "impl.hpp"
+
+namespace openpower
+{
+namespace vpd
+{
+namespace ipz
+{
+namespace parser
+{
+using namespace openpower::vpd::parser;
+using namespace openpower::vpd::constants;
+
+std::variant<kwdVpdMap, Store> IpzVpdParser::parse()
+{
+    Impl p(std::move(vpd));
+    Store s = p.run();
+    return s;
+}
+
+void IpzVpdParser::processHeader()
+{
+    Impl p(std::move(vpd));
+    p.checkVPDHeader();
+}
+
+std::string IpzVpdParser::getInterfaceName() const
+{
+    return ipzVpdInf;
+}
+
+} // namespace parser
+} // namespace ipz
+} // namespace vpd
+} // namespace openpower
diff --git a/vpd-parser/ipz_parser.hpp b/vpd-parser/ipz_parser.hpp
new file mode 100644
index 0000000..cb8c489
--- /dev/null
+++ b/vpd-parser/ipz_parser.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "const.hpp"
+#include "parser_interface.hpp"
+#include "store.hpp"
+#include "types.hpp"
+
+#include <vector>
+
+namespace openpower
+{
+namespace vpd
+{
+namespace ipz
+{
+namespace parser
+{
+
+using ParserInterface = openpower::vpd::parser::interface::ParserInterface;
+using kwdVpdMap = openpower::vpd::inventory::KeywordVpdMap;
+
+class IpzVpdParser : public ParserInterface
+{
+  public:
+    IpzVpdParser() = delete;
+    IpzVpdParser(const IpzVpdParser&) = delete;
+    IpzVpdParser& operator=(const IpzVpdParser&) = delete;
+    IpzVpdParser(IpzVpdParser&&) = delete;
+    IpzVpdParser& operator=(IpzVpdParser&&) = delete;
+    ~IpzVpdParser() = default;
+
+    /**
+     * @brief Constructor
+     */
+    IpzVpdParser(Binary&& VpdVector) : vpd(std::move(VpdVector))
+    {
+    }
+
+    /**
+     * @brief Parse the memory VPD binary data.
+     * Collects and emplace the keyword-value pairs in map.
+     *
+     * @return map of keyword:value
+     */
+    std::variant<kwdVpdMap, Store> parse();
+
+    /**
+     * @brief An api to return interface name with respect to
+     * the parser selected.
+     *
+     * @return - Interface name for that vpd type.
+     */
+    std::string getInterfaceName() const;
+
+    /** @brief API to check vpd header
+     *  @param [in] vpd - VPDheader in binary format
+     */
+    void processHeader();
+
+  private:
+    Binary vpd;
+}; // class IpzVpdParser
+
+} // namespace parser
+} // namespace ipz
+} // namespace vpd
+} // namespace openpower
diff --git a/vpd-parser/keyword_vpd_parser.cpp b/vpd-parser/keyword_vpd_parser.cpp
new file mode 100644
index 0000000..3d204f4
--- /dev/null
+++ b/vpd-parser/keyword_vpd_parser.cpp
@@ -0,0 +1,221 @@
+#include "keyword_vpd_parser.hpp"
+
+#include "const.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <string>
+
+using namespace openpower::vpd::constants;
+using namespace openpower::vpd::inventory;
+using namespace std;
+using namespace openpower::vpd;
+
+namespace vpd
+{
+namespace keyword
+{
+namespace parser
+{
+
+variant<KeywordVpdMap, store> KeywordVpdParser::parse()
+{
+    int kwVpdType;
+    if (keywordVpdVector.empty())
+    {
+        throw std::runtime_error("Blank Vpd Data");
+    }
+
+    validateLargeResourceIdentifierString();
+
+    kwVpdType = validateTheTypeOfKwVpd();
+
+    auto kwValMap = kwValParser();
+
+    // Donot process these two functions for bono type VPD
+    if (!kwVpdType)
+    {
+        validateSmallResourceTypeEnd();
+
+        validateChecksum();
+    }
+
+    validateSmallResourceTypeLastEnd();
+
+#ifdef DEBUG_KW_VPD
+    cerr << '\n' << " KW " << '\t' << "  VALUE " << '\n';
+    for (const auto& it : kwValMap)
+    {
+        cerr << '\n' << " " << it->first << '\t';
+        copy((it->second).begin(), (it->second).end(),
+             ostream_iterator<int>(cout << hex, " "));
+    }
+#endif
+
+    return kwValMap;
+}
+
+void KeywordVpdParser::validateLargeResourceIdentifierString()
+{
+    kwVpdIterator = keywordVpdVector.begin();
+
+    // Check for large resource type identfier string
+    if (*kwVpdIterator != KW_VPD_START_TAG)
+    {
+        throw std::runtime_error(
+            "Invalid Large resource type Identifier String");
+    }
+
+    itrOutOfBoundCheck(1);
+    advance(kwVpdIterator, sizeof(KW_VPD_START_TAG));
+}
+
+int KeywordVpdParser::validateTheTypeOfKwVpd()
+{
+    size_t dataSize = getKwDataSize();
+
+    itrOutOfBoundCheck(TWO_BYTES + dataSize);
+
+#ifdef DEBUG_KW_VPD
+    auto dsDeb = dataSize;
+    auto itDeb = kwVpdIterator + TWO_BYTES;
+    std::cout << '\n' << '\t';
+    while (dsDeb != 0)
+    {
+        std::cout << *itDeb;
+        itDeb++;
+        dsDeb--;
+    }
+    std::cout << '\n';
+#endif
+
+    // +TWO_BYTES is the description's size byte
+    std::advance(kwVpdIterator, TWO_BYTES + dataSize);
+
+    int kwVpdType = 0;
+    // Check for invalid vendor defined large resource type
+    if (*kwVpdIterator != KW_VAL_PAIR_START_TAG)
+    {
+        if (*kwVpdIterator != ALT_KW_VAL_PAIR_START_TAG)
+        {
+            throw std::runtime_error("Invalid Keyword Value Pair Start Tag");
+        }
+        // Bono vpd referred as 1
+        kwVpdType = 1;
+    }
+    return kwVpdType;
+}
+
+KeywordVpdMap KeywordVpdParser::kwValParser()
+{
+    int totalSize = 0;
+    KeywordVpdMap kwValMap;
+
+    checkSumStart = kwVpdIterator;
+
+    itrOutOfBoundCheck(1);
+    kwVpdIterator++;
+
+    // Get the total length of all keyword value pairs
+    totalSize = getKwDataSize();
+
+    if (totalSize == 0)
+    {
+        throw std::runtime_error("Badly formed keyword VPD data");
+    }
+
+    itrOutOfBoundCheck(TWO_BYTES);
+    std::advance(kwVpdIterator, TWO_BYTES);
+
+    // Parse the keyword-value and store the pairs in map
+    while (totalSize > 0)
+    {
+        std::string kwStr(kwVpdIterator, kwVpdIterator + TWO_BYTES);
+
+        totalSize -= TWO_BYTES;
+        itrOutOfBoundCheck(TWO_BYTES);
+        std::advance(kwVpdIterator, TWO_BYTES);
+
+        size_t kwSize = *kwVpdIterator;
+
+        itrOutOfBoundCheck(1);
+        kwVpdIterator++;
+
+        std::vector<uint8_t> valVec(kwVpdIterator, kwVpdIterator + kwSize);
+
+        itrOutOfBoundCheck(kwSize);
+        std::advance(kwVpdIterator, kwSize);
+
+        totalSize -= kwSize + 1;
+
+        kwValMap.emplace(std::make_pair(std::move(kwStr), std::move(valVec)));
+    }
+
+    checkSumEnd = kwVpdIterator - 1;
+
+    return kwValMap;
+}
+
+void KeywordVpdParser::validateSmallResourceTypeEnd()
+{
+    // Check for small resource type end tag
+    if (*kwVpdIterator != KW_VAL_PAIR_END_TAG)
+    {
+        throw std::runtime_error("Invalid Small resource type End");
+    }
+}
+
+void KeywordVpdParser::validateChecksum()
+{
+    uint8_t checkSum = 0;
+
+    // Checksum calculation
+    checkSum = std::accumulate(checkSumStart, checkSumEnd + 1, checkSum);
+    checkSum = ~checkSum + 1;
+
+    if (checkSum != *(kwVpdIterator + 1))
+    {
+        throw std::runtime_error("Invalid Check sum");
+    }
+#ifdef DEBUG_KW_VPD
+    std::cout << "\nCHECKSUM : " << std::hex << static_cast<int>(checkSum)
+              << std::endl;
+#endif
+
+    itrOutOfBoundCheck(TWO_BYTES);
+    std::advance(kwVpdIterator, TWO_BYTES);
+}
+
+void KeywordVpdParser::validateSmallResourceTypeLastEnd()
+{
+    // Check for small resource type last end of data
+    if (*kwVpdIterator != KW_VPD_END_TAG)
+    {
+        throw std::runtime_error(
+            "Invalid Small resource type Last End Of Data");
+    }
+}
+
+size_t KeywordVpdParser::getKwDataSize()
+{
+    return (*(kwVpdIterator + 1) << 8 | *kwVpdIterator);
+}
+
+void KeywordVpdParser::itrOutOfBoundCheck(uint8_t incVar)
+{
+
+    if ((std::distance(keywordVpdVector.begin(), kwVpdIterator + incVar)) >
+        std::distance(keywordVpdVector.begin(), keywordVpdVector.end()))
+    {
+        throw std::runtime_error("Badly formed VPD data");
+    }
+}
+
+std::string KeywordVpdParser::getInterfaceName() const
+{
+    return kwdVpdInf;
+}
+
+} // namespace parser
+} // namespace keyword
+} // namespace vpd
diff --git a/vpd-parser/keyword_vpd_parser.hpp b/vpd-parser/keyword_vpd_parser.hpp
new file mode 100644
index 0000000..2382a5f
--- /dev/null
+++ b/vpd-parser/keyword_vpd_parser.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+namespace vpd
+{
+namespace keyword
+{
+namespace parser
+{
+
+using ParserInterface = openpower::vpd::parser::interface::ParserInterface;
+using kwdVpdMap = openpower::vpd::inventory::KeywordVpdMap;
+using store = openpower::vpd::Store;
+
+/**
+ * @class KeywordVpdParser
+ * @brief Implements parser for Keyword VPD
+ *
+ * KeywordVpdParser object must be constructed by passing in
+ * Keyword VPD in binary format. To parse the VPD, call the
+ * kwVpdParser() method. The kwVpdParser() method returns
+ * a map of keyword-value pairs.
+ *
+ * Following is the algorithm used to parse Keyword VPD data:
+ * 1) Validate if the first byte is 'largeResourceIdentifierString'.
+ * 2) Validate the byte after the description is 'vendor defined large resource
+ * type tag'.
+ * 3) For each keyword-value pairs :
+ * 	3.1) Parse the 2 byte length keyword and emplace it in the map as 'key'.
+ * 	3.2) Parse over the value bytes corresponding to the keyword and
+ * 	     emplace it in the map as 'value' for the key inserted in 3.1.
+ * 4) Validate the byte before checksum byte is 'small resource type end tag'.
+ * 5) Validate the checksum.
+ * 6) Validate the 'small resource type last end tag'.
+ * 7) Return the keyword-value map.
+ */
+class KeywordVpdParser : public ParserInterface
+{
+  public:
+    KeywordVpdParser() = delete;
+    KeywordVpdParser(const KeywordVpdParser&) = delete;
+    KeywordVpdParser(KeywordVpdParser&&) = delete;
+    ~KeywordVpdParser() = default;
+
+    /**
+     * @brief Constructor
+     *
+     * Move kwVpdVector to parser object's kwVpdVector
+     */
+    KeywordVpdParser(openpower::vpd::Binary&& kwVpdVector) :
+        keywordVpdVector(std::move(kwVpdVector))
+    {
+    }
+
+    /**
+     * @brief Parse the keyword VPD binary data.
+     * Calls the sub functions to emplace the
+     * keyword-value pairs in map and to validate
+     * certain tags and checksum data.
+     *
+     * @return map of keyword:value
+     */
+    std::variant<kwdVpdMap, store> parse();
+
+    /**
+     * @brief An api to return interface name with respect to
+     * the parser selected.
+     *
+     * @return - Interface name for that vpd type.
+     */
+    std::string getInterfaceName() const;
+
+  private:
+    openpower::vpd::Binary::iterator
+        checkSumStart; //!< Pointer to the start byte from where
+                       //!< the checksum need to be calculated
+    openpower::vpd::Binary::iterator
+        checkSumEnd; //!< Pointer to the end byte until which the
+                     //!< checksum need to be calculated
+    openpower::vpd::Binary::iterator
+        kwVpdIterator; //!< Iterator to parse the vector
+    openpower::vpd::Binary
+        keywordVpdVector; //!< Vector which stores keyword VPD data
+
+    /**
+     * @brief Validate the large resource identifier string
+     */
+    void validateLargeResourceIdentifierString();
+
+    /**
+     * @brief Validate the type of keyword VPD
+     *
+     * @return integer representing the type of kw VPD.
+     */
+    int validateTheTypeOfKwVpd();
+
+    /**
+     * @brief Parsing keyword-value pairs and emplace into Map.
+     *
+     * @return map of keyword:value
+     */
+    openpower::vpd::inventory::KeywordVpdMap kwValParser();
+
+    /**
+     * @brief Validate small resource type end tag
+     */
+    void validateSmallResourceTypeEnd();
+
+    /**
+     * @brief Validate checksum.
+     *
+     * Finding the 2's complement of sum of all the
+     * keywords,values and large resource identifier string.
+     */
+    void validateChecksum();
+
+    /**
+     * @brief Validate small resource type last end tag
+     */
+    void validateSmallResourceTypeLastEnd();
+
+    /**
+     * @brief Get the size of the keyword
+     *
+     * @return one byte length size data
+     */
+    size_t getKwDataSize();
+
+    /**
+     * @brief Check for iterator Out of Bound exception
+     *
+     * Check if no.of elements from (begining of the vector) to (iterator +
+     * incVar) is lesser than or equal to the total no.of elements in the
+     * vector. This check is performed before the advancement of the iterator.
+     *
+     * @param[incVar] - no.of positions the iterator is going to be iterated
+     */
+    void itrOutOfBoundCheck(uint8_t incVar);
+};
+} // namespace parser
+} // namespace keyword
+} // namespace vpd
diff --git a/vpd-parser/memory_vpd_parser.cpp b/vpd-parser/memory_vpd_parser.cpp
new file mode 100644
index 0000000..6a4c608
--- /dev/null
+++ b/vpd-parser/memory_vpd_parser.cpp
@@ -0,0 +1,65 @@
+#include "memory_vpd_parser.hpp"
+
+#include <iostream>
+#include <numeric>
+#include <string>
+
+namespace openpower
+{
+namespace vpd
+{
+namespace memory
+{
+namespace parser
+{
+using namespace inventory;
+using namespace constants;
+using namespace std;
+using namespace openpower::vpd::parser;
+
+kwdVpdMap memoryVpdParser::readKeywords(Binary::const_iterator iterator)
+{
+    KeywordVpdMap map{};
+
+    vector<uint8_t> partNumber(iterator, iterator + PART_NUM_LEN);
+
+    advance(iterator, PART_NUM_LEN);
+    vector<uint8_t> serialNumber(iterator, iterator + SERIAL_NUM_LEN);
+
+    advance(iterator, SERIAL_NUM_LEN);
+    vector<uint8_t> ccin(iterator, iterator + CCIN_LEN);
+
+    map.emplace("PN", move(partNumber));
+    map.emplace("SN", move(serialNumber));
+    map.emplace("CC", move(ccin));
+
+    return map;
+}
+
+variant<kwdVpdMap, Store> memoryVpdParser::parse()
+{
+    // check if vpd file is empty
+    if (memVpd.empty())
+    {
+        throw runtime_error("VPD file is empty.");
+    }
+
+    // Read the data and return the map
+    auto iterator = memVpd.cbegin();
+    // point the iterator to DIMM data and skip "11S"
+    advance(iterator, MEMORY_VPD_DATA_START + 3);
+
+    auto vpdDataMap = readKeywords(iterator);
+
+    return vpdDataMap;
+}
+
+std::string memoryVpdParser::getInterfaceName() const
+{
+    return memVpdInf;
+}
+
+} // namespace parser
+} // namespace memory
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-parser/memory_vpd_parser.hpp b/vpd-parser/memory_vpd_parser.hpp
new file mode 100644
index 0000000..3d47144
--- /dev/null
+++ b/vpd-parser/memory_vpd_parser.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "impl.hpp"
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+namespace openpower
+{
+namespace vpd
+{
+namespace memory
+{
+namespace parser
+{
+using ParserInterface = openpower::vpd::parser::interface::ParserInterface;
+using kwdVpdMap = openpower::vpd::inventory::KeywordVpdMap;
+
+class memoryVpdParser : public ParserInterface
+{
+  public:
+    memoryVpdParser() = delete;
+    memoryVpdParser(const memoryVpdParser&) = delete;
+    memoryVpdParser& operator=(const memoryVpdParser&) = delete;
+    memoryVpdParser(memoryVpdParser&&) = delete;
+    memoryVpdParser& operator=(memoryVpdParser&&) = delete;
+    ~memoryVpdParser() = default;
+
+    /**
+     * @brief Constructor
+     *
+     * Move kwVpdVector to parser object's kwVpdVector
+     */
+    memoryVpdParser(Binary&& VpdVector) : memVpd(std::move(VpdVector))
+    {
+    }
+
+    /**
+     * @brief Parse the memory VPD binary data.
+     * Collects and emplace the keyword-value pairs in map.
+     *
+     * @return map of keyword:value
+     */
+    std::variant<kwdVpdMap, Store> parse();
+
+    /**
+     * @brief An api to return interface name with respect to
+     * publish data on cache
+     *
+     * @return - Interface name for that vpd type.
+     */
+    std::string getInterfaceName() const;
+
+  private:
+    /**
+     * @brief An api to read keywords.
+     *
+     * @return- map of kwd:value
+     */
+    kwdVpdMap readKeywords(Binary::const_iterator iterator);
+
+    // vdp file to be parsed
+    Binary memVpd;
+};
+} // namespace parser
+} // namespace memory
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-parser/parser_factory.cpp b/vpd-parser/parser_factory.cpp
new file mode 100644
index 0000000..e31d669
--- /dev/null
+++ b/vpd-parser/parser_factory.cpp
@@ -0,0 +1,59 @@
+#include "parser_factory.hpp"
+
+#include "ipz_parser.hpp"
+#include "keyword_vpd_parser.hpp"
+#include "memory_vpd_parser.hpp"
+#include "utils.hpp"
+
+using namespace vpd::keyword::parser;
+using namespace openpower::vpd::memory::parser;
+using namespace openpower::vpd::parser::interface;
+using namespace openpower::vpd::ipz::parser;
+
+namespace openpower
+{
+namespace vpd
+{
+namespace parser
+{
+namespace factory
+{
+interface::ParserInterface* ParserFactory::getParser(Binary&& vpdVector)
+{
+    vpdType type = vpdTypeCheck(vpdVector);
+
+    switch (type)
+    {
+        case IPZ_VPD:
+        {
+            return new IpzVpdParser(std::move(vpdVector));
+        }
+
+        case KEYWORD_VPD:
+        {
+            return new KeywordVpdParser(std::move(vpdVector));
+        }
+
+        case MEMORY_VPD:
+        {
+            return new memoryVpdParser(std::move(vpdVector));
+        }
+
+        default:
+            throw std::runtime_error("Invalid VPD format");
+    }
+}
+
+void ParserFactory::freeParser(interface::ParserInterface* parser)
+{
+    if (parser)
+    {
+        delete parser;
+        parser = nullptr;
+    }
+}
+
+} // namespace factory
+} // namespace parser
+} // namespace vpd
+} // namespace openpower
diff --git a/vpd-parser/parser_factory.hpp b/vpd-parser/parser_factory.hpp
new file mode 100644
index 0000000..c66e42b
--- /dev/null
+++ b/vpd-parser/parser_factory.hpp
@@ -0,0 +1,47 @@
+#pragma once
+#include "parser_interface.hpp"
+#include "types.hpp"
+
+namespace openpower
+{
+namespace vpd
+{
+namespace parser
+{
+namespace factory
+{
+/** @class ParserFactory
+ *  @brief Factory calss to instantiate concrete parser class.
+ *
+ *  This class should be used to instantiate an instance of parser class based
+ *  on the typeof vpd file.
+ */
+
+class ParserFactory
+{
+  public:
+    ParserFactory() = delete;
+    ~ParserFactory() = delete;
+    ParserFactory(const ParserFactory&) = delete;
+    ParserFactory& operator=(const ParserFactory&) = delete;
+    ParserFactory(ParserFactory&&) = delete;
+    ParserFactory& operator=(ParserFactory&&) = delete;
+
+    /**
+     * @brief A method to get object of concrete parser class.
+     * @param[in] - vpd file to check for the type.
+     * @return - Pointer to concrete parser class object.
+     */
+    static interface::ParserInterface* getParser(Binary&& vpdVector);
+
+    /**
+     * @brief A method to delete the parser object.
+     * @param[in] - Pointer to the parser object.
+     */
+    static void freeParser(interface::ParserInterface* parser);
+}; // ParserFactory
+
+} // namespace factory
+} // namespace parser
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-parser/parser_interface.hpp b/vpd-parser/parser_interface.hpp
new file mode 100644
index 0000000..2f11aff
--- /dev/null
+++ b/vpd-parser/parser_interface.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "store.hpp"
+#include "types.hpp"
+
+#include <variant>
+
+namespace openpower
+{
+namespace vpd
+{
+namespace parser
+{
+namespace interface
+{
+using kwdVpdMap = openpower::vpd::inventory::KeywordVpdMap;
+
+/** @class ParserInterface
+ *  @brief Interface class for vpd parsers.
+ *
+ *  Any concrete parser class, implementing the parser logic needs to
+ *  derive from this interface class and ovverride the methods declared
+ *  in this class.
+ */
+class ParserInterface
+{
+  public:
+    /**
+     * @brief An api to implement parsing logic for VPD file.
+     * Needs to be implemented by all the class deriving from
+     * parser inerface.
+     *
+     * @return parsed format for vpd data, depending upon the
+     * parsing logic.
+     */
+    virtual std::variant<kwdVpdMap, Store> parse() = 0;
+
+    /**
+     * @brief An api to return interface name which will hold the
+     * data on cache.
+     * Needs to be implemented by all the class deriving fronm
+     * parser inerface
+     *
+     * @return - Interface name for that vpd type.
+     */
+    virtual std::string getInterfaceName() const = 0;
+    // virtual void test() = 0;
+
+    /**
+     * @brief Virtual destructor for the interface.
+     */
+    virtual ~ParserInterface()
+    {
+    }
+
+}; // class Parserinterface
+} // namespace interface
+} // namespace parser
+} // namespace vpd
+} // namespace openpower