3131 */
3232namespace improbable ::phtree {
3333
34- template <typename T, std:: size_t SIZE >
34+ template <typename Key, typename Value, Key SIZE >
3535class flat_array_map ;
3636
3737namespace detail {
3838
39- template <typename T >
40- using flat_map_pair = std::pair<size_t , T >;
39+ template <typename Key, typename Value >
40+ using flat_map_pair = std::pair<Key, Value >;
4141
42- template <typename T, std:: size_t SIZE >
42+ template <typename Key, typename Value, Key SIZE >
4343class flat_map_iterator {
44- friend flat_array_map<T , SIZE >;
44+ friend flat_array_map<Key, Value , SIZE >;
4545
4646 public:
4747 flat_map_iterator () : first{0 }, map_{nullptr } {};
4848
49- explicit flat_map_iterator (size_t index, const flat_array_map<T , SIZE >* map)
49+ explicit flat_map_iterator (Key index, const flat_array_map<Key, Value , SIZE >* map)
5050 : first{index}, map_{map} {
5151 assert (index <= SIZE );
5252 }
5353
5454 auto & operator *() const {
5555 assert (first < SIZE && map_->occupied (first));
56- return const_cast <flat_map_pair<T >&>(map_->data (first));
56+ return const_cast <flat_map_pair<Key, Value >&>(map_->data (first));
5757 }
5858
5959 auto * operator ->() const {
6060 assert (first < SIZE && map_->occupied (first));
61- return const_cast <flat_map_pair<T >*>(&map_->data (first));
61+ return const_cast <flat_map_pair<Key, Value >*>(&map_->data (first));
6262 }
6363
64- auto & operator ++() {
64+ auto & operator ++() noexcept {
6565 first = (first + 1 ) >= SIZE ? SIZE : map_->lower_bound_index (first + 1 );
6666 return *this ;
6767 }
6868
69- auto operator ++(int ) {
69+ auto operator ++(int ) noexcept {
7070 flat_map_iterator it (first, map_);
7171 ++(*this );
7272 return it;
@@ -81,8 +81,8 @@ class flat_map_iterator {
8181 }
8282
8383 private:
84- size_t first;
85- const flat_array_map<T , SIZE >* map_;
84+ Key first;
85+ const flat_array_map<Key, Value , SIZE >* map_;
8686};
8787} // namespace detail
8888
@@ -93,49 +93,53 @@ class flat_map_iterator {
9393 * It has O(1) insertion/removal time complexity, but O(2^DIM) space complexity, so it is best used
9494 * when DIM is low and/or the map is known to have a high fill ratio.
9595 */
96- template <typename T, std:: size_t SIZE >
96+ template <typename Key, typename Value, Key SIZE >
9797class flat_array_map {
98- using map_pair = detail::flat_map_pair<T>;
99- using iterator = detail::flat_map_iterator<T, SIZE >;
98+ static_assert (std::is_integral<Key>() && " Key type must be integer" );
99+ static_assert (std::is_unsigned<Key>() && " Key type must unsigned" );
100+ using map_pair = detail::flat_map_pair<Key, Value>;
101+ using iterator = detail::flat_map_iterator<Key, Value, SIZE >;
100102 friend iterator;
101103
102104 public:
103- [[nodiscard]] auto find (size_t index) noexcept {
105+ [[nodiscard]] auto find (Key index) noexcept {
104106 return iterator{occupied (index) ? index : SIZE , this };
105107 }
106108
107- [[nodiscard]] auto lower_bound (size_t index) const {
108- return iterator{lower_bound_index (index), this };
109+ [[nodiscard]] auto lower_bound (Key index) const noexcept {
110+ return iterator{lower_bound_index (index), this };
109111 }
110112
111- [[nodiscard]] auto begin () const {
112- return iterator{lower_bound_index (0 ), this };
113+ [[nodiscard]] auto begin () const noexcept {
114+ return iterator{lower_bound_index (0 ), this };
113115 }
114116
115- [[nodiscard]] auto cbegin () const {
116- return iterator{lower_bound_index (0 ), this };
117+ [[nodiscard]] auto cbegin () const noexcept {
118+ return iterator{lower_bound_index (0 ), this };
117119 }
118120
119- [[nodiscard]] auto end () const {
121+ [[nodiscard]] auto end () const noexcept {
120122 return iterator{SIZE , this };
121123 }
122124
123125 ~flat_array_map () noexcept {
124126 if (occupancy != 0 ) {
125- for (size_t i = 0 ; i < SIZE ; ++i) {
127+ for (Key i = 0 ; i < SIZE ; ++i) {
126128 if (occupied (i)) {
127129 data (i).~pair ();
128130 }
129131 }
130132 }
131133 }
132134
133- [[nodiscard]] size_t size () const {
134- return std::bitset<64 >(occupancy).count ();
135+ [[nodiscard]] size_t size () const noexcept {
136+ constexpr size_t BITS =
137+ std::numeric_limits<Key>::digits + std::numeric_limits<Key>::is_signed;
138+ return std::bitset<BITS >(occupancy).count ();
135139 }
136140
137141 template <typename ... Args>
138- std::pair<map_pair*, bool > try_emplace (size_t index, Args&&... args) {
142+ std::pair<map_pair*, bool > try_emplace (Key index, Args&&... args) {
139143 if (!occupied (index)) {
140144 new (reinterpret_cast <void *>(&data_[index])) map_pair (
141145 std::piecewise_construct,
@@ -147,7 +151,7 @@ class flat_array_map {
147151 return {&data (index), false };
148152 }
149153
150- bool erase (size_t index) {
154+ bool erase (Key index) noexcept {
151155 if (occupied (index)) {
152156 data (index).~pair ();
153157 unoccupy (index);
@@ -156,50 +160,50 @@ class flat_array_map {
156160 return false ;
157161 }
158162
159- bool erase (const iterator& iterator) {
163+ bool erase (const iterator& iterator) noexcept {
160164 return erase (iterator.first );
161165 }
162166
163167 private:
164168 /*
165169 * This returns the element at the given index, which is _not_ the n'th element (for n = index).
166170 */
167- map_pair& data (size_t index) {
171+ map_pair& data (Key index) noexcept {
168172 assert (occupied (index));
169173 return *std::launder (reinterpret_cast <map_pair*>(&data_[index]));
170174 }
171175
172- const map_pair& data (size_t index) const {
176+ const map_pair& data (Key index) const noexcept {
173177 assert (occupied (index));
174178 return *std::launder (reinterpret_cast <const map_pair*>(&data_[index]));
175179 }
176180
177- [[nodiscard]] size_t lower_bound_index (size_t index) const {
181+ [[nodiscard]] Key lower_bound_index (Key index) const noexcept {
178182 assert (index < SIZE );
179- size_t num_zeros = CountTrailingZeros (occupancy >> index);
183+ Key num_zeros = CountTrailingZeros (occupancy >> index);
180184 // num_zeros may be equal to SIZE if no bits remain
181185 return std::min (SIZE , index + num_zeros);
182186 }
183187
184- void occupy (size_t index) {
188+ void occupy (Key index) noexcept {
185189 assert (index < SIZE );
186190 assert (!occupied (index));
187191 // flip the bit
188- occupancy ^= (1ul << index);
192+ occupancy ^= (Key{ 1 } << index);
189193 }
190194
191- void unoccupy (size_t index) {
195+ void unoccupy (Key index) noexcept {
192196 assert (index < SIZE );
193197 assert (occupied (index));
194198 // flip the bit
195- occupancy ^= (1ul << index);
199+ occupancy ^= (Key{ 1 } << index);
196200 }
197201
198- [[nodiscard]] bool occupied (size_t index) const {
199- return (occupancy >> index) & 1 ;
202+ [[nodiscard]] bool occupied (Key index) const noexcept {
203+ return (occupancy >> index) & Key{ 1 } ;
200204 }
201205
202- std:: uint64_t occupancy = 0 ;
206+ Key occupancy = 0 ;
203207 // We use an untyped array to avoid implicit calls to constructors and destructors of entries.
204208 std::aligned_storage_t <sizeof (map_pair), alignof (map_pair)> data_[SIZE ];
205209};
@@ -209,15 +213,15 @@ class flat_array_map {
209213 * This is useful to decouple instantiation of a node from instantiation of it's descendants
210214 * (the flat_array_map directly instantiates an array of descendants).
211215 */
212- template <typename T, std:: size_t SIZE >
216+ template <typename Key, typename Value, Key SIZE >
213217class array_map {
214218 static_assert (SIZE <= 64 ); // or else we need to adapt 'occupancy'
215219 static_assert (SIZE > 0 );
216- using iterator = improbable::phtree::detail::flat_map_iterator<T , SIZE >;
220+ using iterator = improbable::phtree::detail::flat_map_iterator<Key, Value , SIZE >;
217221
218222 public:
219223 array_map () {
220- data_ = new flat_array_map<T , SIZE >();
224+ data_ = new flat_array_map<Key, Value , SIZE >();
221225 }
222226
223227 array_map (const array_map& other) = delete ;
@@ -237,15 +241,15 @@ class array_map {
237241 delete data_;
238242 }
239243
240- [[nodiscard]] auto find (size_t index) noexcept {
244+ [[nodiscard]] auto find (Key index) noexcept {
241245 return data_->find (index);
242246 }
243247
244- [[nodiscard]] auto find (size_t key) const noexcept {
248+ [[nodiscard]] auto find (Key key) const noexcept {
245249 return const_cast <array_map&>(*this ).find (key);
246250 }
247251
248- [[nodiscard]] auto lower_bound (size_t index) const {
252+ [[nodiscard]] auto lower_bound (Key index) const {
249253 return data_->lower_bound (index);
250254 }
251255
@@ -267,17 +271,17 @@ class array_map {
267271 }
268272
269273 template <typename ... Args>
270- auto try_emplace (size_t index, Args&&... args) {
274+ auto try_emplace (Key index, Args&&... args) {
271275 return data_->try_emplace (index, std::forward<Args>(args)...);
272276 }
273277
274278 template <typename ... Args>
275- auto try_emplace (const iterator&, size_t index, Args&&... args) {
279+ auto try_emplace (const iterator&, Key index, Args&&... args) {
276280 // We ignore the iterator, this is an array based collection, so access is ~O(1).
277281 return data_->try_emplace (index, std::forward<Args>(args)...).first ;
278282 }
279283
280- bool erase (size_t index) {
284+ bool erase (Key index) {
281285 return data_->erase (index);
282286 }
283287
@@ -290,7 +294,7 @@ class array_map {
290294 }
291295
292296 private:
293- flat_array_map<T , SIZE >* data_;
297+ flat_array_map<Key, Value , SIZE >* data_;
294298};
295299
296300} // namespace improbable::phtree
0 commit comments