/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2021, Danny Robson */ #pragma once #include "../iterator/unordered_insert.hpp" #include "../iterator/placement_output.hpp" #include "../cast.hpp" #include #include #include namespace cruft::map { template < std::size_t SizeV, typename KeyT, typename ValueT, typename ComparatorT = std::less<> > class multi_fixed { public: static constexpr auto elements = SizeV; using key_type = KeyT; using mapped_type = ValueT; using value_type = std::pair; using key_compare = ComparatorT; using reference = value_type&; using const_reference = value_type const&; using pointer = value_type*; using const_pointer = value_type const*; using iterator = pointer; using const_iterator = const_pointer; multi_fixed () = default; ~multi_fixed () { for (std::size_t i = 0; i < m_size; ++i) m_store.data[i].~value_type (); } multi_fixed (std::initializer_list const &src) { if (src.size () > capacity ()) throw std::bad_alloc (); std::copy ( std::make_move_iterator (std::begin (src)), std::make_move_iterator (std::end (src)), m_store.data + 0 ); m_size = src.size (); std::sort ( m_store.data + 0, m_store.data + m_size, ComparatorT {} ); } multi_fixed (multi_fixed const&); multi_fixed (multi_fixed &&) noexcept; iterator begin (void)& { return m_store.data + 0; } iterator end (void)& { return m_store.data + m_size; } const_iterator begin (void) const& { return m_store.data + 0; } const_iterator end (void) const& { return m_store.data + m_size; } const_iterator cbegin (void) const& { return cbegin (); } const_iterator cend (void) const& { return cend (); } iterator insert (value_type const &kv) { // Don't overrun our static buffer if (size () == capacity ()) throw std::bad_alloc (); // Find the insert position. We prefer lte comparison so that we // avoid moving too many key-value pairs backwards to make room // for the data. auto cursor = begin (); while (cursor->first <= kv.first && cursor != end ()) ++cursor; // Shuffle the items back in the storage. auto offset = std::distance (begin (), cursor); auto remain = size () - offset; std::move_backward (cursor, cursor + remain, cursor + remain + 1); // Insert the new data and return new (cursor) value_type (kv); ++m_size; return cursor; } iterator insert (value_type &&kv) { return insert (kv); } template void insert (RandomT first, RandomT last) { static_assert ( std::is_same_v< typename std::iterator_traits::iterator_category, std::random_access_iterator_tag >, "We assume random iterators here to simplify offset " "calculations and make copying a little more efficient. " "There's no reason it needs to be restricted though." ); auto const count = std::distance (first, last); if (cruft::cast::sign (count) > capacity () - size ()) throw std::bad_alloc (); std::copy ( first, last, cruft::iterator::placement_output {m_store.data + size ()} ); std::sort ( m_store.data + size (), m_store.data + size () + count ); std::inplace_merge ( m_store.data + 0, m_store.data + size (), m_store.data + size () + count ); m_size += count; } template bool contains (K const&) const; template iterator find (K const &k)& { ComparatorT cmp {}; for (auto cursor = begin (); cursor != end (); ++cursor) if (!cmp (cursor->first, k) && !cmp (k, cursor->first)) return cursor; return end (); } template const_iterator find (K const&) const&; template std::pair equal_range (K const&)&; void clear (void); auto size (void) const { return m_size; } auto capacity (void) const { return elements; } private: std::size_t m_size = 0; union store { store () { ; } ~store () { ; } char defer; value_type data[elements]; } m_store; }; /// 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. /// /// If both maps have the same value then the output will have duplicates. /// /// If the combined size of the maps exceeds the static capacity a /// std::bad_alloc will be thrown. template multi_fixed operator+ ( multi_fixed const &a, multi_fixed const &b ) { multi_fixed res; res.insert (a.begin (), a.end ()); res.insert (b.begin (), b.end ()); return res; } /// Returns the difference between two multi_fixed maps. /// /// ie, the entries found in the first but not the second. template multi_fixed operator- ( multi_fixed const &a, multi_fixed const &b ) { multi_fixed res; std::set_difference ( std::begin (a), std::end (a), std::begin (b), std::end (b), cruft::iterator::unordered_insert (res) ); return res; } template bool operator== ( multi_fixed const &a, multi_fixed const &b ) { return std::equal ( std::begin (a), std::end (a), std::begin (b), std::end (b) ); } }