From af8dfcf19c3e0b61755424edc9985451602ec61d Mon Sep 17 00:00:00 2001 From: faluco Date: Fri, 7 Aug 2020 21:53:41 +0200 Subject: [PATCH] Extended a bit the span implementation with the following changes: - Documented methods and class. - Adapted interface to the latest standard: no init list and generic container constructors, new iterator pair ctor. - Implemented some SFINAE for managing conversions in constructors. - Re-organised some methods to be grouped by category (modifiers, observers, iterators, ...). - Added convenience make_span functions. - Added some more asserts. - Adapted test to use some syntax that was modified. --- lib/include/srslte/adt/span.h | 171 ++++++++++++++++++++++++++++------ lib/test/adt/span_test.cc | 22 ++--- 2 files changed, 152 insertions(+), 41 deletions(-) diff --git a/lib/include/srslte/adt/span.h b/lib/include/srslte/adt/span.h index b1a6b42dc..55e7c45c4 100644 --- a/lib/include/srslte/adt/span.h +++ b/lib/include/srslte/adt/span.h @@ -26,13 +26,17 @@ #include #include #include +#include namespace srslte { +/// The class template span describes an object that can refer to a contiguous sequence of objects with the first +/// element of the sequence at position zero. template class span { public: + /// Member types. using element_type = T; using value_type = typename std::remove_cv::type; using size_type = std::size_t; @@ -44,62 +48,115 @@ public: using iterator = pointer; using reverse_iterator = std::reverse_iterator; + /// Constructs an empty span with data() == nullptr and size() == 0. constexpr span() noexcept = default; - constexpr span(pointer ptr_, size_type N_) noexcept : ptr(ptr_), len(N_) {} + + /// Constructs a span that is a view over the range [ptr, ptr + len). + constexpr explicit span(pointer ptr, size_type len) noexcept : ptr(ptr), len(len) {} + + /// Constructs a span that is a view over the range [first, last). + constexpr explicit span(pointer first, pointer last) noexcept : ptr(first), len(last - first) {} + + /// Constructs a span that is a view over the array arr. template constexpr span(element_type (&arr)[N]) noexcept : ptr(arr), len(N) {} - template - constexpr span(std::array& arr) noexcept : ptr(arr.data()), len(N) - {} - template - constexpr span(const std::array& arr) noexcept : ptr(arr.data()), len(N) - {} - constexpr span(const std::initializer_list& lst) : - ptr(lst.begin() == lst.end() ? (T*)nullptr : lst.begin()), - len(lst.size()) - {} - constexpr span(byte_buffer_t& buffer) : ptr(buffer.msg), len(buffer.N_bytes) {} - constexpr span(const byte_buffer_t& buffer) : ptr(buffer.msg), len(buffer.N_bytes) {} - constexpr span(unique_byte_buffer_t& buffer) : ptr(buffer->msg), len(buffer->N_bytes) {} - constexpr span(const unique_byte_buffer_t& buffer) : ptr(buffer->msg), len(buffer->N_bytes) {} - template - constexpr span(Container& cont) : ptr(cont.data()), len(cont.size()) + + /// Constructs a span that is a view over the array arr. + template ::value, int>::type = 0> + constexpr span(std::array& arr) noexcept : ptr(arr.data()), len(N) {} - template - constexpr span(const span& other) noexcept : ptr(other.ptr), len(other.size()) + /// Constructs a span that is a view over the array arr. + template ::value, int>::type = 0> + constexpr span(const std::array& arr) noexcept : ptr(arr.data()), len(N) {} - ~span() noexcept = default; + + template ::value, int>::type = 0> + constexpr span(const span& other) noexcept : ptr(other.data()), len(other.size()) + {} + span& operator=(const span& other) noexcept = default; + ~span() noexcept = default; + + /// Returns the number of elements in the span. constexpr size_type size() const noexcept { return len; } - reference operator[](size_type idx) const + + /// Returns the size of the sequence in bytes. + constexpr size_type size_bytes() const noexcept { return len * sizeof(element_type); } + + /// Checks if the span is empty. + constexpr bool empty() const noexcept { return size() == 0; } + + /// Returns a reference to the first element in the span. + /// NOTE: Calling front on an empty span results in undefined behavior. + reference front() const + { + assert(!empty() && "called front with empty span"); + return *data(); + } + + /// Returns a reference to the last element in the span. + /// NOTE: Calling back on an empty span results in undefined behavior. + reference back() const + { + assert(!empty() && "called back with empty span"); + return *(data() + size() - 1); + } + + /// Returns a reference to the idx-th element of the sequence. + /// NOTE: The behavior is undefined if idx is out of range. + reference operator[](size_type idx) const { assert(idx < len && "index out of bounds!"); return ptr[idx]; } - constexpr bool empty() const noexcept { return size() == 0; } - constexpr reference front() const { return *data(); } - constexpr reference back() const { return *(data() + size() - 1); } + /// Returns a pointer to the beginning of the sequence. constexpr pointer data() const noexcept { return ptr; } + /// Returns an iterator to the first element of the span. constexpr iterator begin() const noexcept { return data(); } + + /// Returns an iterator to the element following the last element of the span. constexpr iterator end() const noexcept { return data() + size(); } + + /// Returns a reverse iterator to the first element of the reversed span. constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); } + + /// Returns a reverse iterator to the element following the last element of the reversed span. constexpr reverse_iterator rend() const noexcept { return reverse_iterator(begin()); } - bool equals(span rhs) const { return (len == rhs.len) ? std::equal(begin(), end(), rhs.begin()) : false; } + /// Obtains a span that is a view over the first count elements of this span. + /// NOTE: The behavior is undefined if count > size(). + span first(size_type count) const + { + assert(count <= size() && "count is out of range"); + return subspan(0, count); + } - // slicing operations + /// Obtains a span that is a view over the last count elements of this span. + /// NOTE: The behavior is undefined if count > size(). + span last(size_type count) const + { + assert(count <= size() && "count is out of range"); + return subspan(size() - count, count); + } + + /// Obtains a span that is a view over the count elements of this span starting at offset offset. span subspan(size_type offset, size_type count) const { - assert(count <= len && "size out of bounds!"); - return {data() + offset, count}; + assert(count <= size() - offset && "size out of bounds!"); + return span{data() + offset, count}; } - constexpr span first(size_type count) const { return subspan(0, count); } - constexpr span last(size_type count) const { return subspan(size() - count, count); } + + /// Returns true if the input span has the same elements as this. + bool equals(span rhs) const { return (len == rhs.len) ? std::equal(begin(), end(), rhs.begin()) : false; } private: pointer ptr = nullptr; @@ -118,8 +175,62 @@ inline bool operator!=(span lhs, span rhs) return not lhs.equals(rhs); } +/// +/// Helpers to construct span objects from different types of arrays. +/// + +template +inline span make_span(T (&arr)[N]) +{ + return span{arr}; +} + +template +inline span make_span(std::array& arr) +{ + return span{arr}; +} + +template +inline span make_span(const std::array& arr) +{ + return span{arr}; +} + +template +inline span make_span(std::vector& v) +{ + return span{v.data(), v.size()}; +} + +template +inline span make_span(const std::vector& v) +{ + return span{v.data(), v.size()}; +} + using byte_span = span; +inline byte_span make_span(byte_buffer_t& b) +{ + return byte_span{b.msg, b.N_bytes}; +} + +inline span make_span(const byte_buffer_t& b) +{ + return span{b.msg, b.N_bytes}; +} + +inline byte_span make_span(unique_byte_buffer_t& b) +{ + return byte_span{b->msg, b->N_bytes}; +} + +inline span make_span(const unique_byte_buffer_t& b) +{ + return span{b->msg, b->N_bytes}; +} + } // namespace srslte #endif // SRSLTE_SPAN_H diff --git a/lib/test/adt/span_test.cc b/lib/test/adt/span_test.cc index 69b721f8a..0833e1bd8 100644 --- a/lib/test/adt/span_test.cc +++ b/lib/test/adt/span_test.cc @@ -26,10 +26,10 @@ int test_span_access() { - std::vector values{1, 2, 3, 4, 5, 6, 7}; + std::array values{1, 2, 3, 4, 5, 6, 7}; { - srslte::span view{values}; + auto view = srslte::make_span(values); // access operators TESTASSERT(view.size() == 7); @@ -58,7 +58,7 @@ int test_span_conversion() { // TEST: changing individual values - srslte::span v{values}, v2{values2}; + auto v = srslte::make_span(values), v2 = srslte::make_span(values2); TESTASSERT(v == v2); v[0] = 3; @@ -70,7 +70,7 @@ int test_span_conversion() { // TEST: const context - const srslte::span v{values}, v2{values2}; + const auto v = srslte::make_span(values), v2 = srslte::make_span(values2); TESTASSERT(v != v2); TESTASSERT(v[0] == 3); TESTASSERT(v2[0] == 2); @@ -80,8 +80,8 @@ int test_span_conversion() { // TEST: raw arrays - int carray[] = {2, 3, 4, 5, 6, 7, 8}; - srslte::span v{values}, v2{carray}; + int carray[] = {2, 3, 4, 5, 6, 7, 8}; + auto v = srslte::make_span(values), v2 = srslte::make_span(carray); TESTASSERT(v == v2); TESTASSERT(v2.size() == v.size()); } @@ -109,7 +109,7 @@ int test_byte_buffer_conversion() pdu.msg[4] = 4; { - srslte::byte_span v{pdu}; + auto v = srslte::make_span(pdu); TESTASSERT(v.size() == 5); TESTASSERT(v[0] == 0); TESTASSERT(v[2] == 2); @@ -118,15 +118,15 @@ int test_byte_buffer_conversion() const srslte::byte_buffer_t& pdu2 = pdu; { - const srslte::byte_span v{pdu2}; + const auto v = srslte::make_span(pdu2); TESTASSERT(v.size() == 5); TESTASSERT(v[0] == 0); TESTASSERT(v[2] == 2); TESTASSERT(v[4] == 4); } - TESTASSERT(foo(pdu)); - TESTASSERT(cfoo(pdu)); + TESTASSERT(foo(srslte::make_span(pdu))); + TESTASSERT(cfoo(srslte::make_span(pdu))); return SRSLTE_SUCCESS; } @@ -138,4 +138,4 @@ int main() TESTASSERT(test_byte_buffer_conversion() == SRSLTE_SUCCESS); printf("Success\n"); return SRSLTE_SUCCESS; -} \ No newline at end of file +}