Add dictionary class to hold resource dictionaries

Google-Bug-Id: 322198971
Change-Id: I4c931ed1317d5674a330f400b706af9281229b20
Signed-off-by: Harsh Tyagi <harshtya@google.com>
diff --git a/meson.build b/meson.build
index ce77f2a..1505dcd 100644
--- a/meson.build
+++ b/meson.build
@@ -13,6 +13,7 @@
   'util/matcher/rde_match_handler.cpp',
   'util/state_machine/discovery/base/base_disc_state_machine.cpp',
   'util/state_machine/discovery/rde/rde_disc_state_machine.cpp',
+  'util/state_machine/discovery/rde/dictionary.cpp',
 ]
 
 all_sources = ['rded.cpp'] + lib_sources
diff --git a/tests/discovery/rde/dictionary_test.cpp b/tests/discovery/rde/dictionary_test.cpp
new file mode 100644
index 0000000..824169d
--- /dev/null
+++ b/tests/discovery/rde/dictionary_test.cpp
@@ -0,0 +1,80 @@
+#include "util/state_machine/discovery/rde/dictionary.hpp"
+
+#include <stdplus/print.hpp>
+
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::Return;
+
+class DictionaryTest : public ::testing::Test
+{
+  protected:
+    uint32_t resourceIdTest = 0x00000;
+    std::unique_ptr<Dictionary> dictionary =
+        std::make_unique<Dictionary>(resourceIdTest);
+};
+
+TEST_F(DictionaryTest, GetDictionaryBytesSuccess)
+{
+    std::vector<uint8_t> payloadPart1 = {0x01, 0x02, 0x03};
+    std::vector<uint8_t> payloadPart2 = {0x04, 0x05};
+    std::vector<uint8_t> payloadPart3 = {0x06, 0x07, 0x08, 0x09};
+
+    std::vector<uint8_t> expectedDictionary = {0x01, 0x02, 0x03, 0x04, 0x05,
+                                               0x06, 0x07, 0x08, 0x09};
+    OperationStatus status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadPart1.data(), payloadPart1.size()),
+        false);
+    EXPECT_EQ(OperationStatus::Success, status);
+
+    status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadPart2.data(), payloadPart2.size()),
+        false);
+    EXPECT_EQ(OperationStatus::Success, status);
+
+    dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadPart3.data(), payloadPart3.size()),
+        false);
+    EXPECT_EQ(OperationStatus::Success, status);
+
+    std::span<const uint8_t> resultDictionary =
+        dictionary->getDictionaryBytes();
+    EXPECT_EQ(expectedDictionary, std::vector<uint8_t>(resultDictionary.begin(),
+                                                       resultDictionary.end()));
+}
+
+TEST_F(DictionaryTest, ChecksumFailure)
+{
+    std::vector<uint8_t> payloadPart1 = {0x01, 0x02, 0x03};
+    std::vector<uint8_t> payloadPart2 = {0x04, 0x05, 0x04, 0x05};
+
+    OperationStatus status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadPart1.data(), payloadPart1.size()),
+        false);
+    EXPECT_EQ(OperationStatus::Success, status);
+
+    status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadPart2.data(), payloadPart2.size()),
+        true);
+    EXPECT_EQ(OperationStatus::ChecksumFailure, status);
+}
+
+TEST_F(DictionaryTest, ChecksumSuccess)
+{
+    std::vector<uint8_t> payload = {0x01, 0x02, 0x03, 0x04, 0x05};
+    std::vector<uint8_t> payloadWithChecksum = {0xf4, 0x99, 0x0b, 0x47};
+
+    OperationStatus status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payload.data(), payload.size()), false);
+    EXPECT_EQ(OperationStatus::Success, status);
+
+    status = dictionary->addToDictionaryBytes(
+        std::span<const uint8_t>(payloadWithChecksum.data(),
+                                 payloadWithChecksum.size()),
+        true);
+    EXPECT_EQ(OperationStatus::Success, status);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 969f642..2686806 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -22,7 +22,8 @@
 tests = [
     'mctp_setup_test',
     'discovery/base/base_disc_state_machine_test',
-    'discovery/rde/rde_disc_state_machine_test'
+    'discovery/rde/rde_disc_state_machine_test',
+    'discovery/rde/dictionary_test',
 ]
 
 foreach t : tests
diff --git a/util/state_machine/discovery/rde/dictionary.cpp b/util/state_machine/discovery/rde/dictionary.cpp
new file mode 100644
index 0000000..809f4dc
--- /dev/null
+++ b/util/state_machine/discovery/rde/dictionary.cpp
@@ -0,0 +1,119 @@
+#include "dictionary.hpp"
+
+#include "libpldm/utils.h"
+
+#include <stdplus/print.hpp>
+
+Dictionary::Dictionary(uint32_t resourceId) : resourceId(resourceId)
+{}
+
+std::span<const uint8_t> Dictionary::getDictionaryBytes()
+{
+    return dictionary;
+}
+
+OperationStatus
+    Dictionary::addToDictionaryBytes(std::span<const uint8_t> payload,
+                                     bool hasChecksum)
+{
+    this->dictionary.insert(this->dictionary.end(), payload.begin(),
+                            payload.end());
+
+    if (hasChecksum)
+    {
+        OperationStatus checksumVerification = verifyChecksum();
+
+        // remove the checksum bytes from the dictionary
+        this->dictionary.erase(this->dictionary.begin() +
+                                   (this->dictionary.size() - 4),
+                               this->dictionary.end());
+
+        if (checksumVerification == OperationStatus::ChecksumFailure)
+        {
+            return checksumVerification;
+        }
+    }
+    return OperationStatus::Success;
+}
+
+OperationStatus Dictionary::verifyChecksum()
+{
+    uint32_t payloadLength = this->dictionary.size();
+    auto calculatedChecksum =
+        crc32(&(this->dictionary.front()), payloadLength - 4);
+    uint8_t byte0 = this->dictionary[payloadLength - 4];
+    uint8_t byte1 = this->dictionary[payloadLength - 3];
+    uint8_t byte2 = this->dictionary[payloadLength - 2];
+    uint8_t byte3 = this->dictionary[payloadLength - 1];
+
+    uint32_t receivedChecksum =
+        (byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24));
+
+    if (calculatedChecksum == receivedChecksum)
+    {
+        if (DEBUG)
+        {
+            stdplus::println(stderr,
+                             "Successfully verified checksum in "
+                             "dictionary extraction for resource id: {}"
+                             " with calculated checksum: {} and "
+                             "received checksum: {}",
+                             static_cast<uint32_t>(resourceId),
+                             static_cast<uint32_t>(calculatedChecksum),
+                             static_cast<uint32_t>(receivedChecksum));
+        }
+        return OperationStatus::Success;
+    }
+    stdplus::println(stderr,
+                     "Failed to verify checksum in "
+                     "dictionary extraction for resource id: {}"
+                     " with calculated checksum: {} and "
+                     "received checksum: {}",
+                     static_cast<uint32_t>(resourceId),
+                     static_cast<uint32_t>(calculatedChecksum),
+                     static_cast<uint32_t>(receivedChecksum));
+    return OperationStatus::ChecksumFailure;
+}
+
+// Getters and Setters
+
+uint32_t Dictionary::getResourceId() const
+{
+    return this->resourceId;
+}
+
+// Getter for currentSchemaClass
+uint8_t Dictionary::getCurrentSchemaClass() const
+{
+    return this->currentSchemaClass;
+}
+
+// Setter for currentSchemaClass
+void Dictionary::setCurrentSchemaClass(uint8_t newSchemaClass)
+{
+    this->currentSchemaClass = newSchemaClass;
+}
+
+// Getter for currentTransferHandle
+uint32_t Dictionary::getCurrentTransferHandle() const
+{
+    return this->currentTransferHandle;
+}
+
+// Setter for currentTransferHandle
+void Dictionary::setCurrentTransferHandle(uint32_t newTransferHandle)
+{
+    this->currentTransferHandle = newTransferHandle;
+}
+
+// Getter for currentTransferOperation
+uint8_t Dictionary::getCurrentTransferOperation() const
+{
+    return this->currentTransferOperation;
+}
+
+// Setter for currentTransferOperation
+void Dictionary::setCurrentTransferOperation(uint8_t newTransferOperation)
+{
+    this->currentTransferOperation = newTransferOperation;
+}
diff --git a/util/state_machine/discovery/rde/dictionary.hpp b/util/state_machine/discovery/rde/dictionary.hpp
new file mode 100644
index 0000000..13b248c
--- /dev/null
+++ b/util/state_machine/discovery/rde/dictionary.hpp
@@ -0,0 +1,51 @@
+#include "common.hpp"
+#include "state_machine_factory.hpp"
+
+#include <cstdint>
+#include <optional>
+#include <span>
+#include <vector>
+
+class Dictionary
+{
+  public:
+    Dictionary(uint32_t resourceId);
+
+    /**
+     * @brief Adds bytes to the end of the vector for the dictionary
+     *
+     * @param[in] payload - Bytes to be added
+     * @param[in] payloadLength - Size of the bytes to be added
+     * @param[in] hasChecksum - Contains checksum or not (last 4 bytes are
+     * checksum if it is true)
+     *
+     * @return OperationStatus
+     */
+    OperationStatus addToDictionaryBytes(std::span<const uint8_t> payload,
+                                         bool hasChecksum);
+
+    // Getters and Setters
+    uint32_t getResourceId() const;
+    std::span<const uint8_t> getDictionaryBytes();
+
+    uint8_t getCurrentSchemaClass() const;
+    void setCurrentSchemaClass(uint8_t newSchemaClass);
+
+    uint32_t getCurrentTransferHandle() const;
+    void setCurrentTransferHandle(uint32_t newTransferHandle);
+
+    uint8_t getCurrentTransferOperation() const;
+    void setCurrentTransferOperation(uint8_t newTransferOperation);
+
+  private:
+    uint32_t resourceId;
+    uint8_t currentSchemaClass;
+    uint32_t currentTransferHandle;
+    uint8_t currentTransferOperation;
+    std::vector<uint8_t> dictionary;
+
+    /**
+     * @brief Verifies checksum of the dictionary
+     */
+    OperationStatus verifyChecksum();
+};
diff --git a/util/state_machine/state_machine_factory.hpp b/util/state_machine/state_machine_factory.hpp
index d00a065..a6e5f77 100644
--- a/util/state_machine/state_machine_factory.hpp
+++ b/util/state_machine/state_machine_factory.hpp
@@ -33,6 +33,7 @@
     OperationFailure,
     StateMachineInitializationError,
     PartialFailure,
+    ChecksumFailure,
 };
 
 /**