Skip to content

Commit

Permalink
Merge branch 'extend_memory_view' into 'master'
Browse files Browse the repository at this point in the history
Make MemoryView class compatible with std::span from C++20

See merge request embedded/general-support-library!34
  • Loading branch information
gdex committed Sep 23, 2023
2 parents 0de6fb3 + 4adfeed commit faabcb9
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 17 deletions.
4 changes: 2 additions & 2 deletions BufferedOut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ BufferedOut &BufferedOut::operator<<(float f)
BufferedOut &BufferedOut::operator<<(ConstBytesView span)
{
char digits[] = "0123456789ABCDEF";
const auto availableSize = (dataBuf.end() - pos) / 2;
const size_t availableSize = (dataBuf.end() - pos) / 2;
auto size = span.size() < availableSize ? span.size() : availableSize;
for (int i = 0; i < size; ++i)
for (size_t i = 0; i < size; ++i)
{
unsigned digit = span[i];
*(pos++) = digits[(digit & 0xF0u) >> 4];
Expand Down
67 changes: 53 additions & 14 deletions MemoryView.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ template<typename T>
class MemoryView
{
public:
typedef T value_type;
typedef T element_type;
typedef std::remove_cv_t<T> value_type;
typedef T* pointer;
typedef typename std::add_const<typename std::remove_const<T>::type>::type* const_pointer;
typedef typename std::add_const<std::remove_const_t<T>>::type* const_pointer;
typedef T &reference;
typedef typename std::add_const<typename std::remove_const<T>::type>::type &const_reference;
typedef typename std::add_const<std::remove_const_t<T>>::type &const_reference;
typedef pointer iterator;
typedef const_pointer const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static constexpr size_type npos = static_cast<size_type>(-1);

constexpr MemoryView() : begin_(nullptr), size_(0) {}

constexpr MemoryView(T* p, uint16_t s) : begin_(p), size_(s) {}
constexpr MemoryView(T* p, size_type s) : begin_(p), size_(s) {}

template<typename C = std::remove_const<T>, std::enable_if_t<
std::is_same_v<T, std::add_const_t<C>> || std::is_same_v<T, C>, bool> = true>
Expand All @@ -43,19 +46,55 @@ class MemoryView
return { begin_, size_ };
}

T &operator[](uint16_t i) { return begin_[i]; }

constexpr T &operator[](uint16_t i) const { return begin_[i]; }

constexpr auto begin() const { return begin_; }

constexpr auto end() const { return begin_ + size_; }

constexpr reference operator[](size_type i) const { return begin_[i]; }
constexpr iterator begin() const { return begin_; }
constexpr iterator end() const { return begin_ + size_; }
constexpr const_iterator cbegin() const { return begin_; }
constexpr const_iterator cend() const { return begin_ + size_; }
constexpr reverse_iterator rbegin() const { return reverse_iterator(end()); }
constexpr reverse_iterator rend() const { return reverse_iterator(begin()); }
constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); }
constexpr const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
constexpr auto size() const { return size_; }
constexpr size_type size_bytes() const noexcept { return size_ * sizeof(T); }
constexpr pointer data() const noexcept { return begin_; }
constexpr bool empty() const noexcept { return size_ == 0; }
constexpr reference front() const { return *begin_; }
constexpr reference back() const { return *(begin_ + size_ - 1); }
constexpr MemoryView<T> subspan(size_type offset, size_type count = npos) const
{
if (offset >= size_)
return {};
if (count == npos)
return MemoryView<T>(begin_ + offset, size_ - offset);
return MemoryView<T>(begin_ + offset, std::min(size_ - offset, count));
}
constexpr MemoryView<T> first(size_type count) const
{
if (count >= size_)
return *this;
return MemoryView<T>(begin_, count);
}
constexpr MemoryView<T> last(size_type count) const
{
if (count >= size_)
return *this;
return MemoryView<T>(begin_ + size_ - count, count);
}
constexpr MemoryView<const uint8_t> as_bytes() const noexcept
{
return { reinterpret_cast<const uint8_t*>(begin_), size_bytes() };
}

template<typename = typename std::is_const<T>::type>
constexpr MemoryView<uint8_t> as_writable_bytes() const noexcept
{
return { reinterpret_cast<uint8_t*>(begin_), size_bytes() };
}

private:
T* begin_;
uint16_t size_;
size_type size_;
};

using CharView = MemoryView<char>;
Expand Down
107 changes: 106 additions & 1 deletion tests/MemoryViewTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,113 @@ TEST(MemoryViewTest, OperatorIndexConst)
{
const uint8_t data[] = { 0x01, 0x02, 0x03 };
MemoryView view { data, sizeof(data) };
ASSERT_TRUE((std::is_same_v<decltype(view)::value_type, const uint8_t>));
ASSERT_TRUE((std::is_same_v<decltype(view)::element_type, const uint8_t>));
ASSERT_TRUE((std::is_same_v<decltype(view)::value_type, uint8_t>));
EXPECT_EQ(view[0], 0x01);
EXPECT_EQ(view[1], 0x02);
EXPECT_EQ(view[2], 0x03);
}

TEST(MemoryViewTest, BeginEnd) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView<int> view(arr);
auto it = view.begin();
auto cit = view.cbegin();
EXPECT_EQ(*it, 1);
EXPECT_EQ(*cit, *it);
++it;
EXPECT_EQ(*it, 2);
++cit;
EXPECT_EQ(*cit, *it);

auto endIt = view.end();
--endIt;
EXPECT_EQ(*endIt, 5);

auto cendIt = view.cend();
--cendIt;
EXPECT_EQ(*cendIt, *endIt);
}

TEST(MemoryViewTest, RbeginRend) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView<int> view(arr);
auto it = view.rbegin();
auto cit = view.crbegin();
EXPECT_EQ(*it, 5);
EXPECT_EQ(*cit, *it);
++it;
EXPECT_EQ(*it, 4);
++cit;
EXPECT_EQ(*cit, *it);

auto endIt = view.rend();
--endIt;
EXPECT_EQ(*endIt, 1);

auto cendIt = view.crend();
--cendIt;
EXPECT_EQ(*cendIt, *endIt);
}

TEST(MemoryViewTest, Subspan) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView<int> view(arr);
auto subView = view.subspan(1, 3);
EXPECT_EQ(subView.size(), 3);
EXPECT_EQ(subView[0], 2);
EXPECT_EQ(subView[2], 4);
subView = view.subspan(3);
EXPECT_FALSE(subView.empty());
EXPECT_EQ(subView.size(), 2);
EXPECT_EQ(subView[0], 4);
EXPECT_EQ(subView[1], 5);
EXPECT_TRUE(view.subspan(5).empty());
EXPECT_TRUE(view.subspan(6).empty());
}

TEST(MemoryViewTest, AsBytes) {
const int arr[] = {1, 2, 3, 4, 5};
MemoryView view(arr);
const auto bytes = view.as_bytes();
EXPECT_EQ(bytes.size(), sizeof(int) * 5);
EXPECT_EQ(bytes[0], 1);
EXPECT_EQ(bytes[sizeof(int)], 2);
}

TEST(MemoryViewTest, AsWritableBytes) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView view(arr);
auto writableBytes = view.as_writable_bytes();
EXPECT_EQ(writableBytes.size(), sizeof(int) * 5);
writableBytes[sizeof(int)] = 10;
EXPECT_EQ(view[1], 10);
}

TEST(MemoryViewTest, First) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView<int> view(arr);
auto first = view.first(3);
EXPECT_EQ(first.size(), 3);
EXPECT_EQ(first[0], 1);
first = view.first(5);
EXPECT_EQ(first.size(), 5);
EXPECT_EQ(first[0], 1);
first = view.first(6);
EXPECT_EQ(first.size(), 5);
EXPECT_EQ(first[0], 1);
}

TEST(MemoryViewTest, Last) {
int arr[] = {1, 2, 3, 4, 5};
MemoryView<int> view(arr);
auto last = view.last(3);
EXPECT_EQ(last.size(), 3);
EXPECT_EQ(last[0], 3);
last = view.last(5);
EXPECT_EQ(last.size(), 5);
EXPECT_EQ(last[0], 1);
last = view.last(6);
EXPECT_EQ(last.size(), 5);
EXPECT_EQ(last[0], 1);
}

0 comments on commit faabcb9

Please sign in to comment.