/* OmniaFramework - A collection of useful functionality Copyright (C) 2026 OmniaX-Dev This file is part of OmniaFramework. OmniaFramework is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmniaFramework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OmniaFramework. If not, see . */ #pragma once #include #include #include #include #include namespace ostd { // ============================================================================ // StaticHashMap // A drop-in replacement for stdumap (std::unordered_map) that preserves // insertion order. Provides: // - O(1) lookup by key (via internal unordered_map index) // - O(1) lookup by index (via internal deque) // - O(n) removal (index rebuild after erase) // - Insertion-order iteration via begin()/end() // ============================================================================ template class StaticHashMap { public: using value_type = std::pair; using storage_type = std::deque; using iterator = typename storage_type::iterator; using const_iterator = typename storage_type::const_iterator; // ============================================================ // Capacity // ============================================================ public: inline i32 size(void) const { return cast(m_entries.size()); } inline bool empty(void) const { return m_entries.empty(); } // ============================================================ // Element access — key based // ============================================================ public: // STL-style operator[]: inserts a default-constructed V if key absent, // then returns a reference. Allows map["key"] = value idiom. V& operator[](const K& key) { auto it = m_index.find(key); if (it == m_index.end()) { m_index[key] = cast(m_entries.size()); m_entries.push_back({ key, V{} }); return m_entries.back().second; } return m_entries[it->second].second; } // Bounds-checked key access — throws std::out_of_range if absent. V& at(const K& key) { auto it = m_index.find(key); if (it == m_index.end()) throw std::out_of_range("StaticHashMap::at — key not found"); return m_entries[it->second].second; } const V& at(const K& key) const { auto it = m_index.find(key); if (it == m_index.end()) throw std::out_of_range("StaticHashMap::at — key not found"); return m_entries[it->second].second; } // ============================================================ // Element access — index based (vector-style) // ============================================================ public: // Direct index access — no bounds check (mirrors std::vector::operator[]) V& operator[](i32 index) { return m_entries[cast(index)].second; } const V& operator[](i32 index) const { return m_entries[cast(index)].second; } // Bounds-checked index access — throws std::out_of_range V& at(i32 index) { if (index < 0 || index >= size()) throw std::out_of_range("StaticHashMap::at — index out of range"); return m_entries[cast(index)].second; } const V& at(i32 index) const { if (index < 0 || index >= size()) throw std::out_of_range("StaticHashMap::at — index out of range"); return m_entries[cast(index)].second; } // Access to the full pair at a given index (key + value) value_type& entry(i32 index) { return m_entries[cast(index)]; } const value_type& entry(i32 index) const { return m_entries[cast(index)]; } // Returns the key at a given insertion-order index const K& keyAt(i32 index) const { return m_entries[cast(index)].first; } V& front(void) { return m_entries.front().second; } V& back(void) { return m_entries.back().second; } const V& front(void) const { return m_entries.front().second; } const V& back(void) const { return m_entries.back().second; } // ============================================================ // Lookup // ============================================================ public: // Returns 1 if key exists, 0 otherwise — mirrors stdumap::count() inline i32 count(const K& key) const { return m_index.count(key) ? 1 : 0; } // C++20-style contains — cleaner than count() for boolean checks inline bool contains(const K& key) const { return m_index.count(key) > 0; } // Returns the insertion-order index of a key, or -1 if not found inline i32 indexOf(const K& key) const { auto it = m_index.find(key); if (it == m_index.end()) return -1; return it->second; } // Returns an iterator to the entry with the given key, or end() if absent. // The iterator dereferences to std::pair, same as stdumap. iterator find(const K& key) { auto it = m_index.find(key); if (it == m_index.end()) return m_entries.end(); return m_entries.begin() + it->second; } const_iterator find(const K& key) const { auto it = m_index.find(key); if (it == m_index.end()) return m_entries.end(); return m_entries.begin() + it->second; } // ============================================================ // Insertion // ============================================================ public: // Appends at the end. If key already exists the value is updated in-place, // insertion order is preserved (no re-insertion at end). // Returns a reference to the stored value. V& insert(const K& key, const V& value) { auto it = m_index.find(key); if (it != m_index.end()) { m_entries[it->second].second = value; return m_entries[it->second].second; } m_index[key] = cast(m_entries.size()); m_entries.push_back({ key, value }); return m_entries.back().second; } // STL-style pair insert — mirrors stdumap::insert({k, v}) // Returns pair: iterator to element, true if inserted std::pair insert(const value_type& kv) { auto it = m_index.find(kv.first); if (it != m_index.end()) return { m_entries.begin() + it->second, false }; i32 idx = cast(m_entries.size()); m_index[kv.first] = idx; m_entries.push_back(kv); return { m_entries.begin() + idx, true }; } // Inserts at a specific position (O(n) — rebuilds indices from pos onward) // If key already exists, does nothing and returns false. bool insertAt(i32 pos, const K& key, const V& value) { if (contains(key)) return false; pos = std::clamp(pos, 0, size()); m_entries.insert(m_entries.begin() + pos, { key, value }); // Rebuild index for every entry from pos onward for (i32 i = pos; i < size(); i++) m_index[m_entries[cast(i)].first] = i; return true; } // In-place construction — mirrors stdumap::emplace() // Returns pair template std::pair emplace(const K& key, Args&&... args) { auto it = m_index.find(key); if (it != m_index.end()) return { m_entries.begin() + it->second, false }; i32 idx = cast(m_entries.size()); m_index[key] = idx; m_entries.emplace_back(key, V(std::forward(args)...)); return { m_entries.begin() + idx, true }; } // Appends a pair — mirrors push_back on a vector of pairs inline void push_back(const value_type& kv) { insert(kv); } // Removes the last element void pop_back(void) { if (m_entries.empty()) return; m_index.erase(m_entries.back().first); m_entries.pop_back(); } // ============================================================ // Removal // ============================================================ public: // Removes by key — O(n) index rebuild for entries after the removed slot. // Returns true if the key was found and removed. bool erase(const K& key) { auto it = m_index.find(key); if (it == m_index.end()) return false; i32 pos = it->second; m_entries.erase(m_entries.begin() + pos); m_index.erase(it); // Fix up indices for everything that shifted for (i32 i = pos; i < size(); i++) m_index[m_entries[cast(i)].first] = i; return true; } // Removes by insertion-order index — O(n) index rebuild. bool eraseAt(i32 index) { if (index < 0 || index >= size()) return false; return erase(m_entries[cast(index)].first); } // Removes all entries — O(1) void clear(void) { m_entries.clear(); m_index.clear(); } // ============================================================ // Iteration (insertion order, mirrors stdumap range-for) // ============================================================ public: inline iterator begin(void) { return m_entries.begin(); } inline iterator end(void) { return m_entries.end(); } inline const_iterator begin(void) const { return m_entries.begin(); } inline const_iterator end(void) const { return m_entries.end(); } inline const_iterator cbegin(void) const { return m_entries.cbegin(); } inline const_iterator cend(void) const { return m_entries.cend(); } // ============================================================ // Private // ============================================================ private: storage_type m_entries; // insertion-ordered pairs std::unordered_map m_index; // key → index into m_entries }; // Convenience alias that matches the stdumap naming convention in Types.hpp template using stdomap = StaticHashMap; } // namespace ostd