srsLTE/lib/include/srsran/adt/circular_map.h

256 lines
6.6 KiB
C++

/**
*
* \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 <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>;
using obj_storage_t = typename std::aligned_storage<sizeof(obj_t), alignof(obj_t)>::type;
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->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<K, T, N>;
static_circular_map<K, T, N>* ptr = nullptr;
size_t idx = 0;
};
class const_iterator
{
public:
const_iterator() = default;
const_iterator(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]; }
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<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.size(); ++idx) {
if (present[idx]) {
new (&buffer[idx]) obj_t(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.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<K, T, N>& 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<K, T, N>&& 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<iterator, T> insert(K id, T&& obj)
{
size_t idx = id % N;
if (present[idx]) {
return srsran::expected<iterator, T>(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<obj_t&>(buffer[idx]); }
const obj_t& get_obj_(size_t idx) const { return reinterpret_cast<obj_t&>(buffer[idx]); }
std::array<obj_storage_t, N> buffer;
std::array<bool, N> present;
size_t count = 0;
};
} // namespace srsran
#endif // SRSRAN_ID_MAP_H