PEL: Add class to wrap AdditionalData

The AdditionalData property on the xyz.openbmc_project.Logging.Entry
interface is a vector of strings of the form:  "KEY=VALUE".  The
PEL processing code will be interested in those keys and values, and
this class adds a way to get at those values based on a key without
having to do string parsing each time.  It returns an
std::optional<std::string> value, and if the key isn't found, then the
std::optional value will be empty.

For Example:
    AdditionalData ad{additionalDataPropertyValue};

    // Get the value for the FOO key
    std::optional<std::string> val = ad.getValue("FOO");

    if (val)
        std::cout << (*val).size();

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: I6ba458840278784b1cc6a0ed88a7fece8794df7d
diff --git a/extensions/openpower-pels/additional_data.hpp b/extensions/openpower-pels/additional_data.hpp
new file mode 100644
index 0000000..5cea69b
--- /dev/null
+++ b/extensions/openpower-pels/additional_data.hpp
@@ -0,0 +1,77 @@
+#pragma once
+#include <map>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace openpower
+{
+namespace pels
+{
+
+/**
+ * @class AdditionalData
+ *
+ * This class takes in the contents of the AdditionalData OpenBMC
+ * event log property, and provides access to its values based on
+ * their keys.
+ *
+ * The property is a vector of strings of the form: "KEY=VALUE",
+ * and this class provides a getValue("KEY") API that would return
+ * "VALUE".
+ */
+class AdditionalData
+{
+  public:
+    AdditionalData() = delete;
+    ~AdditionalData() = default;
+    AdditionalData(const AdditionalData&) = default;
+    AdditionalData& operator=(const AdditionalData&) = default;
+    AdditionalData(AdditionalData&&) = default;
+    AdditionalData& operator=(AdditionalData&&) = default;
+
+    /**
+     * @brief constructor
+     *
+     * @param[in] ad - the AdditionalData property vector with
+     *                 entries of "KEY=VALUE"
+     */
+    AdditionalData(const std::vector<std::string>& ad)
+    {
+        for (auto& item : ad)
+        {
+            auto pos = item.find_first_of('=');
+            if (pos == std::string::npos || pos == 0)
+            {
+                continue;
+            }
+
+            _data[item.substr(0, pos)] = std::move(item.substr(pos + 1));
+        }
+    }
+
+    /**
+     * @brief Returns the value of the AdditionalData item for the
+     *        key passed in.
+     * @param[in] key - the key to search for
+     *
+     * @return optional<string> - the value, if found
+     */
+    std::optional<std::string> getValue(const std::string& key)
+    {
+        auto entry = _data.find(key);
+        if (entry != _data.end())
+        {
+            return entry->second;
+        }
+        return std::nullopt;
+    }
+
+  private:
+    /**
+     * @brief a map of keys to values
+     */
+    std::map<std::string, std::string> _data;
+};
+} // namespace pels
+} // namespace openpower