├── extra ├── static_set.hpp ├── small_multiset.hpp ├── static_multiset.hpp ├── static_map.hpp ├── small_set.hpp ├── static_multimap.hpp ├── small_map.hpp └── small_multimap.hpp ├── impl ├── container_traits.hpp ├── class_def.hpp └── flat_impl.hpp ├── LICENSE_1_0.txt ├── README.md ├── flat_multiset.hpp ├── flat_set.hpp ├── flat_multimap.hpp └── flat_map.hpp /extra/static_set.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_SET_HPP 2 | #define LIB_FLAT_STATIC_SET_HPP 3 | 4 | #include "flat_set.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_set = flat_set< 12 | ::boost::container::static_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /extra/small_multiset.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MULTISET_HPP 2 | #define LIB_FLAT_SMALL_MULTISET_HPP 3 | 4 | #include "flat_multiset.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using small_multiset = flat_multiset< 12 | ::boost::container::small_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /extra/static_multiset.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MULTISET_HPP 2 | #define LIB_FLAT_STATIC_MULTISET_HPP 3 | 4 | #include "flat_multiset.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_multiset = flat_multiset< 12 | ::boost::container::static_vector, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /extra/static_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MAP_HPP 2 | #define LIB_FLAT_STATIC_MAP_HPP 3 | 4 | #include "flat_map.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_map = flat_map< 12 | ::boost::container::static_vector, N>, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /extra/small_set.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_SET_HPP 2 | #define LIB_FLAT_SMALL_SET_HPP 3 | 4 | #include "flat_set.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template, typename... Args> 12 | using small_set = flat_set< 13 | ::boost::container::small_vector, Compare>; 14 | 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /extra/static_multimap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_STATIC_MULTIMAP_HPP 2 | #define LIB_FLAT_STATIC_MULTIMAP_HPP 3 | 4 | #include "flat_multimap.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template 11 | using static_multimap = flat_map< 12 | ::boost::container::static_vector, N>, Args...>; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /extra/small_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MAP_HPP 2 | #define LIB_FLAT_SMALL_MAP_HPP 3 | 4 | #include "flat_map.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template,typename... Args> 12 | using small_map = flat_map< 13 | ::boost::container::small_vector, N, Args...>, 14 | Compare>; 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /extra/small_multimap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LIB_FLAT_SMALL_MULTIMAP_HPP 2 | #define LIB_FLAT_SMALL_MULTIMAP_HPP 3 | 4 | #include "flat_multimap.hpp" 5 | #include 6 | 7 | namespace fc 8 | { 9 | 10 | template,typename... Args> 12 | using small_multimap = flat_multimap< 13 | ::boost::container::small_vector, N, Args...>, 14 | Compare>; 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /impl/container_traits.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | using container_type = Container; 6 | using key_type = Key; 7 | using size_type = typename container_type::size_type; 8 | using difference_type = typename container_type::difference_type; 9 | using value_type = typename container_type::value_type; 10 | using iterator 11 | = impl::flat_iterator< 12 | typename container_type::iterator, 13 | impl::dummy_iterator>; 14 | using const_iterator 15 | = impl::flat_iterator< 16 | typename container_type::const_iterator, 17 | iterator>; 18 | using reverse_iterator 19 | = impl::flat_iterator< 20 | typename container_type::reverse_iterator, 21 | impl::dummy_iterator>; 22 | using const_reverse_iterator 23 | = impl::flat_iterator< 24 | typename container_type::const_reverse_iterator, 25 | reverse_iterator>; 26 | -------------------------------------------------------------------------------- /LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /impl/class_def.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | private: 6 | using D = FLATNAME; 7 | using Key = FLATKEY; 8 | public: 9 | #include "container_traits.hpp" 10 | 11 | FLATNAME() = default; 12 | explicit FLATNAME(Compare const& comp) : B(comp) {} 13 | 14 | template 15 | FLATNAME(InputIt first, InputIt last) 16 | : FLATNAME() { this->insert(first, last); } 17 | 18 | template 19 | FLATNAME(InputIt first, InputIt last, Compare const& comp) 20 | : FLATNAME(comp) { this->insert(first, last); } 21 | 22 | template 23 | FLATNAME(InputIt first, InputIt last, delay_sort_t d) 24 | : FLATNAME() { this->insert(first, last, d); } 25 | 26 | template 27 | FLATNAME(InputIt first, InputIt last, Compare const& comp, delay_sort_t d) 28 | : FLATNAME(comp) { this->insert(first, last, d); } 29 | 30 | FLATNAME(FLATNAME const&) = default; 31 | FLATNAME(FLATNAME&&) = default; 32 | 33 | FLATNAME(std::initializer_list ilist) 34 | : FLATNAME() { this->insert(ilist); } 35 | 36 | FLATNAME(std::initializer_list ilist, delay_sort_t d) 37 | : FLATNAME() { this->insert(ilist, d); } 38 | 39 | FLATNAME(std::initializer_list ilist, 40 | Compare const& comp, delay_sort_t d) 41 | : FLATNAME(comp) { this->insert(ilist, d); } 42 | 43 | template 44 | explicit FLATNAME(container_construct_t, Args&&... args) 45 | : container(std::forward(args)...), Compare() {} 46 | 47 | template 48 | FLATNAME(Compare const& comp, container_construct_t, Args&&... args) 49 | : container(std::forward(args)...), Compare(comp) {} 50 | 51 | FLATNAME& operator=(FLATNAME const&) = default; 52 | FLATNAME& operator=(FLATNAME&&) = default; 53 | FLATNAME& operator=(std::initializer_list ilist) 54 | { this->clear(); this->insert(ilist); return *this; } 55 | 56 | Container container; 57 | 58 | #undef FLATNAME 59 | #undef FLATKEY 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Flat Containers Library 2 | 3 | Fast+efficient associative containers using sorted arrays, with an interface based on standard containers. 4 | 5 | This was part of a C++ standard proposal and I recommend that you read it for more details: http://pubby.github.io/proposal.html 6 | 7 | #### Container Adaptors 8 | - `fc::flat_map` 9 | - `fc::flat_multimap` 10 | - `fc::flat_set` 11 | - `fc::flat_multiset` 12 | 13 | #### Class Aliases 14 | - `fc::vector_map` 15 | - `fc::vector_multimap` 16 | - `fc::vector_set` 17 | - `fc::vector_multiset` 18 | 19 | #### Differences from Standard Containrs 20 | 21 | - Due to using vectors internally, map's `value_type` is `std::pair` instead of `std::pair`, and all iterators are `const_iterators`, by default. It's still possible to modify the stored values though - see below. 22 | - Flat containers have `O(log(n))` find complexity, but `O(n log(n))` insertion and deletion. They're ideal for when one doesn't need to store very much data, or for when one performs finds far more often than insertions. 23 | 24 | #### New Member Functions 25 | - `has` 26 | - `map.has(key)` returns a pointer to key's mapped value if it exists, otherwise returns null. 27 | - `insert` (delayed sort) 28 | - `map.insert(first, last, fc::delay_sort)` performs insertion with a delayed sort optimization. 29 | - Constructors (delayed sort overload) 30 | - Constructs flat container using a delayed sort optimization. 31 | - Constructors (container construct overload) 32 | - Constructs flat container by forwarding arguments to the underlying container member. 33 | 34 | #### What's an adaptor? 35 | 36 | Container adaptors allow you to use any vector-like sequence container for the underlying storage. 37 | `std::vector` is the natural choice, but classes like `boost::small_vector` and `boost::static_vector` are useful too. Because `std::vector` is the most common in usage, aliases are provided for convenience. 38 | 39 | For basic use, just use the `std::vector` aliases. 40 | 41 | #### The public Members: `container` and `underlying` 42 | 43 | The public member `container` allows access to the underlying storage container. Note that it is the user's responsibility to keep the container in a sorted, unique state. 44 | 45 | Likewise, the public member of iterators: `underlying`, allows access to the underlying storage container's iterators. 46 | 47 | *Example: Delayed sort optimization using `.container`* 48 | 49 | std::flat_multiset set; 50 | while(cpu_temperature() < 100) 51 | set.container.push_back(cpu_temperature()); 52 | std::sort(set.begin(), set.end()); 53 | 54 | #### Const Iteration by Default 55 | 56 | For safety reasons, flat container iterators are const by default. To bypass this safety and get non-const iterators, one can either iterate `.container` or take the `.underlying` of the iterator. 57 | 58 | *Example: Modify values in a way that preserves sortedness* 59 | 60 | for(auto& v : set.container) 61 | v *= 2; 62 | 63 | *Example: Same thing but with `.underlying`* 64 | 65 | for(auto it = v.begin(); it != v.end(); ++it) 66 | (*v.underlying) *= 2; 67 | 68 | For maps, one can also use `.has` and `.operator[]`: 69 | 70 | *Example: using `.has` and `.operator[]`* 71 | 72 | map["foo"] = "bar"; 73 | 74 | if(std::string* value = map.has("qux")) 75 | *value = "baz"; 76 | 77 | 78 | #### Helper Types 79 | 80 | The directory `include_extra` contains convenience typedefs for use with Boost.Container. These typedefs make it trivial to use flat containers that use stack-based allocation instead of the heap. 81 | -------------------------------------------------------------------------------- /flat_multiset.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | #ifndef LIB_FLAT_FLAT_MULTISET_HPP 6 | #define LIB_FLAT_FLAT_MULTISET_HPP 7 | 8 | #include "impl/flat_impl.hpp" 9 | 10 | namespace fc { 11 | namespace impl { 12 | 13 | template 15 | class flat_multiset_base 16 | : public flat_container_base 17 | { 18 | #include "impl/container_traits.hpp" 19 | using B = flat_container_base; 20 | D const* self() const { return static_cast(this); } 21 | D* self() { return static_cast(this); } 22 | public: 23 | using value_compare = Compare; 24 | value_compare value_comp() const { return value_compare(B::key_comp()); } 25 | 26 | using B::B; 27 | using B::insert; 28 | using B::erase; 29 | 30 | // Modifiers 31 | 32 | iterator insert(value_type const& value) 33 | { 34 | iterator it = self()->upper_bound(value); 35 | return self()->container.insert(it.underlying, value); 36 | } 37 | 38 | iterator insert(value_type&& value) 39 | { 40 | iterator it = self()->upper_bound(value); 41 | return self()->container.insert(it.underlying, std::move(value)); 42 | } 43 | 44 | template 45 | void insert(InputIt first, InputIt last, delay_sort_t) 46 | { this->ds_insert_(first, last); } 47 | 48 | size_type erase(key_type const& key) 49 | { 50 | auto it_pair = self()->equal_range(key); 51 | std::size_t ret = std::distance(it_pair.first, it_pair.second); 52 | self()->container.erase(it_pair.first.underlying, 53 | it_pair.second.underlying); 54 | return ret; 55 | } 56 | 57 | // Lookup 58 | 59 | size_type count(key_type const& key) const 60 | { 61 | auto it_pair = self()->equal_range(key); 62 | return std::distance(it_pair.first, it_pair.second); 63 | } 64 | }; 65 | 66 | template 67 | class flat_multiset_base> 69 | : public flat_multiset_base 70 | { 71 | #include "impl/container_traits.hpp" 72 | using B = flat_multiset_base; 73 | D const* self() const { return static_cast(this); } 74 | D* self() { return static_cast(this); } 75 | public: 76 | using B::B; 77 | using B::count; 78 | 79 | // Lookup 80 | 81 | template 82 | size_type count(K const& key) const 83 | { 84 | auto it_pair = self()->equal_range(key); 85 | return std::distance(it_pair.first, it_pair.second); 86 | } 87 | }; 88 | 89 | } // namespace impl 90 | 91 | template> 92 | class flat_multiset 93 | : public impl::flat_multiset_base, 94 | typename Container::value_type, Container, Compare> 95 | { 96 | using B = impl::flat_multiset_base, 97 | typename Container::value_type, Container, Compare>; 98 | #define FLATNAME flat_multiset 99 | #define FLATKEY typename Container::value_type 100 | #include "impl/class_def.hpp" 101 | #undef FLATNAME 102 | #undef FLATKEY 103 | }; 104 | 105 | template> 106 | using vector_multiset = flat_multiset, Compare>; 107 | 108 | template 109 | inline auto operator<=>(const flat_multiset& lhs, const flat_multiset& rhs) 110 | { 111 | return lhs.container <=> rhs.container; 112 | } 113 | 114 | } // namespace fc 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /flat_set.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | #ifndef LIB_FLAT_FLAT_SET_HPP 6 | #define LIB_FLAT_FLAT_SET_HPP 7 | 8 | #include "impl/flat_impl.hpp" 9 | 10 | namespace fc { 11 | namespace impl { 12 | 13 | template 15 | class flat_set_base 16 | : public flat_container_base 17 | { 18 | #include "impl/container_traits.hpp" 19 | using B = flat_container_base; 20 | D const* self() const { return static_cast(this); } 21 | D* self() { return static_cast(this); } 22 | public: 23 | using value_compare = Compare; 24 | value_compare value_comp() const { return value_compare(B::key_comp()); } 25 | 26 | using B::B; 27 | using B::insert; 28 | using B::erase; 29 | 30 | // Modifiers 31 | 32 | std::pair insert(value_type const& value) 33 | { return insert_(value); } 34 | 35 | std::pair insert(value_type&& value) 36 | { return insert_(std::move(value)); } 37 | 38 | template 39 | void insert(InputIt first, InputIt last, delay_sort_t) 40 | { 41 | this->ds_insert_(first, last); 42 | auto it = std::unique( 43 | self()->container.begin(), self()->container.end(), 44 | impl::eq_comp{value_comp()}); 45 | self()->container.erase(it, self()->container.end()); 46 | } 47 | 48 | size_type erase(key_type const& key) 49 | { 50 | const_iterator it = self()->find(key); 51 | if(it == self()->end()) 52 | return 0; 53 | self()->container.erase(it.underlying); 54 | return 1; 55 | } 56 | 57 | // Lookup 58 | 59 | size_type count(key_type const& key) const 60 | { 61 | return self()->find(key) != self()->end(); 62 | } 63 | 64 | private: 65 | template 66 | std::pair insert_(V&& value) 67 | { 68 | iterator it = self()->lower_bound(value); 69 | if(it == self()->end() || self()->value_comp()(value, *it)) 70 | { 71 | it = self()->container.insert(it.underlying, 72 | std::forward(value)); 73 | return std::make_pair(it, true); 74 | } 75 | return std::make_pair(it, false); 76 | } 77 | }; 78 | 79 | template 80 | class flat_set_base> 82 | : public flat_set_base 83 | { 84 | #include "impl/container_traits.hpp" 85 | using B = flat_set_base; 86 | D const* self() const { return static_cast(this); } 87 | D* self() { return static_cast(this); } 88 | public: 89 | using B::B; 90 | using B::count; 91 | 92 | // Lookup 93 | 94 | template 95 | size_type count(K const& key) const 96 | { 97 | return self()->find(key) != self()->end(); 98 | } 99 | }; 100 | 101 | } // namespace impl 102 | 103 | template> 104 | class flat_set 105 | : public impl::flat_set_base, 106 | typename Container::value_type, Container, Compare> 107 | { 108 | using B = impl::flat_set_base, 109 | typename Container::value_type, Container, Compare>; 110 | #define FLATNAME flat_set 111 | #define FLATKEY typename Container::value_type 112 | #include "impl/class_def.hpp" 113 | #undef FLATNAME 114 | #undef FLATKEY 115 | }; 116 | 117 | template> 118 | using vector_set = flat_set, Compare>; 119 | 120 | template 121 | inline auto operator<=>(const flat_set& lhs, const flat_set& rhs) 122 | { 123 | return lhs.container <=> rhs.container; 124 | } 125 | 126 | } // namespace fc 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /flat_multimap.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | #ifndef LIB_FLAT_FLAT_MULTIMAP_HPP 6 | #define LIB_FLAT_FLAT_MULTIMAP_HPP 7 | 8 | #include "impl/flat_impl.hpp" 9 | 10 | namespace fc { 11 | namespace impl { 12 | 13 | template 15 | class flat_multimap_base 16 | : public flat_container_base 17 | { 18 | #include "impl/container_traits.hpp" 19 | using mapped_type = typename value_type::second_type; 20 | using B = flat_container_base; 21 | D const* self() const { return static_cast(this); } 22 | D* self() { return static_cast(this); } 23 | public: 24 | using value_compare = first_compare; 25 | value_compare value_comp() const { return value_compare(B::key_comp()); } 26 | 27 | using B::B; 28 | using B::insert; 29 | using B::erase; 30 | 31 | // Modifiers 32 | 33 | iterator insert(value_type const& value) 34 | { 35 | iterator it = self()->upper_bound(value.first); 36 | return self()->container.insert(it.underlying, value); 37 | } 38 | 39 | iterator insert(value_type&& value) 40 | { 41 | iterator it = self()->upper_bound(value.first); 42 | return self()->container.insert(it.underlying, std::move(value)); 43 | } 44 | 45 | template 46 | void insert(InputIt first, InputIt last, delay_sort_t) 47 | { this->ds_insert_(first, last); } 48 | 49 | size_type erase(key_type const& key) 50 | { 51 | auto it_pair = self()->equal_range(key); 52 | std::size_t ret = std::distance(it_pair.first, it_pair.second); 53 | self()->container.erase(it_pair.first.underlying, 54 | it_pair.second.underlying); 55 | return ret; 56 | } 57 | 58 | // Lookup 59 | 60 | size_type count(key_type const& key) const 61 | { 62 | auto it_pair = self()->equal_range(key); 63 | return std::distance(it_pair.first, it_pair.second); 64 | } 65 | 66 | }; 67 | 68 | template 69 | class flat_multimap_base> 71 | : public flat_multimap_base 72 | { 73 | #include "impl/container_traits.hpp" 74 | using mapped_type = typename value_type::second_type; 75 | using B = flat_multimap_base; 76 | D const* self() const { return static_cast(this); } 77 | D* self() { return static_cast(this); } 78 | public: 79 | 80 | using B::B; 81 | using B::insert; 82 | using B::count; 83 | 84 | // Modifiers 85 | 86 | template 87 | iterator insert(P&& value) 88 | { 89 | iterator it = self()->upper_bound(value.first); 90 | return self()->container.insert(it.underlying, 91 | std::forward

(value)); 92 | } 93 | 94 | // Lookup 95 | 96 | template 97 | size_type count(K const& key) const 98 | { 99 | auto it_pair = self()->equal_range(key); 100 | return std::distance(it_pair.first, it_pair.second); 101 | } 102 | }; 103 | 104 | } // namespace impl 105 | 106 | template> 107 | class flat_multimap 108 | : public impl::flat_multimap_base, 109 | typename Container::value_type::first_type, Container, Compare> 110 | { 111 | using B = impl::flat_multimap_base, 112 | typename Container::value_type::first_type, Container, Compare>; 113 | #define FLATNAME flat_multimap 114 | #define FLATKEY typename Container::value_type::first_type 115 | #include "impl/class_def.hpp" 116 | #undef FLATNAME 117 | #undef FLATKEY 118 | }; 119 | 120 | template> 121 | using vector_multimap 122 | = flat_multimap>, Compare>; 123 | 124 | template 125 | inline auto operator<=>(const flat_multimap& lhs, const flat_multimap& rhs) 126 | { 127 | return lhs.container <=> rhs.container; 128 | } 129 | 130 | } // namespace fc 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /flat_map.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | #ifndef LIB_FLAT_FLAT_MAP_HPP 6 | #define LIB_FLAT_FLAT_MAP_HPP 7 | 8 | #include "impl/flat_impl.hpp" 9 | #include 10 | 11 | namespace fc { 12 | namespace impl { 13 | 14 | template 16 | class flat_map_base 17 | : public flat_container_base 18 | { 19 | #include "impl/container_traits.hpp" 20 | using B = flat_container_base; 21 | D const* self() const { return static_cast(this); } 22 | D* self() { return static_cast(this); } 23 | public: 24 | using mapped_type = typename value_type::second_type; 25 | using value_compare = first_compare; 26 | value_compare value_comp() const { return value_compare(B::key_comp()); } 27 | 28 | using B::B; 29 | using B::insert; 30 | using B::erase; 31 | 32 | // Element access 33 | 34 | mapped_type const* has(key_type const& key) const 35 | { 36 | const_iterator it = self()->find(key); 37 | return it == self()->end() ? nullptr : &it.underlying->second; 38 | } 39 | 40 | mapped_type* has(key_type const& key) 41 | { 42 | iterator it = self()->find(key); 43 | return it == self()->end() ? nullptr : &it.underlying->second; 44 | } 45 | 46 | mapped_type const& at(key_type const& key) const 47 | { 48 | if(mapped_type const* ptr = has(key)) 49 | return *ptr; 50 | throw std::out_of_range("flat_map::at"); 51 | } 52 | 53 | mapped_type& at(key_type const& key) 54 | { 55 | if(mapped_type* ptr = has(key)) 56 | return *ptr; 57 | throw std::out_of_range("flat_map::at"); 58 | } 59 | 60 | mapped_type& operator[](key_type const& key) 61 | { return self()->try_emplace(key).first.underlying->second; } 62 | 63 | mapped_type& operator[](key_type&& key) 64 | { 65 | return self()->try_emplace(std::move(key)).first.underlying->second; 66 | } 67 | 68 | // Modifiers 69 | 70 | std::pair insert(value_type const& value) 71 | { return insert_(value); } 72 | 73 | std::pair insert(value_type&& value) 74 | { return insert_(std::move(value)); } 75 | 76 | template 77 | void insert(InputIt first, InputIt last, delay_sort_t) 78 | { 79 | this->ds_insert_(first, last); 80 | auto it = std::unique( 81 | self()->container.begin(), self()->container.end(), 82 | impl::eq_comp{value_comp()}); 83 | self()->container.erase(it, self()->container.end()); 84 | } 85 | 86 | template 87 | std::pair insert_or_assign(key_type const& key, M&& obj) 88 | { return insert_or_assign_(key, std::forward(obj)); } 89 | 90 | template 91 | std::pair insert_or_assign(key_type&& key, M&& obj) 92 | { return insert_or_assign_(std::move(key), std::forward(obj)); } 93 | 94 | template 95 | std::pair insert_or_assign(const_iterator /*hint*/, 96 | key_type const& key, M&& obj) 97 | { return insert_or_assign(key, std::forward(obj)); } 98 | 99 | template 100 | std::pair insert_or_assign(const_iterator /*hint*/, 101 | key_type&& key, M&& obj) 102 | { return insert_or_assign(std::move(key), std::forward(obj)); } 103 | 104 | template 105 | std::pair try_emplace(key_type const& key, Args&&... args) 106 | { return try_emplace_(key, std::forward(args)...); } 107 | 108 | template 109 | std::pair try_emplace(key_type&& key, Args&&... args) 110 | { return try_emplace_(std::move(key), std::forward(args)...); } 111 | 112 | template 113 | iterator try_emplace(const_iterator /*hint*/, 114 | key_type const& key, Args&&... args) 115 | { return try_emplace_(key, std::forward(args)...).first; } 116 | 117 | template 118 | iterator try_emplace(const_iterator /*hint*/, key_type&& key, Args&&... args) 119 | { 120 | return try_emplace_(std::move(key), 121 | std::forward(args)...).first; 122 | } 123 | 124 | size_type erase(key_type const& key) 125 | { 126 | const_iterator it = self()->find(key); 127 | if(it == self()->end()) 128 | return 0; 129 | self()->container.erase(it.underlying); 130 | return 1; 131 | } 132 | 133 | // Lookup 134 | 135 | size_type count(key_type const& key) const 136 | { 137 | return self()->find(key) != self()->end(); 138 | } 139 | 140 | private: 141 | template 142 | std::pair insert_(V&& value) 143 | { 144 | iterator it = self()->lower_bound(value.first); 145 | if(it == self()->end() || self()->value_comp()(value, *it)) 146 | { 147 | it = self()->container.insert(it.underlying, 148 | std::forward(value)); 149 | return std::make_pair(it, true); 150 | } 151 | return std::make_pair(it, false); 152 | } 153 | 154 | template 155 | std::pair insert_or_assign_(K&& key, M&& obj) 156 | { 157 | iterator it = self()->lower_bound(key); 158 | if(it == self()->end() || self()->key_comp()(key, it->first)) 159 | { 160 | it = self()->container.insert(it.underlying, 161 | value_type(std::forward(key), std::forward(obj))); 162 | return std::make_pair(it, true); 163 | } 164 | it.underlying->second = std::forward(obj); 165 | return std::make_pair(it, false); 166 | } 167 | 168 | template 169 | std::pair try_emplace_(K&& key, Args&&... args) 170 | { 171 | iterator it = self()->lower_bound(key); 172 | if(it == self()->end() || self()->key_comp()(key, it->first)) 173 | { 174 | it = self()->container.emplace(it.underlying, 175 | value_type(std::piecewise_construct, 176 | std::forward_as_tuple(std::forward(key)), 177 | std::forward_as_tuple(std::forward(args)...))); 178 | return std::make_pair(it, true); 179 | } 180 | return std::make_pair(it, false); 181 | } 182 | }; 183 | 184 | template 185 | class flat_map_base> 187 | : public flat_map_base 188 | { 189 | #include "impl/container_traits.hpp" 190 | using B = flat_map_base; 191 | D const* self() const { return static_cast(this); } 192 | D* self() { return static_cast(this); } 193 | public: 194 | using mapped_type = typename value_type::second_type; 195 | 196 | using B::B; 197 | using B::insert; 198 | using B::count; 199 | 200 | // Modifiers 201 | 202 | template 203 | mapped_type const* has(K const& key) const 204 | { 205 | const_iterator it = self()->find(key); 206 | return it == self()->end() ? nullptr : &it.underlying->second; 207 | } 208 | 209 | template 210 | mapped_type* has(K const& key) 211 | { 212 | iterator it = self()->find(key); 213 | return it == self()->end() ? nullptr : &it.underlying->second; 214 | } 215 | 216 | template 217 | std::pair insert(P&& value) 218 | { 219 | iterator it = self()->lower_bound(value.first); 220 | if(it == self()->end() || self()->value_comp()(value, *it)) 221 | { 222 | it = self()->container.insert( 223 | it.underlying, std::forward

(value)); 224 | return std::make_pair(it, true); 225 | } 226 | return std::make_pair(it, false); 227 | } 228 | 229 | // Lookup 230 | 231 | template 232 | size_type count(K const& key) const 233 | { 234 | return self()->find(key) != self()->end(); 235 | } 236 | }; 237 | 238 | } // namespace impl 239 | 240 | template> 241 | class flat_map 242 | : public impl::flat_map_base, 243 | typename Container::value_type::first_type, Container, Compare> 244 | { 245 | using B = impl::flat_map_base, 246 | typename Container::value_type::first_type, Container, Compare>; 247 | #define FLATNAME flat_map 248 | #define FLATKEY typename Container::value_type::first_type 249 | #include "impl/class_def.hpp" 250 | #undef FLATNAME 251 | #undef FLATKEY 252 | }; 253 | 254 | template> 255 | using vector_map = flat_map>, Compare>; 256 | 257 | template 258 | inline auto operator<=>(const flat_map& lhs, const flat_map& rhs) 259 | { 260 | return lhs.container <=> rhs.container; 261 | } 262 | 263 | } // namespace fc 264 | 265 | #endif 266 | -------------------------------------------------------------------------------- /impl/flat_impl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright Pubby 2016 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // http://www.boost.org/LICENSE_1_0.txt 4 | 5 | #ifndef LIB_FLAT_FLAT_IMPL_HPP 6 | #define LIB_FLAT_FLAT_IMPL_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if __cplusplus >= 202002L && __cpp_lib_three_way_comparison >= 201907L 14 | #include 15 | #endif 16 | 17 | namespace fc { 18 | 19 | struct delay_sort_t {}; 20 | constexpr delay_sort_t delay_sort = {}; 21 | 22 | struct container_construct_t {}; 23 | constexpr container_construct_t container_construct = {}; 24 | 25 | namespace impl { 26 | 27 | template 28 | struct eq_comp 29 | { 30 | template 31 | bool operator()(A const& lhs, B const& rhs) 32 | { return !comp(lhs, rhs) && !comp(rhs, lhs); } 33 | Comp comp; 34 | }; 35 | 36 | 37 | template 38 | struct dummy_iterator 39 | { 40 | dummy_iterator() = delete; 41 | dummy_iterator(dummy_iterator const&) = delete; 42 | It underlying; 43 | }; 44 | 45 | template::iterator_category> 47 | class flat_iterator; 48 | 49 | template 50 | class flat_iterator 51 | { 52 | using traits = std::iterator_traits; 53 | public: 54 | using difference_type = typename traits::difference_type; 55 | using value_type = typename traits::value_type const; 56 | using pointer = value_type*; 57 | using reference = value_type&; 58 | using iterator_category = std::random_access_iterator_tag; 59 | 60 | flat_iterator() = default; 61 | flat_iterator(flat_iterator const&) = default; 62 | flat_iterator(flat_iterator&&) = default; 63 | flat_iterator(Convert const& c) : underlying(c.underlying) {} 64 | flat_iterator(Convert&& c) : underlying(std::move(c.underlying)) {} 65 | flat_iterator(It const& underlying) : underlying(underlying) {} 66 | flat_iterator(It&& underlying) : underlying(std::move(underlying)) {} 67 | 68 | flat_iterator& operator=(flat_iterator const& u) = default; 69 | flat_iterator& operator=(flat_iterator&& u) = default; 70 | flat_iterator& operator=(It const& u) 71 | { this->underlying = u; return *this; } 72 | flat_iterator& operator=(It&& u) 73 | { this->underlying = std::move(u); return *this; } 74 | 75 | reference operator*() const { return *underlying; } 76 | pointer operator->() const { return std::addressof(*underlying); } 77 | 78 | flat_iterator& operator++() { ++this->underlying; return *this; } 79 | flat_iterator operator++(int) 80 | { flat_iterator it = *this; ++this->underlying; return it; } 81 | 82 | flat_iterator& operator--() { --this->underlying; return *this; } 83 | flat_iterator operator--(int) 84 | { flat_iterator it = *this; --this->underlying; return it; } 85 | 86 | flat_iterator& operator+=(difference_type d) 87 | { this->underlying += d; return *this; } 88 | flat_iterator& operator-=(difference_type d) 89 | { this->underlying -= d; return *this; } 90 | 91 | flat_iterator operator+(difference_type d) const 92 | { return this->underlying + d; } 93 | flat_iterator operator-(difference_type d) const 94 | { return this->underlying - d; } 95 | 96 | difference_type operator-(flat_iterator const& o) const 97 | { return this->underlying - o.underlying; } 98 | 99 | reference operator[](difference_type d) const { return *(*this + d); } 100 | 101 | #if __cplusplus >= 202002L && __cpp_lib_three_way_comparison >= 201907L 102 | auto operator<=>(flat_iterator const&) const = default; 103 | #endif 104 | auto operator==(flat_iterator const& o) const 105 | { return this->underlying == o.underlying; } 106 | auto operator!=(flat_iterator const& o) const 107 | { return this->underlying != o.underlying; } 108 | auto operator<(flat_iterator const& o) const 109 | { return this->underlying < o.underlying; } 110 | auto operator<=(flat_iterator const& o) const 111 | { return this->underlying <= o.underlying; } 112 | auto operator>(flat_iterator const& o) const 113 | { return this->underlying > o.underlying; } 114 | auto operator>=(flat_iterator const& o) const 115 | { return this->underlying >= o.underlying; } 116 | 117 | It underlying; 118 | }; 119 | 120 | template 121 | struct first_compare 122 | { 123 | first_compare(Compare const& comp) : compare(comp) {} 124 | 125 | bool operator()(Pair const& lhs, Pair const& rhs) const 126 | { return compare.get()(lhs.first, rhs.first); } 127 | 128 | bool operator()(typename Pair::first_type const& lhs, Pair const& rhs) const 129 | { return compare.get()(lhs, rhs.first); } 130 | 131 | bool operator()(Pair const& lhs, typename Pair::first_type const& rhs) const 132 | { return compare.get()(lhs.first, rhs); } 133 | 134 | std::reference_wrapper compare; 135 | }; 136 | 137 | template 138 | struct first_compare> 139 | { 140 | first_compare(Compare const& comp) : compare(comp) {} 141 | using is_transparent = void; 142 | 143 | bool operator()(Pair const& lhs, Pair const& rhs) const 144 | { return compare.get()(lhs.first, rhs.first); } 145 | 146 | template 147 | bool operator()(K const& lhs, Pair const& rhs) const 148 | { return compare.get()(lhs, rhs.first); } 149 | 150 | template 151 | bool operator()(Pair const& lhs, K const& rhs) const 152 | { return compare.get()(lhs.first, rhs); } 153 | 154 | std::reference_wrapper compare; 155 | }; 156 | 157 | template 160 | class flat_container_base : private Compare 161 | { 162 | #include "container_traits.hpp" 163 | D const* self() const { return static_cast(this); } 164 | D* self() { return static_cast(this); } 165 | public: 166 | using key_compare = Compare; 167 | key_compare const& key_comp() const { return *this; } 168 | 169 | flat_container_base() = default; 170 | explicit flat_container_base(Compare const& c) : Compare(c) {} 171 | 172 | // Iterators 173 | 174 | const_iterator cbegin() const 175 | noexcept(noexcept(std::declval().container.cbegin())) 176 | { return self()->container.cbegin(); } 177 | const_iterator begin() const 178 | noexcept(noexcept(std::declval().container.begin())) 179 | { return self()->container.begin(); } 180 | iterator begin() 181 | noexcept(noexcept(std::declval().container.begin())) 182 | { return self()->container.begin(); } 183 | 184 | const_iterator cend() const 185 | noexcept(noexcept(std::declval().container.cend())) 186 | { return self()->container.cend(); } 187 | const_iterator end() const 188 | noexcept(noexcept(std::declval().container.end())) 189 | { return self()->container.end(); } 190 | iterator end() 191 | noexcept(noexcept(std::declval().container.end())) 192 | { return self()->container.end(); } 193 | 194 | const_reverse_iterator crbegin() const 195 | noexcept(noexcept(std::declval().container.crbegin())) 196 | { return self()->container.crbegin(); } 197 | const_reverse_iterator rbegin() const 198 | noexcept(noexcept(std::declval().container.rbegin())) 199 | { return self()->container.rbegin(); } 200 | reverse_iterator rbegin() 201 | noexcept(noexcept(std::declval().container.rbegin())) 202 | { return self()->container.rbegin(); } 203 | 204 | const_reverse_iterator crend() const 205 | noexcept(noexcept(std::declval().container.crend())) 206 | { return self()->container.crend(); } 207 | const_reverse_iterator rend() const 208 | noexcept(noexcept(std::declval().container.rend())) 209 | { return self()->container.rend(); } 210 | reverse_iterator rend() 211 | noexcept(noexcept(std::declval().container.rend())) 212 | { return self()->container.rend(); } 213 | 214 | // Capacity 215 | 216 | bool empty() const 217 | noexcept(noexcept(std::declval().container.empty())) 218 | { return self()->container.empty(); } 219 | 220 | size_type size() const 221 | noexcept(noexcept(std::declval().container.size())) 222 | { return self()->container.size(); } 223 | 224 | // Modifiers 225 | 226 | iterator insert(const_iterator /*hint*/, value_type const& value) 227 | { return self()->insert(value).first; } 228 | 229 | iterator insert(const_iterator /*hint*/, value_type&& value) 230 | { return self()->insert(std::move(value)).first; } 231 | 232 | template 233 | void insert(InputIt first, InputIt last) 234 | { 235 | for(InputIt it = first; it != last; ++it) 236 | self()->insert(*it); 237 | } 238 | 239 | void insert(std::initializer_list ilist) 240 | { self()->insert(ilist.begin(), ilist.end()); } 241 | 242 | void insert(std::initializer_list ilist, delay_sort_t d) 243 | { self()->insert(ilist.begin(), ilist.end(), d); } 244 | 245 | template 246 | auto emplace(Args&&... args) 247 | { return self()->insert(value_type(std::forward(args)...)); } 248 | 249 | template 250 | auto emplace_hint(const_iterator /*hint*/, Args&&... args) 251 | { return self()->insert(value_type(std::forward(args)...)); } 252 | 253 | iterator erase(const_iterator pos) 254 | noexcept(noexcept(std::declval().container.erase(pos.underlying))) 255 | { return self()->container.erase(pos.underlying); } 256 | 257 | iterator erase(const_iterator first, const_iterator last) 258 | noexcept(noexcept(std::declval().container.erase(first.underlying, 259 | last.underlying))) 260 | { return self()->container.erase(first.underlying, last.underlying); } 261 | 262 | void clear() 263 | noexcept(noexcept(std::declval().container.clear())) 264 | { self()->container.clear(); } 265 | 266 | void swap(D& other) 267 | noexcept(D::has_noexcept_swap()) 268 | { 269 | using std::swap; 270 | swap(self()->container, other.container); 271 | } 272 | 273 | // Lookup 274 | 275 | const_iterator find(key_type const& key) const 276 | { 277 | const_iterator it = self()->lower_bound(key); 278 | if(it == self()->end() || self()->value_comp()(key, *it)) 279 | return self()->end(); 280 | return it; 281 | } 282 | 283 | iterator find(key_type const& key) 284 | { 285 | iterator it = self()->lower_bound(key); 286 | if(it == self()->end() || self()->value_comp()(key, *it)) 287 | return self()->end(); 288 | return it; 289 | } 290 | 291 | const_iterator lower_bound(key_type const& key) const 292 | { 293 | return std::lower_bound( 294 | self()->begin(), self()->end(), 295 | key, self()->value_comp()); 296 | } 297 | 298 | iterator lower_bound(key_type const& key) 299 | { 300 | return std::lower_bound( 301 | self()->begin(), self()->end(), 302 | key, self()->value_comp()); 303 | } 304 | 305 | const_iterator upper_bound(key_type const& key) const 306 | { 307 | return std::upper_bound( 308 | self()->begin(), self()->end(), 309 | key, self()->value_comp()); 310 | } 311 | 312 | iterator upper_bound(key_type const& key) 313 | { 314 | return std::upper_bound( 315 | self()->begin(), self()->end(), 316 | key, self()->value_comp()); 317 | } 318 | 319 | std::pair 320 | equal_range(key_type const& key) const 321 | { 322 | return std::equal_range( 323 | self()->begin(), self()->end(), 324 | key, self()->value_comp()); 325 | } 326 | 327 | std::pair equal_range(key_type const& key) 328 | { 329 | return std::equal_range( 330 | self()->begin(), self()->end(), 331 | key, self()->value_comp()); 332 | } 333 | 334 | private: 335 | static constexpr bool has_noexcept_swap() 336 | { 337 | using std::swap; 338 | return noexcept(swap(*static_cast(nullptr), 339 | *static_cast(nullptr))); 340 | } 341 | 342 | protected: 343 | template 344 | void ds_insert_(InputIt first, InputIt last) 345 | { 346 | size_type const i = self()->size(); 347 | for(InputIt it = first; it != last; ++it) 348 | self()->container.push_back(*it); 349 | std::sort( 350 | self()->container.begin()+i, 351 | self()->container.end(), 352 | self()->value_comp()); 353 | std::inplace_merge( 354 | self()->container.begin(), 355 | self()->container.begin()+i, 356 | self()->container.end()); 357 | // Note: Not calling unique here. Do it in the caller. 358 | } 359 | }; 360 | 361 | template 363 | class flat_container_base> 365 | : public flat_container_base 366 | { 367 | #include "container_traits.hpp" 368 | D const* self() const { return static_cast(this); } 369 | D* self() { return static_cast(this); } 370 | using B = flat_container_base; 371 | public: 372 | 373 | using B::B; 374 | using B::insert; 375 | using B::find; 376 | using B::lower_bound; 377 | using B::upper_bound; 378 | using B::equal_range; 379 | 380 | // Modifiers 381 | 382 | template 383 | std::enable_if_t::value, iterator> 384 | insert(const_iterator /*hint*/, P&& value) 385 | { return insert(std::forward

(value)).first; } 386 | 387 | // Lookup 388 | 389 | template 390 | const_iterator find(K const& key) const 391 | { 392 | const_iterator it = self()->lower_bound(key); 393 | if (it == self()->end() || self()->value_comp()(key, *it)) 394 | return self()->end(); 395 | return it; 396 | } 397 | 398 | template 399 | iterator find(K const& key) 400 | { 401 | iterator it = self()->lower_bound(key); 402 | if (it == self()->end() || self()->value_comp()(key, *it)) 403 | return self()->end(); 404 | return it; 405 | } 406 | 407 | template 408 | const_iterator lower_bound(K const& key) const 409 | { 410 | return std::lower_bound( 411 | self()->begin(), self()->end(), 412 | key, self()->value_comp()); 413 | } 414 | 415 | template 416 | iterator lower_bound(K const& key) 417 | { 418 | return std::lower_bound( 419 | self()->begin(), self()->end(), 420 | key, self()->value_comp()); 421 | } 422 | 423 | template 424 | const_iterator upper_bound(K const& key) const 425 | { 426 | return std::upper_bound( 427 | self()->begin(), self()->end(), 428 | key, self()->value_comp()); 429 | } 430 | 431 | template 432 | iterator upper_bound(K const& key) 433 | { 434 | return std::upper_bound( 435 | self()->begin(), self()->end(), 436 | key, self()->value_comp()); 437 | } 438 | 439 | template 440 | std::pair 441 | equal_range(K const& key) const 442 | { 443 | return std::equal_range( 444 | self()->begin(), self()->end(), 445 | key, self()->value_comp()); 446 | } 447 | 448 | template 449 | std::pair equal_range(K const& key) 450 | { 451 | return std::equal_range( 452 | self()->begin(), self()->end(), 453 | key, self()->value_comp()); 454 | } 455 | }; 456 | 457 | } // namespace fc 458 | } // namespace impl 459 | 460 | #endif 461 | --------------------------------------------------------------------------------