diff --git a/README.md b/README.md index e3da0c9..eefc06f 100644 --- a/README.md +++ b/README.md @@ -25,14 +25,15 @@ ## Installation -[flat.hpp][flat] is a header only library. All you need to do is copy the header files (`flat_set.hpp` and `flat_map.hpp`) into your project and include them: +[flat.hpp][flat] is a header only library. All you need to do is copy the header files (`flat_set.hpp` and `flat_map.hpp`) into your project and include them. -```cpp -#include "flat_set.hpp" -#include "flat_map.hpp" -``` +## API +- [Flat Set](#flat-set) +- [Flat Map](#flat-map) +- [Flat Multiset](#flat-multiset) +- [Flat Multimap](#flat-multimap) -## flat_set +## Flat Set ```cpp template < typename Key @@ -131,6 +132,9 @@ const_reverse_iterator crend() const noexcept; bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; +size_type capacity() const noexcept; +void reserve(size_type ncapacity); +void shrink_to_fit(); ``` ### Modifiers @@ -223,7 +227,7 @@ bool operator>=( const flat_set& r); ``` -## flat_map +## Flat Map ```cpp template < typename Key @@ -329,6 +333,9 @@ const_reverse_iterator crend() const noexcept; bool empty() const noexcept; size_type size() const noexcept; size_type max_size() const noexcept; +size_type capacity() const noexcept; +void reserve(size_type ncapacity); +void shrink_to_fit(); ``` ### Element access @@ -431,4 +438,12 @@ bool operator>=( const flat_map& r); ``` +## Flat Multiset + +> coming soon! + +## Flat Multimap + +> coming soon! + ## [License (MIT)](./LICENSE.md) diff --git a/flat_map.hpp b/flat_map.hpp index 7b19608..c5a5e94 100644 --- a/flat_map.hpp +++ b/flat_map.hpp @@ -192,6 +192,18 @@ namespace flat_hpp return data_.max_size(); } + size_type capacity() const noexcept { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + mapped_type& operator[](key_type&& key) { const iterator iter = find(key); return iter != end() @@ -354,39 +366,88 @@ namespace flat_hpp namespace flat_hpp { - template < typename K, typename V, typename C, typename A > - void swap(flat_map& l, flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + void swap( + flat_map& l, + flat_map& r) + { l.swap(r); } - template < typename K, typename V, typename C, typename A > - bool operator==(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator==( + const flat_map& l, + const flat_map& r) + { return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin(), r.end()); } - template < typename K, typename V, typename C, typename A > - bool operator!=(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator!=( + const flat_map& l, + const flat_map& r) + { return !(l == r); } - template < typename K, typename V, typename C, typename A > - bool operator<(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator<( + const flat_map& l, + const flat_map& r) + { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } - template < typename K, typename V, typename C, typename A > - bool operator>(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator>( + const flat_map& l, + const flat_map& r) + { return r < l; } - template < typename K, typename V, typename C, typename A > - bool operator<=(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator<=( + const flat_map& l, + const flat_map& r) + { return !(r < l); } - template < typename K, typename V, typename C, typename A > - bool operator>=(const flat_map& l, const flat_map& r) { + template < typename Key + , typename Value + , typename Compare + , typename Allocator + , typename Container > + bool operator>=( + const flat_map& l, + const flat_map& r) + { return !(l < r); } } diff --git a/flat_map_tests.cpp b/flat_map_tests.cpp index cdacde3..9424e42 100644 --- a/flat_map_tests.cpp +++ b/flat_map_tests.cpp @@ -7,6 +7,8 @@ #define CATCH_CONFIG_FAST_COMPILE #include "catch.hpp" +#include + #include "flat_map.hpp" using namespace flat_hpp; @@ -15,8 +17,20 @@ namespace template < typename T > class dummy_allocator { public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; using value_type = T; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template < typename U > + struct rebind { using other = dummy_allocator; }; + dummy_allocator() = default; dummy_allocator(int i) : i(i) {} @@ -34,6 +48,15 @@ namespace std::free(p); } + template < typename U, typename... Args > + void construct(U* p, Args&&... args) { + ::new((void*)p) U(std::forward(args)...); + } + + void destroy(pointer p) { + p->~T(); + } + int i = 0; }; @@ -174,29 +197,60 @@ TEST_CASE("flat_map") { } SECTION("capacity") { using map_t = flat_map; - map_t s0; - REQUIRE(s0.empty()); - REQUIRE_FALSE(s0.size()); - REQUIRE(s0.max_size() == std::allocator>().max_size()); + { + map_t s0; - s0.insert({2,42}); + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); - REQUIRE_FALSE(s0.empty()); - REQUIRE(s0.size() == 1u); - REQUIRE(s0.max_size() == std::allocator>().max_size()); + s0.insert({2,42}); - s0.insert({2,84}); - REQUIRE(s0.size() == 1u); + REQUIRE_FALSE(s0.empty()); + REQUIRE(s0.size() == 1u); + REQUIRE(s0.max_size() == std::allocator>().max_size()); - s0.insert({3,84}); - REQUIRE(s0.size() == 2u); + s0.insert({2,84}); + REQUIRE(s0.size() == 1u); - s0.clear(); + s0.insert({3,84}); + REQUIRE(s0.size() == 2u); - REQUIRE(s0.empty()); - REQUIRE_FALSE(s0.size()); - REQUIRE(s0.max_size() == std::allocator>().max_size()); + s0.clear(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + } + + { + map_t s0; + + REQUIRE(s0.capacity() == 0); + s0.reserve(42); + REQUIRE(s0.capacity() == 42); + s0.insert({{1,2},{2,3},{3,4}}); + REQUIRE(s0.capacity() == 42); + s0.shrink_to_fit(); + REQUIRE(s0.size() == 3); + REQUIRE(s0.capacity() == 3); + REQUIRE(s0 == map_t{{1,2},{2,3},{3,4}}); + + using alloc2_t = dummy_allocator< + std::pair>; + + using map2_t = flat_map< + int, + unsigned, + std::less, + alloc2_t, + std::deque, alloc2_t>>; + + map2_t s1; + s1.insert({{1,2},{2,3},{3,4}}); + REQUIRE(s1 == map2_t{{1,2},{2,3},{3,4}}); + } } SECTION("access") { struct obj_t { @@ -225,6 +279,10 @@ TEST_CASE("flat_map") { REQUIRE(s0[1] == 84); REQUIRE(s0 == map_t{{1,84}}); + s0[2] = 21; + REQUIRE(s0[2] == 21); + REQUIRE(s0 == map_t{{1,84},{2,21}}); + REQUIRE(s0.at(1) == 84); REQUIRE(my_as_const(s0).at(k1) == 84); REQUIRE_THROWS_AS(s0.at(0), std::out_of_range); @@ -394,5 +452,13 @@ TEST_CASE("flat_map") { REQUIRE(map_t{{1,2},{3,4}} <= map_t{{1,2},{3,4}}); REQUIRE_FALSE(map_t{{1,2},{3,4}} > map_t{{1,2},{3,4}}); REQUIRE(map_t{{1,2},{3,4}} >= map_t{{1,2},{3,4}}); + + const map_t s0; + REQUIRE(s0 == s0); + REQUIRE_FALSE(s0 != s0); + REQUIRE_FALSE(s0 < s0); + REQUIRE_FALSE(s0 > s0); + REQUIRE(s0 <= s0); + REQUIRE(s0 >= s0); } } diff --git a/flat_set.hpp b/flat_set.hpp index a63e51e..c908d1f 100644 --- a/flat_set.hpp +++ b/flat_set.hpp @@ -170,6 +170,18 @@ namespace flat_hpp return data_.max_size(); } + size_type capacity() const noexcept { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + std::pair insert(value_type&& value) { const iterator iter = lower_bound(value); return iter == end() || compare_(value, *iter) @@ -302,39 +314,81 @@ namespace flat_hpp namespace flat_hpp { - template < typename K, typename C, typename A > - void swap(flat_set& l, flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + void swap( + flat_set& l, + flat_set& r) + { l.swap(r); } - template < typename K, typename C, typename A > - bool operator==(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator==( + const flat_set& l, + const flat_set& r) + { return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin(), r.end()); } - template < typename K, typename C, typename A > - bool operator!=(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator!=( + const flat_set& l, + const flat_set& r) + { return !(l == r); } - template < typename K, typename C, typename A > - bool operator<(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator<( + const flat_set& l, + const flat_set& r) + { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } - template < typename K, typename C, typename A > - bool operator>(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator>( + const flat_set& l, + const flat_set& r) + { return r < l; } - template < typename K, typename C, typename A > - bool operator<=(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator<=( + const flat_set& l, + const flat_set& r) + { return !(r < l); } - template < typename K, typename C, typename A > - bool operator>=(const flat_set& l, const flat_set& r) { + template < typename Key + , typename Compare + , typename Allocator + , typename Container > + bool operator>=( + const flat_set& l, + const flat_set& r) + { return !(l < r); } } diff --git a/flat_set_tests.cpp b/flat_set_tests.cpp index 5331c3c..54e2f2e 100644 --- a/flat_set_tests.cpp +++ b/flat_set_tests.cpp @@ -7,6 +7,8 @@ #define CATCH_CONFIG_FAST_COMPILE #include "catch.hpp" +#include + #include "flat_set.hpp" using namespace flat_hpp; @@ -15,8 +17,20 @@ namespace template < typename T > class dummy_allocator { public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; using value_type = T; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template < typename U > + struct rebind { using other = dummy_allocator; }; + dummy_allocator() = default; dummy_allocator(int i) : i(i) {} @@ -34,6 +48,15 @@ namespace std::free(p); } + template < typename U, typename... Args > + void construct(U* p, Args&&... args) { + ::new((void*)p) U(std::forward(args)...); + } + + void destroy(pointer p) { + p->~T(); + } + int i = 0; }; @@ -158,29 +181,58 @@ TEST_CASE("flat_set") { } SECTION("capacity") { using set_t = flat_set; - set_t s0; - REQUIRE(s0.empty()); - REQUIRE_FALSE(s0.size()); - REQUIRE(s0.max_size() == std::allocator().max_size()); + { + set_t s0; - s0.insert(42); + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator().max_size()); - REQUIRE_FALSE(s0.empty()); - REQUIRE(s0.size() == 1u); - REQUIRE(s0.max_size() == std::allocator().max_size()); + s0.insert(42); - s0.insert(42); - REQUIRE(s0.size() == 1u); + REQUIRE_FALSE(s0.empty()); + REQUIRE(s0.size() == 1u); + REQUIRE(s0.max_size() == std::allocator().max_size()); - s0.insert(84); - REQUIRE(s0.size() == 2u); + s0.insert(42); + REQUIRE(s0.size() == 1u); - s0.clear(); + s0.insert(84); + REQUIRE(s0.size() == 2u); - REQUIRE(s0.empty()); - REQUIRE_FALSE(s0.size()); - REQUIRE(s0.max_size() == std::allocator().max_size()); + s0.clear(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator().max_size()); + } + + { + set_t s0; + + REQUIRE(s0.capacity() == 0); + s0.reserve(42); + REQUIRE(s0.capacity() == 42); + s0.insert({1,2,3}); + REQUIRE(s0.capacity() == 42); + s0.shrink_to_fit(); + REQUIRE(s0.size() == 3); + REQUIRE(s0.capacity() == 3); + REQUIRE(s0 == set_t{1,2,3}); + + using alloc2_t = dummy_allocator; + + using set2_t = flat_set< + int, + std::less, + alloc2_t, + std::deque>; + + set2_t s1; + s1.insert({1,2,3}); + REQUIRE(s1 == set2_t{1,2,3}); + } } SECTION("inserts") { struct obj_t { @@ -365,5 +417,13 @@ TEST_CASE("flat_set") { REQUIRE(set_t{1,2,3} <= set_t{1,2,3}); REQUIRE_FALSE(set_t{1,2,3} > set_t{1,2,3}); REQUIRE(set_t{1,2,3} >= set_t{1,2,3}); + + const set_t s0; + REQUIRE(s0 == s0); + REQUIRE_FALSE(s0 != s0); + REQUIRE_FALSE(s0 < s0); + REQUIRE_FALSE(s0 > s0); + REQUIRE(s0 <= s0); + REQUIRE(s0 >= s0); } }