83 using Slot = std::variant<T, impl::slot_map::FreeNode>;
85 static_assert(std::ranges::forward_range<Container<Slot>&>);
86 static_assert(std::ranges::forward_range<
const Container<Slot>&>);
87 static_assert(std::ranges::sized_range<
const Container<Slot>&>);
88 static_assert(impl::slot_map::Indexable<Container<Slot>&>);
89 static_assert(impl::slot_map::Indexable<
const Container<Slot>&>);
102 template <
std::input_iterator It,
std::sentinel_for<It> Sentinel>
104 insert_range(std::ranges::subrange(std::move(first), std::move(last)));
107 SlotMap(
const SlotMap&) =
default;
108 SlotMap& operator=(
const SlotMap&) =
default;
110 SlotMap(SlotMap&&)
noexcept =
default;
111 SlotMap& operator=(SlotMap&&)
noexcept =
default;
113 ~SlotMap() =
default;
121 template <
typename... Args>
123 if (
const auto entry = TryPopFromFreeList(); entry.slot !=
nullptr) {
125 T& element = entry.slot->
template emplace<T>(std::forward<Args>(args)...);
126 return {element, entry.index};
128 EraseAndPushToFreeList(entry);
133 const std::size_t index = std::ranges::size(slots_);
134 Slot& slot = slots_.emplace_back(std::in_place_type<T>, std::forward<Args>(args)...);
135 auto*
const value = std::get_if<T>(&slot);
137 return {*value, index};
160 template <
std::
ranges::input_range Range>
162 for (
auto&& elem : std::forward<Range>(range)) {
163 emplace(std::forward<
decltype(elem)>(elem));
168 [[nodiscard]] std::size_t
size()
const noexcept {
return std::ranges::size(slots_) - free_list_size_; }
176 [[nodiscard]] std::size_t
capacity()
const noexcept {
177 if constexpr (impl::slot_map::HasCapacity<
const Container<Slot>&>) {
178 return slots_.capacity();
180 return std::ranges::size(slots_);
188 slots_.reserve(capacity);
194 T&
operator[](std::size_t index)
noexcept USERVER_IMPL_LIFETIME_BOUND {
195 UASSERT(index < std::ranges::size(slots_));
196 auto*
const value = std::get_if<T>(&slots_[index]);
202 const T&
operator[](std::size_t index)
const noexcept USERVER_IMPL_LIFETIME_BOUND {
203 UASSERT(index < std::ranges::size(slots_));
204 const auto*
const value = std::get_if<T>(&slots_[index]);
218 std::size_t
erase(std::size_t index) {
219 UASSERT(index < std::ranges::size(slots_));
220 auto& slot = slots_[index];
221 if (std::get_if<T>(&slot) ==
nullptr) {
224 EraseAndPushToFreeList({.slot = &slot, .index = index});
233 return std::ranges::ref_view(slots_) | std::views::filter(IsLive{}) | std::views::transform(ToValue{});
238 return std::ranges::ref_view(slots_) | std::views::filter(IsLive{}) | std::views::transform(ToConstValue{});
243 bool operator()(
const Slot& slot)
const noexcept {
return std::holds_alternative<T>(slot); }
247 T& operator()(Slot& slot)
const noexcept {
248 auto*
const value = std::get_if<T>(&slot);
254 struct ToConstValue {
255 const T& operator()(
const Slot& slot)
const noexcept {
256 const auto*
const value = std::get_if<T>(&slot);
262 struct FreeListEntry
final {
267 FreeListEntry TryPopFromFreeList()
noexcept {
268 const auto index = free_list_head_;
270 if (index == impl::slot_map::kFreeListEnd) {
271 return {.slot =
nullptr, .index = impl::slot_map::kFreeListEnd};
274 Slot& slot = slots_[index];
275 const auto*
const node = std::get_if<impl::slot_map::FreeNode>(&slot);
277 free_list_head_ = node->next_index;
279 return {.slot = &slot, .index = index};
282 void EraseAndPushToFreeList(FreeListEntry entry)
noexcept {
283 UASSERT(entry.slot !=
nullptr);
284 UASSERT(entry.slot == &slots_[entry.index]);
285 entry.slot->
template emplace<impl::slot_map::FreeNode>(free_list_head_);
286 free_list_head_ = entry.index;
290 Container<Slot> slots_;
291 std::size_t free_list_head_{impl::slot_map::kFreeListEnd};
292 std::size_t free_list_size_{0};