map/fixed: add static sized flat map

This commit is contained in:
Danny Robson 2019-03-28 14:27:34 +11:00
parent b9f739324a
commit 9926179e7f
4 changed files with 187 additions and 0 deletions

View File

@ -376,6 +376,8 @@ list (
library.hpp
log.cpp
log.hpp
map/fixed.cpp
map/fixed.hpp
maths.cpp
maths.hpp
maths/fast.hpp
@ -603,6 +605,7 @@ if (TESTS)
job/dispatch
job/queue
kmeans
map/fixed
maths
maths/fast
matrix

1
map/fixed.cpp Normal file
View File

@ -0,0 +1 @@
#include "fixed.hpp"

157
map/fixed.hpp Normal file
View File

@ -0,0 +1,157 @@
/*
* 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 2019 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include <cruft/util/debug.hpp>
#include <array>
#include <utility>
#include <stdexcept>
#include <cstddef>
namespace cruft::map {
/// A flat map structure with a static capacity store and dynamic size.
template <std::size_t SizeV, typename KeyT, typename ValueT>
class fixed {
public:
static constexpr auto elements = SizeV;
using key_type = KeyT;
using mapped_type = ValueT;
using value_type = std::pair<KeyT,ValueT>;
using iterator = value_type*;
///////////////////////////////////////////////////////////////////////
fixed () = default;
//---------------------------------------------------------------------
fixed (std::initializer_list<value_type> keyvals)
{
if (keyvals.size () > capacity ())
throw std::bad_alloc ();
for (auto const &kv: keyvals) {
auto const &[pos,success] = insert (kv);
CHECK (success);
(void)pos;
(void)success;
}
}
fixed (fixed&&) noexcept;
fixed& operator= (fixed&&) noexcept;
fixed (fixed const&);
fixed& operator= (fixed const&);
~fixed () { clear (); }
///////////////////////////////////////////////////////////////////////
mapped_type& at (KeyT const &key) &
{
for (auto &i: *this)
if (i.first == key)
return i.second;
throw std::out_of_range ("Element out of range");
}
//---------------------------------------------------------------------
mapped_type const& at (KeyT const &key) const&
{
for (auto &i: *this)
if (i.first == key)
return i.second;
throw std::out_of_range ("Element out of range");
}
//---------------------------------------------------------------------
std::pair<iterator,bool> insert (value_type const &keyval)
{
for (auto &i: *this) {
if (i.first == keyval.first) {
return { &i, false };
}
}
if (m_size >= capacity ())
throw std::bad_alloc ();
auto ptr = m_store.data + m_size;
new (ptr) value_type (keyval);
++m_size;
return { ptr, true };
}
//---------------------------------------------------------------------
mapped_type& operator[] (KeyT const &key)
{
for (auto &i: *this)
if (i.first == key)
return i.second;
if (m_size >= capacity ())
throw std::bad_alloc ();
auto ptr = m_store.data + m_size;
new (ptr) value_type ({ key, {} });
++m_size;
return ptr->second;
}
///////////////////////////////////////////////////////////////////////
void clear (void)
{
for (auto i: *this)
i.~value_type ();
m_size = 0;
}
//---------------------------------------------------------------------
auto begin (void)& { return std::begin (m_store.data); }
auto end (void)& { return begin () + m_size; }
auto begin (void) const& { return std::begin (m_store.data); }
auto end (void) const& { return begin () + m_size; }
//---------------------------------------------------------------------
auto size (void) const { return m_size; }
static auto capacity (void) { return elements; }
private:
std::size_t m_size = 0;
union storage {
storage () {};
~storage () {};
char defer;
value_type data[elements];
} m_store;
};
}

26
test/map/fixed.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "tap.hpp"
#include "map/fixed.hpp"
int main ()
{
cruft::TAP::logger tap;
cruft::map::fixed<4, int,std::string> map;
auto const [pos, success] = map.insert ({ 0, "zero" });
tap.expect (success, "map insertion reports success");
tap.expect_eq (pos->second, "zero", "inserted value is equal");
map.insert ({ 1, "one" });
map.insert ({ 2, "two" });
map.insert ({ 3, "three" });
tap.expect_throw<std::bad_alloc> (
[&] () { map.insert ({ 4, "four" }); },
"over-allocation throws bad_alloc"
);
return tap.status ();
}