diff --git a/.gitignore b/.gitignore index 4433574..0b0b30a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ __pycache__ # IDEs .vscode +_codeql_detected_source_root diff --git a/runtime/cpp/emboss_array_view.h b/runtime/cpp/emboss_array_view.h index fa8ccd3..11640df 100644 --- a/runtime/cpp/emboss_array_view.h +++ b/runtime/cpp/emboss_array_view.h @@ -247,6 +247,90 @@ class GenericArrayView final { } bool IsComplete() const { return buffer_.Ok(); } + // CopyFrom copies data from another GenericArrayView. + template + void CopyFrom( + const GenericArrayView &other) const { + EMBOSS_CHECK(ElementCount() == other.ElementCount()); + for (::std::size_t i = 0; i < ElementCount(); ++i) { + (*this)[i].CopyFrom(other[i]); + } + } + + // CopyFrom copies data from a C++ container (e.g., std::vector, std::array). + // The container must have at least ElementCount() elements. + template + typename ::std::enable_if< + !::std::is_base_of< + GenericArrayView, + typename ::std::remove_cv< + typename ::std::remove_reference::type>::type>::value, + void>::type + CopyFrom(const Container &container) const { + auto it = container.begin(); + for (::std::size_t i = 0; i < ElementCount(); ++i, ++it) { + EMBOSS_CHECK(it != container.end()); + (*this)[i].Write(*it); + } + } + + // UncheckedCopyFrom copies data from another GenericArrayView without checks. + template + void UncheckedCopyFrom( + const GenericArrayView &other) const { + for (::std::size_t i = 0; i < ElementCount(); ++i) { + (*this)[i].UncheckedCopyFrom(other[i]); + } + } + + // UncheckedCopyFrom copies data from a C++ container without checks. + template + typename ::std::enable_if< + !::std::is_base_of< + GenericArrayView, + typename ::std::remove_cv< + typename ::std::remove_reference::type>::type>::value, + void>::type + UncheckedCopyFrom(const Container &container) const { + auto it = container.begin(); + for (::std::size_t i = 0; i < ElementCount(); ++i, ++it) { + (*this)[i].UncheckedWrite(*it); + } + } + + // TryToCopyFrom attempts to copy data from another GenericArrayView. + // Returns true if the copy succeeds, false otherwise. + template + bool TryToCopyFrom( + const GenericArrayView &other) const { + if (ElementCount() != other.ElementCount()) return false; + for (::std::size_t i = 0; i < ElementCount(); ++i) { + if (!(*this)[i].TryToCopyFrom(other[i])) return false; + } + return true; + } + + // TryToCopyFrom attempts to copy data from a C++ container. + // Returns true if the copy succeeds, false otherwise. + template + typename ::std::enable_if< + !::std::is_base_of< + GenericArrayView, + typename ::std::remove_cv< + typename ::std::remove_reference::type>::type>::value, + bool>::type + TryToCopyFrom(const Container &container) const { + auto it = container.begin(); + for (::std::size_t i = 0; i < ElementCount(); ++i, ++it) { + if (it == container.end()) return false; + if (!(*this)[i].TryToWrite(*it)) return false; + } + return true; + } + template bool UpdateFromTextStream(Stream *stream) const { return ReadArrayFromTextStream(this, stream); diff --git a/runtime/cpp/test/emboss_array_view_test.cc b/runtime/cpp/test/emboss_array_view_test.cc index 1f632e8..2568715 100644 --- a/runtime/cpp/test/emboss_array_view_test.cc +++ b/runtime/cpp/test/emboss_array_view_test.cc @@ -14,8 +14,10 @@ #include "runtime/cpp/emboss_array_view.h" +#include #include #include +#include #include "absl/strings/str_format.h" #include "gtest/gtest.h" @@ -279,6 +281,149 @@ TEST(ArrayView, TextFormatOutput_MultilineComment) { } } +TEST(ArrayView, CopyFromVector) { + ::std::uint8_t bytes[8] = {0}; + auto byte_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{bytes, sizeof bytes}}; + + ::std::vector<::std::uint8_t> source = {1, 2, 3, 4, 5, 6, 7, 8}; + byte_array.CopyFrom(source); + + EXPECT_EQ(1, byte_array[0].Read()); + EXPECT_EQ(2, byte_array[1].Read()); + EXPECT_EQ(3, byte_array[2].Read()); + EXPECT_EQ(4, byte_array[3].Read()); + EXPECT_EQ(5, byte_array[4].Read()); + EXPECT_EQ(6, byte_array[5].Read()); + EXPECT_EQ(7, byte_array[6].Read()); + EXPECT_EQ(8, byte_array[7].Read()); +} + +TEST(ArrayView, CopyFromArray) { + ::std::uint8_t bytes[4] = {0}; + auto byte_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{bytes, sizeof bytes}}; + + ::std::array<::std::uint8_t, 4> source = {{10, 20, 30, 40}}; + byte_array.CopyFrom(source); + + EXPECT_EQ(10, byte_array[0].Read()); + EXPECT_EQ(20, byte_array[1].Read()); + EXPECT_EQ(30, byte_array[2].Read()); + EXPECT_EQ(40, byte_array[3].Read()); +} + +TEST(ArrayView, CopyFromGenericArrayView) { + ::std::uint8_t source_bytes[] = {1, 2, 3, 4}; + ::std::uint8_t dest_bytes[4] = {0}; + + auto source_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}}; + auto dest_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{dest_bytes, sizeof dest_bytes}}; + + dest_array.CopyFrom(source_array); + + EXPECT_EQ(1, dest_array[0].Read()); + EXPECT_EQ(2, dest_array[1].Read()); + EXPECT_EQ(3, dest_array[2].Read()); + EXPECT_EQ(4, dest_array[3].Read()); +} + +TEST(ArrayView, UncheckedCopyFromVector) { + ::std::uint8_t bytes[4] = {0}; + auto byte_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{bytes, sizeof bytes}}; + + ::std::vector<::std::uint8_t> source = {100, 101, 102, 103}; + byte_array.UncheckedCopyFrom(source); + + EXPECT_EQ(100, byte_array[0].UncheckedRead()); + EXPECT_EQ(101, byte_array[1].UncheckedRead()); + EXPECT_EQ(102, byte_array[2].UncheckedRead()); + EXPECT_EQ(103, byte_array[3].UncheckedRead()); +} + +TEST(ArrayView, UncheckedCopyFromGenericArrayView) { + ::std::uint8_t source_bytes[] = {5, 6, 7, 8}; + ::std::uint8_t dest_bytes[4] = {0}; + + auto source_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}}; + auto dest_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{dest_bytes, sizeof dest_bytes}}; + + dest_array.UncheckedCopyFrom(source_array); + + EXPECT_EQ(5, dest_array[0].UncheckedRead()); + EXPECT_EQ(6, dest_array[1].UncheckedRead()); + EXPECT_EQ(7, dest_array[2].UncheckedRead()); + EXPECT_EQ(8, dest_array[3].UncheckedRead()); +} + +TEST(ArrayView, TryToCopyFromVector) { + ::std::uint8_t bytes[4] = {0}; + auto byte_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{bytes, sizeof bytes}}; + + // Source with matching size should succeed + ::std::vector<::std::uint8_t> source = {11, 22, 33, 44}; + EXPECT_TRUE(byte_array.TryToCopyFrom(source)); + EXPECT_EQ(11, byte_array[0].Read()); + EXPECT_EQ(22, byte_array[1].Read()); + EXPECT_EQ(33, byte_array[2].Read()); + EXPECT_EQ(44, byte_array[3].Read()); + + // Source with insufficient size should fail + ::std::vector<::std::uint8_t> short_source = {1, 2}; + EXPECT_FALSE(byte_array.TryToCopyFrom(short_source)); +} + +TEST(ArrayView, TryToCopyFromGenericArrayView) { + ::std::uint8_t source_bytes[] = {9, 8, 7, 6}; + ::std::uint8_t dest_bytes[4] = {0}; + + auto source_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}}; + auto dest_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{dest_bytes, sizeof dest_bytes}}; + + EXPECT_TRUE(dest_array.TryToCopyFrom(source_array)); + EXPECT_EQ(9, dest_array[0].Read()); + EXPECT_EQ(8, dest_array[1].Read()); + EXPECT_EQ(7, dest_array[2].Read()); + EXPECT_EQ(6, dest_array[3].Read()); +} + +TEST(ArrayView, TryToCopyFromGenericArrayViewSizeMismatch) { + ::std::uint8_t source_bytes[] = {1, 2, 3}; + ::std::uint8_t dest_bytes[4] = {0}; + + auto source_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}}; + auto dest_array = ArrayView, ReadWriteContiguousBuffer, 1>{ + ReadWriteContiguousBuffer{dest_bytes, sizeof dest_bytes}}; + + // Different sizes should fail + EXPECT_FALSE(dest_array.TryToCopyFrom(source_array)); +} + +TEST(ArrayView, CopyFrom32BitIntegers) { + ::std::uint8_t bytes[16] = {0}; + auto uint32_array = + ArrayView, ReadWriteContiguousBuffer, 4>{ + ReadWriteContiguousBuffer{bytes, sizeof bytes}}; + + ::std::vector<::std::uint32_t> source = {0x12345678, 0xABCDEF01, 0x11111111, + 0x22222222}; + uint32_array.CopyFrom(source); + + EXPECT_EQ(0x12345678U, uint32_array[0].Read()); + EXPECT_EQ(0xABCDEF01U, uint32_array[1].Read()); + EXPECT_EQ(0x11111111U, uint32_array[2].Read()); + EXPECT_EQ(0x22222222U, uint32_array[3].Read()); +} + } // namespace test } // namespace support } // namespace emboss