From 24d31b216b1b1a3be5a6471444bb52a1a288edc5 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Fri, 5 Feb 2021 12:04:20 +1000 Subject: [PATCH] map/multi_fixed: add insert, erase, find, comparators --- map/multi_fixed.hpp | 140 +++++++++++++++++++++++++++++++++++++-- test/map/multi_fixed.cpp | 14 ++++ 2 files changed, 150 insertions(+), 4 deletions(-) diff --git a/map/multi_fixed.hpp b/map/multi_fixed.hpp index a0d7c67a..bb18302d 100644 --- a/map/multi_fixed.hpp +++ b/map/multi_fixed.hpp @@ -11,6 +11,7 @@ #include "../iterator/unordered_insert.hpp" #include "../iterator/placement_output.hpp" +#include "../iterator/tuple_picker.hpp" #include "../cast.hpp" #include @@ -20,6 +21,7 @@ namespace cruft::map { + /// A multi-map with a compile-time fixed-size backing store. template < std::size_t SizeV, typename KeyT, @@ -71,7 +73,19 @@ namespace cruft::map { } multi_fixed (multi_fixed const&); - multi_fixed (multi_fixed &&) noexcept; + + multi_fixed (multi_fixed &&rhs) noexcept (std::is_nothrow_move_constructible_v) + { + clear (); + + while (m_size != rhs.m_size) { + new (m_store.data + m_size) value_type (std::move (rhs.m_store.data[m_size])); + ++m_size; + } + } + + multi_fixed& operator= (multi_fixed&&) noexcept (std::is_nothrow_move_assignable_v) = default; + multi_fixed& operator= (multi_fixed const&) noexcept (std::is_nothrow_assignable_v) = default; iterator begin (void)& { return m_store.data + 0; } iterator end (void)& { return m_store.data + m_size; } @@ -93,7 +107,8 @@ namespace cruft::map { // avoid moving too many key-value pairs backwards to make room // for the data. auto cursor = begin (); - while (cursor->first <= kv.first && cursor != end ()) + ComparatorT cmp {}; + while (cmp (cursor->first, kv.first) && cursor != end ()) ++cursor; // Shuffle the items back in the storage. @@ -169,15 +184,118 @@ namespace cruft::map { template const_iterator find (K const&) const&; + template + iterator + lower_bound (K const &k)& + { + ComparatorT cmp {}; + return std::find_if ( + begin (), + end (), + [&] (auto const &val) { return !cmp (val.first, k); } + ); + } + + template + iterator + upper_bound (K const &k)& + { + ComparatorT cmp {}; + return std::find_if ( + begin (), + end (), + [&] (auto const &val) { return cmp (k, val.first); } + ); + } + template std::pair - equal_range (K const&)&; + equal_range (K const &key)& + { + return { + lower_bound (key), + upper_bound (key) + }; + } - void clear (void); + void clear (void) + { + for (std::size_t i = 0; i < m_size; ++i) + m_store.data[i].~value_type (); + m_size = 0; + } + void erase (iterator it) + { + CHECK_GE (it, begin ()); + CHECK_LT (it, end ()); + CHECK (!empty ()); + + std::move (it + 1, end (), it); + --m_size; + m_store.data[m_size].~value_type (); + } + + std::size_t erase (key_type const&); + + bool empty (void) const { return m_size == 0; } auto size (void) const { return m_size; } auto capacity (void) const { return elements; } + void swap (multi_fixed &rhs) + { + // To save duplicating the code which moves the trailing excess + // items between containers we instead just reverse the direction + // here. + if (rhs.size () > size ()) + rhs.swap (*this); + + using std::swap; + + // Swap the commonly held indices + std::size_t const common = std::min (size (), rhs.size ()); + for (std::size_t i = 0; i != common; ++i) + swap (m_store.data[i], rhs.m_store.data[i]); + + // Move our indices to the rhs if we're larger. We don't need the + // reverse case because we took care of that earlier. + CHECK_GE (size (), rhs.size ()); + if (size () > common) { + for (std::size_t i = common; i < size (); ++i) + new (rhs.m_store.data + i) value_type (std::move (m_store.data[i])); + + swap (m_size, rhs.m_size); + } + } + + /// A helper class that allows iteration over the keys of a + /// multi_fixed map. + /// + /// The iterators it provides may be invalidated if any mutating + /// operations are performed on the underlying multi_fixed object. + class keys_proxy { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = KeyT; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + keys_proxy (multi_fixed const &_parent) + : m_parent (&_parent) + { ; } + + auto begin (void) const { return cruft::iterator::make_tuple_picker<0> (m_parent->begin ()); } + auto end (void) const { return cruft::iterator::make_tuple_picker<0> (m_parent->end ()); } + + private: + multi_fixed const *m_parent; + }; + + /// Returns a container that allows traversal of the underlying keys + /// of the store. + keys_proxy keys (void) const& { return keys_proxy (*this); } + private: std::size_t m_size = 0; @@ -191,6 +309,20 @@ namespace cruft::map { }; + template < + std::size_t SizeV, + typename KeyT, + typename ValueT, + typename ComparatorT + > + void swap ( + multi_fixed &a, + multi_fixed &b + ) { + a.swap (b); + } + + /// Returns a concatenation of two multi_fixed maps. ie, a copy of /// all entries in the first map, and all entries in the second map. /// diff --git a/test/map/multi_fixed.cpp b/test/map/multi_fixed.cpp index 9bf55c5d..0225fbe1 100644 --- a/test/map/multi_fixed.cpp +++ b/test/map/multi_fixed.cpp @@ -73,6 +73,20 @@ int main () tap.expect_eq (a - b, expected, "difference of two sets"); } + { + cruft::map::multi_fixed<4, int, int> init {{ {0,0}, {1,1}, {2,2}, {3,3} }}; + init.erase (init.find (2)); + cruft::map::multi_fixed<4, int, int> expected {{ {0,0}, {1,1}, {3,3} }}; + tap.expect_eq (init, expected, "erase of inner element"); + } + + { + cruft::map::multi_fixed<4, int, int> init {{ {0,0}, {1,1}, {2,2}, {3,3} }}; + init.erase (init.find (3)); + cruft::map::multi_fixed<4, int, int> expected {{ {0,0}, {1,1}, {2,2} }}; + tap.expect_eq (init, expected, "erase of last element"); + } + test_sorted_iterators (tap); test_bad_alloc (tap);