From 8c24cfebd3d073ceab734d36e5dded35f4bd8038 Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Tue, 17 Aug 2021 12:56:50 +0200 Subject: [PATCH] adt: create data structure type to represent an array of optional fields --- lib/include/srsran/adt/optional_table.h | 153 ++++++++++++++++++++++++ lib/test/adt/CMakeLists.txt | 4 + lib/test/adt/optional_table_test.cc | 58 +++++++++ 3 files changed, 215 insertions(+) create mode 100644 lib/include/srsran/adt/optional_table.h create mode 100644 lib/test/adt/optional_table_test.cc diff --git a/lib/include/srsran/adt/optional_table.h b/lib/include/srsran/adt/optional_table.h new file mode 100644 index 000000000..3f5a86ad4 --- /dev/null +++ b/lib/include/srsran/adt/optional_table.h @@ -0,0 +1,153 @@ +/** + * + * \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_TABLE_H +#define SRSRAN_OPTIONAL_TABLE_H + +#include "optional.h" +#include "srsran/common/srsran_assert.h" +#include + +namespace srsran { + +/** + * 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_table +{ + template + class iterator_impl + { + using It = iterator_impl; + + 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(optional_table* parent_, size_t idx_) : parent(parent_), idx(idx_) + { + if (idx < parent->capacity() and not parent->contains(idx)) { + ++(*this); + } + } + + It& operator++() + { + while (++idx < parent->capacity() and not parent->contains(idx)) { + } + return *this; + } + It& operator--() + { + while (--idx < parent->capacity() 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); } + + protected: + friend class optional_table; + + optional_table* parent = nullptr; + size_t idx = N; + }; + +public: + using iterator = iterator_impl; + using const_iterator = iterator_impl; + + optional_table() {} + optional_table(const optional_table&) = default; + optional_table(optional_table&& other) noexcept : vec(std::move(other.vec)), nof_elems(other.nof_elems) + { + other.nof_elems = 0; + } + optional_table& operator=(const optional_table&) = default; + optional_table& operator =(optional_table&& other) noexcept + { + vec = std::move(other.vec); + nof_elems = other.nof_elems; + nof_elems = 0; + return *this; + } + + // Find first position that is empty + size_t find_first_empty() + { + if (nof_elems == capacity()) { + return N; + } + for (size_t i = 0; i < N; ++i) { + if (not vec[i].has_value()) { + return i; + } + } + return N; + } + template + void insert(size_t idx, U&& u) + { + nof_elems += contains(idx) ? 0 : 1; + vec[idx] = std::forward(u); + } + void erase(size_t idx) + { + if (contains(idx)) { + nof_elems--; + vec[idx].reset(); + } + } + void erase(iterator it) { erase(it.idx); } + void clear() + { + nof_elems = 0; + for (auto& e : *this) { + e.reset(); + } + } + + bool contains(size_t idx) const { return idx < N 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; } + static size_t capacity() { return N; } + + iterator begin() { return iterator{this, 0}; } + iterator end() { return iterator{this, N}; } + const_iterator begin() const { return const_iterator{this, 0}; } + const_iterator end() const { return const_iterator{this, N}; } + +private: + size_t nof_elems = 0; + std::array, N> vec; +}; + +} // namespace srsran + +#endif // SRSRAN_OPTIONAL_TABLE_H diff --git a/lib/test/adt/CMakeLists.txt b/lib/test/adt/CMakeLists.txt index 62a5c152e..890a74d81 100644 --- a/lib/test/adt/CMakeLists.txt +++ b/lib/test/adt/CMakeLists.txt @@ -61,3 +61,7 @@ add_test(optional_test optional_test) add_executable(cached_alloc_test cached_alloc_test.cc) target_link_libraries(cached_alloc_test srsran_common) add_test(cached_alloc_test cached_alloc_test) + +add_executable(optional_table_test optional_table_test.cc) +target_link_libraries(optional_table_test srsran_common) +add_test(optional_table_test optional_table_test) diff --git a/lib/test/adt/optional_table_test.cc b/lib/test/adt/optional_table_test.cc new file mode 100644 index 000000000..466ab1fb4 --- /dev/null +++ b/lib/test/adt/optional_table_test.cc @@ -0,0 +1,58 @@ +/** + * + * \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. + * + */ + +#include "srsran/adt/optional_table.h" +#include "srsran/common/test_common.h" + +namespace srsran { + +void test_slot_table() +{ + optional_table table1; + TESTASSERT(table1.size() == 0 and table1.empty()); + + TESTASSERT(not table1.contains(0)); + table1.insert(0, 5); + TESTASSERT(table1.size() == 1 and not table1.empty()); + table1.erase(0); + TESTASSERT(table1.size() == 0 and table1.empty()); + table1.insert(1, 3); + table1.insert(4, 2); + TESTASSERT(table1.size() == 2); + TESTASSERT(table1[4] == 2 and table1[1] == 3); + + size_t count = 0; + int array[] = {3, 2}; + for (int e : table1) { + TESTASSERT(array[count++] == e); + } + + auto it = table1.begin(); + TESTASSERT(*it == 3); + table1.erase(it); + TESTASSERT(table1.size() == 1); +} + +} // namespace srsran + +int main(int argc, char** argv) +{ + auto& test_log = srslog::fetch_basic_logger("TEST"); + test_log.set_level(srslog::basic_levels::info); + + srsran::test_init(argc, argv); + + srsran::test_slot_table(); + + printf("Success\n"); + return SRSRAN_SUCCESS; +}