Add sequential memcpy helper object
Tested: Unit tests
Google-Bug-Id: 322561358
Change-Id: I980f82637e5f8b84daa29eb2b3b9bd4c6ad75594
Signed-off-by: Aryk Ledet <arykledet@google.com>
diff --git a/include/cper_encoder.hpp b/include/cper_encoder.hpp
index d1b5ae5..20df915 100644
--- a/include/cper_encoder.hpp
+++ b/include/cper_encoder.hpp
@@ -3,9 +3,12 @@
#include "cper.hpp"
#include <chrono>
+#include <concepts>
namespace uefi::cper
{
+template <typename T>
+concept ByteLike = std::is_trivially_copyable_v<T> && sizeof(T) == 1;
/**
* @brief Helper function to create a UTC UEFI CPER timestamp from a
@@ -49,4 +52,47 @@
memcpy(&ret, ×tamp, sizeof(ret));
return ret;
}
+
+template <ByteLike ByteType>
+class SeqMemcopy
+{
+ public:
+ // Prevent the class from outliving the baseDest pointer.
+ SeqMemcopy() = delete;
+ SeqMemcopy(const SeqMemcopy&) = delete;
+ SeqMemcopy& operator=(const SeqMemcopy&) = delete;
+
+ /**
+ * @brief Manages sequentially copying chunks of data to a contiguous memory
+ * block.
+ *
+ * @param[in] baseDest - The memory location to copy to.
+ */
+ SeqMemcopy(std::span<ByteType> baseDest) : baseDest_(baseDest), offset_(0)
+ {}
+
+ /**
+ * @brief Copies the src to the next section in the dest address.
+ *
+ * @param[in] src - The source address to copy from.
+ * @param[in] srcSize - The source size.
+ *
+ * @throws out_of_range If source size goes out of the destinations bounds.
+ */
+ void copy(const void* src, const uint64_t srcSize)
+ {
+ if ((offset_ + srcSize) > baseDest_.size_bytes())
+ {
+ throw std::out_of_range(
+ "Attempt to copy out of the destinations bounds");
+ }
+
+ memcpy(baseDest_.data() + offset_, src, srcSize);
+ offset_ += srcSize;
+ }
+
+ private:
+ std::span<ByteType> baseDest_;
+ uint64_t offset_;
+};
} // namespace uefi::cper
diff --git a/test/cper_encoder_test.cpp b/test/cper_encoder_test.cpp
index 2903936..c1e756f 100644
--- a/test/cper_encoder_test.cpp
+++ b/test/cper_encoder_test.cpp
@@ -35,6 +35,28 @@
}
};
+TEST_F(CperEncoderTest, SeqMemcopy)
+{
+ std::string section1 = "Hello";
+ std::string section2 = " ";
+ std::string section3 = "World";
+
+ std::string dest(section1.size() + section2.size() + section3.size(), 0);
+
+ SeqMemcopy<char> seqMemcpy(dest);
+
+ EXPECT_NO_THROW(seqMemcpy.copy(section1.data(), section1.size()));
+ EXPECT_NO_THROW(seqMemcpy.copy(section2.data(), section2.size()));
+ EXPECT_NO_THROW(seqMemcpy.copy(section3.data(), section3.size()));
+
+ EXPECT_EQ(dest, section1 + section2 + section3);
+
+ std::string section4 = "This is an attempted buffer overflow!";
+
+ EXPECT_THROW(seqMemcpy.copy(section4.data(), section4.size()),
+ std::out_of_range);
+}
+
TEST_F(CperEncoderTest, CreateCperTimestamp)
{
const auto now = std::chrono::system_clock::now();