1
1
/*
2
2
* Copyright 2020 Improbable Worlds Limited
3
+ * Copyright 2022 Tilmann Zäschke
3
4
*
4
5
* Licensed under the Apache License, Version 2.0 (the "License");
5
6
* you may not use this file except in compliance with the License.
30
31
*/
31
32
namespace improbable ::phtree {
32
33
33
- namespace {
34
34
template <typename T, std::size_t SIZE>
35
- class PhFlatMapIterator ;
35
+ class flat_array_map ;
36
+
37
+ namespace detail {
36
38
37
39
template <typename T>
38
- using PhFlatMapPair = std::pair<size_t , T>;
40
+ using flat_map_pair = std::pair<size_t , T>;
41
+
42
+ template <typename T, std::size_t SIZE>
43
+ class flat_map_iterator {
44
+ friend flat_array_map<T, SIZE>;
45
+
46
+ public:
47
+ flat_map_iterator () : first{0 }, map_{nullptr } {};
48
+
49
+ explicit flat_map_iterator (size_t index, const flat_array_map<T, SIZE>* map)
50
+ : first{index }, map_{map} {
51
+ assert (index <= SIZE);
52
+ }
53
+
54
+ auto & operator *() const {
55
+ assert (first < SIZE && map_->occupied (first));
56
+ return const_cast <flat_map_pair<T>&>(map_->data (first));
57
+ }
58
+
59
+ auto * operator ->() const {
60
+ assert (first < SIZE && map_->occupied (first));
61
+ return const_cast <flat_map_pair<T>*>(&map_->data (first));
62
+ }
39
63
40
- using bit_string_t = std::uint64_t ;
41
- constexpr bit_string_t U64_ONE = bit_string_t (1 );
42
- } // namespace
64
+ auto & operator ++() {
65
+ first = (first + 1 ) >= SIZE ? SIZE : map_->lower_bound_index (first + 1 );
66
+ return *this ;
67
+ }
68
+
69
+ auto operator ++(int ) {
70
+ flat_map_iterator it (first, map_);
71
+ ++(*this );
72
+ return it;
73
+ }
74
+
75
+ friend bool operator ==(const flat_map_iterator& left, const flat_map_iterator& right) {
76
+ return left.first == right.first ;
77
+ }
78
+
79
+ friend bool operator !=(const flat_map_iterator& left, const flat_map_iterator& right) {
80
+ return left.first != right.first ;
81
+ }
82
+
83
+ private:
84
+ size_t first;
85
+ const flat_array_map<T, SIZE>* map_;
86
+ };
87
+ } // namespace detail
43
88
44
89
/*
45
90
* The array_map is a flat map implementation that uses an array of SIZE=2^DIM. The key is
@@ -49,30 +94,20 @@ constexpr bit_string_t U64_ONE = bit_string_t(1);
49
94
* when DIM is low and/or the map is known to have a high fill ratio.
50
95
*/
51
96
template <typename T, std::size_t SIZE>
52
- class array_map {
53
- friend PhFlatMapIterator<T, SIZE >;
54
- static_assert (SIZE <= 64 ); // or else we need to adapt 'occupancy'
55
- static_assert (SIZE > 0 ) ;
97
+ class flat_array_map {
98
+ using map_pair = detail::flat_map_pair<T >;
99
+ using iterator = detail::flat_map_iterator<T, SIZE>;
100
+ friend iterator ;
56
101
57
102
public:
58
- ~array_map () {
59
- if (occupancy != 0 ) {
60
- for (size_t i = 0 ; i < SIZE; ++i) {
61
- if (occupied (i)) {
62
- data (i).~pair ();
63
- }
64
- }
65
- }
66
- }
67
-
68
- [[nodiscard]] auto find (size_t index) const {
69
- return occupied (index ) ? PhFlatMapIterator<T, SIZE>{index , *this } : end ();
103
+ [[nodiscard]] auto find (size_t index) noexcept {
104
+ return occupied (index ) ? iterator{index , this } : end ();
70
105
}
71
106
72
107
[[nodiscard]] auto lower_bound (size_t index) const {
73
108
size_t index2 = lower_bound_index (index );
74
109
if (index2 < SIZE) {
75
- return PhFlatMapIterator<T, SIZE> {index2, * this };
110
+ return iterator {index2, this };
76
111
}
77
112
return end ();
78
113
}
@@ -81,52 +116,38 @@ class array_map {
81
116
size_t index = CountTrailingZeros (occupancy);
82
117
// Assert index points to a valid position or outside the map if the map is empty
83
118
assert ((size () == 0 && index >= SIZE) || occupied (index ));
84
- return PhFlatMapIterator<T, SIZE> {index < SIZE ? index : SIZE, * this };
119
+ return iterator {index < SIZE ? index : SIZE, this };
85
120
}
86
121
87
122
[[nodiscard]] auto cbegin () const {
88
123
size_t index = CountTrailingZeros (occupancy);
89
124
// Assert index points to a valid position or outside the map if the map is empty
90
125
assert ((size () == 0 && index >= SIZE) || occupied (index ));
91
- return PhFlatMapIterator<T, SIZE> {index < SIZE ? index : SIZE, * this };
126
+ return iterator {index < SIZE ? index : SIZE, this };
92
127
}
93
128
94
129
[[nodiscard]] auto end () const {
95
- return PhFlatMapIterator<T, SIZE>{SIZE, *this };
96
- }
97
-
98
- template <typename ... Args>
99
- auto emplace (Args&&... args) {
100
- return try_emplace_base (std::forward<Args>(args)...);
101
- }
102
-
103
- template <typename ... Args>
104
- auto try_emplace (size_t index, Args&&... args) {
105
- return try_emplace_base (index , std::forward<Args>(args)...);
130
+ return iterator{SIZE, this };
106
131
}
107
132
108
- bool erase (size_t index) {
109
- if (occupied (index )) {
110
- data (index ).~pair ();
111
- occupied (index , false );
112
- return true ;
133
+ ~flat_array_map () noexcept {
134
+ if (occupancy != 0 ) {
135
+ for (size_t i = 0 ; i < SIZE; ++i) {
136
+ if (occupied (i)) {
137
+ data (i).~pair ();
138
+ }
139
+ }
113
140
}
114
- return false ;
115
- }
116
-
117
- bool erase (PhFlatMapIterator<T, SIZE>& iterator) {
118
- return erase (iterator.first );
119
141
}
120
142
121
143
[[nodiscard]] size_t size () const {
122
144
return std::bitset<64 >(occupancy).count ();
123
145
}
124
146
125
- private:
126
147
template <typename ... Args>
127
- std::pair<PhFlatMapPair<T> *, bool > try_emplace_base (size_t index, Args&&... args) {
148
+ std::pair<map_pair *, bool > try_emplace_base (size_t index, Args&&... args) {
128
149
if (!occupied (index )) {
129
- new (reinterpret_cast <void *>(&data_[index ])) PhFlatMapPair<T> (
150
+ new (reinterpret_cast <void *>(&data_[index ])) map_pair (
130
151
std::piecewise_construct,
131
152
std::forward_as_tuple (index ),
132
153
std::forward_as_tuple (std::forward<Args>(args)...));
@@ -136,17 +157,31 @@ class array_map {
136
157
return {&data (index ), false };
137
158
}
138
159
160
+ bool erase (size_t index) {
161
+ if (occupied (index )) {
162
+ data (index ).~pair ();
163
+ occupied (index , false );
164
+ return true ;
165
+ }
166
+ return false ;
167
+ }
168
+
169
+ bool erase (const iterator& iterator) {
170
+ return erase (iterator.first );
171
+ }
172
+
173
+ private:
139
174
/*
140
175
* This returns the element at the given index, which is _not_ the n'th element (for n = index).
141
176
*/
142
- PhFlatMapPair<T> & data (size_t index) {
177
+ map_pair & data (size_t index) {
143
178
assert (occupied (index ));
144
- return *std::launder (reinterpret_cast <PhFlatMapPair<T> *>(&data_[index ]));
179
+ return *std::launder (reinterpret_cast <map_pair *>(&data_[index ]));
145
180
}
146
181
147
- const PhFlatMapPair<T> & data (size_t index) const {
182
+ const map_pair & data (size_t index) const {
148
183
assert (occupied (index ));
149
- return *std::launder (reinterpret_cast <const PhFlatMapPair<T> *>(&data_[index ]));
184
+ return *std::launder (reinterpret_cast <const map_pair *>(&data_[index ]));
150
185
}
151
186
152
187
[[nodiscard]] size_t lower_bound_index (size_t index) const {
@@ -161,69 +196,102 @@ class array_map {
161
196
assert (index < SIZE);
162
197
assert (occupied (index ) != flag);
163
198
// flip the bit
164
- occupancy ^= (U64_ONE << index );
199
+ occupancy ^= (1ul << index );
165
200
assert (occupied (index ) == flag);
166
201
}
167
202
168
203
[[nodiscard]] bool occupied (size_t index) const {
169
- return (occupancy >> index ) & U64_ONE ;
204
+ return (occupancy >> index ) & 1ul ;
170
205
}
171
206
172
- bit_string_t occupancy = 0 ;
207
+ std:: uint64_t occupancy = 0 ;
173
208
// We use an untyped array to avoid implicit calls to constructors and destructors of entries.
174
- std::aligned_storage_t <sizeof (PhFlatMapPair<T> ), alignof(PhFlatMapPair<T> )> data_[SIZE];
209
+ std::aligned_storage_t <sizeof (map_pair ), alignof(map_pair )> data_[SIZE];
175
210
};
176
211
177
- namespace {
212
+ /*
213
+ * array_map is a wrapper around flat_array_map. It introduces one layer of indirection.
214
+ * This is useful to decouple instantiation of a node from instantiation of it's descendants
215
+ * (the flat_array_map directly instantiates an array of descendants).
216
+ */
178
217
template <typename T, std::size_t SIZE>
179
- class PhFlatMapIterator {
180
- friend array_map<T, SIZE>;
218
+ class array_map {
219
+ static_assert (SIZE <= 64 ); // or else we need to adapt 'occupancy'
220
+ static_assert (SIZE > 0 );
221
+ using iterator = improbable::phtree::detail::flat_map_iterator<T, SIZE>;
181
222
182
223
public:
183
- PhFlatMapIterator () : first{0 }, map_{nullptr } {};
224
+ array_map () {
225
+ data_ = new flat_array_map<T, SIZE>();
226
+ }
184
227
185
- explicit PhFlatMapIterator (size_t index, const array_map<T, SIZE>& map)
186
- : first{index }, map_{&map} {
187
- assert (index <= SIZE);
228
+ array_map (const array_map& other) = delete ;
229
+ array_map& operator =(const array_map& other) = delete ;
230
+
231
+ array_map (array_map&& other) noexcept : data_{other.data_ } {
232
+ other.data_ = nullptr ;
188
233
}
189
234
190
- auto & operator *() const {
191
- assert (first < SIZE && map_->occupied (first));
192
- return const_cast <PhFlatMapPair<T>&>(map_->data (first));
235
+ array_map& operator =(array_map&& other) noexcept {
236
+ data_ = other.data_ ;
237
+ other.data_ = nullptr ;
238
+ return *this ;
193
239
}
194
240
195
- auto * operator ->() const {
196
- assert (first < SIZE && map_->occupied (first));
197
- return const_cast <PhFlatMapPair<T>*>(&map_->data (first));
241
+ ~array_map () {
242
+ delete data_;
198
243
}
199
244
200
- auto & operator ++() {
201
- first = (first + 1 ) >= SIZE ? SIZE : map_->lower_bound_index (first + 1 );
202
- return *this ;
245
+ [[nodiscard]] auto find (size_t index) noexcept {
246
+ return data_->find (index );
203
247
}
204
248
205
- auto operator ++(int ) {
206
- PhFlatMapIterator iterator (first, *map_);
207
- ++(*this );
208
- return iterator;
249
+ [[nodiscard]] auto find (size_t key) const noexcept {
250
+ return const_cast <array_map&>(*this ).find (key);
209
251
}
210
252
211
- friend bool operator ==(
212
- const PhFlatMapIterator<T, SIZE>& left, const PhFlatMapIterator<T, SIZE>& right) {
213
- return left.first == right.first ;
253
+ [[nodiscard]] auto lower_bound (size_t index) const {
254
+ return data_->lower_bound (index );
214
255
}
215
256
216
- friend bool operator !=(
217
- const PhFlatMapIterator<T, SIZE>& left, const PhFlatMapIterator<T, SIZE>& right) {
218
- return !(left == right);
257
+ [[nodiscard]] auto begin () const {
258
+ return data_->begin ();
259
+ }
260
+
261
+ [[nodiscard]] iterator cbegin () const {
262
+ return data_->cbegin ();
263
+ }
264
+
265
+ [[nodiscard]] auto end () const {
266
+ return data_->end ();
267
+ }
268
+
269
+ template <typename ... Args>
270
+ auto emplace (Args&&... args) {
271
+ return data_->try_emplace_base (std::forward<Args>(args)...);
272
+ }
273
+
274
+ template <typename ... Args>
275
+ auto try_emplace (size_t index, Args&&... args) {
276
+ return data_->try_emplace_base (index , std::forward<Args>(args)...);
277
+ }
278
+
279
+ bool erase (size_t index) {
280
+ return data_->erase (index );
281
+ }
282
+
283
+ bool erase (const iterator& iterator) {
284
+ return data_->erase (iterator);
285
+ }
286
+
287
+ [[nodiscard]] size_t size () const {
288
+ return data_->size ();
219
289
}
220
290
221
291
private:
222
- size_t first;
223
- const array_map<T, SIZE>* map_;
292
+ flat_array_map<T, SIZE>* data_;
224
293
};
225
294
226
- } // namespace
227
295
} // namespace improbable::phtree
228
296
229
297
#endif // PHTREE_COMMON_FLAT_ARRAY_MAP_H
0 commit comments