/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2021-22 gatecat * * 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 #include #include #include #include "nextpnr_assertions.h" #include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN template 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 &other) const { return m_index == other.m_index; } bool operator!=(const store_index &other) const { return m_index != other.m_index; } bool operator<(const store_index &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 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(storage); } inline const T &obj() const { return reinterpret_cast(storage); } friend class indexed_store; public: slot() : next_free(std::numeric_limits::max()), active(false){}; slot(slot &&other) : next_free(other.next_free), active(other.active) { if (active) ::new (static_cast(&storage)) T(std::move(other.obj())); }; slot(const slot &other) : next_free(other.next_free), active(other.active) { if (active) ::new (static_cast(&storage)) T(other.obj()); }; template void create(Args &&...args) { NPNR_ASSERT(!active); active = true; ::new (static_cast(&storage)) T(std::forward(args)...); } bool empty() const { return !active; } T &get() { NPNR_ASSERT(active); return reinterpret_cast(storage); } const T &get() const { NPNR_ASSERT(active); return reinterpret_cast(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 slots; int32_t first_free = 0; int32_t active_count = 0; public: // Create a new entry and return its index template store_index add(Args &&...args) { ++active_count; if (first_free == int32_t(slots.size())) { slots.emplace_back(); slots.back().create(std::forward(args)...); ++first_free; return store_index(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)...); return store_index(idx); } } // Remove an entry at an index void remove(store_index 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 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 idx) { return slots.at(idx.m_index).get(); } const T &at(store_index idx) const { return slots.at(idx.m_index).get(); } T &operator[](store_index idx) { return slots.at(idx.m_index).get(); } const T &operator[](store_index 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 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(index)); } template 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(index)); } template 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 struct enumerated_item { enumerated_item(int32_t index, T &value) : index(index), value(value){}; store_index> index; S &value; }; template class enumerated_iterator { private: It base; public: enumerated_iterator(const It &base) : base(base){}; inline bool operator!=(const enumerated_iterator &other) const { return other.base != base; } inline bool operator==(const enumerated_iterator &other) const { return other.base == base; } inline enumerated_iterator operator++() { ++base; return *this; } inline enumerated_iterator operator++(int) { iterator prior(*this); ++base; return prior; } enumerated_item operator*() { return enumerated_item{base.index, *base}; } }; template struct enumerated_range { enumerated_range(const It &begin, const It &end) : m_begin(begin), m_end(end){}; enumerated_iterator m_begin, m_end; enumerated_iterator begin() { return m_begin; } enumerated_iterator end() { return m_end; } }; enumerated_range enumerate() { return enumerated_range{begin(), end()}; } enumerated_range enumerate() const { return enumerated_range{begin(), end()}; } }; NEXTPNR_NAMESPACE_END #endif