|
|
|
/**
|
|
|
|
*
|
|
|
|
* \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 <array>
|
|
|
|
|
|
|
|
namespace srsran {
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
template <typename Vec>
|
|
|
|
class base_optional_span
|
|
|
|
{
|
|
|
|
using base_t = base_optional_span<Vec>;
|
|
|
|
using T = typename Vec::value_type::value_type;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
template <typename Obj>
|
|
|
|
class iterator_impl
|
|
|
|
{
|
|
|
|
using It = iterator_impl<Obj>;
|
|
|
|
using parent_t = typename std::conditional<std::is_const<Obj>::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<size_t>::max();
|
|
|
|
};
|
|
|
|
|
|
|
|
size_t nof_elems = 0;
|
|
|
|
Vec vec;
|
|
|
|
|
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
using iterator = iterator_impl<T>;
|
|
|
|
using const_iterator = iterator_impl<const T>;
|
|
|
|
|
|
|
|
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 <typename U>
|
|
|
|
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>(u);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
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>(args)...);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename Vec>
|
|
|
|
class base_optional_vector : public base_optional_span<Vec>
|
|
|
|
{
|
|
|
|
using base_t = base_optional_span<Vec>;
|
|
|
|
|
|
|
|
public:
|
|
|
|
using value_type = typename base_optional_span<Vec>::value_type;
|
|
|
|
using iterator = typename base_optional_span<Vec>::iterator;
|
|
|
|
using const_iterator = typename base_optional_span<Vec>::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 <typename T, size_t N>
|
|
|
|
class optional_array : public detail::base_optional_vector<std::array<optional<T>, N> >
|
|
|
|
{};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contrarily to optional_array, this class may allocate and cause pointer/reference/iterator invalidation.
|
|
|
|
* However, the indexes will remain valid.
|
|
|
|
* @tparam T
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
class optional_vector : public detail::base_optional_vector<std::vector<optional<T> > >
|
|
|
|
{
|
|
|
|
using base_t = detail::base_optional_vector<std::vector<optional<T> > >;
|
|
|
|
|
|
|
|
public:
|
|
|
|
/// May allocate and cause pointer invalidation
|
|
|
|
template <typename U>
|
|
|
|
void insert(size_t idx, U&& u)
|
|
|
|
{
|
|
|
|
if (idx >= this->vec.size()) {
|
|
|
|
this->vec.resize(idx + 1);
|
|
|
|
}
|
|
|
|
base_t::insert(idx, std::forward<U>(u));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// May allocate and cause pointer invalidation
|
|
|
|
template <typename... Args>
|
|
|
|
void emplace(size_t idx, Args&&... args)
|
|
|
|
{
|
|
|
|
if (idx >= this->vec.size()) {
|
|
|
|
this->vec.resize(idx + 1);
|
|
|
|
}
|
|
|
|
base_t::emplace(idx, std::forward<Args>(args)...);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
class optional_span : public detail::base_optional_span<srsran::span<optional<T> > >
|
|
|
|
{
|
|
|
|
using base_t = detail::base_optional_span<srsran::span<optional<T> > >;
|
|
|
|
|
|
|
|
public:
|
|
|
|
template <size_t N>
|
|
|
|
optional_span(const optional_array<T, N>& ar) : base_t::vec(ar)
|
|
|
|
{}
|
|
|
|
optional_span(const optional_vector<T>& ar) : base_t::vec(ar) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
class base_split_optional_span
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
using presence_type = typename std::conditional<std::is_const<T>::value, const bool, bool>::type;
|
|
|
|
|
|
|
|
T* ptr = nullptr;
|
|
|
|
presence_type* present_ptr = nullptr;
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
template <typename Obj>
|
|
|
|
class iterator_impl
|
|
|
|
{
|
|
|
|
using It = iterator_impl<Obj>;
|
|
|
|
using Parent = typename std::
|
|
|
|
conditional<std::is_const<Obj>::value, const base_split_optional_span<T>, base_split_optional_span<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* 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<size_t>::max();
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
using iterator = iterator_impl<T>;
|
|
|
|
using const_iterator = iterator_impl<const T>;
|
|
|
|
|
|
|
|
constexpr base_split_optional_span() = default;
|
|
|
|
template <std::size_t N>
|
|
|
|
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 <typename T>
|
|
|
|
class split_optional_span : public detail::base_split_optional_span<T>
|
|
|
|
{
|
|
|
|
using base_t = detail::base_split_optional_span<T>;
|
|
|
|
|
|
|
|
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 <typename U>
|
|
|
|
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>(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 <typename U>
|
|
|
|
class split_optional_span<const U> : public detail::base_split_optional_span<const U>
|
|
|
|
{
|
|
|
|
using base_t = detail::base_split_optional_span<const U>;
|
|
|
|
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 <typename T>
|
|
|
|
split_optional_span<T>
|
|
|
|
make_optional_span(T* array,
|
|
|
|
typename std::conditional<std::is_const<T>::value, const bool, bool>::type* present,
|
|
|
|
size_t N)
|
|
|
|
{
|
|
|
|
return split_optional_span<T>(array, present, N);
|
|
|
|
}
|
|
|
|
template <typename T, size_t N>
|
|
|
|
split_optional_span<T>
|
|
|
|
make_optional_span(T (&array)[N],
|
|
|
|
typename std::conditional<std::is_const<T>::value, const bool, bool>::type (&present)[N])
|
|
|
|
{
|
|
|
|
return split_optional_span<T>(array, present);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace srsran
|
|
|
|
|
|
|
|
#endif // SRSRAN_OPTIONAL_ARRAY_H
|