298 lines
9.6 KiB
C++
298 lines
9.6 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#ifndef INDEXED_STORE_H
|
|
#define INDEXED_STORE_H
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "nextpnr_assertions.h"
|
|
#include "nextpnr_namespaces.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
template <typename T> struct store_index
|
|
{
|
|
int32_t m_index = -1;
|
|
store_index() = default;
|
|
explicit store_index(int32_t index) : m_index(index) {};
|
|
int32_t idx() const { return m_index; }
|
|
void set(int32_t index) { m_index = index; }
|
|
bool empty() const { return m_index == -1; }
|
|
bool operator==(const store_index<T> &other) const { return m_index == other.m_index; }
|
|
bool operator!=(const store_index<T> &other) const { return m_index != other.m_index; }
|
|
bool operator<(const store_index<T> &other) const { return m_index < other.m_index; }
|
|
unsigned int hash() const { return m_index; }
|
|
|
|
operator bool() const { return !empty(); }
|
|
operator int() const = delete;
|
|
bool operator!() const { return empty(); }
|
|
};
|
|
|
|
// "Slotted" indexed object store
|
|
template <typename T> class indexed_store
|
|
{
|
|
private:
|
|
// This should move to using std::optional at some point
|
|
class slot
|
|
{
|
|
private:
|
|
alignas(T) unsigned char storage[sizeof(T)];
|
|
int32_t next_free;
|
|
bool active;
|
|
inline T &obj() { return reinterpret_cast<T &>(storage); }
|
|
inline const T &obj() const { return reinterpret_cast<const T &>(storage); }
|
|
friend class indexed_store<T>;
|
|
|
|
public:
|
|
slot() : next_free(std::numeric_limits<int32_t>::max()), active(false) {};
|
|
slot(slot &&other) : next_free(other.next_free), active(other.active)
|
|
{
|
|
if (active)
|
|
::new (static_cast<void *>(&storage)) T(std::move(other.obj()));
|
|
};
|
|
|
|
slot(const slot &other) : next_free(other.next_free), active(other.active)
|
|
{
|
|
if (active)
|
|
::new (static_cast<void *>(&storage)) T(other.obj());
|
|
};
|
|
|
|
template <class... Args> void create(Args &&...args)
|
|
{
|
|
NPNR_ASSERT(!active);
|
|
active = true;
|
|
::new (static_cast<void *>(&storage)) T(std::forward<Args &&>(args)...);
|
|
}
|
|
bool empty() const { return !active; }
|
|
T &get()
|
|
{
|
|
NPNR_ASSERT(active);
|
|
return reinterpret_cast<T &>(storage);
|
|
}
|
|
const T &get() const
|
|
{
|
|
NPNR_ASSERT(active);
|
|
return reinterpret_cast<const T &>(storage);
|
|
}
|
|
void free(int32_t first_free)
|
|
{
|
|
NPNR_ASSERT(active);
|
|
obj().~T();
|
|
active = false;
|
|
next_free = first_free;
|
|
}
|
|
~slot()
|
|
{
|
|
if (active)
|
|
obj().~T();
|
|
}
|
|
};
|
|
|
|
std::vector<slot> slots;
|
|
int32_t first_free = 0;
|
|
int32_t active_count = 0;
|
|
|
|
public:
|
|
// Create a new entry and return its index
|
|
template <class... Args> store_index<T> add(Args &&...args)
|
|
{
|
|
++active_count;
|
|
if (first_free == int32_t(slots.size())) {
|
|
slots.emplace_back();
|
|
slots.back().create(std::forward<Args &&>(args)...);
|
|
++first_free;
|
|
return store_index<T>(int32_t(slots.size()) - 1);
|
|
} else {
|
|
int32_t idx = first_free;
|
|
auto &slot = slots.at(idx);
|
|
first_free = slot.next_free;
|
|
slot.create(std::forward<Args &&>(args)...);
|
|
return store_index<T>(idx);
|
|
}
|
|
}
|
|
|
|
// Remove an entry at an index
|
|
void remove(store_index<T> idx)
|
|
{
|
|
--active_count;
|
|
slots.at(idx.m_index).free(first_free);
|
|
first_free = idx.m_index;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
active_count = 0;
|
|
first_free = 0;
|
|
slots.clear();
|
|
}
|
|
|
|
// Number of live entries
|
|
int32_t entries() const { return active_count; }
|
|
bool empty() const { return (entries() == 0); }
|
|
|
|
// Reserve a certain amount of space
|
|
void reserve(int32_t size) { slots.reserve(size); }
|
|
|
|
// Check if an index exists
|
|
int32_t count(store_index<T> idx)
|
|
{
|
|
if (idx.m_index < 0 || idx.m_index >= int32_t(slots.size()))
|
|
return 0;
|
|
return slots.at(idx.m_index).empty() ? 0 : 1;
|
|
}
|
|
|
|
// Get an item by index
|
|
T &at(store_index<T> idx) { return slots.at(idx.m_index).get(); }
|
|
const T &at(store_index<T> idx) const { return slots.at(idx.m_index).get(); }
|
|
T &operator[](store_index<T> idx) { return slots.at(idx.m_index).get(); }
|
|
const T &operator[](store_index<T> idx) const { return slots.at(idx.m_index).get(); }
|
|
|
|
// Total size of the container
|
|
int32_t capacity() const { return int32_t(slots.size()); }
|
|
|
|
// Iterate over items
|
|
template <typename It, typename S> class enumerated_iterator;
|
|
|
|
class iterator
|
|
{
|
|
private:
|
|
indexed_store *base;
|
|
int32_t index = 0;
|
|
|
|
public:
|
|
iterator(indexed_store *base, int32_t index) : base(base), index(index) {};
|
|
inline bool operator!=(const iterator &other) const { return other.index != index; }
|
|
inline bool operator==(const iterator &other) const { return other.index == index; }
|
|
inline iterator operator++()
|
|
{
|
|
// skip over unused slots
|
|
do {
|
|
index++;
|
|
} while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
|
|
return *this;
|
|
}
|
|
inline iterator operator++(int)
|
|
{
|
|
iterator prior(*this);
|
|
do {
|
|
index++;
|
|
} while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
|
|
return prior;
|
|
}
|
|
T &operator*() { return base->at(store_index<T>(index)); }
|
|
template <typename It, typename S> friend class indexed_store::enumerated_iterator;
|
|
};
|
|
iterator begin()
|
|
{
|
|
auto it = iterator{this, -1};
|
|
++it;
|
|
return it;
|
|
}
|
|
iterator end() { return iterator{this, int32_t(slots.size())}; }
|
|
|
|
class const_iterator
|
|
{
|
|
private:
|
|
const indexed_store *base;
|
|
int32_t index = 0;
|
|
|
|
public:
|
|
const_iterator(const indexed_store *base, int32_t index) : base(base), index(index) {};
|
|
inline bool operator!=(const const_iterator &other) const { return other.index != index; }
|
|
inline bool operator==(const const_iterator &other) const { return other.index == index; }
|
|
inline const_iterator operator++()
|
|
{
|
|
// skip over unused slots
|
|
do {
|
|
index++;
|
|
} while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
|
|
return *this;
|
|
}
|
|
inline const_iterator operator++(int)
|
|
{
|
|
iterator prior(*this);
|
|
do {
|
|
index++;
|
|
} while (index < int32_t(base->slots.size()) && !base->slots.at(index).active);
|
|
return prior;
|
|
}
|
|
const T &operator*() { return base->at(store_index<T>(index)); }
|
|
template <typename It, typename S> friend class indexed_store::enumerated_iterator;
|
|
};
|
|
const_iterator begin() const
|
|
{
|
|
auto it = const_iterator{this, -1};
|
|
++it;
|
|
return it;
|
|
}
|
|
const_iterator end() const { return const_iterator{this, int32_t(slots.size())}; }
|
|
|
|
template <typename S> struct enumerated_item
|
|
{
|
|
enumerated_item(int32_t index, T &value) : index(index), value(value) {};
|
|
store_index<std::remove_cv_t<S>> index;
|
|
S &value;
|
|
};
|
|
|
|
template <typename It, typename S> class enumerated_iterator
|
|
{
|
|
private:
|
|
It base;
|
|
|
|
public:
|
|
enumerated_iterator(const It &base) : base(base) {};
|
|
inline bool operator!=(const enumerated_iterator<It, S> &other) const { return other.base != base; }
|
|
inline bool operator==(const enumerated_iterator<It, S> &other) const { return other.base == base; }
|
|
inline enumerated_iterator<It, S> operator++()
|
|
{
|
|
++base;
|
|
return *this;
|
|
}
|
|
inline enumerated_iterator<It, S> operator++(int)
|
|
{
|
|
iterator prior(*this);
|
|
++base;
|
|
return prior;
|
|
}
|
|
enumerated_item<S> operator*() { return enumerated_item<S>{base.index, *base}; }
|
|
};
|
|
|
|
template <typename It, typename S> struct enumerated_range
|
|
{
|
|
enumerated_range(const It &begin, const It &end) : m_begin(begin), m_end(end) {};
|
|
enumerated_iterator<It, S> m_begin, m_end;
|
|
enumerated_iterator<It, S> begin() { return m_begin; }
|
|
enumerated_iterator<It, S> end() { return m_end; }
|
|
};
|
|
|
|
enumerated_range<iterator, T> enumerate() { return enumerated_range<iterator, T>{begin(), end()}; }
|
|
enumerated_range<const_iterator, const T> enumerate() const
|
|
{
|
|
return enumerated_range<iterator, T>{begin(), end()};
|
|
}
|
|
};
|
|
|
|
NEXTPNR_NAMESPACE_END
|
|
|
|
#endif
|