Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
martinus committed Dec 22, 2023
1 parent fd2f577 commit 2f91ca1
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
58 changes: 51 additions & 7 deletions include/ankerl/unordered_dense.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
# include <iterator> // for pair, distance
# include <limits> // for numeric_limits
# include <memory> // for allocator, allocator_traits, shared_ptr
# include <optional> // for optional
# include <stdexcept> // for out_of_range
# include <string> // for basic_string
# include <string_view> // for basic_string_view, hash
Expand Down Expand Up @@ -1008,7 +1009,8 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
clear_and_fill_buckets_from_values();
}

void do_erase(value_idx_type bucket_idx) {
template <typename Op>
void do_erase(value_idx_type bucket_idx, Op handle_erased_value) {
auto const value_idx_to_remove = at(m_buckets, bucket_idx).m_value_idx;

// shift down until either empty or an element with correct spot is found
Expand All @@ -1019,6 +1021,7 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
bucket_idx = std::exchange(next_bucket_idx, next(next_bucket_idx));
}
at(m_buckets, bucket_idx) = {};
handle_erased_value(std::move(m_values[value_idx_to_remove]));

// update m_values
if (value_idx_to_remove != m_values.size() - 1) {
Expand All @@ -1039,8 +1042,8 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
m_values.pop_back();
}

template <typename K>
auto do_erase_key(K&& key) -> size_t {
template <typename K, typename Op>
auto do_erase_key(K&& key, Op handle_erased_value) -> size_t {
if (empty()) {
return 0;
}
Expand All @@ -1056,7 +1059,7 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
if (dist_and_fingerprint != at(m_buckets, bucket_idx).m_dist_and_fingerprint) {
return 0;
}
do_erase(bucket_idx);
do_erase(bucket_idx, handle_erased_value);
return 1;
}

Expand Down Expand Up @@ -1619,15 +1622,37 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
bucket_idx = next(bucket_idx);
}

do_erase(bucket_idx);
do_erase(bucket_idx, [](value_type&& /*unused*/) {
});
return begin() + static_cast<difference_type>(value_idx_to_remove);
}

auto extract(iterator it) -> value_type {
auto hash = mixed_hash(get_key(*it));
auto bucket_idx = bucket_idx_from_hash(hash);

auto const value_idx_to_remove = static_cast<value_idx_type>(it - cbegin());
while (at(m_buckets, bucket_idx).m_value_idx != value_idx_to_remove) {
bucket_idx = next(bucket_idx);
}

auto tmp = std::optional<value_type>{};
do_erase(bucket_idx, [&tmp](value_type&& val) {
tmp = std::move(val);
});
return std::move(tmp).value();
}

template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true>
auto erase(const_iterator it) -> iterator {
return erase(begin() + (it - cbegin()));
}

template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true>
auto extract(const_iterator it) -> value_type {
return extract(begin() + (it - cbegin()));
}

auto erase(const_iterator first, const_iterator last) -> iterator {
auto const idx_first = first - cbegin();
auto const idx_last = last - cbegin();
Expand All @@ -1653,12 +1678,31 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
}

auto erase(Key const& key) -> size_t {
return do_erase_key(key);
return do_erase_key(key, [](value_type&& /*unused*/) {
});
}

auto extract(Key const& key) -> std::optional<value_type> {
auto tmp = std::optional<value_type>{};
do_erase_key(key, [&tmp](value_type&& val) {
tmp = std::move(val);
});
return tmp;
}

template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true>
auto erase(K&& key) -> size_t {
return do_erase_key(std::forward<K>(key));
return do_erase_key(std::forward<K>(key), [](value_type&& /*unused*/) {
});
}

template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true>
auto extract(K&& key) -> std::optional<value_type> {
auto tmp = std::optional<value_type>{};
do_erase_key(std::forward<K>(key), [&tmp](value_type&& val) {
tmp = std::move(val);
});
return tmp;
}

void swap(table& other) noexcept(noexcept(std::is_nothrow_swappable_v<value_container_type> &&
Expand Down
22 changes: 22 additions & 0 deletions test/unit/extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <app/counter.h>
#include <app/doctest.h>

#include <fmt/format.h>

TEST_CASE_MAP("extract", counter::obj, counter::obj) {
auto counts = counter();
INFO(counts);
Expand All @@ -24,3 +26,23 @@ TEST_CASE_MAP("extract", counter::obj, counter::obj) {
REQUIRE(container[i].second.get() == i);
}
}

TEST_CASE_MAP("extract_element", counter::obj, counter::obj) {
auto counts = counter();
INFO(counts);

counts("init");
auto map = map_t();
for (size_t i = 0; i < 100; ++i) {
map.try_emplace(counter::obj{i, counts}, i, counts);
}

{
counts("after creation of 100 entries");
auto opt = map.extract(counter::obj{42, counts});
counts("moved out 1 element");
REQUIRE(opt);
REQUIRE(opt->first.get() == 42);
REQUIRE(opt->second.get() == 42);
}
}

0 comments on commit 2f91ca1

Please sign in to comment.