From fdf0bc46a19c9dc1fb77f88f7f2138d8b584493c Mon Sep 17 00:00:00 2001 From: Rick Altherr Date: Mon, 4 Dec 2017 11:03:52 -0800 Subject: [PATCH] lib: adapter to read big-endian words from a span<> containing bytes Signed-off-by: Rick Altherr Signed-off-by: Tim 'mithro' Ansell --- lib/CMakeLists.txt | 5 ++ lib/big_endian_span_test.cc | 42 ++++++++++ lib/include/prjxray/big_endian_span.h | 115 ++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 lib/big_endian_span_test.cc create mode 100644 lib/include/prjxray/big_endian_span.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d6722609..ba2f0142 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,11 @@ target_include_directories(libprjxray PUBLIC "include") target_link_libraries(libprjxray absl::strings) if (PRJXRAY_BUILD_TESTING) + add_executable(big_endian_span_test big_endian_span_test.cc) + target_link_libraries(big_endian_span_test libprjxray gtest_main) + add_test(NAME big_endian_span_test + COMMAND big_endian_span_test) + add_executable(bit_ops_test bit_ops_test.cc) target_link_libraries(bit_ops_test libprjxray gtest_main) add_test(NAME bit_ops_test diff --git a/lib/big_endian_span_test.cc b/lib/big_endian_span_test.cc new file mode 100644 index 00000000..dcc673ab --- /dev/null +++ b/lib/big_endian_span_test.cc @@ -0,0 +1,42 @@ +#include + +#include +#include + +#include + +TEST(BigEndianSpanTest, Read32WithEmptySpan) { + std::vector bytes; + auto words = prjxray::make_big_endian_span(bytes); + EXPECT_EQ(words.size(), static_cast(0)); +} + +TEST(BigEndianSpanTest, Read32WithTooFewBytes) { + std::vector bytes{0x0, 0x1, 0x2}; + auto words = prjxray::make_big_endian_span(bytes); + EXPECT_EQ(words.size(), static_cast(0)); +} + +TEST(BigEndianSpanTest, Read32WithExactBytes) { + std::vector bytes{0x0, 0x1, 0x2, 0x3}; + auto words = prjxray::make_big_endian_span(bytes); + ASSERT_EQ(words.size(), static_cast(1)); + EXPECT_EQ(words[0], static_cast(0x00010203)); +} + +TEST(BigEndianSpanTest, Read32WithMultipleWords) { + std::vector bytes{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; + auto words = prjxray::make_big_endian_span(bytes); + ASSERT_EQ(words.size(), static_cast(2)); + EXPECT_EQ(words[0], static_cast(0x00010203)); + EXPECT_EQ(words[1], static_cast(0x04050607)); +} + +TEST(BigEndianSpanTest, Write32) { + std::vector bytes{0x0, 0x1, 0x2, 0x3}; + auto words = prjxray::make_big_endian_span(bytes); + words[0] = 0x04050607; + + std::vector expected{0x4, 0x5, 0x6, 0x7}; + EXPECT_EQ(bytes, expected); +} diff --git a/lib/include/prjxray/big_endian_span.h b/lib/include/prjxray/big_endian_span.h new file mode 100644 index 00000000..944c52b1 --- /dev/null +++ b/lib/include/prjxray/big_endian_span.h @@ -0,0 +1,115 @@ +#ifndef PRJXRAY_LIB_BIG_ENDIAN_SPAN +#define PRJXRAY_LIB_BIG_ENDIAN_SPAN + +#include +#include + +#include + +namespace prjxray { + +template +class BigEndianSpan { + public: + constexpr static size_t kBytesPerElement = sizeof(WordType); + + using byte_type = ByteType; + using word_type = WordType; + using size_type = std::size_t; + + class value_type { + public: + operator WordType() const { + WordType word = 0; + for(size_t ii = 0; ii < kBytesPerElement; ++ii) { + word |= (static_cast(bytes_[ii]) << ((kBytesPerElement - 1 - ii) * 8)); + } + return word; + } + + value_type& operator=(WordType word) { + for (size_t ii = 0; ii < kBytesPerElement; ++ii) { + bytes_[ii] = ((word >> ((kBytesPerElement - 1 - ii) * 8)) & 0xFF); + } + return *this; + } + + protected: + friend class BigEndianSpan; + + value_type(absl::Span bytes) : bytes_(bytes) {}; + + private: + absl::Span bytes_; + }; + + class iterator + : public std::iterator { + public: + value_type operator*() const { + return value_type(bytes_); + } + + bool operator==(const iterator &other) const { + return bytes_ == other.bytes_; + } + + bool operator!=(const iterator &other) const { + return bytes_ != other.bytes_; + } + + iterator& operator++() { + bytes_ = bytes_.subspan(kBytesPerElement); + return *this; + } + + protected: + friend class BigEndianSpan; + + iterator(absl::Span bytes) : bytes_(bytes) {}; + + private: + absl::Span bytes_; + }; + + + using pointer = value_type*; + using reference = value_type&; + + BigEndianSpan(absl::Span bytes) : bytes_(bytes) {}; + + constexpr size_type size() const noexcept { + return bytes_.size() / kBytesPerElement; + }; + + constexpr size_type length() const noexcept { return size(); } + + constexpr bool empty() const noexcept { return size() == 0; } + + value_type operator[](size_type pos) const { + assert(pos >= 0 && pos < size()); + return value_type(bytes_.subspan((pos*kBytesPerElement))); + } + + constexpr reference at(size_type pos) const { + return this->operator[](pos); + } + + iterator begin() const { return iterator(bytes_); } + iterator end() const { return iterator({}); } + + private: + + absl::Span bytes_; +}; + +template +BigEndianSpan make_big_endian_span( + Container &bytes) { + return BigEndianSpan( + absl::Span(bytes)); +} + +} // namespace prjxray + +#endif // PRJXRAY_LIB_BIG_ENDIAN_SPAN