stellarlib 0.1.0
Loading...
Searching...
No Matches
sparse_map.hpp
1/* clang-format off */
2
3/*
4 stellarlib
5 Copyright (C) 2025-2026 Domán Zana
6
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19 2. Altered source versions must be plainly marked as such, and must not be
20 misrepresented as being the original software.
21 3. This notice may not be removed or altered from any source distribution.
22*/
23
24#ifndef STELLARLIB_ECS_SPARSE_MAP_HPP
25#define STELLARLIB_ECS_SPARSE_MAP_HPP
26
27#include <stellarlib/ecs/any_set.hpp>
28#include <stellarlib/ecs/stack_vector.hpp>
29
30#include <limits>
31#include <memory>
32#include <ranges>
33#include <type_traits>
34#include <utility>
35
36namespace stellarlib::ecs::internal
37{
38template <typename Key, typename T>
39class sparse_map final : public any_set<Key>
40{
41public:
42 [[nodiscard]]
43 constexpr sparse_map() noexcept = default;
44
45 [[nodiscard]]
46 constexpr sparse_map(const sparse_map &) noexcept = delete;
47
48 [[nodiscard]]
49 constexpr sparse_map(sparse_map &&) noexcept = default;
50
51 constexpr auto operator=(const sparse_map &) noexcept
52 -> sparse_map & = delete;
53
54 constexpr auto operator=(sparse_map &&) noexcept
55 -> sparse_map & = default;
56
57 constexpr ~sparse_map() noexcept final = default;
58
59 template <typename ...Args>
60 constexpr void insert(const Key key, Args &&...args) noexcept
61 {
62 if (_sparse.extend(key + 1, std::numeric_limits<Key>::max()) || _sparse[key] == std::numeric_limits<Key>::max()) {
63 _sparse[key] = _keys.size();
64 _keys.push(key);
65 _values.push(std::forward<Args>(args)...);
66 }
67 else if constexpr (sizeof...(Args) == 1 && (std::is_assignable_v<T &, Args> && ...)) {
68 (*this)[key] = (std::forward<Args>(args), ...);
69 }
70 else {
71 std::destroy_at(_values.begin() + _sparse[key]);
72 std::construct_at(_values.begin() + _sparse[key], std::forward<Args>(args)...);
73 }
74 }
75
76 [[nodiscard]]
77 constexpr auto size() const noexcept
78 {
79 return _keys.size();
80 }
81
82 [[nodiscard]]
83 constexpr auto contains(const Key key) const noexcept
84 {
85 return key < _sparse.size() && _sparse[key] != std::numeric_limits<Key>::max();
86 }
87
88 [[nodiscard]]
89 constexpr auto at(const Key key) const noexcept
90 {
91 return contains(key) ? _values.begin() + _sparse[key] : nullptr;
92 }
93
94 [[nodiscard]]
95 constexpr auto at(const Key key) noexcept
96 {
97 return contains(key) ? _values.begin() + _sparse[key] : nullptr;
98 }
99
100 [[nodiscard]]
101 constexpr auto operator[](const Key key) const noexcept
102 -> const auto &
103 {
104 return _values[_sparse[key]];
105 }
106
107 [[nodiscard]]
108 constexpr auto operator[](const Key key) noexcept
109 -> auto &
110 {
111 return _values[_sparse[key]];
112 }
113
114 [[nodiscard]]
115 constexpr auto values() const noexcept
116 {
117 return std::ranges::subrange{_values.begin(), _values.end()};
118 }
119
120 [[nodiscard]]
121 constexpr auto values() noexcept
122 {
123 return std::ranges::subrange{_values.begin(), _values.end()};
124 }
125
126 [[nodiscard]]
127 constexpr auto zip() const noexcept
128 {
129 return std::views::zip(std::ranges::subrange{static_cast<const Key *>(_keys.begin()), static_cast<const Key *>(_keys.end())}, values());
130 }
131
132 [[nodiscard]]
133 constexpr auto zip() noexcept
134 {
135 return std::views::zip(std::ranges::subrange{static_cast<const Key *>(_keys.begin()), static_cast<const Key *>(_keys.end())}, values());
136 }
137
138 constexpr void erase(const Key key) noexcept final
139 {
140 if (!contains(key)) {
141 return;
142 }
143
144 if (_sparse[key] != _keys.size() - 1) {
145 _values[_sparse[key]] = std::move(*(_values.end() - 1));
146 _keys[_sparse[key]] = *(_keys.end() - 1);
147 _sparse[_keys[_sparse[key]]] = _sparse[key];
148 }
149
150 _sparse[key] = std::numeric_limits<Key>::max();
151 _keys.pop();
152 _values.pop();
153 }
154
155 constexpr void clear() noexcept final
156 {
157 _sparse.clear();
158 _keys.clear();
159 _values.clear();
160 }
161
162private:
163 stack_vector<Key, Key> _sparse;
164 stack_vector<T, Key> _values;
165 stack_vector<Key, Key> _keys;
166};
167}
168
169#endif