#pragma once #include "containers/sparse_set.hpp" #include // std::size_t #include // std::format #include // std::out_of_range #include // std::get #include // values container namespace kuiper { namespace containers { template class sparse_map { public: using sparse_set_t = containers::sparse_set; using values_container_t = std::vector; public: // Capacity /// Reserve a number of slots in the underlying containers void reserve(std::size_t size) { if (size > m_capacity) { m_values.resize(size); m_capacity = size; } return; } // Access /// Access value specified by key (non-const) V& at(const K& key) { if (!m_sparse_set.contains(key)) { throw std::out_of_range(std::format("key \"{}\" does not exist in the sparse map", key)); } const auto dense_idx = m_sparse_set.sparse()[key]; return m_values[dense_idx]; } /// Access value specified by key (const, read-only) const V& at(const K& key) const { if (!m_sparse_set.contains(key)) { throw std::out_of_range(std::format("key \"{}\" does not exist in the sparse map", key)); } const auto dense_idx = m_sparse_set.sparse()[key]; return m_values[dense_idx]; } // Modifiers void insert(const K& key, const V& value) { // 1. Insert key into the underlying sparse set // 2. Insert value into the corresponding index in the sparse map if (!m_sparse_set.contains(key)) { // Key does not yet exist in set const auto insertion = m_sparse_set.insert(key); if (!std::get<1>(insertion)) return; const auto dense_idx = std::get<0>(insertion); reserve(dense_idx + 1); // no-op if there's already enough space m_values[dense_idx] = value; ++m_size; return; } else { // Key already exists in set const auto dense_idx = m_sparse_set.sparse()[key]; m_values[dense_idx] = value; ++m_size; return; } } /// Remove an element from the map void erase(const K& key) { // 1. Retrieve dense array index from set // 2. Swap to back // 3. Erase from set // 4. Decrement number of elements if (contains(key)) { const auto dense_idx = m_sparse_set.sparse()[key]; m_values[dense_idx] = m_values[m_size - 1]; m_sparse_set.erase(key); --m_size; } return; } // Lookup /// Check if the key exists in the map bool contains(const K& key) const { return m_sparse_set.contains(key); } // Observers /// Access to the underlying sparse_set container const sparse_set_t& sparse_set() const noexcept { return m_sparse_set; } /// Access to the underlying values container const values_container_t& values() const noexcept { return m_values; } // Operator overloads /// Access/insert element specified by key V& operator[](const K& key) { const auto k = key; if (!contains(k)) { insert(k, {}); } return at(k); } /// Access element specified by key (const, read-only) const V& operator[](const K& key) const { return at(key); } private: containers::sparse_set m_sparse_set {}; values_container_t m_values {}; std::size_t m_size = 0; std::size_t m_capacity = 0; }; } // namespace containers } // namespace kuiper