Skip to content

Commit ed409fd

Browse files
committed
flat_map operator== added; bulk insertion optimized
1 parent 0474926 commit ed409fd

2 files changed

Lines changed: 115 additions & 2 deletions

File tree

container/flat_map.hpp

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ class flat_map
221221
[[nodiscard]] bool batch_open() const noexcept { return _batch_start != no_batch; }
222222
[[nodiscard]] const Compare& key_comp() const noexcept { return _compare; }
223223

224+
[[nodiscard]] friend bool operator==(const flat_map& left, const flat_map& right)
225+
{
226+
left.assert_not_batching();
227+
right.assert_not_batching();
228+
return left._keys == right._keys && left._values == right._values;
229+
}
230+
224231
[[nodiscard]] iterator begin() noexcept { assert_not_batching(); return iterator(this, 0); }
225232
[[nodiscard]] const_iterator begin() const noexcept { assert_not_batching(); return const_iterator(this, 0); }
226233
[[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }
@@ -399,11 +406,15 @@ class flat_map
399406
assert(batch_open());
400407
const auto batch_start = _batch_start;
401408
_batch_start = no_batch;
409+
if (batch_start == 0) {
410+
sort_and_deduplicate();
411+
return;
412+
}
402413

403414
std::vector<size_type> order(size() - batch_start);
404415
for (size_type index = 0; index < order.size(); ++index)
405416
order[index] = batch_start + index;
406-
std::stable_sort(order.begin(), order.end(), [this](size_type left, size_type right) { return _compare(_keys[left], _keys[right]); });
417+
sort_indices_by_key(order);
407418

408419
std::vector<Key> incoming_keys;
409420
std::vector<Mapped> incoming_values;
@@ -462,6 +473,14 @@ class flat_map
462473
assert(incoming_keys.size() == incoming_values.size());
463474
if (incoming_keys.empty())
464475
return;
476+
if (empty()) {
477+
const auto unique_size = deduplicate_sorted(incoming_keys, incoming_values);
478+
incoming_keys.resize(unique_size);
479+
incoming_values.resize(unique_size);
480+
_keys = std::move(incoming_keys);
481+
_values = std::move(incoming_values);
482+
return;
483+
}
465484

466485
std::vector<Key> merged_keys;
467486
std::vector<Mapped> merged_values;
@@ -512,6 +531,70 @@ class flat_map
512531
_values = std::move(merged_values);
513532
}
514533

534+
[[nodiscard]] size_type deduplicate_sorted(std::vector<Key>& keys, std::vector<Mapped>& values) const
535+
{
536+
assert(keys.size() == values.size());
537+
auto unique_size = size_type{ 0 };
538+
for (size_type index = 0; index < keys.size(); ++index) {
539+
if (unique_size != 0 && FlatContainerInternal::sorted_keys_equal(keys[unique_size - 1], keys[index], _compare))
540+
continue;
541+
if (unique_size != index) {
542+
keys[unique_size] = std::move(keys[index]);
543+
values[unique_size] = std::move(values[index]);
544+
}
545+
++unique_size;
546+
}
547+
return unique_size;
548+
}
549+
550+
void sort_indices_by_key(std::vector<size_type>& indices) const
551+
{
552+
std::sort(indices.begin(), indices.end(), [this](size_type left, size_type right) {
553+
if constexpr (FlatContainerInternal::EqualityComparable<Key, Key>) {
554+
return _keys[left] == _keys[right] ? left < right : _compare(_keys[left], _keys[right]);
555+
} else {
556+
if (_compare(_keys[left], _keys[right]))
557+
return true;
558+
if (_compare(_keys[right], _keys[left]))
559+
return false;
560+
return left < right;
561+
}
562+
});
563+
}
564+
565+
void sort_and_deduplicate()
566+
{
567+
std::vector<size_type> order(size());
568+
for (size_type index = 0; index < order.size(); ++index)
569+
order[index] = index;
570+
sort_indices_by_key(order);
571+
572+
for (size_type start = 0; start < order.size(); ++start) {
573+
if (order[start] == start)
574+
continue;
575+
576+
Key key = std::move(_keys[start]);
577+
Mapped value = std::move(_values[start]);
578+
auto destination = start;
579+
for (;;) {
580+
const auto source = order[destination];
581+
order[destination] = destination;
582+
if (source == start) {
583+
_keys[destination] = std::move(key);
584+
_values[destination] = std::move(value);
585+
break;
586+
}
587+
_keys[destination] = std::move(_keys[source]);
588+
_values[destination] = std::move(_values[source]);
589+
destination = source;
590+
}
591+
}
592+
593+
const auto unique_size = deduplicate_sorted(_keys, _values);
594+
_keys.resize(unique_size);
595+
_values.resize(unique_size);
596+
}
597+
515598
std::vector<Key> _keys;
516599
std::vector<Mapped> _values;
517600
[[no_unique_address]] Compare _compare{};
@@ -551,6 +634,13 @@ class flat_set
551634
[[nodiscard]] const Compare& key_comp() const noexcept { return _compare; }
552635
[[nodiscard]] const Compare& value_comp() const noexcept { return _compare; }
553636

637+
[[nodiscard]] friend bool operator==(const flat_set& left, const flat_set& right)
638+
{
639+
left.assert_not_batching();
640+
right.assert_not_batching();
641+
return left._keys == right._keys;
642+
}
643+
554644
[[nodiscard]] const_iterator begin() const noexcept { assert_not_batching(); return _keys.begin(); }
555645
[[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }
556646
[[nodiscard]] const_iterator end() const noexcept { assert_not_batching(); return _keys.end(); }
@@ -640,7 +730,12 @@ class flat_set
640730
assert(batch_open());
641731
const auto batch_start = _batch_start;
642732
_batch_start = no_batch;
643-
std::stable_sort(_keys.begin() + static_cast<difference_type>(batch_start), _keys.end(), _compare);
733+
std::sort(_keys.begin() + static_cast<difference_type>(batch_start), _keys.end(), _compare);
734+
if (batch_start == 0) {
735+
_keys.erase(std::unique(_keys.begin(), _keys.end(),
736+
[this](const Key& left, const Key& right) { return FlatContainerInternal::sorted_keys_equal(left, right, _compare); }), _keys.end());
737+
return;
738+
}
644739

645740
std::vector<Key> incoming_keys(std::make_move_iterator(_keys.begin() + static_cast<difference_type>(batch_start)), std::make_move_iterator(_keys.end()));
646741
_keys.erase(_keys.begin() + static_cast<difference_type>(batch_start), _keys.end());
@@ -689,6 +784,12 @@ class flat_set
689784
{
690785
if (incoming_keys.empty())
691786
return;
787+
if (empty()) {
788+
incoming_keys.erase(std::unique(incoming_keys.begin(), incoming_keys.end(),
789+
[this](const Key& left, const Key& right) { return FlatContainerInternal::sorted_keys_equal(left, right, _compare); }), incoming_keys.end());
790+
_keys = std::move(incoming_keys);
791+
return;
792+
}
692793

693794
std::vector<Key> merged_keys;
694795
merged_keys.reserve(size() + incoming_keys.size());

tests/test-app/flat_map_tests.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ TEST_CASE("flat_map supports pair-like random-access iteration", "[flat-map]")
100100
CHECK((copied == std::vector<std::pair<int, std::string>>{ { 1, "changed again" }, { 2, "two" }, { 3, "three" } }));
101101
}
102102

103+
TEST_CASE("flat containers compare keys and mapped values", "[flat-map][flat-set]")
104+
{
105+
const flat_map<int, std::string> map{ { 2, "two" }, { 1, "one" } };
106+
CHECK((map == flat_map<int, std::string>{ { 1, "one" }, { 2, "two" } }));
107+
CHECK_FALSE((map == flat_map<int, std::string>{ { 1, "one" }, { 2, "changed" } }));
108+
CHECK_FALSE((map == flat_map<int, std::string>{ { 1, "one" } }));
109+
110+
const flat_set<int> set{ 2, 1 };
111+
CHECK((set == flat_set<int>{ 1, 2 }));
112+
CHECK_FALSE((set == flat_set<int>{ 1, 3 }));
113+
}
114+
103115
TEST_CASE("flat containers use equality after lower bound for duplicate detection", "[flat-map][flat-set]")
104116
{
105117
flat_map<counted_key, int, counted_less> map;

0 commit comments

Comments
 (0)