1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * This header defines two classes that very nearly model 19 * AssociativeContainer (but not quite). These implement set-like and 20 * map-like behavior on top of a sorted vector, instead of using 21 * rb-trees like std::set and std::map. 22 * 23 * This is potentially useful in cases where the number of elements in 24 * the set or map is small, or when you want to avoid using more 25 * memory than necessary and insertions/deletions are much more rare 26 * than lookups (these classes have O(N) insertions/deletions). 27 * 28 * In the interest of using these in conditions where the goal is to 29 * minimize memory usage, they support a GrowthPolicy parameter, which 30 * is a class defining a single function called increase_capacity, 31 * which will be called whenever we are about to insert something: you 32 * can then decide to call reserve() based on the current capacity() 33 * and size() of the passed in vector-esque Container type. An 34 * example growth policy that grows one element at a time: 35 * 36 * struct OneAtATimePolicy { 37 * template <class Container> 38 * void increase_capacity(Container& c) { 39 * if (c.size() == c.capacity()) { 40 * c.reserve(c.size() + 1); 41 * } 42 * } 43 * }; 44 * 45 * typedef sorted_vector_set<int, 46 * std::less<int>, 47 * std::allocator<int>, 48 * OneAtATimePolicy> 49 * OneAtATimeIntSet; 50 * 51 * Important differences from std::set and std::map: 52 * - insert() and erase() invalidate iterators and references. 53 erase(iterator) returns an iterator pointing to the next valid element. 54 * - insert() and erase() are O(N) 55 * - our iterators model RandomAccessIterator 56 * - sorted_vector_map::value_type is pair<K,V>, not pair<const K,V>. 57 * (This is basically because we want to store the value_type in 58 * std::vector<>, which requires it to be Assignable.) 59 * - insert() single key variants, emplace(), and emplace_hint() only provide 60 * the strong exception guarantee (unchanged when exception is thrown) when 61 * std::is_nothrow_move_constructible<value_type>::value is true. 62 */ 63 64 #pragma once 65 66 #include <algorithm> 67 #include <cassert> 68 #include <initializer_list> 69 #include <iterator> 70 #include <memory> 71 #include <stdexcept> 72 #include <type_traits> 73 #include <utility> 74 #include <vector> 75 76 #include <folly/ScopeGuard.h> 77 #include <folly/Traits.h> 78 #include <folly/Utility.h> 79 #include <folly/lang/Exception.h> 80 #include <folly/memory/MemoryResource.h> 81 82 namespace folly { 83 84 ////////////////////////////////////////////////////////////////////// 85 86 namespace detail { 87 88 template <typename, typename Compare, typename Key, typename T> 89 struct sorted_vector_enable_if_is_transparent {}; 90 91 template <typename Compare, typename Key, typename T> 92 struct sorted_vector_enable_if_is_transparent< 93 void_t<typename Compare::is_transparent>, 94 Compare, 95 Key, 96 T> { 97 using type = T; 98 }; 99 100 // This wrapper goes around a GrowthPolicy and provides iterator 101 // preservation semantics, but only if the growth policy is not the 102 // default (i.e. nothing). 103 template <class Policy> 104 struct growth_policy_wrapper : private Policy { 105 template <class Container, class Iterator> 106 Iterator increase_capacity(Container& c, Iterator desired_insertion) { 107 typedef typename Container::difference_type diff_t; 108 diff_t d = desired_insertion - c.begin(); 109 Policy::increase_capacity(c); 110 return c.begin() + d; 111 } 112 }; 113 template <> 114 struct growth_policy_wrapper<void> { 115 template <class Container, class Iterator> 116 Iterator increase_capacity(Container&, Iterator it) { 117 return it; 118 } 119 }; 120 121 /* 122 * This helper returns the distance between two iterators if it is 123 * possible to figure it out without messing up the range 124 * (i.e. unless they are InputIterators). Otherwise this returns 125 * -1. 126 */ 127 template <class Iterator> 128 typename std::iterator_traits<Iterator>::difference_type distance_if_multipass( 129 Iterator first, Iterator last) { 130 typedef typename std::iterator_traits<Iterator>::iterator_category categ; 131 if (std::is_same<categ, std::input_iterator_tag>::value) { 132 return -1; 133 } 134 return std::distance(first, last); 135 } 136 137 template <class OurContainer, class Vector, class GrowthPolicy, class Value> 138 typename OurContainer::iterator insert_with_hint( 139 OurContainer& sorted, 140 Vector& cont, 141 typename OurContainer::const_iterator hint, 142 Value&& value, 143 GrowthPolicy& po) { 144 const typename OurContainer::value_compare& cmp(sorted.value_comp()); 145 if (hint == cont.end() || cmp(value, *hint)) { 146 if (hint == cont.begin() || cmp(*(hint - 1), value)) { 147 hint = po.increase_capacity(cont, hint); 148 return cont.emplace(hint, std::forward<Value>(value)); 149 } else { 150 return sorted.emplace(std::forward<Value>(value)).first; 151 } 152 } 153 154 if (cmp(*hint, value)) { 155 if (hint + 1 == cont.end() || cmp(value, *(hint + 1))) { 156 hint = po.increase_capacity(cont, hint + 1); 157 return cont.emplace(hint, std::forward<Value>(value)); 158 } else { 159 return sorted.emplace(std::forward<Value>(value)).first; 160 } 161 } 162 163 // Value and *hint did not compare, so they are equal keys. 164 return sorted.begin() + std::distance(sorted.cbegin(), hint); 165 } 166 167 template <class OurContainer, class Vector, class InputIterator> 168 void bulk_insert( 169 OurContainer& sorted, 170 Vector& cont, 171 InputIterator first, 172 InputIterator last) { 173 // prevent deref of middle where middle == cont.end() 174 if (first == last) { 175 return; 176 } 177 178 auto const& cmp(sorted.value_comp()); 179 180 auto const d = distance_if_multipass(first, last); 181 if (d != -1) { 182 cont.reserve(cont.size() + d); 183 } 184 auto const prev_size = cont.size(); 185 186 std::copy(first, last, std::back_inserter(cont)); 187 auto const middle = cont.begin() + prev_size; 188 if (!std::is_sorted(middle, cont.end(), cmp)) { 189 std::sort(middle, cont.end(), cmp); 190 } 191 if (middle != cont.begin() && !cmp(*(middle - 1), *middle)) { 192 std::inplace_merge(cont.begin(), middle, cont.end(), cmp); 193 } 194 cont.erase( 195 std::unique( 196 cont.begin(), 197 cont.end(), 198 [&](typename OurContainer::value_type const& a, 199 typename OurContainer::value_type const& b) { 200 return !cmp(a, b) && !cmp(b, a); 201 }), 202 cont.end()); 203 } 204 205 template <typename Container, typename Compare> 206 bool is_sorted_unique(Container const& container, Compare const& comp) { 207 if (container.empty()) { 208 return true; 209 } 210 auto const e = container.end(); 211 for (auto a = container.begin(), b = std::next(a); b != e; ++a, ++b) { 212 if (!comp(*a, *b)) { 213 return false; 214 } 215 } 216 return true; 217 } 218 219 template <typename Container, typename Compare> 220 Container&& as_sorted_unique(Container&& container, Compare const& comp) { 221 std::sort(container.begin(), container.end(), comp); 222 container.erase( 223 std::unique( 224 container.begin(), 225 container.end(), 226 [&](auto const& a, auto const& b) { 227 return !comp(a, b) && !comp(b, a); 228 }), 229 container.end()); 230 return static_cast<Container&&>(container); 231 } 232 } // namespace detail 233 234 ////////////////////////////////////////////////////////////////////// 235 236 /** 237 * A sorted_vector_set is a container similar to std::set<>, but 238 * implemented as a sorted array with std::vector<>. 239 * 240 * @tparam T Data type to store 241 * @tparam Compare Comparison function that imposes a 242 * strict weak ordering over instances of T 243 * @tparam Allocator allocation policy 244 * @tparam GrowthPolicy policy object to control growth 245 * 246 * @author Aditya Agarwal <aditya@fb.com> 247 * @author Akhil Wable <akhil@fb.com> 248 * @author Jordan DeLong <delong.j@fb.com> 249 */ 250 template < 251 class T, 252 class Compare = std::less<T>, 253 class Allocator = std::allocator<T>, 254 class GrowthPolicy = void, 255 class Container = std::vector<T, Allocator>> 256 class sorted_vector_set : detail::growth_policy_wrapper<GrowthPolicy> { 257 detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() { 258 return *this; 259 } 260 261 template <typename K, typename V, typename C = Compare> 262 using if_is_transparent = 263 _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; 264 265 struct EBO; 266 267 public: 268 typedef T value_type; 269 typedef T key_type; 270 typedef Compare key_compare; 271 typedef Compare value_compare; 272 typedef Allocator allocator_type; 273 typedef Container container_type; 274 275 typedef typename Container::pointer pointer; 276 typedef typename Container::reference reference; 277 typedef typename Container::const_reference const_reference; 278 /* 279 * XXX: Our normal iterator ought to also be a constant iterator 280 * (cf. Defect Report 103 for std::set), but this is a bit more of a 281 * pain. 282 */ 283 typedef typename Container::iterator iterator; 284 typedef typename Container::const_iterator const_iterator; 285 typedef typename Container::difference_type difference_type; 286 typedef typename Container::size_type size_type; 287 typedef typename Container::reverse_iterator reverse_iterator; 288 typedef typename Container::const_reverse_iterator const_reverse_iterator; 289 290 sorted_vector_set() : m_(Compare(), Allocator()) {} 291 292 sorted_vector_set(const sorted_vector_set&) = default; 293 294 sorted_vector_set(const sorted_vector_set& other, const Allocator& alloc) 295 : m_(other.m_, alloc) {} 296 297 sorted_vector_set(sorted_vector_set&&) = default; 298 299 sorted_vector_set(sorted_vector_set&& other, const Allocator& alloc) noexcept( 300 std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value) 301 : m_(std::move(other.m_), alloc) {} 302 303 explicit sorted_vector_set(const Allocator& alloc) : m_(Compare(), alloc) {} 304 305 explicit sorted_vector_set( 306 const Compare& comp, const Allocator& alloc = Allocator()) 307 : m_(comp, alloc) {} 308 309 template <class InputIterator> 310 sorted_vector_set( 311 InputIterator first, 312 InputIterator last, 313 const Compare& comp = Compare(), 314 const Allocator& alloc = Allocator()) 315 : m_(comp, alloc) { 316 // This is linear if [first, last) is already sorted (and if we 317 // can figure out the distance between the two iterators). 318 insert(first, last); 319 } 320 321 template <class InputIterator> 322 sorted_vector_set( 323 InputIterator first, InputIterator last, const Allocator& alloc) 324 : m_(Compare(), alloc) { 325 // This is linear if [first, last) is already sorted (and if we 326 // can figure out the distance between the two iterators). 327 insert(first, last); 328 } 329 330 /* implicit */ sorted_vector_set( 331 std::initializer_list<value_type> list, 332 const Compare& comp = Compare(), 333 const Allocator& alloc = Allocator()) 334 : m_(comp, alloc) { 335 insert(list.begin(), list.end()); 336 } 337 338 sorted_vector_set( 339 std::initializer_list<value_type> list, const Allocator& alloc) 340 : m_(Compare(), alloc) { 341 insert(list.begin(), list.end()); 342 } 343 344 // Construct a sorted_vector_set by stealing the storage of a prefilled 345 // container. The container need not be sorted already. This supports 346 // bulk construction of sorted_vector_set with zero allocations, not counting 347 // those performed by the caller. (The iterator range constructor performs at 348 // least one allocation). 349 // 350 // Note that `sorted_vector_set(const Container& container)` is not provided, 351 // since the purpose of this constructor is to avoid an unnecessary copy. 352 explicit sorted_vector_set( 353 Container&& container, const Compare& comp = Compare()) 354 : sorted_vector_set( 355 sorted_unique, 356 detail::as_sorted_unique(std::move(container), comp), 357 comp) {} 358 359 // Construct a sorted_vector_set by stealing the storage of a prefilled 360 // container. Its elements must be sorted and unique, as sorted_unique_t 361 // hints. Supports bulk construction of sorted_vector_set with zero 362 // allocations, not counting those performed by the caller. (The iterator 363 // range constructor performs at least one allocation). 364 // 365 // Note that `sorted_vector_set(sorted_unique_t, const Container& container)` 366 // is not provided, since the purpose of this constructor is to avoid an extra 367 // copy. 368 sorted_vector_set( 369 sorted_unique_t, 370 Container&& container, 371 const Compare& comp = Compare()) noexcept(std:: 372 is_nothrow_constructible< 373 EBO, 374 const Compare&, 375 Container&&>::value) 376 : m_(comp, std::move(container)) { 377 assert(detail::is_sorted_unique(m_.cont_, value_comp())); 378 } 379 380 Allocator get_allocator() const { return m_.cont_.get_allocator(); } 381 382 sorted_vector_set& operator=(const sorted_vector_set& other) = default; 383 384 sorted_vector_set& operator=(sorted_vector_set&& other) = default; 385 386 sorted_vector_set& operator=(std::initializer_list<value_type> ilist) { 387 clear(); 388 insert(ilist.begin(), ilist.end()); 389 return *this; 390 } 391 392 key_compare key_comp() const { return m_; } 393 value_compare value_comp() const { return m_; } 394 395 iterator begin() { return m_.cont_.begin(); } 396 iterator end() { return m_.cont_.end(); } 397 const_iterator cbegin() const { return m_.cont_.cbegin(); } 398 const_iterator begin() const { return m_.cont_.begin(); } 399 const_iterator cend() const { return m_.cont_.cend(); } 400 const_iterator end() const { return m_.cont_.end(); } 401 reverse_iterator rbegin() { return m_.cont_.rbegin(); } 402 reverse_iterator rend() { return m_.cont_.rend(); } 403 const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); } 404 const_reverse_iterator rend() const { return m_.cont_.rend(); } 405 406 void clear() { return m_.cont_.clear(); } 407 size_type size() const { return m_.cont_.size(); } 408 size_type max_size() const { return m_.cont_.max_size(); } 409 bool empty() const { return m_.cont_.empty(); } 410 void reserve(size_type s) { return m_.cont_.reserve(s); } 411 void shrink_to_fit() { m_.cont_.shrink_to_fit(); } 412 size_type capacity() const { return m_.cont_.capacity(); } 413 414 std::pair<iterator, bool> insert(const value_type& value) { 415 iterator it = lower_bound(value); 416 if (it == end() || value_comp()(value, *it)) { 417 it = get_growth_policy().increase_capacity(m_.cont_, it); 418 return std::make_pair(m_.cont_.emplace(it, value), true); 419 } 420 return std::make_pair(it, false); 421 } 422 423 std::pair<iterator, bool> insert(value_type&& value) { 424 iterator it = lower_bound(value); 425 if (it == end() || value_comp()(value, *it)) { 426 it = get_growth_policy().increase_capacity(m_.cont_, it); 427 return std::make_pair(m_.cont_.emplace(it, std::move(value)), true); 428 } 429 return std::make_pair(it, false); 430 } 431 432 iterator insert(const_iterator hint, const value_type& value) { 433 return detail::insert_with_hint( 434 *this, m_.cont_, hint, value, get_growth_policy()); 435 } 436 437 iterator insert(const_iterator hint, value_type&& value) { 438 return detail::insert_with_hint( 439 *this, m_.cont_, hint, std::move(value), get_growth_policy()); 440 } 441 442 template <class InputIterator> 443 void insert(InputIterator first, InputIterator last) { 444 detail::bulk_insert(*this, m_.cont_, first, last); 445 } 446 447 void insert(std::initializer_list<value_type> ilist) { 448 insert(ilist.begin(), ilist.end()); 449 } 450 451 // emplace isn't better than insert for sorted_vector_set, but aids 452 // compatibility 453 template <typename... Args> 454 std::pair<iterator, bool> emplace(Args&&... args) { 455 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; 456 value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); 457 auto a = get_allocator(); 458 std::allocator_traits<allocator_type>::construct( 459 a, p, std::forward<Args>(args)...); 460 auto g = makeGuard( 461 [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); 462 return insert(std::move(*p)); 463 } 464 465 std::pair<iterator, bool> emplace(const value_type& value) { 466 return insert(value); 467 } 468 469 std::pair<iterator, bool> emplace(value_type&& value) { 470 return insert(std::move(value)); 471 } 472 473 // emplace_hint isn't better than insert for sorted_vector_set, but aids 474 // compatibility 475 template <typename... Args> 476 iterator emplace_hint(const_iterator hint, Args&&... args) { 477 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; 478 value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); 479 auto a = get_allocator(); 480 std::allocator_traits<allocator_type>::construct( 481 a, p, std::forward<Args>(args)...); 482 auto g = makeGuard( 483 [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); 484 return insert(hint, std::move(*p)); 485 } 486 487 iterator emplace_hint(const_iterator hint, const value_type& value) { 488 return insert(hint, value); 489 } 490 491 iterator emplace_hint(const_iterator hint, value_type&& value) { 492 return insert(hint, std::move(value)); 493 } 494 495 size_type erase(const key_type& key) { 496 iterator it = find(key); 497 if (it == end()) { 498 return 0; 499 } 500 m_.cont_.erase(it); 501 return 1; 502 } 503 504 iterator erase(const_iterator it) { return m_.cont_.erase(it); } 505 506 iterator erase(const_iterator first, const_iterator last) { 507 return m_.cont_.erase(first, last); 508 } 509 510 iterator find(const key_type& key) { return find_(*this, key); } 511 512 const_iterator find(const key_type& key) const { return find_(*this, key); } 513 514 template <typename K> 515 if_is_transparent<K, iterator> find(const K& key) { 516 return find_(*this, key); 517 } 518 519 template <typename K> 520 if_is_transparent<K, const_iterator> find(const K& key) const { 521 return find_(*this, key); 522 } 523 524 size_type count(const key_type& key) const { 525 return find(key) == end() ? 0 : 1; 526 } 527 528 template <typename K> 529 if_is_transparent<K, size_type> count(const K& key) const { 530 return find(key) == end() ? 0 : 1; 531 } 532 533 bool contains(const key_type& key) const { return find(key) != end(); } 534 535 template <typename K> 536 if_is_transparent<K, bool> contains(const K& key) const { 537 return find(key) != end(); 538 } 539 540 iterator lower_bound(const key_type& key) { 541 return std::lower_bound(begin(), end(), key, key_comp()); 542 } 543 544 const_iterator lower_bound(const key_type& key) const { 545 return std::lower_bound(begin(), end(), key, key_comp()); 546 } 547 548 template <typename K> 549 if_is_transparent<K, iterator> lower_bound(const K& key) { 550 return std::lower_bound(begin(), end(), key, key_comp()); 551 } 552 553 template <typename K> 554 if_is_transparent<K, const_iterator> lower_bound(const K& key) const { 555 return std::lower_bound(begin(), end(), key, key_comp()); 556 } 557 558 iterator upper_bound(const key_type& key) { 559 return std::upper_bound(begin(), end(), key, key_comp()); 560 } 561 562 const_iterator upper_bound(const key_type& key) const { 563 return std::upper_bound(begin(), end(), key, key_comp()); 564 } 565 566 template <typename K> 567 if_is_transparent<K, iterator> upper_bound(const K& key) { 568 return std::upper_bound(begin(), end(), key, key_comp()); 569 } 570 571 template <typename K> 572 if_is_transparent<K, const_iterator> upper_bound(const K& key) const { 573 return std::upper_bound(begin(), end(), key, key_comp()); 574 } 575 576 std::pair<iterator, iterator> equal_range(const key_type& key) { 577 return std::equal_range(begin(), end(), key, key_comp()); 578 } 579 580 std::pair<const_iterator, const_iterator> equal_range( 581 const key_type& key) const { 582 return std::equal_range(begin(), end(), key, key_comp()); 583 } 584 585 template <typename K> 586 if_is_transparent<K, std::pair<iterator, iterator>> equal_range( 587 const K& key) { 588 return std::equal_range(begin(), end(), key, key_comp()); 589 } 590 591 template <typename K> 592 if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range( 593 const K& key) const { 594 return std::equal_range(begin(), end(), key, key_comp()); 595 } 596 597 void swap(sorted_vector_set& o) noexcept( 598 IsNothrowSwappable<Compare>::value&& noexcept( 599 std::declval<Container&>().swap(o.m_.cont_))) { 600 using std::swap; // Allow ADL for swap(); fall back to std::swap(). 601 Compare& a = m_; 602 Compare& b = o.m_; 603 swap(a, b); 604 m_.cont_.swap(o.m_.cont_); 605 } 606 607 bool operator==(const sorted_vector_set& other) const { 608 return other.m_.cont_ == m_.cont_; 609 } 610 bool operator!=(const sorted_vector_set& other) const { 611 return !operator==(other); 612 } 613 614 bool operator<(const sorted_vector_set& other) const { 615 return m_.cont_ < other.m_.cont_; 616 } 617 bool operator>(const sorted_vector_set& other) const { return other < *this; } 618 bool operator<=(const sorted_vector_set& other) const { 619 return !operator>(other); 620 } 621 bool operator>=(const sorted_vector_set& other) const { 622 return !operator<(other); 623 } 624 625 const value_type* data() const noexcept { return m_.cont_.data(); } 626 627 private: 628 /* 629 * This structure derives from the comparison object in order to 630 * make use of the empty base class optimization if our comparison 631 * functor is an empty class (usual case). 632 * 633 * Wrapping up this member like this is better than deriving from 634 * the Compare object ourselves (there are some perverse edge cases 635 * involving virtual functions). 636 * 637 * More info: http://www.cantrip.org/emptyopt.html 638 */ 639 struct EBO : Compare { 640 explicit EBO(const Compare& c, const Allocator& alloc) noexcept( 641 std::is_nothrow_default_constructible<Container>::value) 642 : Compare(c), cont_(alloc) {} 643 EBO(const EBO& other, const Allocator& alloc) 644 noexcept(std::is_nothrow_constructible< 645 Container, 646 const Container&, 647 const Allocator&>::value) 648 : Compare(static_cast<const Compare&>(other)), 649 cont_(other.cont_, alloc) {} 650 EBO(EBO&& other, const Allocator& alloc) 651 noexcept(std::is_nothrow_constructible< 652 Container, 653 Container&&, 654 const Allocator&>::value) 655 : Compare(static_cast<Compare&&>(other)), 656 cont_(std::move(other.cont_), alloc) {} 657 EBO(const Compare& c, Container&& cont) 658 noexcept(std::is_nothrow_move_constructible<Container>::value) 659 : Compare(c), cont_(std::move(cont)) {} 660 Container cont_; 661 } m_; 662 663 template <typename Self> 664 using self_iterator_t = _t< 665 std::conditional<std::is_const<Self>::value, const_iterator, iterator>>; 666 667 template <typename Self, typename K> 668 static self_iterator_t<Self> find_(Self& self, K const& key) { 669 auto end = self.end(); 670 auto it = self.lower_bound(key); 671 if (it == end || !self.key_comp()(key, *it)) { 672 return it; 673 } 674 return end; 675 } 676 }; 677 678 // Swap function that can be found using ADL. 679 template <class T, class C, class A, class G> 680 inline void swap( 681 sorted_vector_set<T, C, A, G>& a, sorted_vector_set<T, C, A, G>& b) { 682 return a.swap(b); 683 } 684 685 #if FOLLY_HAS_MEMORY_RESOURCE 686 687 namespace pmr { 688 689 template < 690 class T, 691 class Compare = std::less<T>, 692 class GrowthPolicy = void, 693 class Container = 694 std::vector<T, folly::detail::std_pmr::polymorphic_allocator<T>>> 695 using sorted_vector_set = folly::sorted_vector_set< 696 T, 697 Compare, 698 folly::detail::std_pmr::polymorphic_allocator<T>, 699 GrowthPolicy, 700 Container>; 701 702 } // namespace pmr 703 704 #endif 705 706 ////////////////////////////////////////////////////////////////////// 707 708 /** 709 * A sorted_vector_map is similar to a sorted_vector_set but stores 710 * <key,value> pairs instead of single elements. 711 * 712 * @tparam Key Key type 713 * @tparam Value Value type 714 * @tparam Compare Function that can compare key types and impose 715 * a strict weak ordering over them. 716 * @tparam Allocator allocation policy 717 * @tparam GrowthPolicy policy object to control growth 718 * 719 * @author Aditya Agarwal <aditya@fb.com> 720 * @author Akhil Wable <akhil@fb.com> 721 * @author Jordan DeLong <delong.j@fb.com> 722 */ 723 template < 724 class Key, 725 class Value, 726 class Compare = std::less<Key>, 727 class Allocator = std::allocator<std::pair<Key, Value>>, 728 class GrowthPolicy = void, 729 class Container = std::vector<std::pair<Key, Value>, Allocator>> 730 class sorted_vector_map : detail::growth_policy_wrapper<GrowthPolicy> { 731 detail::growth_policy_wrapper<GrowthPolicy>& get_growth_policy() { 732 return *this; 733 } 734 735 template <typename K, typename V, typename C = Compare> 736 using if_is_transparent = 737 _t<detail::sorted_vector_enable_if_is_transparent<void, C, K, V>>; 738 739 struct EBO; 740 741 public: 742 typedef Key key_type; 743 typedef Value mapped_type; 744 typedef typename Container::value_type value_type; 745 typedef Compare key_compare; 746 typedef Allocator allocator_type; 747 typedef Container container_type; 748 749 struct value_compare : private Compare { 750 bool operator()(const value_type& a, const value_type& b) const { 751 return Compare::operator()(a.first, b.first); 752 } 753 754 protected: 755 friend class sorted_vector_map; 756 explicit value_compare(const Compare& c) : Compare(c) {} 757 }; 758 759 typedef typename Container::pointer pointer; 760 typedef typename Container::reference reference; 761 typedef typename Container::const_reference const_reference; 762 typedef typename Container::iterator iterator; 763 typedef typename Container::const_iterator const_iterator; 764 typedef typename Container::difference_type difference_type; 765 typedef typename Container::size_type size_type; 766 typedef typename Container::reverse_iterator reverse_iterator; 767 typedef typename Container::const_reverse_iterator const_reverse_iterator; 768 769 sorted_vector_map() noexcept( 770 std::is_nothrow_constructible<EBO, value_compare, Allocator>::value) 771 : m_(value_compare(Compare()), Allocator()) {} 772 773 sorted_vector_map(const sorted_vector_map&) = default; 774 775 sorted_vector_map(const sorted_vector_map& other, const Allocator& alloc) 776 : m_(other.m_, alloc) {} 777 778 sorted_vector_map(sorted_vector_map&&) = default; 779 780 sorted_vector_map(sorted_vector_map&& other, const Allocator& alloc) noexcept( 781 std::is_nothrow_constructible<EBO, EBO&&, const Allocator&>::value) 782 : m_(std::move(other.m_), alloc) {} 783 784 explicit sorted_vector_map(const Allocator& alloc) 785 : m_(value_compare(Compare()), alloc) {} 786 787 explicit sorted_vector_map( 788 const Compare& comp, const Allocator& alloc = Allocator()) 789 : m_(value_compare(comp), alloc) {} 790 791 template <class InputIterator> 792 explicit sorted_vector_map( 793 InputIterator first, 794 InputIterator last, 795 const Compare& comp = Compare(), 796 const Allocator& alloc = Allocator()) 797 : m_(value_compare(comp), alloc) { 798 insert(first, last); 799 } 800 801 template <class InputIterator> 802 sorted_vector_map( 803 InputIterator first, InputIterator last, const Allocator& alloc) 804 : m_(value_compare(Compare()), alloc) { 805 insert(first, last); 806 } 807 808 /* implicit */ sorted_vector_map( 809 std::initializer_list<value_type> list, 810 const Compare& comp = Compare(), 811 const Allocator& alloc = Allocator()) 812 : m_(value_compare(comp), alloc) { 813 insert(list.begin(), list.end()); 814 } 815 816 sorted_vector_map( 817 std::initializer_list<value_type> list, const Allocator& alloc) 818 : m_(value_compare(Compare()), alloc) { 819 insert(list.begin(), list.end()); 820 } 821 822 // Construct a sorted_vector_map by stealing the storage of a prefilled 823 // container. The container need not be sorted already. This supports 824 // bulk construction of sorted_vector_map with zero allocations, not counting 825 // those performed by the caller. (The iterator range constructor performs at 826 // least one allocation). 827 // 828 // Note that `sorted_vector_map(const Container& container)` is not provided, 829 // since the purpose of this constructor is to avoid an unnecessary copy. 830 explicit sorted_vector_map( 831 Container&& container, const Compare& comp = Compare()) 832 : sorted_vector_map( 833 sorted_unique, 834 detail::as_sorted_unique(std::move(container), value_compare(comp)), 835 comp) {} 836 837 // Construct a sorted_vector_map by stealing the storage of a prefilled 838 // container. Its elements must be sorted and unique, as sorted_unique_t 839 // hints. Supports bulk construction of sorted_vector_map with zero 840 // allocations, not counting those performed by the caller. (The iterator 841 // range constructor performs at least one allocation). 842 // 843 // Note that `sorted_vector_map(sorted_unique_t, const Container& container)` 844 // is not provided, since the purpose of this constructor is to avoid an extra 845 // copy. 846 sorted_vector_map( 847 sorted_unique_t, 848 Container&& container, 849 const Compare& comp = Compare()) noexcept(std:: 850 is_nothrow_constructible< 851 EBO, 852 value_compare, 853 Container&&>::value) 854 : m_(value_compare(comp), std::move(container)) { 855 assert(std::is_sorted(m_.cont_.begin(), m_.cont_.end(), value_comp())); 856 assert(detail::is_sorted_unique(m_.cont_, value_comp())); 857 } 858 859 Allocator get_allocator() const { return m_.cont_.get_allocator(); } 860 861 sorted_vector_map& operator=(const sorted_vector_map& other) = default; 862 863 sorted_vector_map& operator=(sorted_vector_map&& other) = default; 864 865 sorted_vector_map& operator=(std::initializer_list<value_type> ilist) { 866 clear(); 867 insert(ilist.begin(), ilist.end()); 868 return *this; 869 } 870 871 key_compare key_comp() const { return m_; } 872 value_compare value_comp() const { return m_; } 873 874 iterator begin() { return m_.cont_.begin(); } 875 iterator end() { return m_.cont_.end(); } 876 const_iterator cbegin() const { return m_.cont_.cbegin(); } 877 const_iterator begin() const { return m_.cont_.begin(); } 878 const_iterator cend() const { return m_.cont_.cend(); } 879 const_iterator end() const { return m_.cont_.end(); } 880 reverse_iterator rbegin() { return m_.cont_.rbegin(); } 881 reverse_iterator rend() { return m_.cont_.rend(); } 882 const_reverse_iterator crbegin() const { return m_.cont_.crbegin(); } 883 const_reverse_iterator rbegin() const { return m_.cont_.rbegin(); } 884 const_reverse_iterator crend() const { return m_.cont_.crend(); } 885 const_reverse_iterator rend() const { return m_.cont_.rend(); } 886 887 void clear() { return m_.cont_.clear(); } 888 size_type size() const { return m_.cont_.size(); } 889 size_type max_size() const { return m_.cont_.max_size(); } 890 bool empty() const { return m_.cont_.empty(); } 891 void reserve(size_type s) { return m_.cont_.reserve(s); } 892 void shrink_to_fit() { m_.cont_.shrink_to_fit(); } 893 size_type capacity() const { return m_.cont_.capacity(); } 894 895 std::pair<iterator, bool> insert(const value_type& value) { 896 iterator it = lower_bound(value.first); 897 if (it == end() || value_comp()(value, *it)) { 898 it = get_growth_policy().increase_capacity(m_.cont_, it); 899 return std::make_pair(m_.cont_.emplace(it, value), true); 900 } 901 return std::make_pair(it, false); 902 } 903 904 std::pair<iterator, bool> insert(value_type&& value) { 905 iterator it = lower_bound(value.first); 906 if (it == end() || value_comp()(value, *it)) { 907 it = get_growth_policy().increase_capacity(m_.cont_, it); 908 return std::make_pair(m_.cont_.emplace(it, std::move(value)), true); 909 } 910 return std::make_pair(it, false); 911 } 912 913 iterator insert(const_iterator hint, const value_type& value) { 914 return detail::insert_with_hint( 915 *this, m_.cont_, hint, value, get_growth_policy()); 916 } 917 918 iterator insert(const_iterator hint, value_type&& value) { 919 return detail::insert_with_hint( 920 *this, m_.cont_, hint, std::move(value), get_growth_policy()); 921 } 922 923 template <class InputIterator> 924 void insert(InputIterator first, InputIterator last) { 925 detail::bulk_insert(*this, m_.cont_, first, last); 926 } 927 928 void insert(std::initializer_list<value_type> ilist) { 929 insert(ilist.begin(), ilist.end()); 930 } 931 932 // emplace isn't better than insert for sorted_vector_map, but aids 933 // compatibility 934 template <typename... Args> 935 std::pair<iterator, bool> emplace(Args&&... args) { 936 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; 937 value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); 938 auto a = get_allocator(); 939 std::allocator_traits<allocator_type>::construct( 940 a, p, std::forward<Args>(args)...); 941 auto g = makeGuard( 942 [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); 943 return insert(std::move(*p)); 944 } 945 946 std::pair<iterator, bool> emplace(const value_type& value) { 947 return insert(value); 948 } 949 950 std::pair<iterator, bool> emplace(value_type&& value) { 951 return insert(std::move(value)); 952 } 953 954 // emplace_hint isn't better than insert for sorted_vector_set, but aids 955 // compatibility 956 template <typename... Args> 957 iterator emplace_hint(const_iterator hint, Args&&... args) { 958 std::aligned_storage_t<sizeof(value_type), alignof(value_type)> b; 959 value_type* p = static_cast<value_type*>(static_cast<void*>(&b)); 960 auto a = get_allocator(); 961 std::allocator_traits<allocator_type>::construct( 962 a, p, std::forward<Args>(args)...); 963 auto g = makeGuard( 964 [&]() { std::allocator_traits<allocator_type>::destroy(a, p); }); 965 return insert(hint, std::move(*p)); 966 } 967 968 iterator emplace_hint(const_iterator hint, const value_type& value) { 969 return insert(hint, value); 970 } 971 972 iterator emplace_hint(const_iterator hint, value_type&& value) { 973 return insert(hint, std::move(value)); 974 } 975 976 size_type erase(const key_type& key) { 977 iterator it = find(key); 978 if (it == end()) { 979 return 0; 980 } 981 m_.cont_.erase(it); 982 return 1; 983 } 984 985 iterator erase(const_iterator it) { return m_.cont_.erase(it); } 986 987 iterator erase(const_iterator first, const_iterator last) { 988 return m_.cont_.erase(first, last); 989 } 990 991 iterator find(const key_type& key) { return find_(*this, key); } 992 993 const_iterator find(const key_type& key) const { return find_(*this, key); } 994 995 template <typename K> 996 if_is_transparent<K, iterator> find(const K& key) { 997 return find_(*this, key); 998 } 999 1000 template <typename K> 1001 if_is_transparent<K, const_iterator> find(const K& key) const { 1002 return find_(*this, key); 1003 } 1004 1005 mapped_type& at(const key_type& key) { 1006 iterator it = find(key); 1007 if (it != end()) { 1008 return it->second; 1009 } 1010 throw_exception<std::out_of_range>("sorted_vector_map::at"); 1011 } 1012 1013 const mapped_type& at(const key_type& key) const { 1014 const_iterator it = find(key); 1015 if (it != end()) { 1016 return it->second; 1017 } 1018 throw_exception<std::out_of_range>("sorted_vector_map::at"); 1019 } 1020 1021 size_type count(const key_type& key) const { 1022 return find(key) == end() ? 0 : 1; 1023 } 1024 1025 template <typename K> 1026 if_is_transparent<K, size_type> count(const K& key) const { 1027 return find(key) == end() ? 0 : 1; 1028 } 1029 1030 bool contains(const key_type& key) const { return find(key) != end(); } 1031 1032 template <typename K> 1033 if_is_transparent<K, bool> contains(const K& key) const { 1034 return find(key) != end(); 1035 } 1036 1037 iterator lower_bound(const key_type& key) { return lower_bound(*this, key); } 1038 1039 const_iterator lower_bound(const key_type& key) const { 1040 return lower_bound(*this, key); 1041 } 1042 1043 template <typename K> 1044 if_is_transparent<K, iterator> lower_bound(const K& key) { 1045 return lower_bound(*this, key); 1046 } 1047 1048 template <typename K> 1049 if_is_transparent<K, const_iterator> lower_bound(const K& key) const { 1050 return lower_bound(*this, key); 1051 } 1052 1053 iterator upper_bound(const key_type& key) { return upper_bound(*this, key); } 1054 1055 const_iterator upper_bound(const key_type& key) const { 1056 return upper_bound(*this, key); 1057 } 1058 1059 template <typename K> 1060 if_is_transparent<K, iterator> upper_bound(const K& key) { 1061 return upper_bound(*this, key); 1062 } 1063 1064 template <typename K> 1065 if_is_transparent<K, const_iterator> upper_bound(const K& key) const { 1066 return upper_bound(*this, key); 1067 } 1068 1069 std::pair<iterator, iterator> equal_range(const key_type& key) { 1070 return equal_range(*this, key); 1071 } 1072 1073 std::pair<const_iterator, const_iterator> equal_range( 1074 const key_type& key) const { 1075 return equal_range(*this, key); 1076 } 1077 1078 template <typename K> 1079 if_is_transparent<K, std::pair<iterator, iterator>> equal_range( 1080 const K& key) { 1081 return equal_range(*this, key); 1082 } 1083 1084 template <typename K> 1085 if_is_transparent<K, std::pair<const_iterator, const_iterator>> equal_range( 1086 const K& key) const { 1087 return equal_range(*this, key); 1088 } 1089 1090 // Nothrow as long as swap() on the Compare type is nothrow. 1091 void swap(sorted_vector_map& o) { 1092 using std::swap; // Allow ADL for swap(); fall back to std::swap(). 1093 Compare& a = m_; 1094 Compare& b = o.m_; 1095 swap(a, b); 1096 m_.cont_.swap(o.m_.cont_); 1097 } 1098 1099 mapped_type& operator[](const key_type& key) { 1100 iterator it = lower_bound(key); 1101 if (it == end() || key_comp()(key, it->first)) { 1102 return insert(it, value_type(key, mapped_type()))->second; 1103 } 1104 return it->second; 1105 } 1106 1107 bool operator==(const sorted_vector_map& other) const { 1108 return m_.cont_ == other.m_.cont_; 1109 } 1110 bool operator!=(const sorted_vector_map& other) const { 1111 return !operator==(other); 1112 } 1113 1114 bool operator<(const sorted_vector_map& other) const { 1115 return m_.cont_ < other.m_.cont_; 1116 } 1117 bool operator>(const sorted_vector_map& other) const { return other < *this; } 1118 bool operator<=(const sorted_vector_map& other) const { 1119 return !operator>(other); 1120 } 1121 bool operator>=(const sorted_vector_map& other) const { 1122 return !operator<(other); 1123 } 1124 1125 const value_type* data() const noexcept { return m_.cont_.data(); } 1126 1127 private: 1128 // This is to get the empty base optimization; see the comment in 1129 // sorted_vector_set. 1130 struct EBO : value_compare { 1131 explicit EBO(const value_compare& c, const Allocator& alloc) noexcept( 1132 std::is_nothrow_default_constructible<Container>::value) 1133 : value_compare(c), cont_(alloc) {} 1134 EBO(const EBO& other, const Allocator& alloc) 1135 noexcept(std::is_nothrow_constructible< 1136 Container, 1137 const Container&, 1138 const Allocator&>::value) 1139 : value_compare(static_cast<const value_compare&>(other)), 1140 cont_(other.cont_, alloc) {} 1141 EBO(EBO&& other, const Allocator& alloc) 1142 noexcept(std::is_nothrow_constructible< 1143 Container, 1144 Container&&, 1145 const Allocator&>::value) 1146 : value_compare(static_cast<value_compare&&>(other)), 1147 cont_(std::move(other.cont_), alloc) {} 1148 EBO(const Compare& c, Container&& cont) 1149 noexcept(std::is_nothrow_move_constructible<Container>::value) 1150 : value_compare(c), cont_(std::move(cont)) {} 1151 Container cont_; 1152 } m_; 1153 1154 template <typename Self> 1155 using self_iterator_t = _t< 1156 std::conditional<std::is_const<Self>::value, const_iterator, iterator>>; 1157 1158 template <typename Self, typename K> 1159 static self_iterator_t<Self> find_(Self& self, K const& key) { 1160 auto end = self.end(); 1161 auto it = self.lower_bound(key); 1162 if (it == end || !self.key_comp()(key, it->first)) { 1163 return it; 1164 } 1165 return end; 1166 } 1167 1168 template <typename Self, typename K> 1169 static self_iterator_t<Self> lower_bound(Self& self, K const& key) { 1170 auto f = [c = self.key_comp()](value_type const& a, K const& b) { 1171 return c(a.first, b); 1172 }; 1173 return std::lower_bound(self.begin(), self.end(), key, f); 1174 } 1175 1176 template <typename Self, typename K> 1177 static self_iterator_t<Self> upper_bound(Self& self, K const& key) { 1178 auto f = [c = self.key_comp()](K const& a, value_type const& b) { 1179 return c(a, b.first); 1180 }; 1181 return std::upper_bound(self.begin(), self.end(), key, f); 1182 } 1183 1184 template <typename Self, typename K> 1185 static std::pair<self_iterator_t<Self>, self_iterator_t<Self>> equal_range( 1186 Self& self, K const& key) { 1187 // Note: std::equal_range can't be passed a functor that takes 1188 // argument types different from the iterator value_type, so we 1189 // have to do this. 1190 return {lower_bound(self, key), upper_bound(self, key)}; 1191 } 1192 }; 1193 1194 // Swap function that can be found using ADL. 1195 template <class K, class V, class C, class A, class G> 1196 inline void swap( 1197 sorted_vector_map<K, V, C, A, G>& a, sorted_vector_map<K, V, C, A, G>& b) { 1198 return a.swap(b); 1199 } 1200 1201 #if FOLLY_HAS_MEMORY_RESOURCE 1202 1203 namespace pmr { 1204 1205 template < 1206 class Key, 1207 class Value, 1208 class Compare = std::less<Key>, 1209 class GrowthPolicy = void, 1210 class Container = std::vector< 1211 std::pair<Key, Value>, 1212 folly::detail::std_pmr::polymorphic_allocator<std::pair<Key, Value>>>> 1213 using sorted_vector_map = folly::sorted_vector_map< 1214 Key, 1215 Value, 1216 Compare, 1217 folly::detail::std_pmr::polymorphic_allocator<std::pair<Key, Value>>, 1218 GrowthPolicy, 1219 Container>; 1220 1221 } // namespace pmr 1222 1223 #endif 1224 1225 ////////////////////////////////////////////////////////////////////// 1226 1227 } // namespace folly 1228