buffer: Implement "updateReadPtr"

This is a helper function for reading the circular buffer

Tested: Unit tested

Signed-off-by: Brandon Kim <brandonkim@google.com>
Change-Id: I1c34a40bd279ee601de33669aabfec27a959ec44
diff --git a/include/buffer.hpp b/include/buffer.hpp
index 08d400d..6c42401 100644
--- a/include/buffer.hpp
+++ b/include/buffer.hpp
@@ -80,6 +80,12 @@
      * @return cached CircularBufferHeader
      */
     virtual struct CircularBufferHeader getCachedBufferHeader() const = 0;
+
+    /**
+     * Write to the bufferHeader and update the read pointer
+     * @param[in] newReadPtr - read pointer to update to
+     */
+    virtual void updateReadPtr(const uint32_t newReadPtr) = 0;
 };
 
 /**
@@ -97,6 +103,7 @@
                     const std::array<uint32_t, 4>& magicNumber) override;
     void readBufferHeader() override;
     struct CircularBufferHeader getCachedBufferHeader() const override;
+    void updateReadPtr(const uint32_t newReadPtr) override;
 
   private:
     std::unique_ptr<DataInterface> dataInterface;
diff --git a/src/buffer.cpp b/src/buffer.cpp
index 524984a..c07bba6 100644
--- a/src/buffer.cpp
+++ b/src/buffer.cpp
@@ -9,6 +9,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cstddef>
 #include <cstdint>
 #include <memory>
 #include <span>
@@ -85,4 +86,27 @@
     return cachedBufferHeader;
 }
 
+void BufferImpl::updateReadPtr(const uint32_t newReadPtr)
+{
+    constexpr uint8_t bmcReadPtrOffset =
+        offsetof(struct CircularBufferHeader, bmcReadPtr);
+
+    little_uint16_t truncatedReadPtr =
+        boost::endian::native_to_little(newReadPtr & 0xffff);
+    uint8_t* truncatedReadPtrPtr =
+        reinterpret_cast<uint8_t*>(&truncatedReadPtr);
+
+    size_t writtenSize = dataInterface->write(
+        bmcReadPtrOffset, std::span<const uint8_t>{
+                              truncatedReadPtrPtr,
+                              truncatedReadPtrPtr + sizeof(little_uint16_t)});
+    if (writtenSize != sizeof(little_uint16_t))
+    {
+        throw std::runtime_error(fmt::format(
+            "[updateReadPtr] Wrote '{}' bytes, instead of expected '{}'",
+            writtenSize, sizeof(little_uint16_t)));
+    }
+    cachedBufferHeader.bmcReadPtr = truncatedReadPtr;
+}
+
 } // namespace bios_bmc_smm_error_logger
diff --git a/test/buffer_test.cpp b/test/buffer_test.cpp
index e8943a9..4307825 100644
--- a/test/buffer_test.cpp
+++ b/test/buffer_test.cpp
@@ -152,5 +152,37 @@
     EXPECT_EQ(bufferImpl->getCachedBufferHeader(), testInitializationHeader);
 }
 
+TEST_F(BufferTest, BufferUpdateReadPtrFail)
+{
+    // Return write size that is not 2 which is sizeof(little_uint16_t)
+    constexpr size_t wrongWriteSize = 1;
+    EXPECT_CALL(*dataInterfaceMockPtr, write(_, _))
+        .WillOnce(Return(wrongWriteSize));
+    EXPECT_THROW(
+        try {
+            bufferImpl->updateReadPtr(0);
+        } catch (const std::runtime_error& e) {
+            EXPECT_STREQ(
+                e.what(),
+                "[updateReadPtr] Wrote '1' bytes, instead of expected '2'");
+            throw;
+        },
+        std::runtime_error);
+}
+
+TEST_F(BufferTest, BufferUpdateReadPtrPass)
+{
+    constexpr size_t expectedWriteSize = 2;
+    constexpr uint8_t expectedBmcReadPtrOffset = 0x20;
+    // Check that we truncate the highest 16bits
+    const uint32_t testNewReadPtr = 0x99881234;
+    const std::vector<uint8_t> expectedReadPtr{0x34, 0x12};
+
+    EXPECT_CALL(*dataInterfaceMockPtr, write(expectedBmcReadPtrOffset,
+                                             ElementsAreArray(expectedReadPtr)))
+        .WillOnce(Return(expectedWriteSize));
+    EXPECT_NO_THROW(bufferImpl->updateReadPtr(testNewReadPtr));
+}
+
 } // namespace
 } // namespace bios_bmc_smm_error_logger