libcruft-util/map/multi_fixed.hpp

245 lines
7.2 KiB
C++

/*
* 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 <danny@nerdcruft.net>
*/
#pragma once
#include "../iterator/unordered_insert.hpp"
#include "../iterator/placement_output.hpp"
#include "../cast.hpp"
#include <algorithm>
#include <functional>
#include <cstddef>
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<key_type, mapped_type>;
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<value_type> 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 <typename RandomT>
void
insert (RandomT first, RandomT last)
{
static_assert (
std::is_same_v<
typename std::iterator_traits<RandomT>::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<size_t> (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 <typename K>
bool contains (K const&) const;
template <typename K>
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 <typename K>
const_iterator find (K const&) const&;
template <typename K>
std::pair<iterator, iterator>
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 <std::size_t SizeV, typename KeyT, typename ValueT, typename ComparatorT>
multi_fixed<SizeV, KeyT, ValueT, ComparatorT>
operator+ (
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &a,
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &b
) {
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> 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 <std::size_t SizeV, typename KeyT, typename ValueT, typename ComparatorT>
multi_fixed<SizeV, KeyT, ValueT, ComparatorT>
operator- (
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &a,
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &b
) {
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> res;
std::set_difference (
std::begin (a), std::end (a),
std::begin (b), std::end (b),
cruft::iterator::unordered_insert (res)
);
return res;
}
template <std::size_t SizeV, typename KeyT, typename ValueT, typename ComparatorT>
bool
operator== (
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &a,
multi_fixed<SizeV, KeyT, ValueT, ComparatorT> const &b
) {
return std::equal (
std::begin (a), std::end (a),
std::begin (b), std::end (b)
);
}
}