/** * * \section COPYRIGHT * * Copyright 2013-2021 Software Radio Systems Limited * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the distribution. * */ #ifndef SRSRAN_OPTIONAL_ARRAY_H #define SRSRAN_OPTIONAL_ARRAY_H #include "optional.h" #include "span.h" #include "srsran/support/srsran_assert.h" #include namespace srsran { namespace detail { template class base_optional_span { using base_t = base_optional_span; using T = typename Vec::value_type::value_type; protected: template class iterator_impl { using It = iterator_impl; using parent_t = typename std::conditional::value, const base_t, base_t>::type; public: using iterator_category = std::forward_iterator_tag; using value_type = Obj; using difference_type = std::ptrdiff_t; using pointer = Obj*; using reference = Obj&; iterator_impl() = default; iterator_impl(parent_t* parent_, size_t idx_) : parent(parent_), idx(idx_) { if (idx < parent->vec.size() and not parent->contains(idx)) { ++(*this); } } It& operator++() { while (++idx < parent->vec.size() and not parent->contains(idx)) { } return *this; } It& operator--() { while (--idx < parent->vec.size() and not parent->contains(idx)) { } return *this; } reference operator*() { return parent->operator[](idx); } pointer operator->() { return &parent->operator[](idx); } bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; } bool operator!=(const It& other) const { return not(*this == other); } private: friend base_t; parent_t* parent = nullptr; size_t idx = std::numeric_limits::max(); }; size_t nof_elems = 0; Vec vec; public: using value_type = T; using iterator = iterator_impl; using const_iterator = iterator_impl; base_optional_span() = default; base_optional_span(Vec&& v, size_t nof_elems_) : vec(std::move(v)), nof_elems(nof_elems_) {} base_optional_span(const Vec& v, size_t nof_elems_) : vec(v), nof_elems(nof_elems_) {} // Find first position that is empty size_t find_first_empty(size_t start_guess = 0) { if (nof_elems == vec.size()) { return vec.size(); } for (size_t i = start_guess; i < vec.size(); ++i) { if (not vec[i].has_value()) { return i; } } return vec.size(); } bool contains(size_t idx) const { return idx < vec.size() and vec[idx].has_value(); } T& operator[](size_t idx) { return *vec[idx]; } const T& operator[](size_t idx) const { return *vec[idx]; } bool empty() const { return nof_elems == 0; } size_t size() const { return nof_elems; } iterator begin() { return iterator{this, 0}; } iterator end() { return iterator{this, vec.size()}; } const_iterator begin() const { return const_iterator{this, 0}; } const_iterator end() const { return const_iterator{this, vec.size()}; } void clear() { this->nof_elems = 0; for (auto& e : *this) { e.reset(); } } void erase(size_t idx) { srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); if (this->contains(idx)) { this->nof_elems--; this->vec[idx].reset(); } } void erase(iterator it) { erase(it.idx); } template void insert(size_t idx, U&& u) { srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); this->nof_elems += this->contains(idx) ? 0 : 1; this->vec[idx] = std::forward(u); } template void emplace(size_t idx, Args&&... args) { srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); if (not this->contains(idx)) { this->nof_elems++; } this->vec[idx].emplace(std::forward(args)...); } }; template class base_optional_vector : public base_optional_span { using base_t = base_optional_span; public: using value_type = typename base_optional_span::value_type; using iterator = typename base_optional_span::iterator; using const_iterator = typename base_optional_span::const_iterator; base_optional_vector() = default; base_optional_vector(const base_optional_vector&) = default; base_optional_vector(base_optional_vector&& other) noexcept : base_t(std::move(other.vec), other.size()) { other.nof_elems = 0; } base_optional_vector& operator=(const base_optional_vector&) = default; base_optional_vector& operator =(base_optional_vector&& other) noexcept { this->vec = std::move(other.vec); this->nof_elems = other.nof_elems; other.nof_elems = 0; return *this; } }; } // namespace detail /** * Array of optional items. The iteration is in order of indexes and correctly skips non-present items * Pointer/References/Iterators remain valid throughout the object lifetime * NOTE: The sorted iteration and pointer validation guarantees add some overhead if the array is very fragmented * @tparam T type of objects * @tparam N static size of max nof items */ template class optional_array : public detail::base_optional_vector, N> > {}; /** * Contrarily to optional_array, this class may allocate and cause pointer/reference/iterator invalidation. * However, the indexes will remain valid. * @tparam T */ template class optional_vector : public detail::base_optional_vector > > { using base_t = detail::base_optional_vector > >; public: /// May allocate and cause pointer invalidation template void insert(size_t idx, U&& u) { if (idx >= this->vec.size()) { this->vec.resize(idx + 1); } base_t::insert(idx, std::forward(u)); } /// May allocate and cause pointer invalidation template void emplace(size_t idx, Args&&... args) { if (idx >= this->vec.size()) { this->vec.resize(idx + 1); } base_t::emplace(idx, std::forward(args)...); } }; template class optional_span : public detail::base_optional_span > > { using base_t = detail::base_optional_span > >; public: template optional_span(const optional_array& ar) : base_t::vec(ar) {} optional_span(const optional_vector& ar) : base_t::vec(ar) {} }; namespace detail { template class base_split_optional_span { protected: using presence_type = typename std::conditional::value, const bool, bool>::type; T* ptr = nullptr; presence_type* present_ptr = nullptr; size_t len = 0; template class iterator_impl { using It = iterator_impl; using Parent = typename std:: conditional::value, const base_split_optional_span, base_split_optional_span >::type; public: using iterator_category = std::forward_iterator_tag; using value_type = Obj; using difference_type = std::ptrdiff_t; using pointer = Obj*; using reference = Obj&; iterator_impl() = default; iterator_impl(Parent* parent_, size_t idx_) : parent(parent_), idx(idx_) { if (idx < parent->len and not parent->contains(idx)) { ++(*this); } } It& operator++() { while (++idx < parent->len and not parent->contains(idx)) { } return *this; } It& operator--() { while (--idx < parent->len and not parent->contains(idx)) { } return *this; } reference operator*() { return parent->operator[](idx); } pointer operator->() { return &parent->operator[](idx); } bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; } bool operator!=(const It& other) const { return not(*this == other); } size_t get_idx() const { return idx; } private: Parent* parent = nullptr; size_t idx = std::numeric_limits::max(); }; public: using value_type = T; using iterator = iterator_impl; using const_iterator = iterator_impl; constexpr base_split_optional_span() = default; template constexpr base_split_optional_span(value_type (&arr)[N], presence_type (&present)[N]) noexcept : ptr(arr), present_ptr(present), len(N) {} constexpr base_split_optional_span(value_type* arr, presence_type* present, size_t N) : ptr(arr), present_ptr(present), len(N) {} bool contains(size_t idx) const { return idx < len and present_ptr[idx]; } bool empty() const { for (size_t i = 0; i < len; ++i) { if (present_ptr[i]) { return false; } } return true; } size_t size() const { size_t c = 0; for (size_t i = 0; i < len; ++i) { c += present_ptr[i] ? 1 : 0; } return c; } size_t capacity() const { return len; } const T& operator[](size_t idx) const { return ptr[idx]; } T& operator[](size_t idx) { return ptr[idx]; } const T& at(size_t idx) const { srsran_assert(contains(idx), "Access to inexistent element of index=%zd", idx); return ptr[idx]; } T& at(size_t idx) { srsran_assert(this->contains(idx), "Access to inexistent element of index=%zd", idx); return this->ptr[idx]; } const_iterator begin() const { return const_iterator(this, 0); } const_iterator end() const { return const_iterator(this, len); } iterator begin() { return iterator(this, 0); } iterator end() { return iterator(this, this->len); } // Find first position that is empty size_t find_first_empty(size_t start_guess = 0) { return begin().get_idx(); } }; } // namespace detail template class split_optional_span : public detail::base_split_optional_span { using base_t = detail::base_split_optional_span; public: using value_type = T; using const_iterator = typename base_t::const_iterator; using iterator = typename base_t::iterator; using base_t::base_t; template void insert(size_t idx, U&& u) { srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len); this->present_ptr[idx] = true; this->ptr[idx] = std::forward(u); } void erase(size_t idx) { srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len); this->present_ptr[idx] = false; } void erase(iterator it) { erase(it.get_idx()); } void clear() { for (size_t i = 0; i < this->len; ++i) { this->present_ptr[i] = false; } } }; template class split_optional_span : public detail::base_split_optional_span { using base_t = detail::base_split_optional_span; using presence_type = typename base_t::presence_type; public: using value_type = const U; using const_iterator = typename base_t::const_iterator; using base_t::base_t; }; template split_optional_span make_optional_span(T* array, typename std::conditional::value, const bool, bool>::type* present, size_t N) { return split_optional_span(array, present, N); } template split_optional_span make_optional_span(T (&array)[N], typename std::conditional::value, const bool, bool>::type (&present)[N]) { return split_optional_span(array, present); } } // namespace srsran #endif // SRSRAN_OPTIONAL_ARRAY_H