/* * 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