From 98d0637deddc177b60b935115ee52b5de76f1503 Mon Sep 17 00:00:00 2001 From: Francisco Date: Fri, 19 Mar 2021 12:40:04 +0000 Subject: [PATCH] adt addition - implemented reusable circular map --- lib/include/srslte/adt/circular_map.h | 169 ++++++++++++++++++++++++++ lib/test/adt/CMakeLists.txt | 4 + lib/test/adt/circular_map_test.cc | 70 +++++++++++ 3 files changed, 243 insertions(+) create mode 100644 lib/include/srslte/adt/circular_map.h create mode 100644 lib/test/adt/circular_map_test.cc diff --git a/lib/include/srslte/adt/circular_map.h b/lib/include/srslte/adt/circular_map.h new file mode 100644 index 000000000..67d27a572 --- /dev/null +++ b/lib/include/srslte/adt/circular_map.h @@ -0,0 +1,169 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 SRSLTE_ID_MAP_H +#define SRSLTE_ID_MAP_H + +#include "expected.h" +#include +#include + +template +class static_circular_map +{ + static_assert(std::is_integral::value, "Map key must be an integer"); + + using obj_t = std::pair; + using obj_storage_t = typename std::aligned_storage::type; + +public: + bool has_key(K id) + { + size_t idx = id % N; + return present[idx] and get_obj_(idx).first == id; + } + + bool insert(K id, const T& obj) + { + size_t idx = id % N; + if (present[idx]) { + return false; + } + new (&buffer[idx]) obj_t(id, obj); + present[idx] = true; + count++; + return true; + } + srslte::error_type insert(K id, T&& obj) + { + size_t idx = id % N; + if (present[idx]) { + return srslte::error_type(std::move(obj)); + } + new (&buffer[idx]) obj_t(id, std::move(obj)); + present[idx] = true; + count++; + return {}; + } + + bool erase(K id) + { + if (not has_key(id)) { + return false; + } + size_t idx = id % N; + get_obj_(idx).~obj_t(); + present[idx] = false; + --count; + return true; + } + + T& operator[](K id) + { + assert(has_key(id)); + return get_obj_(id % N).second; + } + const T& operator[](K id) const + { + assert(has_key(id)); + return get_obj_(id % N).second; + } + + size_t size() const { return count; } + bool empty() const { return count == 0; } + bool full() const { return count == N; } + size_t capacity() const { return N; } + + class iterator + { + public: + iterator() = default; + iterator(static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) + { + if (idx < ptr->capacity() and not ptr->present[idx]) { + ++(*this); + } + } + + iterator& operator++() + { + while (++idx < ptr->capacity() and not ptr->present[idx]) { + } + return *this; + } + + obj_t& operator*() { return ptr->get_obj_(idx); } + obj_t* operator->() { return &ptr->get_obj_(idx); } + const obj_t* operator*() const { return ptr->buffer[idx]; } + const obj_t* operator->() const { return ptr->buffer[idx]; } + + bool operator==(const iterator& other) const { return ptr == other.ptr and idx == other.idx; } + bool operator!=(const iterator& other) const { return not(*this == other); } + + private: + static_circular_map* ptr = nullptr; + size_t idx = 0; + }; + class const_iterator + { + public: + const_iterator() = default; + const_iterator(static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) {} + + const_iterator& operator++() + { + while (++idx < ptr->capacity() and not ptr->present[idx]) { + } + return *this; + } + + const obj_t* operator*() const { return ptr->buffer[idx]; } + const obj_t* operator->() const { return ptr->buffer[idx]; } + + bool operator==(const const_iterator& other) const { return ptr == other.ptr and idx == other.idx; } + bool operator!=(const const_iterator& other) const { return not(*this == other); } + + private: + const static_circular_map* ptr = nullptr; + size_t idx = 0; + }; + + iterator begin() { return iterator(this, 0); } + iterator end() { return iterator(this, N); } + const_iterator begin() const { return iterator(this, 0); } + const_iterator end() const { return iterator(this, N); } + + iterator find(K id) + { + if (has_key(id)) { + return iterator(this, id % N); + } + return end(); + } + const_iterator find(K id) const + { + if (has_key(id)) { + return iterator(this, id % N); + } + return end(); + } + +private: + obj_t& get_obj_(size_t idx) { return reinterpret_cast(buffer[idx]); } + const obj_t& get_obj_(size_t idx) const { return reinterpret_cast(buffer[idx]); } + + std::array buffer; + std::array present = {false}; + size_t count = 0; +}; + +#endif // SRSLTE_ID_MAP_H diff --git a/lib/test/adt/CMakeLists.txt b/lib/test/adt/CMakeLists.txt index fb2276200..1051c8c4b 100644 --- a/lib/test/adt/CMakeLists.txt +++ b/lib/test/adt/CMakeLists.txt @@ -45,3 +45,7 @@ add_test(mem_pool_test mem_pool_test) add_executable(circular_buffer_test circular_buffer_test.cc) target_link_libraries(circular_buffer_test srsran_common) add_test(circular_buffer_test circular_buffer_test) + +add_executable(circular_map_test circular_map_test.cc) +target_link_libraries(circular_map_test srslte_common) +add_test(circular_map_test circular_map_test) diff --git a/lib/test/adt/circular_map_test.cc b/lib/test/adt/circular_map_test.cc new file mode 100644 index 000000000..1a2d31c79 --- /dev/null +++ b/lib/test/adt/circular_map_test.cc @@ -0,0 +1,70 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2020 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 "srslte/adt/circular_map.h" +#include "srslte/common/test_common.h" + +namespace srslte { + +int test_id_map() +{ + static_circular_map myobj; + TESTASSERT(myobj.size() == 0 and myobj.empty() and not myobj.full()); + TESTASSERT(myobj.begin() == myobj.end()); + + TESTASSERT(not myobj.has_key(0)); + TESTASSERT(myobj.insert(0, "obj0")); + TESTASSERT(myobj.has_key(0) and myobj[0] == "obj0"); + TESTASSERT(myobj.size() == 1 and not myobj.empty() and not myobj.full()); + TESTASSERT(myobj.begin() != myobj.end()); + + TESTASSERT(not myobj.insert(0, "obj0")); + TESTASSERT(myobj.insert(1, "obj1")); + TESTASSERT(myobj.has_key(0) and myobj.has_key(1) and myobj[1] == "obj1"); + TESTASSERT(myobj.size() == 2 and not myobj.empty() and not myobj.full()); + + TESTASSERT(myobj.find(1) != myobj.end()); + TESTASSERT(myobj.find(1)->first == 1); + TESTASSERT(myobj.find(1)->second == "obj1"); + + // TEST: iteration + uint32_t count = 0; + for (std::pair& obj : myobj) { + TESTASSERT(obj.second == "obj" + std::to_string(count++)); + } + + // TEST: const iteration + count = 0; + for (const std::pair& obj : myobj) { + TESTASSERT(obj.second == "obj" + std::to_string(count++)); + } + + TESTASSERT(myobj.erase(0)); + TESTASSERT(myobj.erase(1)); + TESTASSERT(myobj.size() == 0 and myobj.empty()); + + return SRSLTE_SUCCESS; +} + +} // namespace srslte + +int main() +{ + auto& test_log = srslog::fetch_basic_logger("TEST"); + test_log.set_level(srslog::basic_levels::info); + + // Start the log backend. + srslog::init(); + + TESTASSERT(srslte::test_id_map() == SRSLTE_SUCCESS); + return SRSLTE_SUCCESS; +} \ No newline at end of file