/** * * \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_ID_MAP_H #define SRSRAN_ID_MAP_H #include "expected.h" #include "srsran/common/srsran_assert.h" #include namespace srsran { template class static_circular_map { static_assert(std::is_integral::value and std::is_unsigned::value, "Map key must be an unsigned integer"); using obj_t = std::pair; using obj_storage_t = typename std::aligned_storage::type; public: 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*() { srsran_assert(idx < ptr->buffer.size(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->buffer.size()); return ptr->get_obj_(idx); } obj_t* operator->() { srsran_assert(idx < ptr->buffer.size(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->buffer.size()); return &ptr->get_obj_(idx); } const obj_t* operator*() const { srsran_assert(idx < ptr->buffer.size(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->buffer.size()); return ptr->buffer[idx]; } const obj_t* operator->() const { srsran_assert(idx < ptr->buffer.size(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->buffer.size()); 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: friend class static_circular_map; 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: friend class static_circular_map; const static_circular_map* ptr = nullptr; size_t idx = 0; }; static_circular_map() { std::fill(present.begin(), present.end(), false); } static_circular_map(const static_circular_map& other) : present(other.present), count(other.count) { for (size_t idx = 0; idx < other.size(); ++idx) { if (present[idx]) { new (&buffer[idx]) obj_t(other.get_obj_(idx)); } } } static_circular_map(static_circular_map&& other) noexcept : present(other.present), count(other.count) { for (size_t idx = 0; idx < other.size(); ++idx) { if (present[idx]) { new (&buffer[idx]) obj_t(std::move(other.get_obj_(idx))); } } other.clear(); } ~static_circular_map() { clear(); } static_circular_map& operator=(const static_circular_map& other) { if (this == &other) { return *this; } clear(); count = other.count; present = other.present; for (size_t idx = 0; idx < other.size(); ++idx) { if (present[idx]) { new (&buffer[idx]) obj_t(other.get_obj_(idx)); } } } static_circular_map& operator=(static_circular_map&& other) noexcept { clear(); count = other.count; present = other.present; for (size_t idx = 0; idx < other.size(); ++idx) { if (present[idx]) { new (&buffer[idx]) obj_t(std::move(other.get_obj_(idx))); } } clear(); } bool contains(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; } srsran::expected insert(K id, T&& obj) { size_t idx = id % N; if (present[idx]) { return srsran::expected(std::move(obj)); } new (&buffer[idx]) obj_t(id, std::move(obj)); present[idx] = true; count++; return iterator(this, idx); } bool erase(K id) { if (not contains(id)) { return false; } size_t idx = id % N; get_obj_(idx).~obj_t(); present[idx] = false; --count; return true; } iterator erase(iterator it) { srsran_assert(it.idx < N, "Iterator out-of-bounds (%zd >= %zd)", it.idx, N); iterator next = it; ++next; it->~obj_t(); present[it->first] = false; --count; return next; } void clear() { for (auto it = begin(); it != end();) { it = erase(it); } } T& operator[](K id) { srsran_assert(contains(id), "Accessing non-existent ID=%zd", (size_t)id); return get_obj_(id % N).second; } const T& operator[](K id) const { srsran_assert(contains(id), "Accessing non-existent ID=%zd", (size_t)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; } 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 (contains(id)) { return iterator(this, id % N); } return end(); } const_iterator find(K id) const { if (contains(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; size_t count = 0; }; } // namespace srsran #endif // SRSRAN_ID_MAP_H