Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ __pycache__
# IDEs
.vscode

_codeql_detected_source_root
84 changes: 84 additions & 0 deletions runtime/cpp/emboss_array_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,90 @@ class GenericArrayView final {
}
bool IsComplete() const { return buffer_.Ok(); }

// CopyFrom copies data from another GenericArrayView.
template <class OtherElementView, class OtherBufferType>
void CopyFrom(
const GenericArrayView<OtherElementView, OtherBufferType, kElementSize,
kAddressableUnitSize> &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 <class Container>
typename ::std::enable_if<
!::std::is_base_of<
GenericArrayView,
typename ::std::remove_cv<
typename ::std::remove_reference<Container>::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 <class OtherElementView, class OtherBufferType>
void UncheckedCopyFrom(
const GenericArrayView<OtherElementView, OtherBufferType, kElementSize,
kAddressableUnitSize> &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 <class Container>
typename ::std::enable_if<
!::std::is_base_of<
GenericArrayView,
typename ::std::remove_cv<
typename ::std::remove_reference<Container>::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 <class OtherElementView, class OtherBufferType>
bool TryToCopyFrom(
const GenericArrayView<OtherElementView, OtherBufferType, kElementSize,
kAddressableUnitSize> &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 <class Container>
typename ::std::enable_if<
!::std::is_base_of<
GenericArrayView,
typename ::std::remove_cv<
typename ::std::remove_reference<Container>::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 <class Stream>
bool UpdateFromTextStream(Stream *stream) const {
return ReadArrayFromTextStream(this, stream);
Expand Down
145 changes: 145 additions & 0 deletions runtime/cpp/test/emboss_array_view_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

#include "runtime/cpp/emboss_array_view.h"

#include <array>
#include <string>
#include <type_traits>
#include <vector>

#include "absl/strings/str_format.h"
#include "gtest/gtest.h"
Expand Down Expand Up @@ -279,6 +281,149 @@ TEST(ArrayView, TextFormatOutput_MultilineComment) {
}
}

TEST(ArrayView, CopyFromVector) {
::std::uint8_t bytes[8] = {0};
auto byte_array = ArrayView<FixedUIntView<8>, 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<FixedUIntView<8>, 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<FixedUIntView<8>, ReadWriteContiguousBuffer, 1>{
ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}};
auto dest_array = ArrayView<FixedUIntView<8>, 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<FixedUIntView<8>, 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<FixedUIntView<8>, ReadWriteContiguousBuffer, 1>{
ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}};
auto dest_array = ArrayView<FixedUIntView<8>, 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<FixedUIntView<8>, 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<FixedUIntView<8>, ReadWriteContiguousBuffer, 1>{
ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}};
auto dest_array = ArrayView<FixedUIntView<8>, 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<FixedUIntView<8>, ReadWriteContiguousBuffer, 1>{
ReadWriteContiguousBuffer{source_bytes, sizeof source_bytes}};
auto dest_array = ArrayView<FixedUIntView<8>, 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<FixedUIntView<32>, 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