-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmulti_index.hpp
159 lines (126 loc) · 4.54 KB
/
multi_index.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#pragma once
#include "container/multimap_helpers.hpp"
#include "utility/extra_type_traits.hpp"
#include <assert.h>
#include <algorithm>
#include <initializer_list>
#include <map>
#include <memory>
#include <set>
#include <utility>
template <typename T, auto primaryKeyFieldPtr, auto secondaryKeyFieldPtr>
class MultiIndexSet {
public:
using PrimaryKeyType = member_type_from_ptr_t<primaryKeyFieldPtr>;
using SecondaryKeyType = member_type_from_ptr_t<secondaryKeyFieldPtr>;
using value_type = const T;
private:
struct PrimaryKeyComparator
{
inline constexpr bool operator()(const T& left, const T& right) const noexcept
{
return left.*primaryKeyFieldPtr < right.*primaryKeyFieldPtr;
}
inline constexpr bool operator()(const PrimaryKeyType& key, const T& item) const noexcept
{
return key < item.*primaryKeyFieldPtr;
}
inline constexpr bool operator()(const T& item, const PrimaryKeyType& key) const noexcept
{
return item.*primaryKeyFieldPtr < key;
}
using is_transparent = void;
};
std::set<T, PrimaryKeyComparator> _primarySet;
std::multimap<SecondaryKeyType, value_type*> _secondaryIndex;
public:
using iterator = typename decltype(_primarySet)::iterator;
using const_iterator = typename decltype(_primarySet)::iterator;
using secondary_key_iterator = multimap_value_iterator<typename decltype(_secondaryIndex)::const_iterator>;
constexpr MultiIndexSet() = default;
constexpr MultiIndexSet(std::initializer_list<T> list) noexcept {
for (const auto& item: list)
emplace(item);
}
template <typename... Args>
auto emplace(Args&&... args) {
const auto result = _primarySet.emplace(std::forward<Args>(args)...);
if (result.second)
_secondaryIndex.emplace((*(result.first)).*secondaryKeyFieldPtr, std::addressof(*(result.first)));
assert(_primarySet.size() == _secondaryIndex.size());
return result;
}
[[nodiscard]] auto findPrimary(const PrimaryKeyType& key) const noexcept {
return _primarySet.find(key);
}
std::pair<secondary_key_iterator, secondary_key_iterator> findSecondary(const SecondaryKeyType& key) const noexcept {
const auto range = _secondaryIndex.equal_range(key);
return {range.first, range.second};
}
// Returns a pair of iterators [begin, end) matching the range of keys [lowerBound, upperBound)
// such that [begin, end) contains all the items for which lowerBound <= key < upperBound
std::pair<secondary_key_iterator, secondary_key_iterator> findSecondaryInRange(const SecondaryKeyType& lowerBound, const SecondaryKeyType& upperBound) const noexcept {
assert(lowerBound <= upperBound);
return { _secondaryIndex.lower_bound(lowerBound), _secondaryIndex.upper_bound(upperBound) };
}
void erase(typename decltype(_primarySet)::iterator it) noexcept
{
assert(it != _primarySet.end());
const auto secondaryRange = findSecondary((*it).*secondaryKeyFieldPtr);
assert(secondaryRange.first != secondaryRange.second);
for (auto secondaryIt = secondaryRange.first; secondaryIt != secondaryRange.second;)
{
const auto current = secondaryIt;
++secondaryIt;
if (*current == std::addressof(*it))
_secondaryIndex.erase(current.nativeIterator());
}
_primarySet.erase(it);
assert(_primarySet.size() == _secondaryIndex.size());
}
size_t erase(const PrimaryKeyType& primaryKey) noexcept
{
const auto it = findPrimary(primaryKey);
if (it == _primarySet.end())
return 0;
erase(it);
return 1;
}
size_t erase(const T& item) noexcept
{
const auto it = _primarySet.find(item);
if (it == _primarySet.end() || !(*it == item))
return 0;
erase(it);
return 1;
}
[[nodiscard]] size_t size() const noexcept {
return _primarySet.size();
}
[[nodiscard]] bool empty() const noexcept {
return _primarySet.empty();
}
void clear() noexcept {
_secondaryIndex.clear();
_primarySet.clear();
}
[[nodiscard]] auto begin() const {
return _primarySet.begin();
}
[[nodiscard]] auto end() const {
return _primarySet.end();
}
[[nodiscard]] bool debugCheckSecondaryIndex() const noexcept {
if (_secondaryIndex.size() != _primarySet.size())
return false;
for (auto& item : _primarySet)
{
const auto range = findSecondary(item.*secondaryKeyFieldPtr);
if (range.first == range.second)
return false;
else if (std::find_if(range.first, range.second, [&](const auto& localItem) {return item.*secondaryKeyFieldPtr == localItem->*secondaryKeyFieldPtr;}) == range.second)
return false;
}
return true;
}
};