You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
8.7 KiB
C++

/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsRAN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSRAN_ID_MAP_H
#define SRSRAN_ID_MAP_H
#include "detail/type_storage.h"
#include "expected.h"
#include "srsran/common/srsran_assert.h"
#include <array>
namespace srsran {
template <typename K, typename T, size_t N>
class static_circular_map
{
static_assert(std::is_integral<K>::value and std::is_unsigned<K>::value, "Map key must be an unsigned integer");
using obj_t = std::pair<K, T>;
public:
class iterator
{
public:
iterator() = default;
iterator(static_circular_map<K, T, N>* 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->capacity(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->capacity());
return ptr->get_obj_(idx);
}
obj_t* operator->()
{
srsran_assert(idx < ptr->capacity(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->capacity());
return &ptr->get_obj_(idx);
}
const obj_t* operator*() const
{
srsran_assert(idx < ptr->capacity(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->capacity());
return &ptr->get_obj_(idx);
}
const obj_t* operator->() const
{
srsran_assert(idx < ptr->capacity(), "Iterator out-of-bounds (%zd >= %zd)", idx, ptr->capacity());
return &ptr->get_obj_(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<K, T, N>;
static_circular_map<K, T, N>* ptr = nullptr;
size_t idx = 0;
};
class const_iterator
{
public:
const_iterator() = default;
const_iterator(const static_circular_map<K, T, N>* 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].get(); }
const obj_t* operator->() const { return &ptr->buffer[idx].get(); }
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<K, T, N>;
const static_circular_map<K, T, N>* ptr = nullptr;
size_t idx = 0;
};
static_circular_map() { std::fill(present.begin(), present.end(), false); }
static_circular_map(const static_circular_map<K, T, N>& other) : present(other.present), count(other.count)
{
for (size_t idx = 0; idx < other.capacity(); ++idx) {
if (present[idx]) {
buffer[idx].template emplace(other.get_obj_(idx));
}
}
}
static_circular_map(static_circular_map<K, T, N>&& other) noexcept : present(other.present), count(other.count)
{
for (size_t idx = 0; idx < other.capacity(); ++idx) {
if (present[idx]) {
buffer[idx].template emplace(std::move(other.get_obj_(idx)));
}
}
other.clear();
}
~static_circular_map() { clear(); }
static_circular_map& operator=(const static_circular_map<K, T, N>& other)
{
if (this == &other) {
return *this;
}
for (size_t idx = 0; idx < other.capacity(); ++idx) {
copy_if_present_helper(buffer[idx], other.buffer[idx], present[idx], other.present[idx]);
}
count = other.count;
present = other.present;
}
static_circular_map& operator=(static_circular_map<K, T, N>&& other) noexcept
{
for (size_t idx = 0; idx < other.capacity(); ++idx) {
move_if_present_helper(buffer[idx], other.buffer[idx], present[idx], other.present[idx]);
}
count = other.count;
present = other.present;
other.clear();
return *this;
}
bool contains(K id) const
{
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;
}
buffer[idx].template emplace(id, obj);
present[idx] = true;
count++;
return true;
}
srsran::expected<iterator, T> insert(K id, T&& obj)
{
size_t idx = id % N;
if (present[idx]) {
return srsran::expected<iterator, T>(std::move(obj));
}
buffer[idx].template emplace(id, std::move(obj));
present[idx] = true;
count++;
return iterator(this, idx);
}
template <typename U>
void overwrite(K id, U&& obj)
{
size_t idx = id % N;
if (present[idx]) {
erase(buffer[idx].get().first);
}
insert(id, std::forward<U>(obj));
}
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 and it.ptr == this, "Iterator out-of-bounds (%zd >= %zd)", it.idx, N);
iterator next = it;
++next;
present[it.idx] = false;
get_obj_(it.idx).~obj_t();
--count;
return next;
}
void clear()
{
for (size_t i = 0; i < N; ++i) {
if (present[i]) {
present[i] = false;
get_obj_(i).~obj_t();
}
}
count = 0;
}
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; }
bool has_space(K id) { return not present[id % N]; }
size_t capacity() const { 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); }
iterator find(K id)
{
if (contains(id)) {
return iterator(this, id % N);
}
return end();
}
const_iterator find(K id) const
{
if (contains(id)) {
return const_iterator(this, id % N);
}
return end();
}
private:
obj_t& get_obj_(size_t idx) { return buffer[idx].get(); }
const obj_t& get_obj_(size_t idx) const { return buffer[idx].get(); }
std::array<detail::type_storage<obj_t>, N> buffer;
std::array<bool, N> present;
size_t count = 0;
};
/**
* Operates like a circular map, but automatically assigns the ID/key to inserted objects in a monotonically
* increasing way. The assigned IDs are not necessarily contiguous, as they are selected based on the available slots
* in the circular map
* @tparam K type of ID/key
* @tparam T object being inserted
* @tparam MAX_N maximum size of pool
*/
template <typename K, typename T, size_t MAX_N>
class static_id_obj_pool : private static_circular_map<K, T, MAX_N>
{
using base_t = static_circular_map<K, T, MAX_N>;
public:
using iterator = typename base_t::iterator;
using const_iterator = typename base_t::const_iterator;
using base_t::operator[];
using base_t::begin;
using base_t::contains;
using base_t::empty;
using base_t::end;
using base_t::erase;
using base_t::find;
using base_t::full;
using base_t::size;
explicit static_id_obj_pool(K first_id = 0) : next_id(first_id) {}
template <typename U>
srsran::expected<K> insert(U&& t)
{
if (full()) {
return srsran::default_error_t{};
}
while (not base_t::has_space(next_id)) {
++next_id;
}
base_t::insert(next_id, std::forward<U>(t));
return next_id++;
}
private:
K next_id = 0;
};
} // namespace srsran
#endif // SRSRAN_ID_MAP_H