bios: Implement BIOSStringAttribute

Implement BIOSStringAttribute, most of the code is copied from
bios/bios_parser.cpp.

Implement SetAttrValueOnDbus and constructEntry for string attribute

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Ic7c6b35d32738d698d7649f97cb7843606b8a2ba
diff --git a/libpldmresponder/bios_string_attribute.cpp b/libpldmresponder/bios_string_attribute.cpp
new file mode 100644
index 0000000..1d9ec5d
--- /dev/null
+++ b/libpldmresponder/bios_string_attribute.cpp
@@ -0,0 +1,115 @@
+#include "bios_string_attribute.hpp"
+
+#include "utils.hpp"
+
+#include <iostream>
+#include <tuple>
+#include <variant>
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+BIOSStringAttribute::BIOSStringAttribute(const Json& entry,
+                                         DBusHandler* const dbusHandler) :
+    BIOSAttribute(entry, dbusHandler)
+{
+    std::string strTypeTmp = entry.at("string_type");
+    auto iter = strTypeMap.find(strTypeTmp);
+    if (iter == strTypeMap.end())
+    {
+        std::cerr << "Wrong string type, STRING_TYPE=" << strTypeTmp
+                  << " ATTRIBUTE_NAME=" << name << "\n";
+        throw std::invalid_argument("Wrong string type");
+    }
+    stringInfo.stringType = static_cast<uint8_t>(iter->second);
+
+    stringInfo.minLength = entry.at("minimum_string_length");
+    stringInfo.maxLength = entry.at("maximum_string_length");
+    stringInfo.defLength = entry.at("default_string_length");
+    stringInfo.defString = entry.at("default_string");
+
+    pldm_bios_table_attr_entry_string_info info = {
+        0,
+        readOnly,
+        stringInfo.stringType,
+        stringInfo.minLength,
+        stringInfo.maxLength,
+        stringInfo.defLength,
+        stringInfo.defString.data(),
+    };
+
+    const char* errmsg;
+    auto rc = pldm_bios_table_attr_entry_string_info_check(&info, &errmsg);
+    if (rc != PLDM_SUCCESS)
+    {
+        std::cerr << "Wrong field for string attribute, ATTRIBUTE_NAME=" << name
+                  << " ERRMSG=" << errmsg
+                  << " MINIMUM_STRING_LENGTH=" << stringInfo.minLength
+                  << " MAXIMUM_STRING_LENGTH=" << stringInfo.maxLength
+                  << " DEFAULT_STRING_LENGTH=" << stringInfo.defLength
+                  << " DEFAULT_STRING=" << stringInfo.defString << "\n";
+        throw std::invalid_argument("Wrong field for string attribute");
+    }
+}
+
+void BIOSStringAttribute::setAttrValueOnDbus(
+    const pldm_bios_attr_val_table_entry* attrValueEntry,
+    const pldm_bios_attr_table_entry*, const BIOSStringTable&)
+{
+    if (readOnly)
+    {
+        return;
+    }
+
+    PropertyValue value =
+        table::attribute_value::decodeStringEntry(attrValueEntry);
+    dbusHandler->setDbusProperty(*dBusMap, value);
+}
+
+std::string BIOSStringAttribute::getAttrValue()
+{
+    if (readOnly)
+    {
+        return stringInfo.defString;
+    }
+    try
+    {
+        return dbusHandler->getDbusProperty<std::string>(
+            dBusMap->objectPath.c_str(), dBusMap->propertyName.c_str(),
+            dBusMap->interface.c_str());
+    }
+    catch (const std::exception& e)
+    {
+        std::cerr << "Get String Attribute Value Error: AttributeName = "
+                  << name << std::endl;
+        return stringInfo.defString;
+    }
+}
+
+void BIOSStringAttribute::constructEntry(const BIOSStringTable& stringTable,
+                                         Table& attrTable,
+                                         Table& attrValueTable)
+{
+    pldm_bios_table_attr_entry_string_info info = {
+        stringTable.findHandle(name), readOnly,
+        stringInfo.stringType,        stringInfo.minLength,
+        stringInfo.maxLength,         stringInfo.defLength,
+        stringInfo.defString.data(),
+    };
+
+    auto attrTableEntry =
+        table::attribute::constructStringEntry(attrTable, &info);
+    auto [attrHandle, attrType, _] =
+        table::attribute::decodeHeader(attrTableEntry);
+    auto currStr = getAttrValue();
+    table::attribute_value::constructStringEntry(attrValueTable, attrHandle,
+                                                 attrType, currStr);
+}
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios_string_attribute.hpp b/libpldmresponder/bios_string_attribute.hpp
new file mode 100644
index 0000000..48b0d2d
--- /dev/null
+++ b/libpldmresponder/bios_string_attribute.hpp
@@ -0,0 +1,83 @@
+#pragma once
+#include "bios_attribute.hpp"
+
+#include <string>
+
+class TestBIOSStringAttribute;
+
+namespace pldm
+{
+namespace responder
+{
+namespace bios
+{
+
+/** @class BIOSStringAttribute
+ *  @brief Associate string entry(attr table and attribute value table) and dbus
+ *         attribute
+ */
+class BIOSStringAttribute : public BIOSAttribute
+{
+  public:
+    friend class ::TestBIOSStringAttribute;
+
+    /** @brief BIOS string types */
+    enum class Encoding : uint8_t
+    {
+        UNKNOWN = 0x00,
+        ASCII = 0x01,
+        HEX = 0x02,
+        UTF_8 = 0x03,
+        UTF_16LE = 0x04,
+        UTF_16BE = 0x05,
+        VENDOR_SPECIFIC = 0xFF
+    };
+
+    /** brief Mapping of string to enum for string type */
+    inline static const std::map<std::string, Encoding> strTypeMap{
+        {"Unknown", Encoding::UNKNOWN},
+        {"ASCII", Encoding::ASCII},
+        {"Hex", Encoding::HEX},
+        {"UTF-8", Encoding::UTF_8},
+        {"UTF-16LE", Encoding::UTF_16LE},
+        {"UTF-16LE", Encoding::UTF_16LE},
+        {"Vendor Specific", Encoding::VENDOR_SPECIFIC}};
+
+    /** @brief Construct a bios string attribute
+     *  @param[in] entry - Json Object
+     *  @param[in] dbusHandler - Dbus Handler
+     */
+    BIOSStringAttribute(const Json& entry, DBusHandler* const dbusHandler);
+
+    /** @brief Set Attribute value On Dbus according to the attribute value
+     *         entry
+     *  @param[in] attrValueEntry - The attribute value entry
+     *  @param[in] attrEntry - The attribute entry corresponding to the
+     *                         attribute value entry
+     *  @param[in] stringTable - The string table
+     */
+    void
+        setAttrValueOnDbus(const pldm_bios_attr_val_table_entry* attrValueEntry,
+                           const pldm_bios_attr_table_entry* attrEntry,
+                           const BIOSStringTable& stringTable) override;
+
+    /** @brief Construct corresponding entries at the end of the attribute table
+     *         and attribute value tables
+     *  @param[in] stringTable - The string Table
+     *  @param[in,out] attrTable - The attribute table
+     *  @param[in,out] attrValueTable - The attribute value table
+     */
+    void constructEntry(const BIOSStringTable& stringTable, Table& attrTable,
+                        Table& attrValueTable) override;
+
+  private:
+    /** @brief string field from json */
+    table::attribute::StringField stringInfo;
+
+    /** @brief Get attribute value on dbus */
+    std::string getAttrValue();
+};
+
+} // namespace bios
+} // namespace responder
+} // namespace pldm
diff --git a/libpldmresponder/bios_table.cpp b/libpldmresponder/bios_table.cpp
index 5fd6ece..e68884b 100644
--- a/libpldmresponder/bios_table.cpp
+++ b/libpldmresponder/bios_table.cpp
@@ -64,7 +64,7 @@
     {
         throw std::invalid_argument("Invalid String Handle");
     }
-    return decodeString(stringEntry);
+    return table::string::decodeString(stringEntry);
 }
 
 uint16_t BIOSStringTable::findHandle(const std::string& name) const
@@ -76,17 +76,21 @@
         throw std::invalid_argument("Invalid String Name");
     }
 
-    return decodeHandle(stringEntry);
+    return table::string::decodeHandle(stringEntry);
 }
 
-uint16_t
-    BIOSStringTable::decodeHandle(const pldm_bios_string_table_entry* entry)
+namespace table
+{
+
+namespace string
+{
+
+uint16_t decodeHandle(const pldm_bios_string_table_entry* entry)
 {
     return pldm_bios_table_string_entry_decode_handle(entry);
 }
 
-std::string
-    BIOSStringTable::decodeString(const pldm_bios_string_table_entry* entry)
+std::string decodeString(const pldm_bios_string_table_entry* entry)
 {
     auto strLength = pldm_bios_table_string_entry_decode_string_length(entry);
     std::vector<char> buffer(strLength + 1 /* sizeof '\0' */);
@@ -95,6 +99,91 @@
     return std::string(buffer.data(), buffer.data() + strLength);
 }
 
+} // namespace string
+
+namespace attribute
+{
+
+TableHeader decodeHeader(const pldm_bios_attr_table_entry* entry)
+{
+    auto attrHandle = pldm_bios_table_attr_entry_decode_attribute_handle(entry);
+    auto attrType = pldm_bios_table_attr_entry_decode_attribute_type(entry);
+    auto stringHandle = pldm_bios_table_attr_entry_decode_string_handle(entry);
+    return {attrHandle, attrType, stringHandle};
+}
+
+const pldm_bios_attr_table_entry*
+    constructStringEntry(Table& table,
+                         pldm_bios_table_attr_entry_string_info* info)
+{
+    auto entryLength =
+        pldm_bios_table_attr_entry_string_encode_length(info->def_length);
+
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength, 0);
+    pldm_bios_table_attr_entry_string_encode(table.data() + tableSize,
+                                             entryLength, info);
+    return reinterpret_cast<pldm_bios_attr_table_entry*>(table.data() +
+                                                         tableSize);
+}
+
+StringField decodeStringEntry(const pldm_bios_attr_table_entry* entry)
+{
+    auto strType = pldm_bios_table_attr_entry_string_decode_string_type(entry);
+    auto minLength = pldm_bios_table_attr_entry_string_decode_min_length(entry);
+    auto maxLength = pldm_bios_table_attr_entry_string_decode_max_length(entry);
+    auto defLength =
+        pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
+
+    std::vector<char> buffer(defLength + 1);
+    pldm_bios_table_attr_entry_string_decode_def_string(entry, buffer.data(),
+                                                        buffer.size());
+    return {strType, minLength, maxLength, defLength,
+            std::string(buffer.data(), buffer.data() + defLength)};
+}
+
+} // namespace attribute
+
+namespace attribute_value
+{
+
+TableHeader decodeHeader(const pldm_bios_attr_val_table_entry* entry)
+{
+    auto handle =
+        pldm_bios_table_attr_value_entry_decode_attribute_handle(entry);
+    auto type = pldm_bios_table_attr_value_entry_decode_attribute_type(entry);
+    return {handle, type};
+}
+
+std::string decodeStringEntry(const pldm_bios_attr_val_table_entry* entry)
+{
+    variable_field currentString{};
+    pldm_bios_table_attr_value_entry_string_decode_string(entry,
+                                                          &currentString);
+    return std::string(currentString.ptr,
+                       currentString.ptr + currentString.length);
+}
+
+const pldm_bios_attr_val_table_entry*
+    constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                         const std::string& str)
+{
+    auto strLen = str.size();
+    auto entryLength =
+        pldm_bios_table_attr_value_entry_encode_string_length(strLen);
+    auto tableSize = table.size();
+    table.resize(tableSize + entryLength);
+    pldm_bios_table_attr_value_entry_encode_string(
+        table.data() + tableSize, entryLength, attrHandle, attrType, strLen,
+        str.c_str());
+    return reinterpret_cast<pldm_bios_attr_val_table_entry*>(table.data() +
+                                                             tableSize);
+}
+
+} // namespace attribute_value
+
+} // namespace table
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/bios_table.hpp b/libpldmresponder/bios_table.hpp
index 7b28d49..d1dc7c9 100644
--- a/libpldmresponder/bios_table.hpp
+++ b/libpldmresponder/bios_table.hpp
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "libpldm/bios.h"
+#include "libpldm/bios_table.h"
 
 namespace pldm
 {
@@ -129,22 +130,119 @@
      */
     uint16_t findHandle(const std::string& name) const override;
 
-    /** @brief Get the string handle for the entry
-     *  @param[in] entry - Pointer to a bios string table entry
-     *  @return Handle to identify a string in the bios string table
-     */
-    static uint16_t decodeHandle(const pldm_bios_string_table_entry* entry);
-
-    /** @brief Get the string from the entry
-     *  @param[in] entry - Pointer to a bios string table entry
-     *  @return The String
-     */
-    static std::string decodeString(const pldm_bios_string_table_entry* entry);
-
   private:
     Table stringTable;
 };
 
+namespace table
+{
+
+namespace string
+{
+
+/** @brief Get the string handle for the entry
+ *  @param[in] entry - Pointer to a bios string table entry
+ *  @return Handle to identify a string in the bios string table
+ */
+uint16_t decodeHandle(const pldm_bios_string_table_entry* entry);
+
+/** @brief Get the string from the entry
+ *  @param[in] entry - Pointer to a bios string table entry
+ *  @return The String
+ */
+std::string decodeString(const pldm_bios_string_table_entry* entry);
+
+} // namespace string
+
+namespace attribute
+{
+
+/** @struct TableHeader
+ *  @brief Header of attribute table
+ */
+struct TableHeader
+{
+    uint16_t attrHandle;
+    uint8_t attrType;
+    uint16_t stringHandle;
+};
+
+/** @brief Decode header of attribute table entry
+ *  @param[in] entry - Pointer to an attribute table entry
+ *  @return Attribute table header
+ */
+TableHeader decodeHeader(const pldm_bios_attr_table_entry* entry);
+
+/** @struct StringField
+ *  @brief String field of attribute table
+ */
+struct StringField
+{
+    uint8_t stringType;
+    uint16_t minLength;
+    uint16_t maxLength;
+    uint16_t defLength;
+    std::string defString;
+};
+
+/** @brief decode string entry of attribute table
+ *  @param[in] entry - Pointer to an attribute table entry
+ *  @return String field of the entry
+ */
+StringField decodeStringEntry(const pldm_bios_attr_table_entry* entry);
+
+/** @brief construct string entry of attribute table at the end of the given
+ *         table
+ *  @param[in,out] table - The given table
+ *  @param[in] info - string info
+ *  @return pointer to the constructed entry
+ */
+const pldm_bios_attr_table_entry*
+    constructStringEntry(Table& table,
+                         pldm_bios_table_attr_entry_string_info* info);
+
+} // namespace attribute
+
+namespace attribute_value
+{
+
+/** @struct TableHeader
+ *  @brief Header of attribute value table
+ */
+struct TableHeader
+{
+    uint16_t attrHandle;
+    uint8_t attrType;
+};
+
+/** @brief Decode header of attribute value table
+ *  @param[in] entry - Pointer to an attribute value table entry
+ *  @return Attribute value table header
+ */
+TableHeader decodeHeader(const pldm_bios_attr_val_table_entry* entry);
+
+/** @brief Decode string entry of attribute value table
+ *  @param[in] entry - Pointer to an attribute value table entry
+ *  @return The decoded string
+ */
+std::string decodeStringEntry(const pldm_bios_attr_val_table_entry* entry);
+
+/** @brief Construct string entry of attribute value table at the end of the
+ *         given table
+ *  @param[in] table - The given table
+ *  @param[in] attrHandle - attribute handle
+ *  @param[in] attrType - attribute type
+ *  @param[in] str - The string
+ *  @return Pointer to the constructed entry
+ */
+const pldm_bios_attr_val_table_entry*
+    constructStringEntry(Table& table, uint16_t attrHandle, uint8_t attrType,
+                         const std::string& str);
+
+} // namespace attribute_value
+
+} // namespace table
+
 } // namespace bios
 } // namespace responder
 } // namespace pldm
diff --git a/libpldmresponder/meson.build b/libpldmresponder/meson.build
index 2797f51..372cae0 100644
--- a/libpldmresponder/meson.build
+++ b/libpldmresponder/meson.build
@@ -11,6 +11,7 @@
   'bios_table.cpp',
   'bios_parser.cpp',
   'bios_attribute.cpp',
+  'bios_string_attribute.cpp',
   'pdr_utils.cpp',
   'pdr.cpp',
   'platform.cpp',