82 using Slot = std::variant<T, impl::slot_map::FreeNode>;
84 static_assert(std::ranges::forward_range<Container<Slot>&>);
85 static_assert(std::ranges::forward_range<
const Container<Slot>&>);
86 static_assert(std::ranges::sized_range<
const Container<Slot>&>);
87 static_assert(impl::slot_map::Indexable<Container<Slot>&>);
88 static_assert(impl::slot_map::Indexable<
const Container<Slot>&>);
101 template <
std::input_iterator It,
std::sentinel_for<It> Sentinel>
103 insert_range(std::ranges::subrange(std::move(first), std::move(last)));
106 SlotMap(
const SlotMap&) =
default;
107 SlotMap& operator=(
const SlotMap&) =
default;
109 SlotMap(SlotMap&&)
noexcept =
default;
110 SlotMap& operator=(SlotMap&&)
noexcept =
default;
112 ~SlotMap() =
default;
120 template <
typename... Args>
122 if (
const auto entry = TryPopFromFreeList(); entry.slot !=
nullptr) {
124 T& element = entry.slot->
template emplace<T>(std::forward<Args>(args)...);
125 return {element, entry.index};
127 EraseAndPushToFreeList(entry);
132 const std::size_t index = std::ranges::size(slots_);
133 Slot& slot = slots_.emplace_back(std::in_place_type<T>, std::forward<Args>(args)...);
134 auto*
const value = std::get_if<T>(&slot);
136 return {*value, index};
159 template <
std::
ranges::input_range Range>
161 for (
auto&& elem : std::forward<Range>(range)) {
162 emplace(std::forward<
decltype(elem)>(elem));
167 [[nodiscard]] std::size_t
size()
const noexcept {
return std::ranges::size(slots_) - free_list_size_; }
175 [[nodiscard]] std::size_t
capacity()
const noexcept {
176 if constexpr (impl::slot_map::HasCapacity<
const Container<Slot>&>) {
177 return slots_.capacity();
179 return std::ranges::size(slots_);
185 requires meta::IsReservable<Container<Slot>>
187 slots_.reserve(capacity);
193 T&
operator[](std::size_t index)
noexcept USERVER_IMPL_LIFETIME_BOUND {
194 UASSERT(index < std::ranges::size(slots_));
195 auto*
const value = std::get_if<T>(&slots_[index]);
201 const T&
operator[](std::size_t index)
const noexcept USERVER_IMPL_LIFETIME_BOUND {
202 UASSERT(index < std::ranges::size(slots_));
203 const auto*
const value = std::get_if<T>(&slots_[index]);
217 std::size_t
erase(std::size_t index) {
218 UASSERT(index < std::ranges::size(slots_));
219 auto& slot = slots_[index];
220 if (std::get_if<T>(&slot) ==
nullptr) {
223 EraseAndPushToFreeList({.slot = &slot, .index = index});
232 return std::ranges::ref_view(slots_) | std::views::filter(IsLive{}) | std::views::transform(ToValue{});
237 return std::ranges::ref_view(slots_) | std::views::filter(IsLive{}) | std::views::transform(ToConstValue{});
242 bool operator()(
const Slot& slot)
const noexcept {
return std::holds_alternative<T>(slot); }
246 T& operator()(Slot& slot)
const noexcept {
247 auto*
const value = std::get_if<T>(&slot);
253 struct ToConstValue {
254 const T& operator()(
const Slot& slot)
const noexcept {
255 const auto*
const value = std::get_if<T>(&slot);
261 struct FreeListEntry
final {
266 FreeListEntry TryPopFromFreeList()
noexcept {
267 const auto index = free_list_head_;
269 if (index == impl::slot_map::kFreeListEnd) {
270 return {.slot =
nullptr, .index = impl::slot_map::kFreeListEnd};
273 Slot& slot = slots_[index];
274 const auto*
const node = std::get_if<impl::slot_map::FreeNode>(&slot);
276 free_list_head_ = node->next_index;
278 return {.slot = &slot, .index = index};
281 void EraseAndPushToFreeList(FreeListEntry entry)
noexcept {
282 UASSERT(entry.slot !=
nullptr);
283 UASSERT(entry.slot == &slots_[entry.index]);
284 entry.slot->
template emplace<impl::slot_map::FreeNode>(free_list_head_);
285 free_list_head_ = entry.index;
289 Container<Slot> slots_;
290 std::size_t free_list_head_{impl::slot_map::kFreeListEnd};
291 std::size_t free_list_size_{0};